version 1.14, 2011/07/01 10:46:32 |
version 1.19, 2011/07/11 21:56:06 |
|
|
#define TYPE_DESC 0x100 |
#define TYPE_DESC 0x100 |
#define TYPE_XREF 0x200 |
#define TYPE_XREF 0x200 |
#define TYPE_PATH 0x400 |
#define TYPE_PATH 0x400 |
|
#define TYPE_ENV 0x800 |
|
#define TYPE_ERR 0x1000 |
|
|
/* Buffer for storing growable data. */ |
/* Buffer for storing growable data. */ |
|
|
|
|
size_t size; |
size_t size; |
}; |
}; |
|
|
|
/* Operation we're going to perform. */ |
|
|
|
enum op { |
|
OP_NEW = 0, /* new database */ |
|
OP_UPDATE, /* update entries in existing database */ |
|
OP_DELETE /* delete entries from existing database */ |
|
}; |
|
|
#define MAN_ARGS DB *hash, \ |
#define MAN_ARGS DB *hash, \ |
struct buf *buf, \ |
struct buf *buf, \ |
struct buf *dbuf, \ |
struct buf *dbuf, \ |
Line 82 static void buf_appendb(struct buf *, |
|
Line 92 static void buf_appendb(struct buf *, |
|
const void *, size_t); |
const void *, size_t); |
static void dbt_put(DB *, const char *, DBT *, DBT *); |
static void dbt_put(DB *, const char *, DBT *, DBT *); |
static void hash_put(DB *, const struct buf *, int); |
static void hash_put(DB *, const struct buf *, int); |
|
static void hash_reset(DB **); |
|
static void op_delete(const char *, int, DB *, |
|
const char *, DB *, const char *); |
static int pman_node(MAN_ARGS); |
static int pman_node(MAN_ARGS); |
static void pmdoc_node(MDOC_ARGS); |
static void pmdoc_node(MDOC_ARGS); |
static void pmdoc_An(MDOC_ARGS); |
static void pmdoc_An(MDOC_ARGS); |
static void pmdoc_Cd(MDOC_ARGS); |
static void pmdoc_Cd(MDOC_ARGS); |
|
static void pmdoc_Er(MDOC_ARGS); |
|
static void pmdoc_Ev(MDOC_ARGS); |
static void pmdoc_Fd(MDOC_ARGS); |
static void pmdoc_Fd(MDOC_ARGS); |
static void pmdoc_In(MDOC_ARGS); |
static void pmdoc_In(MDOC_ARGS); |
static void pmdoc_Fn(MDOC_ARGS); |
static void pmdoc_Fn(MDOC_ARGS); |
Line 121 static const pmdoc_nf mdocs[MDOC_MAX] = { |
|
Line 136 static const pmdoc_nf mdocs[MDOC_MAX] = { |
|
pmdoc_Cd, /* Cd */ |
pmdoc_Cd, /* Cd */ |
NULL, /* Cm */ |
NULL, /* Cm */ |
NULL, /* Dv */ |
NULL, /* Dv */ |
NULL, /* Er */ |
pmdoc_Er, /* Er */ |
NULL, /* Ev */ |
pmdoc_Ev, /* Ev */ |
NULL, /* Ex */ |
NULL, /* Ex */ |
NULL, /* Fa */ |
NULL, /* Fa */ |
pmdoc_Fd, /* Fd */ |
pmdoc_Fd, /* Fd */ |
Line 233 main(int argc, char *argv[]) |
|
Line 248 main(int argc, char *argv[]) |
|
struct mparse *mp; /* parse sequence */ |
struct mparse *mp; /* parse sequence */ |
struct mdoc *mdoc; /* resulting mdoc */ |
struct mdoc *mdoc; /* resulting mdoc */ |
struct man *man; /* resulting man */ |
struct man *man; /* resulting man */ |
|
enum op op; |
char *fn; /* current file being parsed */ |
char *fn; /* current file being parsed */ |
const char *msec, /* manual section */ |
const char *msec, /* manual section */ |
*mtitle, /* manual title */ |
*mtitle, /* manual title */ |
*arch, /* manual architecture */ |
*arch, /* manual architecture */ |
*dir; /* result dir (default: cwd) */ |
*dir; /* result dir (default: cwd) */ |
char ibuf[MAXPATHLEN], /* index fname */ |
char ibuf[MAXPATHLEN], /* index fname */ |
ibbuf[MAXPATHLEN], /* index backup fname */ |
|
fbuf[MAXPATHLEN], /* btree fname */ |
fbuf[MAXPATHLEN], /* btree fname */ |
fbbuf[MAXPATHLEN], /* btree backup fname */ |
|
vbuf[8]; /* stringified record number */ |
vbuf[8]; /* stringified record number */ |
int ch, seq, verb; |
int ch, seq, verb, i; |
DB *idx, /* index database */ |
DB *idx, /* index database */ |
*db, /* keyword database */ |
*db, /* keyword database */ |
*hash; /* temporary keyword hashtable */ |
*hash; /* temporary keyword hashtable */ |
DBT key, val; |
DBT key, val; |
|
enum mandocerr ec; |
size_t sv; |
size_t sv; |
BTREEINFO info; /* btree configuration */ |
BTREEINFO info; /* btree configuration */ |
recno_t rec; /* current record number */ |
recno_t rec, /* current record number */ |
|
maxrec; |
|
recno_t *recs; |
|
size_t recsz; |
struct buf buf, /* keyword buffer */ |
struct buf buf, /* keyword buffer */ |
dbuf; /* description buffer */ |
dbuf; /* description buffer */ |
extern int optind; |
extern int optind; |
Line 264 main(int argc, char *argv[]) |
|
Line 282 main(int argc, char *argv[]) |
|
|
|
dir = ""; |
dir = ""; |
verb = 0; |
verb = 0; |
|
db = idx = NULL; |
|
mp = NULL; |
|
hash = NULL; |
|
recs = NULL; |
|
recsz = 0; |
|
op = OP_NEW; |
|
ec = MANDOCLEVEL_SYSERR; |
|
|
while (-1 != (ch = getopt(argc, argv, "d:v"))) |
memset(&buf, 0, sizeof(struct buf)); |
|
memset(&dbuf, 0, sizeof(struct buf)); |
|
|
|
while (-1 != (ch = getopt(argc, argv, "d:ruv"))) |
switch (ch) { |
switch (ch) { |
case ('d'): |
case ('d'): |
dir = optarg; |
dir = optarg; |
break; |
break; |
|
case ('r'): |
|
op = OP_DELETE; |
|
break; |
|
case ('u'): |
|
op = OP_UPDATE; |
|
break; |
case ('v'): |
case ('v'): |
verb++; |
verb++; |
break; |
break; |
Line 281 main(int argc, char *argv[]) |
|
Line 315 main(int argc, char *argv[]) |
|
argc -= optind; |
argc -= optind; |
argv += optind; |
argv += optind; |
|
|
/* |
|
* Set up temporary file-names into which we're going to write |
|
* all of our data (both for the index and database). These |
|
* will be securely renamed to the real file-names after we've |
|
* written all of our data. |
|
*/ |
|
|
|
ibuf[0] = ibuf[MAXPATHLEN - 2] = |
ibuf[0] = ibuf[MAXPATHLEN - 2] = |
ibbuf[0] = ibbuf[MAXPATHLEN - 2] = |
fbuf[0] = fbuf[MAXPATHLEN - 2] = '\0'; |
fbuf[0] = fbuf[MAXPATHLEN - 2] = |
|
fbbuf[0] = fbbuf[MAXPATHLEN - 2] = '\0'; |
|
|
|
strlcat(fbuf, dir, MAXPATHLEN); |
strlcat(fbuf, dir, MAXPATHLEN); |
strlcat(fbuf, MANDOC_DB, MAXPATHLEN); |
strlcat(fbuf, MANDOC_DB, MAXPATHLEN); |
|
|
strlcat(fbbuf, fbuf, MAXPATHLEN); |
|
strlcat(fbbuf, "~", MAXPATHLEN); |
|
|
|
strlcat(ibuf, dir, MAXPATHLEN); |
strlcat(ibuf, dir, MAXPATHLEN); |
strlcat(ibuf, MANDOC_IDX, MAXPATHLEN); |
strlcat(ibuf, MANDOC_IDX, MAXPATHLEN); |
|
|
strlcat(ibbuf, ibuf, MAXPATHLEN); |
|
strlcat(ibbuf, "~", MAXPATHLEN); |
|
|
|
if ('\0' != fbuf[MAXPATHLEN - 2] || |
if ('\0' != fbuf[MAXPATHLEN - 2] || |
'\0' != fbbuf[MAXPATHLEN - 2] || |
'\0' != ibuf[MAXPATHLEN - 2]) { |
'\0' != ibuf[MAXPATHLEN - 2] || |
|
'\0' != ibbuf[MAXPATHLEN - 2]) { |
|
fprintf(stderr, "%s: Path too long\n", dir); |
fprintf(stderr, "%s: Path too long\n", dir); |
exit((int)MANDOCLEVEL_SYSERR); |
goto out; |
} |
} |
|
|
/* |
/* |
* For the keyword database, open a BTREE database that allows |
* For the keyword database, open a BTREE database that allows |
* duplicates. |
* duplicates. |
* For the index database, use a standard RECNO database type. |
* For the index database, use a standard RECNO database type. |
|
* Truncate the database if we're creating a new one. |
*/ |
*/ |
|
|
memset(&info, 0, sizeof(BTREEINFO)); |
memset(&info, 0, sizeof(BTREEINFO)); |
info.flags = R_DUP; |
info.flags = R_DUP; |
db = dbopen(fbbuf, MANDOC_FLAGS, 0644, DB_BTREE, &info); |
|
|
|
|
if (OP_NEW == op) { |
|
db = dbopen(fbuf, MANDOC_FLAGS, 0644, DB_BTREE, &info); |
|
idx = dbopen(ibuf, MANDOC_FLAGS, 0644, DB_RECNO, NULL); |
|
} else { |
|
db = dbopen(fbuf, O_CREAT|O_RDWR, 0644, DB_BTREE, &info); |
|
idx = dbopen(ibuf, O_CREAT|O_RDWR, 0644, DB_RECNO, NULL); |
|
} |
|
|
if (NULL == db) { |
if (NULL == db) { |
perror(fbbuf); |
perror(fbuf); |
exit((int)MANDOCLEVEL_SYSERR); |
goto out; |
|
} else if (NULL == db) { |
|
perror(ibuf); |
|
goto out; |
} |
} |
|
|
idx = dbopen(ibbuf, MANDOC_FLAGS, 0644, DB_RECNO, NULL); |
/* |
|
* If we're going to delete or update a database, remove the |
|
* entries now. This doesn't actually remove them; it only sets |
|
* their record value lengths to zero. |
|
*/ |
|
|
if (NULL == db) { |
if (OP_DELETE == op || OP_UPDATE == op) |
perror(ibbuf); |
for (i = 0; i < argc; i++) |
(*db->close)(db); |
op_delete(argv[i], verb, idx, ibuf, db, fbuf); |
exit((int)MANDOCLEVEL_SYSERR); |
|
|
if (OP_DELETE == op) { |
|
ec = MANDOCLEVEL_OK; |
|
goto out; |
} |
} |
|
|
/* |
/* |
|
* Compile a list of all available "empty" records to use. This |
|
* keeps the size of the database small. |
|
*/ |
|
|
|
if (OP_UPDATE == op) { |
|
i = 0; |
|
seq = R_FIRST; |
|
while (0 == (ch = (*idx->seq)(idx, &key, &val, seq))) { |
|
seq = R_NEXT; |
|
maxrec = *(recno_t *)key.data; |
|
if (val.size > 0) |
|
continue; |
|
if ((size_t)i >= recsz) { |
|
recsz += 1024; |
|
recs = mandoc_realloc |
|
(recs, recsz * sizeof(recno_t)); |
|
} |
|
recs[i++] = maxrec; |
|
} |
|
if (ch < 0) { |
|
perror(ibuf); |
|
exit((int)MANDOCLEVEL_SYSERR); |
|
} |
|
recsz = (size_t)i; |
|
maxrec++; |
|
assert(recsz < maxrec); |
|
} else |
|
maxrec = 0; |
|
|
|
/* |
|
* Add records to the database. |
* Try parsing each manual given on the command line. |
* Try parsing each manual given on the command line. |
* If we fail, then emit an error and keep on going. |
* If we fail, then emit an error and keep on going. |
* Take resulting trees and push them down into the database code. |
* Take resulting trees and push them down into the database code. |
Line 345 main(int argc, char *argv[]) |
|
Line 411 main(int argc, char *argv[]) |
|
|
|
mp = mparse_alloc(MPARSE_AUTO, MANDOCLEVEL_FATAL, NULL, NULL); |
mp = mparse_alloc(MPARSE_AUTO, MANDOCLEVEL_FATAL, NULL, NULL); |
|
|
rec = 1; |
|
hash = NULL; |
|
|
|
memset(&buf, 0, sizeof(struct buf)); |
|
memset(&dbuf, 0, sizeof(struct buf)); |
|
|
|
buf.size = dbuf.size = MANDOC_BUFSZ; |
buf.size = dbuf.size = MANDOC_BUFSZ; |
|
|
buf.cp = mandoc_malloc(buf.size); |
buf.cp = mandoc_malloc(buf.size); |
dbuf.cp = mandoc_malloc(dbuf.size); |
dbuf.cp = mandoc_malloc(dbuf.size); |
|
|
while (NULL != (fn = *argv++)) { |
for (rec = 0, i = 0; i < argc; i++) { |
|
fn = argv[i]; |
|
if (OP_UPDATE == op) { |
|
if (recsz > 0) { |
|
--recsz; |
|
rec = recs[(int)recsz]; |
|
} else if (maxrec > 0) { |
|
rec = maxrec; |
|
maxrec = 0; |
|
} else |
|
rec++; |
|
} else |
|
rec++; |
|
|
mparse_reset(mp); |
mparse_reset(mp); |
|
hash_reset(&hash); |
|
|
/* Initialise the in-memory hash of keywords. */ |
|
|
|
if (hash) |
|
(*hash->close)(hash); |
|
|
|
hash = dbopen(NULL, MANDOC_FLAGS, 0644, DB_HASH, NULL); |
|
|
|
if (NULL == hash) { |
|
perror("hash"); |
|
exit((int)MANDOCLEVEL_SYSERR); |
|
} |
|
|
|
/* Parse and get (non-empty) AST. */ |
|
|
|
if (mparse_readfd(mp, -1, fn) >= MANDOCLEVEL_FATAL) { |
if (mparse_readfd(mp, -1, fn) >= MANDOCLEVEL_FATAL) { |
fprintf(stderr, "%s: Parse failure\n", fn); |
fprintf(stderr, "%s: Parse failure\n", fn); |
continue; |
continue; |
} |
} |
|
|
mparse_result(mp, &mdoc, &man); |
mparse_result(mp, &mdoc, &man); |
|
|
if (NULL == mdoc && NULL == man) |
if (NULL == mdoc && NULL == man) |
continue; |
continue; |
|
|
msec = NULL != mdoc ? |
msec = NULL != mdoc ? |
mdoc_meta(mdoc)->msec : |
mdoc_meta(mdoc)->msec : man_meta(man)->msec; |
man_meta(man)->msec; |
|
mtitle = NULL != mdoc ? |
mtitle = NULL != mdoc ? |
mdoc_meta(mdoc)->title : |
mdoc_meta(mdoc)->title : man_meta(man)->title; |
man_meta(man)->title; |
|
arch = NULL != mdoc ? mdoc_meta(mdoc)->arch : NULL; |
arch = NULL != mdoc ? mdoc_meta(mdoc)->arch : NULL; |
|
|
|
if (NULL == arch) |
|
arch = ""; |
|
|
/* |
/* |
* The index record value consists of a nil-terminated |
* The index record value consists of a nil-terminated |
* filename, a nil-terminated manual section, and a |
* filename, a nil-terminated manual section, and a |
Line 403 main(int argc, char *argv[]) |
|
Line 462 main(int argc, char *argv[]) |
|
buf_appendb(&dbuf, fn, strlen(fn) + 1); |
buf_appendb(&dbuf, fn, strlen(fn) + 1); |
buf_appendb(&dbuf, msec, strlen(msec) + 1); |
buf_appendb(&dbuf, msec, strlen(msec) + 1); |
buf_appendb(&dbuf, mtitle, strlen(mtitle) + 1); |
buf_appendb(&dbuf, mtitle, strlen(mtitle) + 1); |
buf_appendb(&dbuf, arch ? arch : "", |
buf_appendb(&dbuf, arch, strlen(arch) + 1); |
arch ? strlen(arch) + 1 : 1); |
|
|
|
sv = dbuf.len; |
sv = dbuf.len; |
|
|
Line 433 main(int argc, char *argv[]) |
|
Line 491 main(int argc, char *argv[]) |
|
val.data = vbuf; |
val.data = vbuf; |
|
|
if (verb > 1) |
if (verb > 1) |
printf("%s: Keyword %s (%zu): 0x%x\n", |
printf("Indexed: %s, %s, 0x%x\n", |
fn, (char *)key.data, key.size, |
fn, (char *)key.data, |
*(int *)val.data); |
*(int *)val.data); |
|
dbt_put(db, fbuf, &key, &val); |
dbt_put(db, fbbuf, &key, &val); |
|
|
|
} |
} |
|
|
if (ch < 0) { |
if (ch < 0) { |
perror("hash"); |
perror("hash"); |
exit((int)MANDOCLEVEL_SYSERR); |
exit((int)MANDOCLEVEL_SYSERR); |
Line 461 main(int argc, char *argv[]) |
|
Line 516 main(int argc, char *argv[]) |
|
val.size = dbuf.len; |
val.size = dbuf.len; |
|
|
if (verb > 0) |
if (verb > 0) |
printf("%s: Indexed\n", fn); |
printf("Indexed: %s\n", fn); |
|
|
dbt_put(idx, ibbuf, &key, &val); |
dbt_put(idx, ibuf, &key, &val); |
rec++; |
|
} |
} |
|
|
(*db->close)(db); |
ec = MANDOCLEVEL_OK; |
(*idx->close)(idx); |
out: |
|
if (db) |
|
(*db->close)(db); |
|
if (idx) |
|
(*idx->close)(idx); |
if (hash) |
if (hash) |
(*hash->close)(hash); |
(*hash->close)(hash); |
|
if (mp) |
|
mparse_free(mp); |
|
|
mparse_free(mp); |
|
|
|
free(buf.cp); |
free(buf.cp); |
free(dbuf.cp); |
free(dbuf.cp); |
|
free(recs); |
|
|
/* Atomically replace the file with our temporary one. */ |
return((int)ec); |
|
} |
|
|
if (-1 == rename(fbbuf, fbuf)) |
static void |
perror(fbuf); |
op_delete(const char *fn, int verb, DB *idx, |
if (-1 == rename(ibbuf, ibuf)) |
const char *ibuf, DB *db, const char *fbuf) |
perror(fbuf); |
{ |
|
int ch; |
|
DBT key, val; |
|
recno_t rec; |
|
unsigned int seq, sseq; |
|
|
return((int)MANDOCLEVEL_OK); |
seq = R_FIRST; |
|
while (0 == (ch = (*idx->seq)(idx, &key, &val, seq))) { |
|
seq = R_NEXT; |
|
if (0 == val.size) |
|
continue; |
|
if (strcmp((char *)val.data, fn)) |
|
continue; |
|
|
|
rec = *(recno_t *)key.data; |
|
|
|
sseq = R_FIRST; |
|
while (0 == (ch = (*db->seq)(db, &key, &val, sseq))) { |
|
sseq = R_NEXT; |
|
assert(8 == val.size); |
|
if (rec != *(recno_t *)(val.data + 4)) |
|
continue; |
|
if (verb > 1) |
|
printf("Deleted: %s, %s\n", |
|
fn, (char *)key.data); |
|
ch = (*db->del)(db, &key, R_CURSOR); |
|
if (ch < 0) |
|
break; |
|
} |
|
if (ch < 0) { |
|
perror(fbuf); |
|
exit((int)MANDOCLEVEL_SYSERR); |
|
} |
|
|
|
val.size = 0; |
|
if (verb) |
|
printf("Deleted: %s\n", fn); |
|
ch = (*idx->put) |
|
(idx, &key, &val, R_CURSOR); |
|
if (ch < 0) |
|
break; |
|
} |
|
if (ch < 0) { |
|
perror(ibuf); |
|
exit((int)MANDOCLEVEL_SYSERR); |
|
} |
} |
} |
|
|
/* |
/* |
Line 565 pmdoc_An(MDOC_ARGS) |
|
Line 667 pmdoc_An(MDOC_ARGS) |
|
hash_put(hash, buf, TYPE_AUTHOR); |
hash_put(hash, buf, TYPE_AUTHOR); |
} |
} |
|
|
|
static void |
|
hash_reset(DB **db) |
|
{ |
|
DB *hash; |
|
|
|
if (NULL != (hash = *db)) |
|
(*hash->close)(hash); |
|
|
|
*db = dbopen(NULL, MANDOC_FLAGS, 0644, DB_HASH, NULL); |
|
if (NULL == *db) { |
|
perror("hash"); |
|
exit((int)MANDOCLEVEL_SYSERR); |
|
} |
|
} |
|
|
/* ARGSUSED */ |
/* ARGSUSED */ |
static void |
static void |
pmdoc_Fd(MDOC_ARGS) |
pmdoc_Fd(MDOC_ARGS) |
Line 764 pmdoc_Nd(MDOC_ARGS) |
|
Line 881 pmdoc_Nd(MDOC_ARGS) |
|
|
|
/* ARGSUSED */ |
/* ARGSUSED */ |
static void |
static void |
|
pmdoc_Er(MDOC_ARGS) |
|
{ |
|
|
|
if (SEC_ERRORS != n->sec) |
|
return; |
|
|
|
buf_appendmdoc(buf, n->child, 0); |
|
hash_put(hash, buf, TYPE_ERR); |
|
} |
|
|
|
/* ARGSUSED */ |
|
static void |
|
pmdoc_Ev(MDOC_ARGS) |
|
{ |
|
|
|
if (SEC_ENVIRONMENT != n->sec) |
|
return; |
|
|
|
buf_appendmdoc(buf, n->child, 0); |
|
hash_put(hash, buf, TYPE_ENV); |
|
} |
|
|
|
/* ARGSUSED */ |
|
static void |
pmdoc_Pa(MDOC_ARGS) |
pmdoc_Pa(MDOC_ARGS) |
{ |
{ |
|
|
Line 933 pman_node(MAN_ARGS) |
|
Line 1074 pman_node(MAN_ARGS) |
|
start++; |
start++; |
} |
} |
|
|
|
buf->len = 0; |
|
|
if (sv == start) { |
if (sv == start) { |
buf->len = 0; |
|
buf_append(buf, start); |
buf_append(buf, start); |
return(1); |
return(1); |
} |
} |
Line 957 pman_node(MAN_ARGS) |
|
Line 1099 pman_node(MAN_ARGS) |
|
sz = strlen(start) + 1; |
sz = strlen(start) + 1; |
buf_appendb(dbuf, start, sz); |
buf_appendb(dbuf, start, sz); |
buf_appendb(buf, start, sz); |
buf_appendb(buf, start, sz); |
|
|
|
hash_put(hash, buf, TYPE_DESC); |
} |
} |
} |
} |
|
|
|
|
usage(void) |
usage(void) |
{ |
{ |
|
|
fprintf(stderr, "usage: %s [-v] [-d path] [file...]\n", |
fprintf(stderr, "usage: %s [-ruv] [-d path] [file...]\n", |
progname); |
progname); |
} |
} |