summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKristaps Dzonsons <kristaps@bsd.lv>2011-11-20 15:43:14 +0000
committerKristaps Dzonsons <kristaps@bsd.lv>2011-11-20 15:43:14 +0000
commit8ff9b6d7b2b12cfb863578f68ec6572aa3f3d698 (patch)
treeef050ad362793a35f0d3a5b5c0f098cc1388d2d6
parent739d6983d048fe131ded5ec372a95b930b72640d (diff)
downloadmandoc-8ff9b6d7b2b12cfb863578f68ec6572aa3f3d698.tar.gz
Integrate a moderately-patched version of schwarze@'s support for multiple
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.
-rw-r--r--apropos.115
-rw-r--r--apropos.c108
-rw-r--r--apropos_db.c142
-rw-r--r--apropos_db.h2
4 files changed, 186 insertions, 81 deletions
diff --git a/apropos.1 b/apropos.1
index 9de71454..839f22bb 100644
--- a/apropos.1
+++ b/apropos.1
@@ -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
diff --git a/apropos.c b/apropos.c
index d4c09914..e9d3ac97 100644
--- a/apropos.c
+++ b/apropos.c
@@ -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 *);