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