Annotation of src/usr.bin/mandoc/mandocdb.c, Revision 1.12
1.12 ! schwarze 1: /* $Id: mandocdb.c,v 1.11 2011/11/27 22:57:28 schwarze Exp $ */
1.1 schwarze 2: /*
3: * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
1.10 schwarze 4: * Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org>
1.1 schwarze 5: *
6: * Permission to use, copy, modify, and distribute this software for any
7: * purpose with or without fee is hereby granted, provided that the above
8: * copyright notice and this permission notice appear in all copies.
9: *
10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17: */
18: #include <sys/param.h>
1.11 schwarze 19: #include <sys/types.h>
20: #include <sys/stat.h>
1.1 schwarze 21:
22: #include <assert.h>
1.2 schwarze 23: #include <dirent.h>
1.1 schwarze 24: #include <fcntl.h>
25: #include <getopt.h>
26: #include <stdio.h>
27: #include <stdint.h>
28: #include <stdlib.h>
29: #include <string.h>
30: #include <db.h>
31:
32: #include "man.h"
33: #include "mdoc.h"
34: #include "mandoc.h"
1.5 schwarze 35: #include "mandocdb.h"
1.10 schwarze 36: #include "manpath.h"
1.1 schwarze 37:
38: #define MANDOC_BUFSZ BUFSIZ
39: #define MANDOC_SLOP 1024
40:
1.11 schwarze 41: #define MANDOC_SRC 0x1
42: #define MANDOC_FORM 0x2
43:
1.2 schwarze 44: /* Tiny list for files. No need to bring in QUEUE. */
45:
46: struct of {
47: char *fname; /* heap-allocated */
1.6 schwarze 48: char *sec;
49: char *arch;
50: char *title;
1.11 schwarze 51: int src_form;
1.2 schwarze 52: struct of *next; /* NULL for last one */
53: struct of *first; /* first in list */
54: };
55:
1.1 schwarze 56: /* Buffer for storing growable data. */
57:
58: struct buf {
59: char *cp;
1.2 schwarze 60: size_t len; /* current length */
61: size_t size; /* total buffer size */
1.1 schwarze 62: };
63:
64: /* Operation we're going to perform. */
65:
66: enum op {
67: OP_NEW = 0, /* new database */
1.2 schwarze 68: OP_UPDATE, /* delete/add entries in existing database */
1.1 schwarze 69: OP_DELETE /* delete entries from existing database */
70: };
71:
72: #define MAN_ARGS DB *hash, \
73: struct buf *buf, \
74: struct buf *dbuf, \
75: const struct man_node *n
76: #define MDOC_ARGS DB *hash, \
77: struct buf *buf, \
78: struct buf *dbuf, \
79: const struct mdoc_node *n, \
80: const struct mdoc_meta *m
81:
82: static void buf_appendmdoc(struct buf *,
83: const struct mdoc_node *, int);
84: static void buf_append(struct buf *, const char *);
85: static void buf_appendb(struct buf *,
86: const void *, size_t);
87: static void dbt_put(DB *, const char *, DBT *, DBT *);
1.9 schwarze 88: static void hash_put(DB *, const struct buf *, uint64_t);
1.1 schwarze 89: static void hash_reset(DB **);
1.2 schwarze 90: static void index_merge(const struct of *, struct mparse *,
91: struct buf *, struct buf *,
92: DB *, DB *, const char *,
1.6 schwarze 93: DB *, const char *, int, int,
1.2 schwarze 94: recno_t, const recno_t *, size_t);
95: static void index_prune(const struct of *, DB *,
96: const char *, DB *, const char *,
97: int, recno_t *, recno_t **, size_t *);
1.6 schwarze 98: static void ofile_argbuild(char *[], int, int, int,
99: struct of **);
100: static int ofile_dirbuild(const char *, const char *,
1.11 schwarze 101: const char *, int, int, int,
102: struct of **);
1.2 schwarze 103: static void ofile_free(struct of *);
1.11 schwarze 104: static void pformatted(DB *, struct buf *, struct buf *,
105: const struct of *);
1.1 schwarze 106: static int pman_node(MAN_ARGS);
107: static void pmdoc_node(MDOC_ARGS);
108: static void pmdoc_An(MDOC_ARGS);
109: static void pmdoc_Cd(MDOC_ARGS);
110: static void pmdoc_Er(MDOC_ARGS);
111: static void pmdoc_Ev(MDOC_ARGS);
112: static void pmdoc_Fd(MDOC_ARGS);
113: static void pmdoc_In(MDOC_ARGS);
114: static void pmdoc_Fn(MDOC_ARGS);
115: static void pmdoc_Fo(MDOC_ARGS);
116: static void pmdoc_Nd(MDOC_ARGS);
117: static void pmdoc_Nm(MDOC_ARGS);
118: static void pmdoc_Pa(MDOC_ARGS);
119: static void pmdoc_St(MDOC_ARGS);
120: static void pmdoc_Vt(MDOC_ARGS);
121: static void pmdoc_Xr(MDOC_ARGS);
122: static void usage(void);
123:
124: typedef void (*pmdoc_nf)(MDOC_ARGS);
125:
126: static const pmdoc_nf mdocs[MDOC_MAX] = {
127: NULL, /* Ap */
128: NULL, /* Dd */
129: NULL, /* Dt */
130: NULL, /* Os */
131: NULL, /* Sh */
132: NULL, /* Ss */
133: NULL, /* Pp */
134: NULL, /* D1 */
135: NULL, /* Dl */
136: NULL, /* Bd */
137: NULL, /* Ed */
138: NULL, /* Bl */
139: NULL, /* El */
140: NULL, /* It */
141: NULL, /* Ad */
142: pmdoc_An, /* An */
143: NULL, /* Ar */
144: pmdoc_Cd, /* Cd */
145: NULL, /* Cm */
146: NULL, /* Dv */
147: pmdoc_Er, /* Er */
148: pmdoc_Ev, /* Ev */
149: NULL, /* Ex */
150: NULL, /* Fa */
151: pmdoc_Fd, /* Fd */
152: NULL, /* Fl */
153: pmdoc_Fn, /* Fn */
154: NULL, /* Ft */
155: NULL, /* Ic */
156: pmdoc_In, /* In */
157: NULL, /* Li */
158: pmdoc_Nd, /* Nd */
159: pmdoc_Nm, /* Nm */
160: NULL, /* Op */
161: NULL, /* Ot */
162: pmdoc_Pa, /* Pa */
163: NULL, /* Rv */
164: pmdoc_St, /* St */
165: pmdoc_Vt, /* Va */
166: pmdoc_Vt, /* Vt */
167: pmdoc_Xr, /* Xr */
168: NULL, /* %A */
169: NULL, /* %B */
170: NULL, /* %D */
171: NULL, /* %I */
172: NULL, /* %J */
173: NULL, /* %N */
174: NULL, /* %O */
175: NULL, /* %P */
176: NULL, /* %R */
177: NULL, /* %T */
178: NULL, /* %V */
179: NULL, /* Ac */
180: NULL, /* Ao */
181: NULL, /* Aq */
182: NULL, /* At */
183: NULL, /* Bc */
184: NULL, /* Bf */
185: NULL, /* Bo */
186: NULL, /* Bq */
187: NULL, /* Bsx */
188: NULL, /* Bx */
189: NULL, /* Db */
190: NULL, /* Dc */
191: NULL, /* Do */
192: NULL, /* Dq */
193: NULL, /* Ec */
194: NULL, /* Ef */
195: NULL, /* Em */
196: NULL, /* Eo */
197: NULL, /* Fx */
198: NULL, /* Ms */
199: NULL, /* No */
200: NULL, /* Ns */
201: NULL, /* Nx */
202: NULL, /* Ox */
203: NULL, /* Pc */
204: NULL, /* Pf */
205: NULL, /* Po */
206: NULL, /* Pq */
207: NULL, /* Qc */
208: NULL, /* Ql */
209: NULL, /* Qo */
210: NULL, /* Qq */
211: NULL, /* Re */
212: NULL, /* Rs */
213: NULL, /* Sc */
214: NULL, /* So */
215: NULL, /* Sq */
216: NULL, /* Sm */
217: NULL, /* Sx */
218: NULL, /* Sy */
219: NULL, /* Tn */
220: NULL, /* Ux */
221: NULL, /* Xc */
222: NULL, /* Xo */
223: pmdoc_Fo, /* Fo */
224: NULL, /* Fc */
225: NULL, /* Oo */
226: NULL, /* Oc */
227: NULL, /* Bk */
228: NULL, /* Ek */
229: NULL, /* Bt */
230: NULL, /* Hf */
231: NULL, /* Fr */
232: NULL, /* Ud */
233: NULL, /* Lb */
234: NULL, /* Lp */
235: NULL, /* Lk */
236: NULL, /* Mt */
237: NULL, /* Brq */
238: NULL, /* Bro */
239: NULL, /* Brc */
240: NULL, /* %C */
241: NULL, /* Es */
242: NULL, /* En */
243: NULL, /* Dx */
244: NULL, /* %Q */
245: NULL, /* br */
246: NULL, /* sp */
247: NULL, /* %U */
248: NULL, /* Ta */
249: };
250:
251: static const char *progname;
252:
253: int
1.3 schwarze 254: mandocdb(int argc, char *argv[])
1.1 schwarze 255: {
256: struct mparse *mp; /* parse sequence */
1.10 schwarze 257: struct manpaths dirs;
1.1 schwarze 258: enum op op; /* current operation */
1.2 schwarze 259: const char *dir;
1.1 schwarze 260: char ibuf[MAXPATHLEN], /* index fname */
1.2 schwarze 261: fbuf[MAXPATHLEN]; /* btree fname */
262: int verb, /* output verbosity */
1.6 schwarze 263: use_all, /* use all directories and files */
1.2 schwarze 264: ch, i, flags;
1.1 schwarze 265: DB *idx, /* index database */
266: *db, /* keyword database */
267: *hash; /* temporary keyword hashtable */
268: BTREEINFO info; /* btree configuration */
1.8 schwarze 269: recno_t maxrec; /* last record number in the index */
270: recno_t *recs; /* the numbers of all empty records */
1.2 schwarze 271: size_t sz1, sz2,
1.8 schwarze 272: recsz, /* number of allocated slots in recs */
273: reccur; /* current number of empty records */
1.1 schwarze 274: struct buf buf, /* keyword buffer */
275: dbuf; /* description buffer */
1.2 schwarze 276: struct of *of; /* list of files for processing */
1.1 schwarze 277: extern int optind;
278: extern char *optarg;
279:
280: progname = strrchr(argv[0], '/');
281: if (progname == NULL)
282: progname = argv[0];
283: else
284: ++progname;
285:
1.10 schwarze 286: memset(&dirs, 0, sizeof(struct manpaths));
287:
1.1 schwarze 288: verb = 0;
1.6 schwarze 289: use_all = 0;
1.2 schwarze 290: of = NULL;
1.1 schwarze 291: db = idx = NULL;
292: mp = NULL;
293: hash = NULL;
294: recs = NULL;
295: recsz = reccur = 0;
296: maxrec = 0;
297: op = OP_NEW;
1.2 schwarze 298: dir = NULL;
1.1 schwarze 299:
1.6 schwarze 300: while (-1 != (ch = getopt(argc, argv, "ad:u:v")))
1.1 schwarze 301: switch (ch) {
1.6 schwarze 302: case ('a'):
303: use_all = 1;
304: break;
1.1 schwarze 305: case ('d'):
306: dir = optarg;
1.2 schwarze 307: op = OP_UPDATE;
1.1 schwarze 308: break;
1.2 schwarze 309: case ('u'):
310: dir = optarg;
1.1 schwarze 311: op = OP_DELETE;
312: break;
313: case ('v'):
314: verb++;
315: break;
316: default:
317: usage();
318: return((int)MANDOCLEVEL_BADARG);
319: }
320:
321: argc -= optind;
322: argv += optind;
323:
1.2 schwarze 324: memset(&info, 0, sizeof(BTREEINFO));
325: info.flags = R_DUP;
326:
327: mp = mparse_alloc(MPARSE_AUTO, MANDOCLEVEL_FATAL, NULL, NULL);
328:
329: memset(&buf, 0, sizeof(struct buf));
330: memset(&dbuf, 0, sizeof(struct buf));
331:
332: buf.size = dbuf.size = MANDOC_BUFSZ;
333:
334: buf.cp = mandoc_malloc(buf.size);
335: dbuf.cp = mandoc_malloc(dbuf.size);
336:
337: flags = OP_NEW == op ? O_CREAT|O_TRUNC|O_RDWR : O_CREAT|O_RDWR;
1.1 schwarze 338:
1.2 schwarze 339: if (OP_UPDATE == op || OP_DELETE == op) {
340: ibuf[0] = fbuf[0] = '\0';
1.1 schwarze 341:
1.2 schwarze 342: strlcat(fbuf, dir, MAXPATHLEN);
343: strlcat(fbuf, "/", MAXPATHLEN);
344: sz1 = strlcat(fbuf, MANDOC_DB, MAXPATHLEN);
345:
346: strlcat(ibuf, dir, MAXPATHLEN);
347: strlcat(ibuf, "/", MAXPATHLEN);
348: sz2 = strlcat(ibuf, MANDOC_IDX, MAXPATHLEN);
349:
350: if (sz1 >= MAXPATHLEN || sz2 >= MAXPATHLEN) {
351: fprintf(stderr, "%s: Path too long\n", dir);
352: exit((int)MANDOCLEVEL_BADARG);
353: }
1.1 schwarze 354:
1.2 schwarze 355: db = dbopen(fbuf, flags, 0644, DB_BTREE, &info);
356: idx = dbopen(ibuf, flags, 0644, DB_RECNO, NULL);
1.1 schwarze 357:
1.2 schwarze 358: if (NULL == db) {
359: perror(fbuf);
360: exit((int)MANDOCLEVEL_SYSERR);
1.8 schwarze 361: } else if (NULL == idx) {
1.2 schwarze 362: perror(ibuf);
363: exit((int)MANDOCLEVEL_SYSERR);
364: }
1.1 schwarze 365:
1.2 schwarze 366: if (verb > 2) {
367: printf("%s: Opened\n", fbuf);
368: printf("%s: Opened\n", ibuf);
369: }
1.1 schwarze 370:
1.6 schwarze 371: ofile_argbuild(argv, argc, use_all, verb, &of);
1.2 schwarze 372: if (NULL == of)
373: goto out;
374:
375: of = of->first;
376:
377: index_prune(of, db, fbuf, idx, ibuf, verb,
378: &maxrec, &recs, &recsz);
379:
1.10 schwarze 380: if (OP_UPDATE == op)
381: index_merge(of, mp, &dbuf, &buf, hash,
1.6 schwarze 382: db, fbuf, idx, ibuf, use_all,
383: verb, maxrec, recs, reccur);
1.1 schwarze 384:
385: goto out;
386: }
387:
1.10 schwarze 388: /*
389: * Configure the directories we're going to scan.
390: * If we have command-line arguments, use them.
391: * If not, we use man(1)'s method (see mandocdb.8).
392: */
393:
394: if (argc > 0) {
395: dirs.paths = mandoc_malloc(argc * sizeof(char *));
396: dirs.sz = argc;
397: for (i = 0; i < argc; i++)
398: dirs.paths[i] = mandoc_strdup(argv[i]);
399: } else
400: manpath_parse(&dirs, NULL, NULL);
1.7 schwarze 401:
1.10 schwarze 402: for (i = 0; i < dirs.sz; i++) {
1.2 schwarze 403: ibuf[0] = fbuf[0] = '\0';
404:
1.10 schwarze 405: strlcat(fbuf, dirs.paths[i], MAXPATHLEN);
1.2 schwarze 406: strlcat(fbuf, "/", MAXPATHLEN);
407: sz1 = strlcat(fbuf, MANDOC_DB, MAXPATHLEN);
408:
1.10 schwarze 409: strlcat(ibuf, dirs.paths[i], MAXPATHLEN);
1.2 schwarze 410: strlcat(ibuf, "/", MAXPATHLEN);
411: sz2 = strlcat(ibuf, MANDOC_IDX, MAXPATHLEN);
412:
413: if (sz1 >= MAXPATHLEN || sz2 >= MAXPATHLEN) {
1.10 schwarze 414: fprintf(stderr, "%s: Path too long\n",
415: dirs.paths[i]);
1.2 schwarze 416: exit((int)MANDOCLEVEL_BADARG);
417: }
418:
1.7 schwarze 419: if (db)
420: (*db->close)(db);
421: if (idx)
422: (*idx->close)(idx);
423:
1.2 schwarze 424: db = dbopen(fbuf, flags, 0644, DB_BTREE, &info);
425: idx = dbopen(ibuf, flags, 0644, DB_RECNO, NULL);
1.1 schwarze 426:
1.2 schwarze 427: if (NULL == db) {
428: perror(fbuf);
429: exit((int)MANDOCLEVEL_SYSERR);
1.8 schwarze 430: } else if (NULL == idx) {
1.2 schwarze 431: perror(ibuf);
432: exit((int)MANDOCLEVEL_SYSERR);
433: }
1.1 schwarze 434:
1.2 schwarze 435: if (verb > 2) {
436: printf("%s: Truncated\n", fbuf);
437: printf("%s: Truncated\n", ibuf);
438: }
1.1 schwarze 439:
1.2 schwarze 440: ofile_free(of);
441: of = NULL;
1.1 schwarze 442:
1.10 schwarze 443: if ( ! ofile_dirbuild(dirs.paths[i], NULL, NULL,
1.11 schwarze 444: 0, use_all, verb, &of))
1.2 schwarze 445: exit((int)MANDOCLEVEL_SYSERR);
1.1 schwarze 446:
1.2 schwarze 447: if (NULL == of)
448: continue;
1.1 schwarze 449:
1.2 schwarze 450: of = of->first;
1.1 schwarze 451:
1.10 schwarze 452: index_merge(of, mp, &dbuf, &buf, hash, db, fbuf,
1.6 schwarze 453: idx, ibuf, use_all, verb,
454: maxrec, recs, reccur);
1.1 schwarze 455: }
456:
1.2 schwarze 457: out:
458: if (db)
459: (*db->close)(db);
460: if (idx)
461: (*idx->close)(idx);
462: if (hash)
463: (*hash->close)(hash);
464: if (mp)
465: mparse_free(mp);
1.1 schwarze 466:
1.10 schwarze 467: manpath_free(&dirs);
1.2 schwarze 468: ofile_free(of);
469: free(buf.cp);
470: free(dbuf.cp);
471: free(recs);
1.1 schwarze 472:
1.2 schwarze 473: return(MANDOCLEVEL_OK);
474: }
1.1 schwarze 475:
1.2 schwarze 476: void
477: index_merge(const struct of *of, struct mparse *mp,
478: struct buf *dbuf, struct buf *buf,
1.10 schwarze 479: DB *hash, DB *db, const char *dbf,
1.6 schwarze 480: DB *idx, const char *idxf, int use_all, int verb,
1.2 schwarze 481: recno_t maxrec, const recno_t *recs, size_t reccur)
482: {
483: recno_t rec;
484: int ch;
485: DBT key, val;
486: struct mdoc *mdoc;
487: struct man *man;
488: const char *fn, *msec, *mtitle, *arch;
489: size_t sv;
490: unsigned seq;
1.9 schwarze 491: struct db_val vbuf;
1.1 schwarze 492:
1.2 schwarze 493: for (rec = 0; of; of = of->next) {
494: fn = of->fname;
1.11 schwarze 495:
496: /*
497: * Reclaim an empty index record, if available.
498: */
499:
1.2 schwarze 500: if (reccur > 0) {
501: --reccur;
502: rec = recs[(int)reccur];
503: } else if (maxrec > 0) {
504: rec = maxrec;
505: maxrec = 0;
1.1 schwarze 506: } else
507: rec++;
508:
509: mparse_reset(mp);
510: hash_reset(&hash);
1.11 schwarze 511: mdoc = NULL;
512: man = NULL;
1.1 schwarze 513:
1.11 schwarze 514: /*
515: * Try interpreting the file as mdoc(7) or man(7)
516: * source code, unless it is already known to be
517: * formatted. Fall back to formatted mode.
518: */
519:
520: if ((MANDOC_SRC & of->src_form ||
521: ! (MANDOC_FORM & of->src_form)) &&
522: MANDOCLEVEL_FATAL > mparse_readfd(mp, -1, fn))
523: mparse_result(mp, &mdoc, &man);
524:
525: if (NULL != mdoc) {
526: msec = mdoc_meta(mdoc)->msec;
527: arch = mdoc_meta(mdoc)->arch;
528: mtitle = mdoc_meta(mdoc)->title;
529: } else if (NULL != man) {
530: msec = man_meta(man)->msec;
531: arch = NULL;
532: mtitle = man_meta(man)->title;
533: } else {
534: msec = of->sec;
535: arch = of->arch;
536: mtitle = of->title;
1.1 schwarze 537: }
538:
1.6 schwarze 539: /*
1.8 schwarze 540: * By default, skip a file if the manual section
541: * and architecture given in the file disagree
542: * with the directory where the file is located.
1.6 schwarze 543: */
544:
545: if (0 == use_all) {
546: assert(of->sec);
547: assert(msec);
548: if (strcmp(msec, of->sec))
549: continue;
550:
551: if (NULL == arch) {
552: if (NULL != of->arch)
553: continue;
554: } else if (NULL == of->arch ||
555: strcmp(arch, of->arch))
556: continue;
557: }
558:
1.1 schwarze 559: if (NULL == arch)
560: arch = "";
561:
562: /*
1.8 schwarze 563: * By default, skip a file if the title given
564: * in the file disagrees with the file name.
565: * If both agree, use the file name as the title,
566: * because the one in the file usually is all caps.
1.6 schwarze 567: */
568:
569: assert(of->title);
570: assert(mtitle);
571:
572: if (0 == strcasecmp(mtitle, of->title))
573: mtitle = of->title;
574: else if (0 == use_all)
575: continue;
576:
577: /*
1.1 schwarze 578: * The index record value consists of a nil-terminated
579: * filename, a nil-terminated manual section, and a
580: * nil-terminated description. Since the description
581: * may not be set, we set a sentinel to see if we're
582: * going to write a nil byte in its place.
583: */
584:
1.2 schwarze 585: dbuf->len = 0;
1.12 ! schwarze 586: buf_append(dbuf, mdoc ? "mdoc" : (man ? "man" : "cat"));
1.2 schwarze 587: buf_appendb(dbuf, fn, strlen(fn) + 1);
588: buf_appendb(dbuf, msec, strlen(msec) + 1);
589: buf_appendb(dbuf, mtitle, strlen(mtitle) + 1);
590: buf_appendb(dbuf, arch, strlen(arch) + 1);
1.1 schwarze 591:
1.2 schwarze 592: sv = dbuf->len;
1.1 schwarze 593:
594: /* Fix the record number in the btree value. */
595:
596: if (mdoc)
1.2 schwarze 597: pmdoc_node(hash, buf, dbuf,
1.1 schwarze 598: mdoc_node(mdoc), mdoc_meta(mdoc));
1.11 schwarze 599: else if (man)
1.2 schwarze 600: pman_node(hash, buf, dbuf, man_node(man));
1.11 schwarze 601: else
602: pformatted(hash, buf, dbuf, of);
1.1 schwarze 603:
604: /*
605: * Copy from the in-memory hashtable of pending keywords
606: * into the database.
607: */
608:
1.9 schwarze 609: vbuf.rec = rec;
1.1 schwarze 610: seq = R_FIRST;
611: while (0 == (ch = (*hash->seq)(hash, &key, &val, seq))) {
612: seq = R_NEXT;
613:
1.9 schwarze 614: vbuf.mask = *(uint64_t *)val.data;
615: val.size = sizeof(struct db_val);
616: val.data = &vbuf;
1.1 schwarze 617:
618: if (verb > 1)
1.2 schwarze 619: printf("%s: Added keyword: %s\n",
620: fn, (char *)key.data);
621: dbt_put(db, dbf, &key, &val);
1.1 schwarze 622: }
623: if (ch < 0) {
624: perror("hash");
625: exit((int)MANDOCLEVEL_SYSERR);
626: }
627:
628: /*
629: * Apply to the index. If we haven't had a description
630: * set, put an empty one in now.
631: */
632:
1.2 schwarze 633: if (dbuf->len == sv)
634: buf_appendb(dbuf, "", 1);
1.1 schwarze 635:
636: key.data = &rec;
637: key.size = sizeof(recno_t);
638:
1.2 schwarze 639: val.data = dbuf->cp;
640: val.size = dbuf->len;
1.1 schwarze 641:
1.2 schwarze 642: if (verb)
1.1 schwarze 643: printf("%s: Added index\n", fn);
1.2 schwarze 644: dbt_put(idx, idxf, &key, &val);
645: }
646: }
647:
648: /*
649: * Scan through all entries in the index file `idx' and prune those
650: * entries in `ofile'.
651: * Pruning consists of removing from `db', then invalidating the entry
652: * in `idx' (zeroing its value size).
653: */
654: static void
655: index_prune(const struct of *ofile, DB *db, const char *dbf,
656: DB *idx, const char *idxf, int verb,
657: recno_t *maxrec, recno_t **recs, size_t *recsz)
658: {
659: const struct of *of;
660: const char *fn;
1.9 schwarze 661: struct db_val *vbuf;
1.2 schwarze 662: unsigned seq, sseq;
663: DBT key, val;
664: size_t reccur;
665: int ch;
666:
667: reccur = 0;
668: seq = R_FIRST;
669: while (0 == (ch = (*idx->seq)(idx, &key, &val, seq))) {
670: seq = R_NEXT;
671: *maxrec = *(recno_t *)key.data;
672: if (0 == val.size) {
673: if (reccur >= *recsz) {
674: *recsz += MANDOC_SLOP;
675: *recs = mandoc_realloc(*recs,
676: *recsz * sizeof(recno_t));
677: }
678: (*recs)[(int)reccur] = *maxrec;
679: reccur++;
680: continue;
681: }
682:
683: fn = (char *)val.data;
684: for (of = ofile; of; of = of->next)
685: if (0 == strcmp(fn, of->fname))
686: break;
687:
688: if (NULL == of)
689: continue;
690:
691: sseq = R_FIRST;
692: while (0 == (ch = (*db->seq)(db, &key, &val, sseq))) {
693: sseq = R_NEXT;
1.9 schwarze 694: assert(sizeof(struct db_val) == val.size);
695: vbuf = val.data;
696: if (*maxrec != vbuf->rec)
1.2 schwarze 697: continue;
698: if (verb)
699: printf("%s: Deleted keyword: %s\n",
700: fn, (char *)key.data);
701: ch = (*db->del)(db, &key, R_CURSOR);
702: if (ch < 0)
703: break;
704: }
705: if (ch < 0) {
706: perror(dbf);
707: exit((int)MANDOCLEVEL_SYSERR);
708: }
1.1 schwarze 709:
1.2 schwarze 710: if (verb)
711: printf("%s: Deleted index\n", fn);
1.1 schwarze 712:
1.2 schwarze 713: val.size = 0;
714: ch = (*idx->put)(idx, &key, &val, R_CURSOR);
715: if (ch < 0) {
716: perror(idxf);
717: exit((int)MANDOCLEVEL_SYSERR);
718: }
1.1 schwarze 719:
1.2 schwarze 720: if (reccur >= *recsz) {
721: *recsz += MANDOC_SLOP;
722: *recs = mandoc_realloc
723: (*recs, *recsz * sizeof(recno_t));
724: }
1.1 schwarze 725:
1.2 schwarze 726: (*recs)[(int)reccur] = *maxrec;
727: reccur++;
728: }
729: (*maxrec)++;
1.1 schwarze 730: }
731:
732: /*
733: * Grow the buffer (if necessary) and copy in a binary string.
734: */
735: static void
736: buf_appendb(struct buf *buf, const void *cp, size_t sz)
737: {
738:
739: /* Overshoot by MANDOC_BUFSZ. */
740:
741: while (buf->len + sz >= buf->size) {
742: buf->size = buf->len + sz + MANDOC_BUFSZ;
743: buf->cp = mandoc_realloc(buf->cp, buf->size);
744: }
745:
746: memcpy(buf->cp + (int)buf->len, cp, sz);
747: buf->len += sz;
748: }
749:
750: /*
751: * Append a nil-terminated string to the buffer.
752: * This can be invoked multiple times.
753: * The buffer string will be nil-terminated.
754: * If invoked multiple times, a space is put between strings.
755: */
756: static void
757: buf_append(struct buf *buf, const char *cp)
758: {
759: size_t sz;
760:
761: if (0 == (sz = strlen(cp)))
762: return;
763:
764: if (buf->len)
765: buf->cp[(int)buf->len - 1] = ' ';
766:
767: buf_appendb(buf, cp, sz + 1);
768: }
769:
770: /*
771: * Recursively add all text from a given node.
772: * This is optimised for general mdoc nodes in this context, which do
773: * not consist of subexpressions and having a recursive call for n->next
774: * would be wasteful.
775: * The "f" variable should be 0 unless called from pmdoc_Nd for the
776: * description buffer, which does not start at the beginning of the
777: * buffer.
778: */
779: static void
780: buf_appendmdoc(struct buf *buf, const struct mdoc_node *n, int f)
781: {
782:
783: for ( ; n; n = n->next) {
784: if (n->child)
785: buf_appendmdoc(buf, n->child, f);
786:
787: if (MDOC_TEXT == n->type && f) {
788: f = 0;
789: buf_appendb(buf, n->string,
790: strlen(n->string) + 1);
791: } else if (MDOC_TEXT == n->type)
792: buf_append(buf, n->string);
793:
794: }
795: }
796:
797: /* ARGSUSED */
798: static void
799: pmdoc_An(MDOC_ARGS)
800: {
801:
802: if (SEC_AUTHORS != n->sec)
803: return;
804:
805: buf_appendmdoc(buf, n->child, 0);
1.5 schwarze 806: hash_put(hash, buf, TYPE_An);
1.1 schwarze 807: }
808:
809: static void
810: hash_reset(DB **db)
811: {
812: DB *hash;
813:
814: if (NULL != (hash = *db))
815: (*hash->close)(hash);
816:
1.2 schwarze 817: *db = dbopen(NULL, O_CREAT|O_RDWR, 0644, DB_HASH, NULL);
1.1 schwarze 818: if (NULL == *db) {
819: perror("hash");
820: exit((int)MANDOCLEVEL_SYSERR);
821: }
822: }
823:
824: /* ARGSUSED */
825: static void
826: pmdoc_Fd(MDOC_ARGS)
827: {
828: const char *start, *end;
829: size_t sz;
830:
831: if (SEC_SYNOPSIS != n->sec)
832: return;
833: if (NULL == (n = n->child) || MDOC_TEXT != n->type)
834: return;
835:
836: /*
837: * Only consider those `Fd' macro fields that begin with an
838: * "inclusion" token (versus, e.g., #define).
839: */
840: if (strcmp("#include", n->string))
841: return;
842:
843: if (NULL == (n = n->next) || MDOC_TEXT != n->type)
844: return;
845:
846: /*
847: * Strip away the enclosing angle brackets and make sure we're
848: * not zero-length.
849: */
850:
851: start = n->string;
852: if ('<' == *start || '"' == *start)
853: start++;
854:
855: if (0 == (sz = strlen(start)))
856: return;
857:
858: end = &start[(int)sz - 1];
859: if ('>' == *end || '"' == *end)
860: end--;
861:
862: assert(end >= start);
863:
864: buf_appendb(buf, start, (size_t)(end - start + 1));
865: buf_appendb(buf, "", 1);
866:
1.5 schwarze 867: hash_put(hash, buf, TYPE_In);
1.1 schwarze 868: }
869:
870: /* ARGSUSED */
871: static void
872: pmdoc_Cd(MDOC_ARGS)
873: {
874:
875: if (SEC_SYNOPSIS != n->sec)
876: return;
877:
878: buf_appendmdoc(buf, n->child, 0);
1.5 schwarze 879: hash_put(hash, buf, TYPE_Cd);
1.1 schwarze 880: }
881:
882: /* ARGSUSED */
883: static void
884: pmdoc_In(MDOC_ARGS)
885: {
886:
887: if (SEC_SYNOPSIS != n->sec)
888: return;
889: if (NULL == n->child || MDOC_TEXT != n->child->type)
890: return;
891:
892: buf_append(buf, n->child->string);
1.5 schwarze 893: hash_put(hash, buf, TYPE_In);
1.1 schwarze 894: }
895:
896: /* ARGSUSED */
897: static void
898: pmdoc_Fn(MDOC_ARGS)
899: {
900: const char *cp;
901:
902: if (SEC_SYNOPSIS != n->sec)
903: return;
904: if (NULL == n->child || MDOC_TEXT != n->child->type)
905: return;
906:
907: /* .Fn "struct type *arg" "foo" */
908:
909: cp = strrchr(n->child->string, ' ');
910: if (NULL == cp)
911: cp = n->child->string;
912:
913: /* Strip away pointer symbol. */
914:
915: while ('*' == *cp)
916: cp++;
917:
918: buf_append(buf, cp);
1.5 schwarze 919: hash_put(hash, buf, TYPE_Fn);
1.1 schwarze 920: }
921:
922: /* ARGSUSED */
923: static void
924: pmdoc_St(MDOC_ARGS)
925: {
926:
927: if (SEC_STANDARDS != n->sec)
928: return;
929: if (NULL == n->child || MDOC_TEXT != n->child->type)
930: return;
931:
932: buf_append(buf, n->child->string);
1.5 schwarze 933: hash_put(hash, buf, TYPE_St);
1.1 schwarze 934: }
935:
936: /* ARGSUSED */
937: static void
938: pmdoc_Xr(MDOC_ARGS)
939: {
940:
941: if (NULL == (n = n->child))
942: return;
943:
944: buf_appendb(buf, n->string, strlen(n->string));
945:
946: if (NULL != (n = n->next)) {
947: buf_appendb(buf, ".", 1);
948: buf_appendb(buf, n->string, strlen(n->string) + 1);
949: } else
950: buf_appendb(buf, ".", 2);
951:
1.5 schwarze 952: hash_put(hash, buf, TYPE_Xr);
1.1 schwarze 953: }
954:
955: /* ARGSUSED */
956: static void
957: pmdoc_Vt(MDOC_ARGS)
958: {
959: const char *start;
960: size_t sz;
961:
962: if (SEC_SYNOPSIS != n->sec)
963: return;
964: if (MDOC_Vt == n->tok && MDOC_BODY != n->type)
965: return;
966: if (NULL == n->last || MDOC_TEXT != n->last->type)
967: return;
968:
969: /*
970: * Strip away leading pointer symbol '*' and trailing ';'.
971: */
972:
973: start = n->last->string;
974:
975: while ('*' == *start)
976: start++;
977:
978: if (0 == (sz = strlen(start)))
979: return;
980:
981: if (';' == start[(int)sz - 1])
982: sz--;
983:
984: if (0 == sz)
985: return;
986:
987: buf_appendb(buf, start, sz);
988: buf_appendb(buf, "", 1);
1.5 schwarze 989: hash_put(hash, buf, TYPE_Va);
1.1 schwarze 990: }
991:
992: /* ARGSUSED */
993: static void
994: pmdoc_Fo(MDOC_ARGS)
995: {
996:
997: if (SEC_SYNOPSIS != n->sec || MDOC_HEAD != n->type)
998: return;
999: if (NULL == n->child || MDOC_TEXT != n->child->type)
1000: return;
1001:
1002: buf_append(buf, n->child->string);
1.5 schwarze 1003: hash_put(hash, buf, TYPE_Fn);
1.1 schwarze 1004: }
1005:
1006:
1007: /* ARGSUSED */
1008: static void
1009: pmdoc_Nd(MDOC_ARGS)
1010: {
1011:
1012: if (MDOC_BODY != n->type)
1013: return;
1014:
1015: buf_appendmdoc(dbuf, n->child, 1);
1016: buf_appendmdoc(buf, n->child, 0);
1017:
1.5 schwarze 1018: hash_put(hash, buf, TYPE_Nd);
1.1 schwarze 1019: }
1020:
1021: /* ARGSUSED */
1022: static void
1023: pmdoc_Er(MDOC_ARGS)
1024: {
1025:
1026: if (SEC_ERRORS != n->sec)
1027: return;
1028:
1029: buf_appendmdoc(buf, n->child, 0);
1.5 schwarze 1030: hash_put(hash, buf, TYPE_Er);
1.1 schwarze 1031: }
1032:
1033: /* ARGSUSED */
1034: static void
1035: pmdoc_Ev(MDOC_ARGS)
1036: {
1037:
1038: if (SEC_ENVIRONMENT != n->sec)
1039: return;
1040:
1041: buf_appendmdoc(buf, n->child, 0);
1.5 schwarze 1042: hash_put(hash, buf, TYPE_Ev);
1.1 schwarze 1043: }
1044:
1045: /* ARGSUSED */
1046: static void
1047: pmdoc_Pa(MDOC_ARGS)
1048: {
1049:
1050: if (SEC_FILES != n->sec)
1051: return;
1052:
1053: buf_appendmdoc(buf, n->child, 0);
1.5 schwarze 1054: hash_put(hash, buf, TYPE_Pa);
1.1 schwarze 1055: }
1056:
1057: /* ARGSUSED */
1058: static void
1059: pmdoc_Nm(MDOC_ARGS)
1060: {
1061:
1062: if (SEC_NAME == n->sec) {
1063: buf_appendmdoc(buf, n->child, 0);
1.5 schwarze 1064: hash_put(hash, buf, TYPE_Nm);
1.1 schwarze 1065: return;
1066: } else if (SEC_SYNOPSIS != n->sec || MDOC_HEAD != n->type)
1067: return;
1068:
1069: if (NULL == n->child)
1070: buf_append(buf, m->name);
1071:
1072: buf_appendmdoc(buf, n->child, 0);
1.5 schwarze 1073: hash_put(hash, buf, TYPE_Nm);
1.1 schwarze 1074: }
1075:
1076: static void
1.9 schwarze 1077: hash_put(DB *db, const struct buf *buf, uint64_t mask)
1.1 schwarze 1078: {
1079: DBT key, val;
1080: int rc;
1081:
1082: if (buf->len < 2)
1083: return;
1084:
1085: key.data = buf->cp;
1086: key.size = buf->len;
1087:
1088: if ((rc = (*db->get)(db, &key, &val, 0)) < 0) {
1089: perror("hash");
1090: exit((int)MANDOCLEVEL_SYSERR);
1091: } else if (0 == rc)
1.9 schwarze 1092: mask |= *(uint64_t *)val.data;
1.1 schwarze 1093:
1094: val.data = &mask;
1.9 schwarze 1095: val.size = sizeof(uint64_t);
1.1 schwarze 1096:
1097: if ((rc = (*db->put)(db, &key, &val, 0)) < 0) {
1098: perror("hash");
1099: exit((int)MANDOCLEVEL_SYSERR);
1100: }
1101: }
1102:
1103: static void
1104: dbt_put(DB *db, const char *dbn, DBT *key, DBT *val)
1105: {
1106:
1107: assert(key->size);
1108: assert(val->size);
1109:
1110: if (0 == (*db->put)(db, key, val, 0))
1111: return;
1112:
1113: perror(dbn);
1114: exit((int)MANDOCLEVEL_SYSERR);
1115: /* NOTREACHED */
1116: }
1117:
1118: /*
1119: * Call out to per-macro handlers after clearing the persistent database
1120: * key. If the macro sets the database key, flush it to the database.
1121: */
1122: static void
1123: pmdoc_node(MDOC_ARGS)
1124: {
1125:
1126: if (NULL == n)
1127: return;
1128:
1129: switch (n->type) {
1130: case (MDOC_HEAD):
1131: /* FALLTHROUGH */
1132: case (MDOC_BODY):
1133: /* FALLTHROUGH */
1134: case (MDOC_TAIL):
1135: /* FALLTHROUGH */
1136: case (MDOC_BLOCK):
1137: /* FALLTHROUGH */
1138: case (MDOC_ELEM):
1139: if (NULL == mdocs[n->tok])
1140: break;
1141:
1142: buf->len = 0;
1143: (*mdocs[n->tok])(hash, buf, dbuf, n, m);
1144: break;
1145: default:
1146: break;
1147: }
1148:
1149: pmdoc_node(hash, buf, dbuf, n->child, m);
1150: pmdoc_node(hash, buf, dbuf, n->next, m);
1151: }
1152:
1153: static int
1154: pman_node(MAN_ARGS)
1155: {
1156: const struct man_node *head, *body;
1157: const char *start, *sv;
1158: size_t sz;
1159:
1160: if (NULL == n)
1161: return(0);
1162:
1163: /*
1164: * We're only searching for one thing: the first text child in
1165: * the BODY of a NAME section. Since we don't keep track of
1166: * sections in -man, run some hoops to find out whether we're in
1167: * the correct section or not.
1168: */
1169:
1170: if (MAN_BODY == n->type && MAN_SH == n->tok) {
1171: body = n;
1172: assert(body->parent);
1173: if (NULL != (head = body->parent->head) &&
1174: 1 == head->nchild &&
1175: NULL != (head = (head->child)) &&
1176: MAN_TEXT == head->type &&
1177: 0 == strcmp(head->string, "NAME") &&
1178: NULL != (body = body->child) &&
1179: MAN_TEXT == body->type) {
1180:
1181: assert(body->string);
1182: start = sv = body->string;
1183:
1184: /*
1185: * Go through a special heuristic dance here.
1186: * This is why -man manuals are great!
1187: * (I'm being sarcastic: my eyes are bleeding.)
1188: * Conventionally, one or more manual names are
1189: * comma-specified prior to a whitespace, then a
1190: * dash, then a description. Try to puzzle out
1191: * the name parts here.
1192: */
1193:
1194: for ( ;; ) {
1195: sz = strcspn(start, " ,");
1196: if ('\0' == start[(int)sz])
1197: break;
1198:
1199: buf->len = 0;
1200: buf_appendb(buf, start, sz);
1201: buf_appendb(buf, "", 1);
1202:
1.5 schwarze 1203: hash_put(hash, buf, TYPE_Nm);
1.1 schwarze 1204:
1205: if (' ' == start[(int)sz]) {
1206: start += (int)sz + 1;
1207: break;
1208: }
1209:
1210: assert(',' == start[(int)sz]);
1211: start += (int)sz + 1;
1212: while (' ' == *start)
1213: start++;
1214: }
1215:
1216: buf->len = 0;
1217:
1218: if (sv == start) {
1219: buf_append(buf, start);
1220: return(1);
1221: }
1222:
1223: while (' ' == *start)
1224: start++;
1225:
1226: if (0 == strncmp(start, "-", 1))
1227: start += 1;
1228: else if (0 == strncmp(start, "\\-", 2))
1229: start += 2;
1230: else if (0 == strncmp(start, "\\(en", 4))
1231: start += 4;
1232: else if (0 == strncmp(start, "\\(em", 4))
1233: start += 4;
1234:
1235: while (' ' == *start)
1236: start++;
1237:
1238: sz = strlen(start) + 1;
1239: buf_appendb(dbuf, start, sz);
1240: buf_appendb(buf, start, sz);
1241:
1.5 schwarze 1242: hash_put(hash, buf, TYPE_Nd);
1.1 schwarze 1243: }
1244: }
1245:
1.4 schwarze 1246: for (n = n->child; n; n = n->next)
1247: if (pman_node(hash, buf, dbuf, n))
1248: return(1);
1.1 schwarze 1249:
1250: return(0);
1251: }
1252:
1.11 schwarze 1253: /*
1254: * Parse a formatted manual page.
1255: * By necessity, this involves rather crude guesswork.
1256: */
1257: static void
1258: pformatted(DB *hash, struct buf *buf, struct buf *dbuf,
1259: const struct of *of)
1260: {
1261: FILE *stream;
1262: char *line, *p;
1263: size_t len, plen;
1264:
1265: if (NULL == (stream = fopen(of->fname, "r"))) {
1266: perror(of->fname);
1267: return;
1268: }
1269:
1270: /*
1271: * Always use the title derived from the filename up front,
1272: * do not even try to find it in the file. This also makes
1273: * sure we don't end up with an orphan index record, even if
1274: * the file content turns out to be completely unintelligible.
1275: */
1276:
1277: buf->len = 0;
1278: buf_append(buf, of->title);
1279: hash_put(hash, buf, TYPE_Nm);
1280:
1281: while (NULL != (line = fgetln(stream, &len)) && '\n' != *line)
1282: /* Skip to first blank line. */ ;
1283:
1284: while (NULL != (line = fgetln(stream, &len)) &&
1285: ('\n' == *line || ' ' == *line))
1286: /* Skip to first section header. */ ;
1287:
1288: /*
1289: * If no page content can be found,
1290: * reuse the page title as the page description.
1291: */
1292:
1293: if (NULL == (line = fgetln(stream, &len))) {
1294: buf_appendb(dbuf, buf->cp, buf->size);
1295: hash_put(hash, buf, TYPE_Nd);
1296: fclose(stream);
1297: return;
1298: }
1299: fclose(stream);
1300:
1301: /*
1302: * If there is a dash, skip to the text following it.
1303: */
1304:
1305: for (p = line, plen = len; plen; p++, plen--)
1306: if ('-' == *p)
1307: break;
1308: for ( ; plen; p++, plen--)
1309: if ('-' != *p && ' ' != *p && 8 != *p)
1310: break;
1311: if (0 == plen) {
1312: p = line;
1313: plen = len;
1314: }
1315:
1316: /*
1317: * Copy the rest of the line, but no more than 70 bytes.
1318: */
1319:
1320: if (70 < plen)
1321: plen = 70;
1322: p[plen-1] = '\0';
1323: buf_appendb(dbuf, p, plen);
1324: buf->len = 0;
1325: buf_appendb(buf, p, plen);
1326: hash_put(hash, buf, TYPE_Nd);
1327: }
1328:
1.1 schwarze 1329: static void
1.6 schwarze 1330: ofile_argbuild(char *argv[], int argc, int use_all, int verb,
1331: struct of **of)
1.2 schwarze 1332: {
1.6 schwarze 1333: char buf[MAXPATHLEN];
1334: char *sec, *arch, *title, *p;
1.11 schwarze 1335: int i, src_form;
1.2 schwarze 1336: struct of *nof;
1337:
1338: for (i = 0; i < argc; i++) {
1.6 schwarze 1339:
1340: /*
1.8 schwarze 1341: * Try to infer the manual section, architecture and
1342: * page title from the path, assuming it looks like
1.11 schwarze 1343: * man*[/<arch>]/<title>.<section> or
1344: * cat<section>[/<arch>]/<title>.0
1.6 schwarze 1345: */
1346:
1347: if (strlcpy(buf, argv[i], sizeof(buf)) >= sizeof(buf)) {
1348: fprintf(stderr, "%s: Path too long\n", argv[i]);
1349: continue;
1350: }
1351: sec = arch = title = NULL;
1.11 schwarze 1352: src_form = 0;
1.6 schwarze 1353: p = strrchr(buf, '\0');
1354: while (p-- > buf) {
1355: if (NULL == sec && '.' == *p) {
1356: sec = p + 1;
1357: *p = '\0';
1.11 schwarze 1358: if ('0' == *sec)
1359: src_form |= MANDOC_FORM;
1360: else if ('1' <= *sec && '9' >= *sec)
1361: src_form |= MANDOC_SRC;
1.6 schwarze 1362: continue;
1363: }
1364: if ('/' != *p)
1365: continue;
1366: if (NULL == title) {
1367: title = p + 1;
1368: *p = '\0';
1369: continue;
1370: }
1.11 schwarze 1371: if (strncmp("man", p + 1, 3)) {
1372: src_form |= MANDOC_SRC;
1373: arch = p + 1;
1374: } else if (strncmp("cat", p + 1, 3)) {
1375: src_form |= MANDOC_FORM;
1.6 schwarze 1376: arch = p + 1;
1.11 schwarze 1377: }
1.6 schwarze 1378: break;
1379: }
1380: if (NULL == title)
1381: title = buf;
1382:
1383: /*
1384: * Build the file structure.
1385: */
1386:
1.2 schwarze 1387: nof = mandoc_calloc(1, sizeof(struct of));
1.6 schwarze 1388: nof->fname = mandoc_strdup(argv[i]);
1389: if (NULL != sec)
1390: nof->sec = mandoc_strdup(sec);
1391: if (NULL != arch)
1392: nof->arch = mandoc_strdup(arch);
1393: nof->title = mandoc_strdup(title);
1.11 schwarze 1394: nof->src_form = src_form;
1.6 schwarze 1395:
1396: /*
1397: * Add the structure to the list.
1398: */
1399:
1.2 schwarze 1400: if (verb > 2)
1401: printf("%s: Scheduling\n", argv[i]);
1402: if (NULL == *of) {
1403: *of = nof;
1404: (*of)->first = nof;
1405: } else {
1406: nof->first = (*of)->first;
1407: (*of)->next = nof;
1408: *of = nof;
1409: }
1410: }
1411: }
1412:
1413: /*
1414: * Recursively build up a list of files to parse.
1415: * We use this instead of ftw() and so on because I don't want global
1416: * variables hanging around.
1417: * This ignores the mandoc.db and mandoc.index files, but assumes that
1418: * everything else is a manual.
1419: * Pass in a pointer to a NULL structure for the first invocation.
1420: */
1421: static int
1.6 schwarze 1422: ofile_dirbuild(const char *dir, const char* psec, const char *parch,
1.11 schwarze 1423: int p_src_form, int use_all, int verb, struct of **of)
1.2 schwarze 1424: {
1425: char buf[MAXPATHLEN];
1.11 schwarze 1426: struct stat sb;
1.2 schwarze 1427: size_t sz;
1428: DIR *d;
1.6 schwarze 1429: const char *fn, *sec, *arch;
1.11 schwarze 1430: char *p, *q, *suffix;
1.2 schwarze 1431: struct of *nof;
1432: struct dirent *dp;
1.11 schwarze 1433: int src_form;
1.2 schwarze 1434:
1435: if (NULL == (d = opendir(dir))) {
1436: perror(dir);
1437: return(0);
1438: }
1439:
1440: while (NULL != (dp = readdir(d))) {
1441: fn = dp->d_name;
1.6 schwarze 1442:
1443: if ('.' == *fn)
1444: continue;
1445:
1.11 schwarze 1446: src_form = p_src_form;
1447:
1.2 schwarze 1448: if (DT_DIR == dp->d_type) {
1.6 schwarze 1449: sec = psec;
1450: arch = parch;
1451:
1452: /*
1.8 schwarze 1453: * By default, only use directories called:
1.11 schwarze 1454: * man<section>/[<arch>/] or
1455: * cat<section>/[<arch>/]
1.6 schwarze 1456: */
1457:
1458: if (NULL == sec) {
1.11 schwarze 1459: if(0 == strncmp("man", fn, 3)) {
1460: src_form |= MANDOC_SRC;
1.6 schwarze 1461: sec = fn + 3;
1.11 schwarze 1462: } else if (0 == strncmp("cat", fn, 3)) {
1463: src_form |= MANDOC_FORM;
1464: sec = fn + 3;
1465: } else if (use_all)
1.6 schwarze 1466: sec = fn;
1467: else
1468: continue;
1469: } else if (NULL == arch && (use_all ||
1470: NULL == strchr(fn, '.')))
1471: arch = fn;
1472: else if (0 == use_all)
1.2 schwarze 1473: continue;
1474:
1475: buf[0] = '\0';
1476: strlcat(buf, dir, MAXPATHLEN);
1477: strlcat(buf, "/", MAXPATHLEN);
1478: sz = strlcat(buf, fn, MAXPATHLEN);
1479:
1.6 schwarze 1480: if (MAXPATHLEN <= sz) {
1481: fprintf(stderr, "%s: Path too long\n", dir);
1482: return(0);
1483: }
1484:
1485: if (verb > 2)
1486: printf("%s: Scanning\n", buf);
1487:
1488: if ( ! ofile_dirbuild(buf, sec, arch,
1.11 schwarze 1489: src_form, use_all, verb, of))
1.6 schwarze 1490: return(0);
1491: }
1492: if (DT_REG != dp->d_type ||
1493: (NULL == psec && !use_all) ||
1494: !strcmp(MANDOC_DB, fn) ||
1495: !strcmp(MANDOC_IDX, fn))
1496: continue;
1497:
1498: /*
1.8 schwarze 1499: * By default, skip files where the file name suffix
1500: * does not agree with the section directory
1501: * they are located in.
1.6 schwarze 1502: */
1503:
1504: suffix = strrchr(fn, '.');
1505: if (0 == use_all) {
1506: if (NULL == suffix)
1.2 schwarze 1507: continue;
1.11 schwarze 1508: if ((MANDOC_SRC & src_form &&
1509: strcmp(suffix + 1, psec)) ||
1510: (MANDOC_FORM & src_form &&
1511: strcmp(suffix + 1, "0")))
1512: continue;
1513: }
1514: if (NULL != suffix) {
1515: if ('0' == suffix[1])
1516: src_form |= MANDOC_FORM;
1517: else if ('1' <= suffix[1] && '9' >= suffix[1])
1518: src_form |= MANDOC_SRC;
1519: }
1520:
1521:
1522: /*
1523: * Skip formatted manuals if a source version is
1524: * available. Ignore the age: it is very unlikely
1525: * that people install newer formatted base manuals
1526: * when they used to have source manuals before,
1527: * and in ports, old manuals get removed on update.
1528: */
1529: if (0 == use_all && MANDOC_FORM & src_form &&
1530: NULL != psec) {
1531: buf[0] = '\0';
1532: strlcat(buf, dir, MAXPATHLEN);
1533: p = strrchr(buf, '/');
1534: if (NULL == p)
1535: p = buf;
1536: else
1537: p++;
1538: if (0 == strncmp("cat", p, 3))
1539: memcpy(p, "man", 3);
1540: strlcat(buf, "/", MAXPATHLEN);
1541: sz = strlcat(buf, fn, MAXPATHLEN);
1542: if (sz >= MAXPATHLEN) {
1543: fprintf(stderr, "%s: Path too long\n", buf);
1.2 schwarze 1544: continue;
1.11 schwarze 1545: }
1546: q = strrchr(buf, '.');
1547: if (NULL != q && p < q++) {
1548: *q = '\0';
1549: sz = strlcat(buf, psec, MAXPATHLEN);
1550: if (sz >= MAXPATHLEN) {
1551: fprintf(stderr,
1552: "%s: Path too long\n", buf);
1553: continue;
1554: }
1555: if (0 == stat(buf, &sb))
1556: continue;
1557: }
1.2 schwarze 1558: }
1559:
1560: buf[0] = '\0';
1561: strlcat(buf, dir, MAXPATHLEN);
1562: strlcat(buf, "/", MAXPATHLEN);
1563: sz = strlcat(buf, fn, MAXPATHLEN);
1564: if (sz >= MAXPATHLEN) {
1565: fprintf(stderr, "%s: Path too long\n", dir);
1.11 schwarze 1566: continue;
1.2 schwarze 1567: }
1568:
1569: nof = mandoc_calloc(1, sizeof(struct of));
1570: nof->fname = mandoc_strdup(buf);
1.6 schwarze 1571: if (NULL != psec)
1572: nof->sec = mandoc_strdup(psec);
1573: if (NULL != parch)
1574: nof->arch = mandoc_strdup(parch);
1.11 schwarze 1575: nof->src_form = src_form;
1.8 schwarze 1576:
1577: /*
1578: * Remember the file name without the extension,
1579: * to be used as the page title in the database.
1580: */
1581:
1.6 schwarze 1582: if (NULL != suffix)
1583: *suffix = '\0';
1584: nof->title = mandoc_strdup(fn);
1.2 schwarze 1585:
1.11 schwarze 1586: /*
1587: * Add the structure to the list.
1588: */
1589:
1.2 schwarze 1590: if (verb > 2)
1591: printf("%s: Scheduling\n", buf);
1592: if (NULL == *of) {
1593: *of = nof;
1594: (*of)->first = nof;
1595: } else {
1596: nof->first = (*of)->first;
1597: (*of)->next = nof;
1598: *of = nof;
1599: }
1600: }
1601:
1.4 schwarze 1602: closedir(d);
1.2 schwarze 1603: return(1);
1604: }
1605:
1606: static void
1607: ofile_free(struct of *of)
1608: {
1609: struct of *nof;
1610:
1611: while (of) {
1612: nof = of->next;
1613: free(of->fname);
1.6 schwarze 1614: free(of->sec);
1615: free(of->arch);
1616: free(of->title);
1.2 schwarze 1617: free(of);
1618: of = nof;
1619: }
1620: }
1621:
1622: static void
1.1 schwarze 1623: usage(void)
1624: {
1625:
1.2 schwarze 1626: fprintf(stderr, "usage: %s [-v] "
1627: "[-d dir [files...] |"
1628: " -u dir [files...] |"
1629: " dir...]\n", progname);
1.1 schwarze 1630: }