diff options
-rw-r--r-- | apropos.1 | 15 | ||||
-rw-r--r-- | apropos.c | 108 | ||||
-rw-r--r-- | apropos_db.c | 142 | ||||
-rw-r--r-- | apropos_db.h | 2 |
4 files changed, 186 insertions, 81 deletions
@@ -19,22 +19,28 @@ .Os .Sh NAME .Nm apropos -.Nd search the manual page database +.Nd search manual page databases .Sh SYNOPSIS .Nm +.Op Fl m Ar manpath .Op Fl S Ar arch .Op Fl s Ar section .Ar expression... .Sh DESCRIPTION The .Nm -utility queries a manual page database generated by +utility queries manual page databases generated by .Xr mandocdb 8 , evaluating on .Ar expression -for each file in the database. +for each file in each database. Its arguments are as follows: .Bl -tag -width Ds +.It Fl m Ar manpath +A colon-separated list of paths containing +.Xr mandocdb 8 +databases. +Paths may be relative or absolute. .It Fl S Ar arch Search only for a particular architecture. .It Fl s Ar cat @@ -105,6 +111,9 @@ is evaluated case-insensitively. .Pp By default, .Nm +searches for +.Xr mandocdb 8 +database in the current working directory and parses terms as case-sensitive regular expressions .Pq the Li \&~ operator over manual names and descriptions @@ -1,6 +1,7 @@ /* $Id$ */ /* * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv> + * Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -28,8 +29,18 @@ #include "apropos_db.h" #include "mandoc.h" +/* + * List of paths to be searched for manual databases. + */ +struct manpaths { + int sz; + char **paths; +}; + static int cmp(const void *, const void *); static void list(struct res *, size_t, void *); +static int manpath_add(struct manpaths *, const char *); +static int manpath_parse(struct manpaths *, char *); static void usage(void); static char *progname; @@ -37,23 +48,32 @@ static char *progname; int main(int argc, char *argv[]) { - int ch; + int i, ch, rc; + struct manpaths paths; size_t terms; struct opts opts; struct expr *e; extern int optind; extern char *optarg; - memset(&opts, 0, sizeof(struct opts)); - progname = strrchr(argv[0], '/'); if (progname == NULL) progname = argv[0]; else ++progname; - while (-1 != (ch = getopt(argc, argv, "S:s:"))) + memset(&paths, 0, sizeof(struct manpaths)); + memset(&opts, 0, sizeof(struct opts)); + + e = NULL; + rc = 0; + + while (-1 != (ch = getopt(argc, argv, "m:S:s:"))) switch (ch) { + case ('m'): + if ( ! manpath_parse(&paths, optarg)) + goto out; + break; case ('S'): opts.arch = optarg; break; @@ -62,32 +82,40 @@ main(int argc, char *argv[]) break; default: usage(); - return(EXIT_FAILURE); + goto out; } argc -= optind; argv += optind; - if (0 == argc) - return(EXIT_SUCCESS); + if (0 == argc) { + rc = 1; + goto out; + } + + if (0 == paths.sz && ! manpath_add(&paths, ".")) + goto out; if (NULL == (e = exprcomp(argc, argv, &terms))) { + /* FIXME: be more specific about this. */ fprintf(stderr, "Bad expression\n"); - return(EXIT_FAILURE); + goto out; } - /* - * Configure databases. - * The keyword database is a btree that allows for duplicate - * entries. - * The index database is a recno. - */ + rc = apropos_search + (paths.sz, paths.paths, + &opts, e, terms, NULL, list); + + /* FIXME: report an error based on ch. */ - ch = apropos_search(&opts, e, terms, NULL, list); +out: + for (i = 0; i < paths.sz; i++) + free(paths.paths[i]); + + free(paths.paths); exprfree(e); - if (0 == ch) - fprintf(stderr, "%s: Database error\n", progname); - return(ch ? EXIT_SUCCESS : EXIT_FAILURE); + + return(rc ? EXIT_SUCCESS : EXIT_FAILURE); } /* ARGSUSED */ @@ -118,6 +146,48 @@ static void usage(void) { - fprintf(stderr, "usage: %s [-S arch] [-s section] " + fprintf(stderr, "usage: %s " + "[-m dirs] " + "[-S arch] " + "[-s section] " "expression...\n", progname); } + +/* + * Parse a FULL pathname from a colon-separated list of arrays. + */ +static int +manpath_parse(struct manpaths *dirs, char *path) +{ + char *dir; + + for (dir = strtok(path, ":"); dir; dir = strtok(NULL, ":")) + if ( ! manpath_add(dirs, dir)) + return(0); + + return(1); +} + +/* + * Add a directory to the array. + * Grow the array one-by-one for simplicity's sake. + * Return 0 if the directory is not a real path. + */ +static int +manpath_add(struct manpaths *dirs, const char *dir) +{ + char buf[PATH_MAX]; + char *cp; + + if (NULL == (cp = realpath(dir, buf))) { + fprintf(stderr, "%s: Invalid path\n", dir); + return(0); + } + + dirs->paths = mandoc_realloc + (dirs->paths, + ((size_t)dirs->sz + 1) * sizeof(char *)); + + dirs->paths[dirs->sz++] = mandoc_strdup(cp); + return(1); +} diff --git a/apropos_db.c b/apropos_db.c index 9c17c3c2..317211a3 100644 --- a/apropos_db.c +++ b/apropos_db.c @@ -22,6 +22,7 @@ #include <stdint.h> #include <stdlib.h> #include <string.h> +#include <unistd.h> #ifdef __linux__ # include <db_185.h> @@ -64,6 +65,11 @@ struct type { const char *name; }; +struct rectree { + struct rec *node; /* record array for dir tree */ + int len; /* length of record array */ +}; + static const struct type types[] = { { TYPE_An, "An" }, { TYPE_Ar, "Ar" }, @@ -125,6 +131,9 @@ static void norm_string(const char *, const struct mchars *, char **); static size_t norm_utf8(unsigned int, char[7]); static void recfree(struct rec *); +static int single_search(struct rectree *, const struct opts *, + const struct expr *, size_t terms, + struct mchars *); /* * Open the keyword mandoc-db database. @@ -365,46 +374,92 @@ index_read(const DBT *key, const DBT *val, } /* - * Search the mandocdb database for the expression "expr". + * Search mandocdb databases in argv (size argc) for the expression + * "expr". * Filter out by "opts". * Call "res" with the results, which may be zero. * Return 0 if there was a database error, else return 1. */ int -apropos_search(const struct opts *opts, const struct expr *expr, - size_t terms, void *arg, +apropos_search(int argc, char *argv[], const struct opts *opts, + const struct expr *expr, size_t terms, void *arg, void (*res)(struct res *, size_t, void *)) { - int i, rsz, root, leaf, mlen, rc, ch; + struct rectree tree; + struct mchars *mc; + struct res *ress; + int i, mlen, rc; + + memset(&tree, 0, sizeof(struct rectree)); + + mc = mchars_alloc(); + + for (rc = 1, i = 0; rc && i < argc; i++) { + /* FIXME: ugly warning: we shouldn't get here! */ + if (chdir(argv[i])) + continue; + rc = single_search(&tree, opts, expr, terms, mc); + /* FIXME: warn and continue... ? */ + } + + /* + * Count the matching files + * and feed them to the output handler. + */ + + for (mlen = i = 0; i < tree.len; i++) + if (tree.node[i].matched) + mlen++; + + ress = mandoc_malloc(mlen * sizeof(struct res)); + + for (mlen = i = 0; i < tree.len; i++) + if (tree.node[i].matched) + memcpy(&ress[mlen++], &tree.node[i].res, + sizeof(struct res)); + + (*res)(ress, mlen, arg); + free(ress); + + for (i = 0; i < tree.len; i++) + recfree(&tree.node[i]); + + free(tree.node); + mchars_free(mc); + return(rc); +} + +static int +single_search(struct rectree *tree, const struct opts *opts, + const struct expr *expr, size_t terms, + struct mchars *mc) +{ + int root, leaf, ch; uint64_t mask; DBT key, val; DB *btree, *idx; - struct mchars *mc; char *buf; recno_t rec; struct rec *rs; - struct res *ress; struct rec r; struct db_val *vbuf; - rc = 0; root = -1; leaf = -1; btree = NULL; idx = NULL; - mc = NULL; buf = NULL; - rs = NULL; - rsz = 0; + rs = tree->node; memset(&r, 0, sizeof(struct rec)); - mc = mchars_alloc(); - if (NULL == (btree = btree_open())) - goto out; - if (NULL == (idx = index_open())) - goto out; + return(0); + + if (NULL == (idx = index_open())) { + (*btree->close)(btree); + return(0); + } while (0 == (ch = (*btree->seq)(btree, &key, &val, R_NEXT))) { if (key.size < 2 || sizeof(struct db_val) != val.size) @@ -474,62 +529,33 @@ apropos_search(const struct opts *opts, const struct expr *expr, if (opts->arch && strcasecmp(opts->arch, r.res.arch)) continue; - rs = mandoc_realloc - (rs, (rsz + 1) * sizeof(struct rec)); + tree->node = rs = mandoc_realloc + (rs, (tree->len + 1) * sizeof(struct rec)); - memcpy(&rs[rsz], &r, sizeof(struct rec)); - rs[rsz].matches = mandoc_calloc(terms, sizeof(int)); + memcpy(&rs[tree->len], &r, sizeof(struct rec)); + rs[tree->len].matches = + mandoc_calloc(terms, sizeof(int)); - exprexec(expr, buf, mask, &rs[rsz]); + exprexec(expr, buf, mask, &rs[tree->len]); /* Append to our tree. */ if (leaf >= 0) { if (rec > rs[leaf].res.rec) - rs[leaf].rhs = rsz; + rs[leaf].rhs = tree->len; else - rs[leaf].lhs = rsz; + rs[leaf].lhs = tree->len; } else - root = rsz; + root = tree->len; memset(&r, 0, sizeof(struct rec)); - rsz++; + tree->len++; } - /* - * If we haven't encountered any database errors, then construct - * an array of results and push them to the caller. - */ - - if (1 == ch) { - for (mlen = i = 0; i < rsz; i++) - if (rs[i].matched) - mlen++; - ress = mandoc_malloc(mlen * sizeof(struct res)); - for (mlen = i = 0; i < rsz; i++) - if (rs[i].matched) - memcpy(&ress[mlen++], &rs[i].res, - sizeof(struct res)); - (*res)(ress, mlen, arg); - free(ress); - rc = 1; - } - -out: - for (i = 0; i < rsz; i++) - recfree(&rs[i]); - - recfree(&r); - - if (mc) - mchars_free(mc); - if (btree) - (*btree->close)(btree); - if (idx) - (*idx->close)(idx); + (*btree->close)(btree); + (*idx->close)(idx); free(buf); - free(rs); - return(rc); + return(1 == ch); } static void @@ -570,7 +596,7 @@ exprcomp(int argc, char *argv[], size_t *tt) * Return the root of the expression sequence if alright. */ static struct expr * -exprexpr(int argc, char *argv[], int *pos, int *lvl, size_t *tt) +exprexpr(int argc, char **argv, int *pos, int *lvl, size_t *tt) { struct expr *e, *first, *next; int log; diff --git a/apropos_db.h b/apropos_db.h index 95ac1567..62a392f3 100644 --- a/apropos_db.h +++ b/apropos_db.h @@ -35,7 +35,7 @@ __BEGIN_DECLS struct expr; -int apropos_search(const struct opts *, +int apropos_search(int, char **, const struct opts *, const struct expr *, size_t, void *, void (*)(struct res *, size_t, void *)); struct expr *exprcomp(int, char *[], size_t *); |