version 1.1, 2011/10/06 23:00:54 |
version 1.2, 2011/10/07 13:22:33 |
|
|
|
|
#include "mandoc.h" |
#include "mandoc.h" |
|
|
#define MAXRESULTS 100 |
#define MAXRESULTS 256 |
|
|
#define TYPE_NAME 0x01 |
/* Bit-fields. See mandocdb.8. */ |
#define TYPE_FUNCTION 0x02 |
|
#define TYPE_UTILITY 0x04 |
|
#define TYPE_INCLUDES 0x08 |
|
#define TYPE_VARIABLE 0x10 |
|
#define TYPE_STANDARD 0x20 |
|
#define TYPE_AUTHOR 0x40 |
|
#define TYPE_CONFIG 0x80 |
|
#define TYPE_DESC 0x100 |
|
#define TYPE_XREF 0x200 |
|
#define TYPE_PATH 0x400 |
|
#define TYPE_ENV 0x800 |
|
#define TYPE_ERR 0x1000 |
|
|
|
|
#define TYPE_NAME 0x01 |
|
#define TYPE_FUNCTION 0x02 |
|
#define TYPE_UTILITY 0x04 |
|
#define TYPE_INCLUDES 0x08 |
|
#define TYPE_VARIABLE 0x10 |
|
#define TYPE_STANDARD 0x20 |
|
#define TYPE_AUTHOR 0x40 |
|
#define TYPE_CONFIG 0x80 |
|
#define TYPE_DESC 0x100 |
|
#define TYPE_XREF 0x200 |
|
#define TYPE_PATH 0x400 |
|
#define TYPE_ENV 0x800 |
|
#define TYPE_ERR 0x1000 |
|
|
enum match { |
enum match { |
MATCH_SUBSTR = 0, |
MATCH_SUBSTR = 0, |
MATCH_REGEX, |
MATCH_REGEX, |
|
|
|
|
struct type { |
struct type { |
int mask; |
int mask; |
const char *name; |
const char *name; /* command-line type name */ |
}; |
}; |
|
|
struct rec { |
struct rec { |
char *file; |
char *file; /* file in file-system */ |
char *cat; |
char *cat; /* category (3p, 3, etc.) */ |
char *title; |
char *title; /* title (FOO, etc.) */ |
char *arch; |
char *arch; /* arch (or empty string) */ |
char *desc; |
char *desc; /* description (from Nd) */ |
recno_t rec; |
recno_t rec; /* record in index */ |
}; |
}; |
|
|
struct res { |
struct res { |
|
|
DB *idx; /* index */ |
DB *idx; /* index */ |
const char *dbf; /* database name */ |
const char *dbf; /* database name */ |
const char *idxf; /* index name */ |
const char *idxf; /* index name */ |
void (*err)(const char *); |
|
void (*errx)(const char *, ...); |
|
}; |
}; |
|
|
static const char * const sorts[SORT__MAX] = { |
static const char * const sorts[SORT__MAX] = { |
Line 139 static void buf_alloc(char **, size_t *, size_t); |
|
Line 139 static void buf_alloc(char **, size_t *, size_t); |
|
static void buf_dup(struct mchars *, char **, const char *); |
static void buf_dup(struct mchars *, char **, const char *); |
static void buf_redup(struct mchars *, char **, |
static void buf_redup(struct mchars *, char **, |
size_t *, const char *); |
size_t *, const char *); |
static void error(const char *, ...); |
|
static int sort_cat(const void *, const void *); |
static int sort_cat(const void *, const void *); |
static int sort_title(const void *, const void *); |
static int sort_title(const void *, const void *); |
static void state_destroy(struct state *); |
static int state_getrecord(struct state *, |
static int state_getrecord(struct state *, recno_t, struct rec *); |
recno_t, struct rec *); |
static int state_init(struct state *, |
|
const char *, const char *, |
|
void (*err)(const char *), |
|
void (*errx)(const char *, ...)); |
|
static void state_output(const struct res *, int); |
static void state_output(const struct res *, int); |
static void state_search(struct state *, |
static void state_search(struct state *, |
const struct opts *, char *); |
const struct opts *, char *); |
|
|
static void usage(void); |
static void usage(void); |
|
|
static const char *progname; |
static char *progname; |
|
|
int |
int |
main(int argc, char *argv[]) |
main(int argc, char *argv[]) |
{ |
{ |
int ch, i; |
BTREEINFO info; |
|
int ch, i, rc; |
const char *dbf, *idxf; |
const char *dbf, *idxf; |
struct state state; |
struct state state; |
char *q, *v; |
char *q, *v; |
Line 168 main(int argc, char *argv[]) |
|
Line 163 main(int argc, char *argv[]) |
|
extern char *optarg; |
extern char *optarg; |
|
|
memset(&opts, 0, sizeof(struct opts)); |
memset(&opts, 0, sizeof(struct opts)); |
|
memset(&state, 0, sizeof(struct state)); |
|
|
dbf = "mandoc.db"; |
dbf = "mandoc.db"; |
idxf = "mandoc.index"; |
idxf = "mandoc.index"; |
q = NULL; |
q = NULL; |
|
rc = EXIT_FAILURE; |
|
|
progname = strrchr(argv[0], '/'); |
progname = strrchr(argv[0], '/'); |
if (progname == NULL) |
if (progname == NULL) |
Line 209 main(int argc, char *argv[]) |
|
Line 206 main(int argc, char *argv[]) |
|
if (i < SORT__MAX) |
if (i < SORT__MAX) |
break; |
break; |
|
|
error("%s: Bad sort\n", optarg); |
fprintf(stderr, "%s: Bad sort\n", optarg); |
return(EXIT_FAILURE); |
return(EXIT_FAILURE); |
case ('t'): |
case ('t'): |
while (NULL != (v = strsep(&optarg, ","))) { |
while (NULL != (v = strsep(&optarg, ","))) { |
Line 227 main(int argc, char *argv[]) |
|
Line 224 main(int argc, char *argv[]) |
|
if (NULL == v) |
if (NULL == v) |
break; |
break; |
|
|
error("%s: Bad type\n", v); |
fprintf(stderr, "%s: Bad type\n", v); |
return(EXIT_FAILURE); |
return(EXIT_FAILURE); |
default: |
default: |
usage(); |
usage(); |
Line 239 main(int argc, char *argv[]) |
|
Line 236 main(int argc, char *argv[]) |
|
|
|
if (0 == argc || '\0' == **argv) { |
if (0 == argc || '\0' == **argv) { |
usage(); |
usage(); |
return(EXIT_FAILURE); |
goto out; |
} else |
} else |
q = *argv; |
q = *argv; |
|
|
if (0 == opts.types) |
if (0 == opts.types) |
opts.types = TYPE_NAME | TYPE_DESC; |
opts.types = TYPE_NAME | TYPE_DESC; |
|
|
if ( ! state_init(&state, dbf, idxf, perror, error)) { |
/* |
state_destroy(&state); |
* Configure databases. |
return(EXIT_FAILURE); |
* The keyword database is a btree that allows for duplicate |
|
* entries. |
|
* The index database is a recno. |
|
*/ |
|
|
|
memset(&info, 0, sizeof(BTREEINFO)); |
|
info.flags = R_DUP; |
|
|
|
state.db = dbopen(dbf, O_RDONLY, 0, DB_BTREE, &info); |
|
if (NULL == state.db) { |
|
perror(dbf); |
|
goto out; |
} |
} |
|
|
|
state.idx = dbopen(idxf, O_RDONLY, 0, DB_RECNO, NULL); |
|
if (NULL == state.idx) { |
|
perror(idxf); |
|
goto out; |
|
} |
|
|
|
/* Main search function. */ |
|
|
state_search(&state, &opts, q); |
state_search(&state, &opts, q); |
state_destroy(&state); |
|
|
|
return(EXIT_SUCCESS); |
rc = EXIT_SUCCESS; |
|
out: |
|
if (state.db) |
|
(*state.db->close)(state.db); |
|
if (state.idx) |
|
(*state.idx->close)(state.idx); |
|
|
|
return(rc); |
} |
} |
|
|
static void |
static void |
Line 279 state_search(struct state *p, const struct opts *opts, |
|
Line 301 state_search(struct state *p, const struct opts *opts, |
|
ch = 0; |
ch = 0; |
regp = NULL; |
regp = NULL; |
|
|
|
/* |
|
* Configure how we scan through results to see if we match: |
|
* whether by regexp or exact matches. |
|
*/ |
|
|
switch (opts->match) { |
switch (opts->match) { |
case (MATCH_REGEX): |
case (MATCH_REGEX): |
rflags = REG_EXTENDED | REG_NOSUB | |
rflags = REG_EXTENDED | REG_NOSUB | |
(opts->insens ? REG_ICASE : 0); |
(opts->insens ? REG_ICASE : 0); |
|
|
if (0 != regcomp(®, q, rflags)) { |
if (0 != regcomp(®, q, rflags)) { |
error("%s: Bad pattern\n", q); |
fprintf(stderr, "%s: Bad pattern\n", q); |
return; |
return; |
} |
} |
|
|
Line 328 state_search(struct state *p, const struct opts *opts, |
|
Line 355 state_search(struct state *p, const struct opts *opts, |
|
*/ |
*/ |
|
|
if (key.size < 2 || 8 != val.size) { |
if (key.size < 2 || 8 != val.size) { |
error("%s: Corrupt database\n", p->dbf); |
fprintf(stderr, "%s: Corrupt database\n", p->dbf); |
exit(EXIT_FAILURE); |
exit(EXIT_FAILURE); |
} |
} |
|
|
|
|
exit(EXIT_FAILURE); |
exit(EXIT_FAILURE); |
} |
} |
|
|
|
/* |
|
* Sort our results. |
|
* We do this post-scan (instead of an in-line sort) because |
|
* it's more or less the same in terms of run-time. Assuming we |
|
* sort in-line with a tree versus post: |
|
* |
|
* In-place: n * O(lg n) |
|
* After: n + O(n lg n) |
|
* |
|
* Whatever. This also buys us simplicity. |
|
*/ |
|
|
switch (opts->sort) { |
switch (opts->sort) { |
case (SORT_CAT): |
case (SORT_CAT): |
qsort(res, len, sizeof(struct res), sort_cat); |
qsort(res, len, sizeof(struct res), sort_cat); |
Line 544 buf_redup(struct mchars *mc, char **buf, |
|
Line 583 buf_redup(struct mchars *mc, char **buf, |
|
} |
} |
|
|
static void |
static void |
error(const char *fmt, ...) |
|
{ |
|
va_list ap; |
|
|
|
va_start(ap, fmt); |
|
vfprintf(stderr, fmt, ap); |
|
va_end(ap); |
|
} |
|
|
|
static void |
|
state_output(const struct res *res, int sz) |
state_output(const struct res *res, int sz) |
{ |
{ |
int i; |
int i; |
|
|
} |
} |
|
|
static int |
static int |
state_init(struct state *p, |
|
const char *dbf, const char *idxf, |
|
void (*err)(const char *), |
|
void (*errx)(const char *, ...)) |
|
{ |
|
BTREEINFO info; |
|
|
|
memset(p, 0, sizeof(struct state)); |
|
memset(&info, 0, sizeof(BTREEINFO)); |
|
|
|
info.flags = R_DUP; |
|
|
|
p->dbf = dbf; |
|
p->idxf = idxf; |
|
p->err = err; |
|
|
|
p->db = dbopen(p->dbf, O_RDONLY, 0, DB_BTREE, &info); |
|
if (NULL == p->db) { |
|
(*err)(p->dbf); |
|
return(0); |
|
} |
|
|
|
p->idx = dbopen(p->idxf, O_RDONLY, 0, DB_RECNO, NULL); |
|
if (NULL == p->idx) { |
|
(*err)(p->idxf); |
|
return(0); |
|
} |
|
|
|
return(1); |
|
} |
|
|
|
static void |
|
state_destroy(struct state *p) |
|
{ |
|
|
|
if (p->db) |
|
(*p->db->close)(p->db); |
|
if (p->idx) |
|
(*p->idx->close)(p->idx); |
|
} |
|
|
|
static int |
|
state_getrecord(struct state *p, recno_t rec, struct rec *rp) |
state_getrecord(struct state *p, recno_t rec, struct rec *rp) |
{ |
{ |
DBT key, val; |
DBT key, val; |
Line 633 state_getrecord(struct state *p, recno_t rec, struct r |
|
Line 620 state_getrecord(struct state *p, recno_t rec, struct r |
|
|
|
rc = (*p->idx->get)(p->idx, &key, &val, 0); |
rc = (*p->idx->get)(p->idx, &key, &val, 0); |
if (rc < 0) { |
if (rc < 0) { |
(*p->err)(p->idxf); |
perror(p->idxf); |
return(0); |
return(0); |
} else if (rc > 0) { |
} else if (rc > 0) |
(*p->errx)("%s: Corrupt index\n", p->idxf); |
goto err; |
return(0); |
|
} |
|
|
|
rp->file = (char *)val.data; |
rp->file = (char *)val.data; |
if ((sz = strlen(rp->file) + 1) >= val.size) { |
if ((sz = strlen(rp->file) + 1) >= val.size) |
(*p->errx)("%s: Corrupt index\n", p->idxf); |
goto err; |
return(0); |
|
} |
|
|
|
rp->cat = (char *)val.data + (int)sz; |
rp->cat = (char *)val.data + (int)sz; |
if ((sz += strlen(rp->cat) + 1) >= val.size) { |
if ((sz += strlen(rp->cat) + 1) >= val.size) |
(*p->errx)("%s: Corrupt index\n", p->idxf); |
goto err; |
return(0); |
|
} |
|
|
|
rp->title = (char *)val.data + (int)sz; |
rp->title = (char *)val.data + (int)sz; |
if ((sz += strlen(rp->title) + 1) >= val.size) { |
if ((sz += strlen(rp->title) + 1) >= val.size) |
(*p->errx)("%s: Corrupt index\n", p->idxf); |
goto err; |
return(0); |
|
} |
|
|
|
rp->arch = (char *)val.data + (int)sz; |
rp->arch = (char *)val.data + (int)sz; |
if ((sz += strlen(rp->arch) + 1) >= val.size) { |
if ((sz += strlen(rp->arch) + 1) >= val.size) |
(*p->errx)("%s: Corrupt index\n", p->idxf); |
goto err; |
return(0); |
|
} |
|
|
|
rp->desc = (char *)val.data + (int)sz; |
rp->desc = (char *)val.data + (int)sz; |
rp->rec = rec; |
rp->rec = rec; |
return(1); |
return(1); |
|
err: |
|
fprintf(stderr, "%s: Corrupt index\n", p->idxf); |
|
return(0); |
} |
} |
|
|
static int |
static int |