]> git.cameronkatri.com Git - mandoc.git/commitdiff
Integrate a moderately-patched version of schwarze@'s support for multiple
authorKristaps Dzonsons <kristaps@bsd.lv>
Sun, 20 Nov 2011 15:43:14 +0000 (15:43 +0000)
committerKristaps Dzonsons <kristaps@bsd.lv>
Sun, 20 Nov 2011 15:43:14 +0000 (15:43 +0000)
directories containing mandocdb(8) databases.  Some changes follow:

 (1) don't support -M yet;
 (2) fall back to cwd if no prior manpath has been specified;
 (3) resolve manpages using realpath() to prevent consecutive chdir()'s
     over relative paths;
 (4) note where further error-reporting is required;
 (5) fix leaking memory on exit in several cases.

apropos.1
apropos.c
apropos_db.c
apropos_db.h

index 850d06a8469917c649aaf2f15bbb060a116b471c..027ea3f7bfab09bf1d0b9645cdc704c6aba981cd 100644 (file)
--- a/apropos.1
+++ b/apropos.1
@@ -1,4 +1,4 @@
-.\"    $Id: apropos.1,v 1.4 2011/11/18 07:02:19 kristaps Exp $
+.\"    $Id: apropos.1,v 1.5 2011/11/20 15:43:14 kristaps Exp $
 .\"
 .\" Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
 .\"
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: November 18 2011 $
+.Dd $Mdocdate: November 20 2011 $
 .Dt APROPOS 1
 .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
index 8268b03586e32236d729d2dc154e7dd64f5fe29d..ec5a5060fbaa49a99a3ea28394677ba58838f3f2 100644 (file)
--- a/apropos.c
+++ b/apropos.c
@@ -1,6 +1,7 @@
-/*     $Id: apropos.c,v 1.14 2011/11/18 07:02:19 kristaps Exp $ */
+/*     $Id: apropos.c,v 1.15 2011/11/20 15:43:14 kristaps Exp $ */
 /*
  * 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
 #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);
+}
index 798ada4005757f4c40325e4eabf264fd40b098fa..fdca2f80a745d75cf63b80efc443bc3daf568654 100644 (file)
@@ -1,4 +1,4 @@
-/*     $Id: apropos_db.c,v 1.7 2011/11/20 12:46:53 kristaps Exp $ */
+/*     $Id: apropos_db.c,v 1.8 2011/11/20 15:43:14 kristaps Exp $ */
 /*
  * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
  * Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org>
@@ -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;
index 9151c959b358dea68a85064db23fb3091e173833..ce6d96f9e55ad1f6b3df25b746c436a5d18947ac 100644 (file)
@@ -1,4 +1,4 @@
-/*     $Id: apropos_db.h,v 1.5 2011/11/18 07:02:19 kristaps Exp $ */
+/*     $Id: apropos_db.h,v 1.6 2011/11/20 15:43:14 kristaps Exp $ */
 /*
  * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
  *
@@ -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 *);