Annotation of mandoc/mandocdb.c, Revision 1.49.2.2
1.49.2.2! schwarze 1: /* $Id: mandocdb.c,v 1.49.2.1 2013/09/17 21:32:07 schwarze Exp $ */
1.1 kristaps 2: /*
1.48 schwarze 3: * Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
4: * Copyright (c) 2011, 2012 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:
1.14 schwarze 22: #include <sys/types.h>
1.1 kristaps 23:
24: #include <assert.h>
1.43 kristaps 25: #include <ctype.h>
1.4 kristaps 26: #include <dirent.h>
1.45 kristaps 27: #include <errno.h>
1.1 kristaps 28: #include <fcntl.h>
29: #include <getopt.h>
1.49.2.1 schwarze 30: #include <limits.h>
1.1 kristaps 31: #include <stdio.h>
32: #include <stdint.h>
33: #include <stdlib.h>
34: #include <string.h>
1.17 schwarze 35: #include <unistd.h>
1.1 kristaps 36:
1.21 kristaps 37: #if defined(__linux__)
38: # include <endian.h>
1.1 kristaps 39: # include <db_185.h>
1.21 kristaps 40: #elif defined(__APPLE__)
41: # include <libkern/OSByteOrder.h>
42: # include <db.h>
1.1 kristaps 43: #else
44: # include <db.h>
45: #endif
46:
47: #include "man.h"
48: #include "mdoc.h"
49: #include "mandoc.h"
1.8 schwarze 50: #include "mandocdb.h"
1.10 kristaps 51: #include "manpath.h"
1.1 kristaps 52:
53: #define MANDOC_BUFSZ BUFSIZ
54: #define MANDOC_SLOP 1024
55:
1.14 schwarze 56: #define MANDOC_SRC 0x1
57: #define MANDOC_FORM 0x2
58:
1.38 schwarze 59: /* Access to the mandoc database on disk. */
60:
61: struct mdb {
1.49.2.1 schwarze 62: char idxn[PATH_MAX]; /* index db filename */
63: char dbn[PATH_MAX]; /* keyword db filename */
1.38 schwarze 64: DB *idx; /* index recno database */
65: DB *db; /* keyword btree database */
66: };
67:
68: /* Stack of temporarily unused index records. */
69:
70: struct recs {
71: recno_t *stack; /* pointer to a malloc'ed array */
72: size_t size; /* number of allocated slots */
73: size_t cur; /* current number of empty records */
74: recno_t last; /* last record number in the index */
75: };
76:
1.5 kristaps 77: /* Tiny list for files. No need to bring in QUEUE. */
78:
1.3 kristaps 79: struct of {
1.5 kristaps 80: char *fname; /* heap-allocated */
1.12 schwarze 81: char *sec;
82: char *arch;
83: char *title;
1.14 schwarze 84: int src_form;
1.5 kristaps 85: struct of *next; /* NULL for last one */
86: struct of *first; /* first in list */
1.3 kristaps 87: };
88:
1.1 kristaps 89: /* Buffer for storing growable data. */
90:
91: struct buf {
92: char *cp;
1.5 kristaps 93: size_t len; /* current length */
94: size_t size; /* total buffer size */
1.1 kristaps 95: };
96:
97: /* Operation we're going to perform. */
98:
99: enum op {
1.38 schwarze 100: OP_DEFAULT = 0, /* new dbs from dir list or default config */
101: OP_CONFFILE, /* new databases from custom config file */
1.5 kristaps 102: OP_UPDATE, /* delete/add entries in existing database */
1.38 schwarze 103: OP_DELETE, /* delete entries from existing database */
104: OP_TEST /* change no databases, report potential problems */
1.1 kristaps 105: };
106:
107: #define MAN_ARGS DB *hash, \
108: struct buf *buf, \
109: struct buf *dbuf, \
110: const struct man_node *n
111: #define MDOC_ARGS DB *hash, \
112: struct buf *buf, \
113: struct buf *dbuf, \
114: const struct mdoc_node *n, \
115: const struct mdoc_meta *m
116:
117: static void buf_appendmdoc(struct buf *,
118: const struct mdoc_node *, int);
119: static void buf_append(struct buf *, const char *);
120: static void buf_appendb(struct buf *,
121: const void *, size_t);
122: static void dbt_put(DB *, const char *, DBT *, DBT *);
1.9 kristaps 123: static void hash_put(DB *, const struct buf *, uint64_t);
1.1 kristaps 124: static void hash_reset(DB **);
1.3 kristaps 125: static void index_merge(const struct of *, struct mparse *,
1.16 schwarze 126: struct buf *, struct buf *, DB *,
1.49.2.2! schwarze 127: struct mdb *, struct recs *);
1.38 schwarze 128: static void index_prune(const struct of *, struct mdb *,
1.49.2.2! schwarze 129: struct recs *);
! 130: static void ofile_argbuild(int, char *[], struct of **,
! 131: const char *);
1.35 kristaps 132: static void ofile_dirbuild(const char *, const char *,
1.49.2.2! schwarze 133: const char *, int, struct of **);
1.4 kristaps 134: static void ofile_free(struct of *);
1.49.2.2! schwarze 135: static void pformatted(DB *, struct buf *,
! 136: struct buf *, const struct of *);
1.1 kristaps 137: static int pman_node(MAN_ARGS);
138: static void pmdoc_node(MDOC_ARGS);
1.25 schwarze 139: static int pmdoc_head(MDOC_ARGS);
140: static int pmdoc_body(MDOC_ARGS);
141: static int pmdoc_Fd(MDOC_ARGS);
142: static int pmdoc_In(MDOC_ARGS);
143: static int pmdoc_Fn(MDOC_ARGS);
144: static int pmdoc_Nd(MDOC_ARGS);
145: static int pmdoc_Nm(MDOC_ARGS);
146: static int pmdoc_Sh(MDOC_ARGS);
147: static int pmdoc_St(MDOC_ARGS);
148: static int pmdoc_Xr(MDOC_ARGS);
1.1 kristaps 149:
1.25 schwarze 150: #define MDOCF_CHILD 0x01 /* Automatically index child nodes. */
1.1 kristaps 151:
1.25 schwarze 152: struct mdoc_handler {
153: int (*fp)(MDOC_ARGS); /* Optional handler. */
154: uint64_t mask; /* Set unless handler returns 0. */
155: int flags; /* For use by pmdoc_node. */
156: };
157:
158: static const struct mdoc_handler mdocs[MDOC_MAX] = {
159: { NULL, 0, 0 }, /* Ap */
160: { NULL, 0, 0 }, /* Dd */
161: { NULL, 0, 0 }, /* Dt */
162: { NULL, 0, 0 }, /* Os */
163: { pmdoc_Sh, TYPE_Sh, MDOCF_CHILD }, /* Sh */
164: { pmdoc_head, TYPE_Ss, MDOCF_CHILD }, /* Ss */
165: { NULL, 0, 0 }, /* Pp */
166: { NULL, 0, 0 }, /* D1 */
167: { NULL, 0, 0 }, /* Dl */
168: { NULL, 0, 0 }, /* Bd */
169: { NULL, 0, 0 }, /* Ed */
170: { NULL, 0, 0 }, /* Bl */
171: { NULL, 0, 0 }, /* El */
172: { NULL, 0, 0 }, /* It */
173: { NULL, 0, 0 }, /* Ad */
174: { NULL, TYPE_An, MDOCF_CHILD }, /* An */
175: { NULL, TYPE_Ar, MDOCF_CHILD }, /* Ar */
176: { NULL, TYPE_Cd, MDOCF_CHILD }, /* Cd */
177: { NULL, TYPE_Cm, MDOCF_CHILD }, /* Cm */
178: { NULL, TYPE_Dv, MDOCF_CHILD }, /* Dv */
179: { NULL, TYPE_Er, MDOCF_CHILD }, /* Er */
180: { NULL, TYPE_Ev, MDOCF_CHILD }, /* Ev */
181: { NULL, 0, 0 }, /* Ex */
182: { NULL, TYPE_Fa, MDOCF_CHILD }, /* Fa */
183: { pmdoc_Fd, TYPE_In, 0 }, /* Fd */
184: { NULL, TYPE_Fl, MDOCF_CHILD }, /* Fl */
185: { pmdoc_Fn, 0, 0 }, /* Fn */
186: { NULL, TYPE_Ft, MDOCF_CHILD }, /* Ft */
187: { NULL, TYPE_Ic, MDOCF_CHILD }, /* Ic */
188: { pmdoc_In, TYPE_In, 0 }, /* In */
189: { NULL, TYPE_Li, MDOCF_CHILD }, /* Li */
190: { pmdoc_Nd, TYPE_Nd, MDOCF_CHILD }, /* Nd */
191: { pmdoc_Nm, TYPE_Nm, MDOCF_CHILD }, /* Nm */
192: { NULL, 0, 0 }, /* Op */
193: { NULL, 0, 0 }, /* Ot */
194: { NULL, TYPE_Pa, MDOCF_CHILD }, /* Pa */
195: { NULL, 0, 0 }, /* Rv */
196: { pmdoc_St, TYPE_St, 0 }, /* St */
197: { NULL, TYPE_Va, MDOCF_CHILD }, /* Va */
198: { pmdoc_body, TYPE_Va, MDOCF_CHILD }, /* Vt */
199: { pmdoc_Xr, TYPE_Xr, 0 }, /* Xr */
200: { NULL, 0, 0 }, /* %A */
201: { NULL, 0, 0 }, /* %B */
202: { NULL, 0, 0 }, /* %D */
203: { NULL, 0, 0 }, /* %I */
204: { NULL, 0, 0 }, /* %J */
205: { NULL, 0, 0 }, /* %N */
206: { NULL, 0, 0 }, /* %O */
207: { NULL, 0, 0 }, /* %P */
208: { NULL, 0, 0 }, /* %R */
209: { NULL, 0, 0 }, /* %T */
210: { NULL, 0, 0 }, /* %V */
211: { NULL, 0, 0 }, /* Ac */
212: { NULL, 0, 0 }, /* Ao */
213: { NULL, 0, 0 }, /* Aq */
214: { NULL, TYPE_At, MDOCF_CHILD }, /* At */
215: { NULL, 0, 0 }, /* Bc */
216: { NULL, 0, 0 }, /* Bf */
217: { NULL, 0, 0 }, /* Bo */
218: { NULL, 0, 0 }, /* Bq */
219: { NULL, TYPE_Bsx, MDOCF_CHILD }, /* Bsx */
220: { NULL, TYPE_Bx, MDOCF_CHILD }, /* Bx */
221: { NULL, 0, 0 }, /* Db */
222: { NULL, 0, 0 }, /* Dc */
223: { NULL, 0, 0 }, /* Do */
224: { NULL, 0, 0 }, /* Dq */
225: { NULL, 0, 0 }, /* Ec */
226: { NULL, 0, 0 }, /* Ef */
227: { NULL, TYPE_Em, MDOCF_CHILD }, /* Em */
228: { NULL, 0, 0 }, /* Eo */
229: { NULL, TYPE_Fx, MDOCF_CHILD }, /* Fx */
230: { NULL, TYPE_Ms, MDOCF_CHILD }, /* Ms */
231: { NULL, 0, 0 }, /* No */
232: { NULL, 0, 0 }, /* Ns */
233: { NULL, TYPE_Nx, MDOCF_CHILD }, /* Nx */
234: { NULL, TYPE_Ox, MDOCF_CHILD }, /* Ox */
235: { NULL, 0, 0 }, /* Pc */
236: { NULL, 0, 0 }, /* Pf */
237: { NULL, 0, 0 }, /* Po */
238: { NULL, 0, 0 }, /* Pq */
239: { NULL, 0, 0 }, /* Qc */
240: { NULL, 0, 0 }, /* Ql */
241: { NULL, 0, 0 }, /* Qo */
242: { NULL, 0, 0 }, /* Qq */
243: { NULL, 0, 0 }, /* Re */
244: { NULL, 0, 0 }, /* Rs */
245: { NULL, 0, 0 }, /* Sc */
246: { NULL, 0, 0 }, /* So */
247: { NULL, 0, 0 }, /* Sq */
248: { NULL, 0, 0 }, /* Sm */
249: { NULL, 0, 0 }, /* Sx */
250: { NULL, TYPE_Sy, MDOCF_CHILD }, /* Sy */
251: { NULL, TYPE_Tn, MDOCF_CHILD }, /* Tn */
252: { NULL, 0, 0 }, /* Ux */
253: { NULL, 0, 0 }, /* Xc */
254: { NULL, 0, 0 }, /* Xo */
255: { pmdoc_head, TYPE_Fn, 0 }, /* Fo */
256: { NULL, 0, 0 }, /* Fc */
257: { NULL, 0, 0 }, /* Oo */
258: { NULL, 0, 0 }, /* Oc */
259: { NULL, 0, 0 }, /* Bk */
260: { NULL, 0, 0 }, /* Ek */
261: { NULL, 0, 0 }, /* Bt */
262: { NULL, 0, 0 }, /* Hf */
263: { NULL, 0, 0 }, /* Fr */
264: { NULL, 0, 0 }, /* Ud */
265: { NULL, TYPE_Lb, MDOCF_CHILD }, /* Lb */
266: { NULL, 0, 0 }, /* Lp */
267: { NULL, TYPE_Lk, MDOCF_CHILD }, /* Lk */
268: { NULL, TYPE_Mt, MDOCF_CHILD }, /* Mt */
269: { NULL, 0, 0 }, /* Brq */
270: { NULL, 0, 0 }, /* Bro */
271: { NULL, 0, 0 }, /* Brc */
272: { NULL, 0, 0 }, /* %C */
273: { NULL, 0, 0 }, /* Es */
274: { NULL, 0, 0 }, /* En */
275: { NULL, TYPE_Dx, MDOCF_CHILD }, /* Dx */
276: { NULL, 0, 0 }, /* %Q */
277: { NULL, 0, 0 }, /* br */
278: { NULL, 0, 0 }, /* sp */
279: { NULL, 0, 0 }, /* %U */
280: { NULL, 0, 0 }, /* Ta */
1.1 kristaps 281: };
282:
283: static const char *progname;
1.16 schwarze 284: static int use_all; /* Use all directories and files. */
285: static int verb; /* Output verbosity level. */
1.38 schwarze 286: static int warnings; /* Potential problems in manuals. */
1.1 kristaps 287:
288: int
289: main(int argc, char *argv[])
290: {
291: struct mparse *mp; /* parse sequence */
1.10 kristaps 292: struct manpaths dirs;
1.38 schwarze 293: struct mdb mdb;
294: struct recs recs;
1.1 kristaps 295: enum op op; /* current operation */
1.5 kristaps 296: const char *dir;
1.49.2.2! schwarze 297: char *cp;
! 298: char pbuf[PATH_MAX];
1.16 schwarze 299: int ch, i, flags;
1.38 schwarze 300: DB *hash; /* temporary keyword hashtable */
1.1 kristaps 301: BTREEINFO info; /* btree configuration */
1.38 schwarze 302: size_t sz1, sz2;
1.1 kristaps 303: struct buf buf, /* keyword buffer */
304: dbuf; /* description buffer */
1.5 kristaps 305: struct of *of; /* list of files for processing */
1.1 kristaps 306: extern int optind;
307: extern char *optarg;
308:
309: progname = strrchr(argv[0], '/');
310: if (progname == NULL)
311: progname = argv[0];
312: else
313: ++progname;
314:
1.10 kristaps 315: memset(&dirs, 0, sizeof(struct manpaths));
1.38 schwarze 316: memset(&mdb, 0, sizeof(struct mdb));
317: memset(&recs, 0, sizeof(struct recs));
1.10 kristaps 318:
1.4 kristaps 319: of = NULL;
1.1 kristaps 320: mp = NULL;
321: hash = NULL;
1.38 schwarze 322: op = OP_DEFAULT;
1.5 kristaps 323: dir = NULL;
1.1 kristaps 324:
1.38 schwarze 325: while (-1 != (ch = getopt(argc, argv, "aC:d:tu:vW")))
1.1 kristaps 326: switch (ch) {
1.12 schwarze 327: case ('a'):
328: use_all = 1;
329: break;
1.34 schwarze 330: case ('C'):
1.38 schwarze 331: if (op) {
332: fprintf(stderr,
333: "-C: conflicting options\n");
334: goto usage;
335: }
336: dir = optarg;
337: op = OP_CONFFILE;
1.34 schwarze 338: break;
1.5 kristaps 339: case ('d'):
1.38 schwarze 340: if (op) {
341: fprintf(stderr,
342: "-d: conflicting options\n");
343: goto usage;
344: }
1.5 kristaps 345: dir = optarg;
346: op = OP_UPDATE;
347: break;
1.38 schwarze 348: case ('t'):
349: dup2(STDOUT_FILENO, STDERR_FILENO);
350: if (op) {
351: fprintf(stderr,
352: "-t: conflicting options\n");
353: goto usage;
354: }
355: op = OP_TEST;
356: use_all = 1;
357: warnings = 1;
358: break;
1.5 kristaps 359: case ('u'):
1.38 schwarze 360: if (op) {
361: fprintf(stderr,
362: "-u: conflicting options\n");
363: goto usage;
364: }
1.5 kristaps 365: dir = optarg;
366: op = OP_DELETE;
367: break;
368: case ('v'):
369: verb++;
370: break;
1.38 schwarze 371: case ('W'):
372: warnings = 1;
373: break;
1.1 kristaps 374: default:
1.38 schwarze 375: goto usage;
1.1 kristaps 376: }
377:
378: argc -= optind;
379: argv += optind;
380:
1.38 schwarze 381: if (OP_CONFFILE == op && argc > 0) {
382: fprintf(stderr, "-C: too many arguments\n");
383: goto usage;
384: }
385:
1.4 kristaps 386: memset(&info, 0, sizeof(BTREEINFO));
1.44 kristaps 387: info.lorder = 4321;
1.4 kristaps 388: info.flags = R_DUP;
1.1 kristaps 389:
1.49 schwarze 390: mp = mparse_alloc(MPARSE_AUTO, MANDOCLEVEL_FATAL, NULL, NULL, NULL);
1.1 kristaps 391:
1.5 kristaps 392: memset(&buf, 0, sizeof(struct buf));
393: memset(&dbuf, 0, sizeof(struct buf));
1.1 kristaps 394:
1.4 kristaps 395: buf.size = dbuf.size = MANDOC_BUFSZ;
1.1 kristaps 396:
1.4 kristaps 397: buf.cp = mandoc_malloc(buf.size);
398: dbuf.cp = mandoc_malloc(dbuf.size);
1.1 kristaps 399:
1.38 schwarze 400: if (OP_TEST == op) {
1.49.2.2! schwarze 401: ofile_argbuild(argc, argv, &of, NULL);
1.38 schwarze 402: if (NULL == of)
403: goto out;
1.49.2.2! schwarze 404: index_merge(of, mp, &dbuf, &buf, hash, &mdb, &recs);
1.38 schwarze 405: goto out;
406: }
1.5 kristaps 407:
408: if (OP_UPDATE == op || OP_DELETE == op) {
1.49.2.2! schwarze 409: if (NULL == realpath(dir, pbuf)) {
! 410: perror(dir);
! 411: exit((int)MANDOCLEVEL_BADARG);
! 412: }
! 413: if (strlcat(pbuf, "/", PATH_MAX) >= PATH_MAX) {
! 414: fprintf(stderr, "%s: path too long\n", pbuf);
! 415: exit((int)MANDOCLEVEL_BADARG);
! 416: }
! 417:
! 418: strlcat(mdb.dbn, pbuf, PATH_MAX);
1.49.2.1 schwarze 419: sz1 = strlcat(mdb.dbn, MANDOC_DB, PATH_MAX);
420:
1.49.2.2! schwarze 421: strlcat(mdb.idxn, pbuf, PATH_MAX);
1.49.2.1 schwarze 422: sz2 = strlcat(mdb.idxn, MANDOC_IDX, PATH_MAX);
1.5 kristaps 423:
1.49.2.1 schwarze 424: if (sz1 >= PATH_MAX || sz2 >= PATH_MAX) {
1.49.2.2! schwarze 425: fprintf(stderr, "%s: path too long\n", mdb.idxn);
1.5 kristaps 426: exit((int)MANDOCLEVEL_BADARG);
427: }
428:
1.44 kristaps 429: flags = O_CREAT | O_RDWR;
1.38 schwarze 430: mdb.db = dbopen(mdb.dbn, flags, 0644, DB_BTREE, &info);
431: mdb.idx = dbopen(mdb.idxn, flags, 0644, DB_RECNO, NULL);
1.5 kristaps 432:
1.38 schwarze 433: if (NULL == mdb.db) {
434: perror(mdb.dbn);
1.5 kristaps 435: exit((int)MANDOCLEVEL_SYSERR);
1.38 schwarze 436: } else if (NULL == mdb.idx) {
437: perror(mdb.idxn);
1.5 kristaps 438: exit((int)MANDOCLEVEL_SYSERR);
439: }
440:
1.49.2.2! schwarze 441: ofile_argbuild(argc, argv, &of, pbuf);
1.5 kristaps 442:
443: if (NULL == of)
444: goto out;
445:
1.49.2.2! schwarze 446: index_prune(of, &mdb, &recs);
1.5 kristaps 447:
1.17 schwarze 448: /*
1.35 kristaps 449: * Go to the root of the respective manual tree.
450: * This must work or no manuals may be found (they're
451: * indexed relative to the root).
1.17 schwarze 452: */
453:
454: if (OP_UPDATE == op) {
1.35 kristaps 455: if (-1 == chdir(dir)) {
456: perror(dir);
457: exit((int)MANDOCLEVEL_SYSERR);
458: }
1.13 schwarze 459: index_merge(of, mp, &dbuf, &buf, hash,
1.49.2.2! schwarze 460: &mdb, &recs);
1.17 schwarze 461: }
1.5 kristaps 462:
463: goto out;
464: }
465:
1.10 kristaps 466: /*
467: * Configure the directories we're going to scan.
468: * If we have command-line arguments, use them.
469: * If not, we use man(1)'s method (see mandocdb.8).
470: */
471:
472: if (argc > 0) {
1.26 kristaps 473: dirs.paths = mandoc_calloc(argc, sizeof(char *));
1.10 kristaps 474: dirs.sz = argc;
1.49.2.2! schwarze 475: for (i = 0; i < argc; i++) {
! 476: if (NULL == (cp = realpath(argv[i], pbuf))) {
! 477: perror(argv[i]);
! 478: goto out;
! 479: }
! 480: dirs.paths[i] = mandoc_strdup(cp);
! 481: }
1.10 kristaps 482: } else
1.38 schwarze 483: manpath_parse(&dirs, dir, NULL, NULL);
1.10 kristaps 484:
485: for (i = 0; i < dirs.sz; i++) {
1.49.2.2! schwarze 486:
1.44 kristaps 487: /*
488: * Go to the root of the respective manual tree.
489: * This must work or no manuals may be found:
490: * They are indexed relative to the root.
491: */
1.1 kristaps 492:
1.44 kristaps 493: if (-1 == chdir(dirs.paths[i])) {
494: perror(dirs.paths[i]);
495: exit((int)MANDOCLEVEL_SYSERR);
1.4 kristaps 496: }
1.3 kristaps 497:
1.49.2.1 schwarze 498: strlcpy(mdb.dbn, MANDOC_DB, PATH_MAX);
499: strlcpy(mdb.idxn, MANDOC_IDX, PATH_MAX);
1.13 schwarze 500:
1.44 kristaps 501: flags = O_CREAT | O_TRUNC | O_RDWR;
1.38 schwarze 502: mdb.db = dbopen(mdb.dbn, flags, 0644, DB_BTREE, &info);
503: mdb.idx = dbopen(mdb.idxn, flags, 0644, DB_RECNO, NULL);
1.3 kristaps 504:
1.38 schwarze 505: if (NULL == mdb.db) {
506: perror(mdb.dbn);
1.5 kristaps 507: exit((int)MANDOCLEVEL_SYSERR);
1.38 schwarze 508: } else if (NULL == mdb.idx) {
509: perror(mdb.idxn);
1.5 kristaps 510: exit((int)MANDOCLEVEL_SYSERR);
511: }
512:
1.44 kristaps 513: /*
514: * Search for manuals and fill the new database.
515: */
1.1 kristaps 516:
1.49.2.2! schwarze 517: ofile_dirbuild(".", "", "", 0, &of);
1.1 kristaps 518:
1.44 kristaps 519: if (NULL != of) {
520: index_merge(of, mp, &dbuf, &buf, hash,
1.49.2.2! schwarze 521: &mdb, &recs);
1.44 kristaps 522: ofile_free(of);
523: of = NULL;
1.35 kristaps 524: }
525:
1.44 kristaps 526: (*mdb.db->close)(mdb.db);
527: (*mdb.idx->close)(mdb.idx);
528: mdb.db = NULL;
529: mdb.idx = NULL;
1.4 kristaps 530: }
1.3 kristaps 531:
1.5 kristaps 532: out:
1.38 schwarze 533: if (mdb.db)
534: (*mdb.db->close)(mdb.db);
535: if (mdb.idx)
536: (*mdb.idx->close)(mdb.idx);
1.3 kristaps 537: if (hash)
538: (*hash->close)(hash);
539: if (mp)
540: mparse_free(mp);
541:
1.10 kristaps 542: manpath_free(&dirs);
1.4 kristaps 543: ofile_free(of);
1.3 kristaps 544: free(buf.cp);
545: free(dbuf.cp);
1.38 schwarze 546: free(recs.stack);
1.3 kristaps 547:
1.5 kristaps 548: return(MANDOCLEVEL_OK);
1.38 schwarze 549:
550: usage:
551: fprintf(stderr,
1.49.2.2! schwarze 552: "usage: %s [-avvv] [-C file] | dir ... | -t file ...\n"
1.38 schwarze 553: " -d dir [file ...] | "
554: "-u dir [file ...]\n",
555: progname);
556:
557: return((int)MANDOCLEVEL_BADARG);
1.3 kristaps 558: }
559:
560: void
561: index_merge(const struct of *of, struct mparse *mp,
1.16 schwarze 562: struct buf *dbuf, struct buf *buf, DB *hash,
1.49.2.2! schwarze 563: struct mdb *mdb, struct recs *recs)
1.3 kristaps 564: {
565: recno_t rec;
1.38 schwarze 566: int ch, skip;
1.3 kristaps 567: DBT key, val;
1.44 kristaps 568: DB *files; /* temporary file name table */
1.3 kristaps 569: struct mdoc *mdoc;
570: struct man *man;
1.38 schwarze 571: const char *fn, *msec, *march, *mtitle;
1.49.2.2! schwarze 572: char *p;
1.37 schwarze 573: uint64_t mask;
1.3 kristaps 574: size_t sv;
575: unsigned seq;
1.39 schwarze 576: uint64_t vbuf[2];
1.36 kristaps 577: char type;
1.3 kristaps 578:
1.44 kristaps 579: if (warnings) {
580: files = NULL;
581: hash_reset(&files);
582: }
583:
1.38 schwarze 584: rec = 0;
585: for (of = of->first; of; of = of->next) {
1.3 kristaps 586: fn = of->fname;
1.14 schwarze 587:
588: /*
1.33 schwarze 589: * Try interpreting the file as mdoc(7) or man(7)
590: * source code, unless it is already known to be
591: * formatted. Fall back to formatted mode.
1.14 schwarze 592: */
593:
1.1 kristaps 594: mparse_reset(mp);
1.14 schwarze 595: mdoc = NULL;
596: man = NULL;
1.1 kristaps 597:
1.14 schwarze 598: if ((MANDOC_SRC & of->src_form ||
599: ! (MANDOC_FORM & of->src_form)) &&
600: MANDOCLEVEL_FATAL > mparse_readfd(mp, -1, fn))
601: mparse_result(mp, &mdoc, &man);
602:
603: if (NULL != mdoc) {
604: msec = mdoc_meta(mdoc)->msec;
1.38 schwarze 605: march = mdoc_meta(mdoc)->arch;
606: if (NULL == march)
607: march = "";
1.14 schwarze 608: mtitle = mdoc_meta(mdoc)->title;
609: } else if (NULL != man) {
610: msec = man_meta(man)->msec;
1.38 schwarze 611: march = "";
1.14 schwarze 612: mtitle = man_meta(man)->title;
613: } else {
614: msec = of->sec;
1.38 schwarze 615: march = of->arch;
1.14 schwarze 616: mtitle = of->title;
1.1 kristaps 617: }
618:
1.12 schwarze 619: /*
1.44 kristaps 620: * Check whether the manual section given in a file
621: * agrees with the directory where the file is located.
622: * Some manuals have suffixes like (3p) on their
623: * section number either inside the file or in the
624: * directory name, some are linked into more than one
625: * section, like encrypt(1) = makekey(8). Do not skip
626: * manuals for such reasons.
1.12 schwarze 627: */
628:
1.38 schwarze 629: skip = 0;
630: assert(of->sec);
631: assert(msec);
1.49.2.2! schwarze 632: if (warnings)
! 633: if (strcasecmp(msec, of->sec))
! 634: fprintf(stderr, "%s: "
! 635: "section \"%s\" manual "
! 636: "in \"%s\" directory\n",
! 637: fn, msec, of->sec);
! 638:
1.42 schwarze 639: /*
640: * Manual page directories exist for each kernel
641: * architecture as returned by machine(1).
642: * However, many manuals only depend on the
643: * application architecture as returned by arch(1).
644: * For example, some (2/ARM) manuals are shared
645: * across the "armish" and "zaurus" kernel
646: * architectures.
647: * A few manuals are even shared across completely
648: * different architectures, for example fdformat(1)
649: * on amd64, i386, sparc, and sparc64.
650: * Thus, warn about architecture mismatches,
651: * but don't skip manuals for this reason.
652: */
653:
1.38 schwarze 654: assert(of->arch);
655: assert(march);
1.49.2.2! schwarze 656: if (warnings)
! 657: if (strcasecmp(march, of->arch))
! 658: fprintf(stderr, "%s: "
! 659: "architecture \"%s\" manual "
! 660: "in \"%s\" directory\n",
! 661: fn, march, of->arch);
1.12 schwarze 662:
1.38 schwarze 663: /*
1.12 schwarze 664: * By default, skip a file if the title given
665: * in the file disagrees with the file name.
1.44 kristaps 666: * Do not warn, this happens for all MLINKs.
1.12 schwarze 667: */
668:
669: assert(of->title);
670: assert(mtitle);
1.44 kristaps 671: if (strcasecmp(mtitle, of->title))
1.38 schwarze 672: skip = 1;
1.44 kristaps 673:
674: /*
675: * Build a title string for the file. If it matches
676: * the location of the file, remember the title as
677: * found; else, remember it as missing.
678: */
679:
680: if (warnings) {
681: buf->len = 0;
682: buf_appendb(buf, mtitle, strlen(mtitle));
683: buf_appendb(buf, "(", 1);
684: buf_appendb(buf, msec, strlen(msec));
685: if ('\0' != *march) {
686: buf_appendb(buf, "/", 1);
687: buf_appendb(buf, march, strlen(march));
688: }
689: buf_appendb(buf, ")", 2);
690: for (p = buf->cp; '\0' != *p; p++)
691: *p = tolower(*p);
692: key.data = buf->cp;
693: key.size = buf->len;
694: val.data = NULL;
695: val.size = 0;
696: if (0 == skip)
1.49.2.2! schwarze 697: val.data = "";
1.44 kristaps 698: else {
699: ch = (*files->get)(files, &key, &val, 0);
700: if (ch < 0) {
701: perror("hash");
702: exit((int)MANDOCLEVEL_SYSERR);
703: } else if (ch > 0) {
704: val.data = (void *)fn;
705: val.size = strlen(fn) + 1;
706: } else
707: val.data = NULL;
708: }
709: if (NULL != val.data &&
710: (*files->put)(files, &key, &val, 0) < 0) {
711: perror("hash");
712: exit((int)MANDOCLEVEL_SYSERR);
713: }
714: }
1.12 schwarze 715:
1.38 schwarze 716: if (skip && !use_all)
1.12 schwarze 717: continue;
718:
1.38 schwarze 719: /*
1.1 kristaps 720: * The index record value consists of a nil-terminated
721: * filename, a nil-terminated manual section, and a
1.44 kristaps 722: * nil-terminated description. Use the actual
723: * location of the file, such that the user can find
724: * it with man(1). Since the description may not be
725: * set, we set a sentinel to see if we're going to
726: * write a nil byte in its place.
1.1 kristaps 727: */
728:
1.3 kristaps 729: dbuf->len = 0;
1.36 kristaps 730: type = mdoc ? 'd' : (man ? 'a' : 'c');
731: buf_appendb(dbuf, &type, 1);
1.3 kristaps 732: buf_appendb(dbuf, fn, strlen(fn) + 1);
1.44 kristaps 733: buf_appendb(dbuf, of->sec, strlen(of->sec) + 1);
734: buf_appendb(dbuf, of->title, strlen(of->title) + 1);
735: buf_appendb(dbuf, of->arch, strlen(of->arch) + 1);
1.1 kristaps 736:
1.3 kristaps 737: sv = dbuf->len;
1.1 kristaps 738:
1.33 schwarze 739: /*
740: * Collect keyword/mask pairs.
741: * Each pair will become a new btree node.
742: */
1.1 kristaps 743:
1.33 schwarze 744: hash_reset(&hash);
1.1 kristaps 745: if (mdoc)
1.3 kristaps 746: pmdoc_node(hash, buf, dbuf,
1.1 kristaps 747: mdoc_node(mdoc), mdoc_meta(mdoc));
1.14 schwarze 748: else if (man)
1.3 kristaps 749: pman_node(hash, buf, dbuf, man_node(man));
1.14 schwarze 750: else
1.49.2.2! schwarze 751: pformatted(hash, buf, dbuf, of);
1.1 kristaps 752:
1.38 schwarze 753: /* Test mode, do not access any database. */
754:
755: if (NULL == mdb->db || NULL == mdb->idx)
756: continue;
757:
1.1 kristaps 758: /*
1.44 kristaps 759: * Make sure the file name is always registered
760: * as an .Nm search key.
761: */
762: buf->len = 0;
763: buf_append(buf, of->title);
764: hash_put(hash, buf, TYPE_Nm);
765:
766: /*
1.33 schwarze 767: * Reclaim an empty index record, if available.
768: * Use its record number for all new btree nodes.
1.1 kristaps 769: */
770:
1.38 schwarze 771: if (recs->cur > 0) {
772: recs->cur--;
773: rec = recs->stack[(int)recs->cur];
774: } else if (recs->last > 0) {
775: rec = recs->last;
776: recs->last = 0;
1.33 schwarze 777: } else
778: rec++;
1.39 schwarze 779: vbuf[1] = htobe64(rec);
1.33 schwarze 780:
781: /*
782: * Copy from the in-memory hashtable of pending
783: * keyword/mask pairs into the database.
784: */
785:
1.1 kristaps 786: seq = R_FIRST;
787: while (0 == (ch = (*hash->seq)(hash, &key, &val, seq))) {
788: seq = R_NEXT;
1.37 schwarze 789: assert(sizeof(uint64_t) == val.size);
790: memcpy(&mask, val.data, val.size);
1.39 schwarze 791: vbuf[0] = htobe64(mask);
792: val.size = sizeof(vbuf);
1.9 kristaps 793: val.data = &vbuf;
1.38 schwarze 794: dbt_put(mdb->db, mdb->dbn, &key, &val);
1.1 kristaps 795: }
796: if (ch < 0) {
797: perror("hash");
1.49.2.2! schwarze 798: unlink(mdb->dbn);
! 799: unlink(mdb->idxn);
1.1 kristaps 800: exit((int)MANDOCLEVEL_SYSERR);
801: }
1.38 schwarze 802:
1.1 kristaps 803: /*
804: * Apply to the index. If we haven't had a description
805: * set, put an empty one in now.
806: */
807:
1.3 kristaps 808: if (dbuf->len == sv)
809: buf_appendb(dbuf, "", 1);
1.1 kristaps 810:
811: key.data = &rec;
812: key.size = sizeof(recno_t);
813:
1.3 kristaps 814: val.data = dbuf->cp;
815: val.size = dbuf->len;
1.1 kristaps 816:
1.5 kristaps 817: if (verb)
1.49.2.2! schwarze 818: printf("%s: adding to index\n", fn);
1.18 kristaps 819:
1.38 schwarze 820: dbt_put(mdb->idx, mdb->idxn, &key, &val);
1.3 kristaps 821: }
1.44 kristaps 822:
823: /*
824: * Iterate the remembered file titles and check that
825: * all files can be found by their main title.
826: */
827:
828: if (warnings) {
829: seq = R_FIRST;
830: while (0 == (*files->seq)(files, &key, &val, seq)) {
831: seq = R_NEXT;
832: if (val.size)
1.49.2.2! schwarze 833: fprintf(stderr, "%s: probably "
! 834: "unreachable, title is %s\n",
! 835: (char *)val.data, (char *)key.data);
1.44 kristaps 836: }
837: (*files->close)(files);
838: }
1.3 kristaps 839: }
840:
841: /*
842: * Scan through all entries in the index file `idx' and prune those
843: * entries in `ofile'.
844: * Pruning consists of removing from `db', then invalidating the entry
845: * in `idx' (zeroing its value size).
846: */
847: static void
1.49.2.2! schwarze 848: index_prune(const struct of *ofile, struct mdb *mdb, struct recs *recs)
1.3 kristaps 849: {
850: const struct of *of;
1.36 kristaps 851: const char *fn;
1.39 schwarze 852: uint64_t vbuf[2];
1.3 kristaps 853: unsigned seq, sseq;
854: DBT key, val;
855: int ch;
856:
1.38 schwarze 857: recs->cur = 0;
1.3 kristaps 858: seq = R_FIRST;
1.38 schwarze 859: while (0 == (ch = (*mdb->idx->seq)(mdb->idx, &key, &val, seq))) {
1.3 kristaps 860: seq = R_NEXT;
1.37 schwarze 861: assert(sizeof(recno_t) == key.size);
1.38 schwarze 862: memcpy(&recs->last, key.data, key.size);
1.18 kristaps 863:
864: /* Deleted records are zero-sized. Skip them. */
865:
866: if (0 == val.size)
867: goto cont;
868:
869: /*
870: * Make sure we're sane.
871: * Read past our mdoc/man/cat type to the next string,
872: * then make sure it's bounded by a NUL.
873: * Failing any of these, we go into our error handler.
874: */
875:
1.36 kristaps 876: fn = (char *)val.data + 1;
877: if (NULL == memchr(fn, '\0', val.size - 1))
1.18 kristaps 878: break;
879:
1.38 schwarze 880: /*
1.18 kristaps 881: * Search for the file in those we care about.
882: * XXX: build this into a tree. Too slow.
883: */
1.3 kristaps 884:
1.38 schwarze 885: for (of = ofile->first; of; of = of->next)
1.3 kristaps 886: if (0 == strcmp(fn, of->fname))
887: break;
888:
889: if (NULL == of)
890: continue;
891:
1.18 kristaps 892: /*
893: * Search through the keyword database, throwing out all
894: * references to our file.
895: */
896:
1.3 kristaps 897: sseq = R_FIRST;
1.38 schwarze 898: while (0 == (ch = (*mdb->db->seq)(mdb->db,
899: &key, &val, sseq))) {
1.3 kristaps 900: sseq = R_NEXT;
1.39 schwarze 901: if (sizeof(vbuf) != val.size)
1.18 kristaps 902: break;
903:
1.39 schwarze 904: memcpy(vbuf, val.data, val.size);
905: if (recs->last != betoh64(vbuf[1]))
1.3 kristaps 906: continue;
1.18 kristaps 907:
1.38 schwarze 908: if ((ch = (*mdb->db->del)(mdb->db,
909: &key, R_CURSOR)) < 0)
1.3 kristaps 910: break;
911: }
1.18 kristaps 912:
1.3 kristaps 913: if (ch < 0) {
1.38 schwarze 914: perror(mdb->dbn);
1.3 kristaps 915: exit((int)MANDOCLEVEL_SYSERR);
1.18 kristaps 916: } else if (1 != ch) {
1.38 schwarze 917: fprintf(stderr, "%s: corrupt database\n",
918: mdb->dbn);
1.18 kristaps 919: exit((int)MANDOCLEVEL_SYSERR);
1.3 kristaps 920: }
1.1 kristaps 921:
1.5 kristaps 922: if (verb)
1.49.2.2! schwarze 923: printf("%s: deleting from index\n", fn);
1.1 kristaps 924:
1.3 kristaps 925: val.size = 0;
1.38 schwarze 926: ch = (*mdb->idx->put)(mdb->idx, &key, &val, R_CURSOR);
1.1 kristaps 927:
1.18 kristaps 928: if (ch < 0)
929: break;
930: cont:
1.38 schwarze 931: if (recs->cur >= recs->size) {
932: recs->size += MANDOC_SLOP;
933: recs->stack = mandoc_realloc(recs->stack,
934: recs->size * sizeof(recno_t));
1.3 kristaps 935: }
1.1 kristaps 936:
1.38 schwarze 937: recs->stack[(int)recs->cur] = recs->last;
938: recs->cur++;
1.3 kristaps 939: }
1.18 kristaps 940:
941: if (ch < 0) {
1.38 schwarze 942: perror(mdb->idxn);
1.18 kristaps 943: exit((int)MANDOCLEVEL_SYSERR);
944: } else if (1 != ch) {
1.38 schwarze 945: fprintf(stderr, "%s: corrupt index\n", mdb->idxn);
1.18 kristaps 946: exit((int)MANDOCLEVEL_SYSERR);
947: }
948:
1.38 schwarze 949: recs->last++;
1.1 kristaps 950: }
951:
952: /*
953: * Grow the buffer (if necessary) and copy in a binary string.
954: */
955: static void
956: buf_appendb(struct buf *buf, const void *cp, size_t sz)
957: {
958:
959: /* Overshoot by MANDOC_BUFSZ. */
960:
961: while (buf->len + sz >= buf->size) {
962: buf->size = buf->len + sz + MANDOC_BUFSZ;
963: buf->cp = mandoc_realloc(buf->cp, buf->size);
964: }
965:
966: memcpy(buf->cp + (int)buf->len, cp, sz);
967: buf->len += sz;
968: }
969:
970: /*
971: * Append a nil-terminated string to the buffer.
972: * This can be invoked multiple times.
973: * The buffer string will be nil-terminated.
974: * If invoked multiple times, a space is put between strings.
975: */
976: static void
977: buf_append(struct buf *buf, const char *cp)
978: {
979: size_t sz;
980:
981: if (0 == (sz = strlen(cp)))
982: return;
983:
984: if (buf->len)
985: buf->cp[(int)buf->len - 1] = ' ';
986:
987: buf_appendb(buf, cp, sz + 1);
988: }
989:
990: /*
991: * Recursively add all text from a given node.
992: * This is optimised for general mdoc nodes in this context, which do
993: * not consist of subexpressions and having a recursive call for n->next
994: * would be wasteful.
995: * The "f" variable should be 0 unless called from pmdoc_Nd for the
996: * description buffer, which does not start at the beginning of the
997: * buffer.
998: */
999: static void
1000: buf_appendmdoc(struct buf *buf, const struct mdoc_node *n, int f)
1001: {
1002:
1003: for ( ; n; n = n->next) {
1004: if (n->child)
1005: buf_appendmdoc(buf, n->child, f);
1006:
1007: if (MDOC_TEXT == n->type && f) {
1008: f = 0;
1009: buf_appendb(buf, n->string,
1010: strlen(n->string) + 1);
1011: } else if (MDOC_TEXT == n->type)
1012: buf_append(buf, n->string);
1013:
1014: }
1015: }
1016:
1017: static void
1018: hash_reset(DB **db)
1019: {
1020: DB *hash;
1021:
1022: if (NULL != (hash = *db))
1023: (*hash->close)(hash);
1024:
1.5 kristaps 1025: *db = dbopen(NULL, O_CREAT|O_RDWR, 0644, DB_HASH, NULL);
1.1 kristaps 1026: if (NULL == *db) {
1027: perror("hash");
1028: exit((int)MANDOCLEVEL_SYSERR);
1029: }
1030: }
1031:
1032: /* ARGSUSED */
1.25 schwarze 1033: static int
1034: pmdoc_head(MDOC_ARGS)
1035: {
1036:
1037: return(MDOC_HEAD == n->type);
1038: }
1039:
1040: /* ARGSUSED */
1041: static int
1042: pmdoc_body(MDOC_ARGS)
1043: {
1044:
1045: return(MDOC_BODY == n->type);
1046: }
1047:
1048: /* ARGSUSED */
1049: static int
1.1 kristaps 1050: pmdoc_Fd(MDOC_ARGS)
1051: {
1052: const char *start, *end;
1053: size_t sz;
1.25 schwarze 1054:
1.1 kristaps 1055: if (SEC_SYNOPSIS != n->sec)
1.25 schwarze 1056: return(0);
1.1 kristaps 1057: if (NULL == (n = n->child) || MDOC_TEXT != n->type)
1.25 schwarze 1058: return(0);
1.1 kristaps 1059:
1060: /*
1061: * Only consider those `Fd' macro fields that begin with an
1062: * "inclusion" token (versus, e.g., #define).
1063: */
1064: if (strcmp("#include", n->string))
1.25 schwarze 1065: return(0);
1.1 kristaps 1066:
1067: if (NULL == (n = n->next) || MDOC_TEXT != n->type)
1.25 schwarze 1068: return(0);
1.1 kristaps 1069:
1070: /*
1071: * Strip away the enclosing angle brackets and make sure we're
1072: * not zero-length.
1073: */
1074:
1075: start = n->string;
1076: if ('<' == *start || '"' == *start)
1077: start++;
1078:
1079: if (0 == (sz = strlen(start)))
1.25 schwarze 1080: return(0);
1.1 kristaps 1081:
1082: end = &start[(int)sz - 1];
1083: if ('>' == *end || '"' == *end)
1084: end--;
1085:
1086: assert(end >= start);
1087:
1088: buf_appendb(buf, start, (size_t)(end - start + 1));
1089: buf_appendb(buf, "", 1);
1.25 schwarze 1090: return(1);
1.1 kristaps 1091: }
1092:
1093: /* ARGSUSED */
1.25 schwarze 1094: static int
1095: pmdoc_In(MDOC_ARGS)
1.1 kristaps 1096: {
1097:
1098: if (NULL == n->child || MDOC_TEXT != n->child->type)
1.25 schwarze 1099: return(0);
1.1 kristaps 1100:
1101: buf_append(buf, n->child->string);
1.25 schwarze 1102: return(1);
1.1 kristaps 1103: }
1104:
1105: /* ARGSUSED */
1.25 schwarze 1106: static int
1.1 kristaps 1107: pmdoc_Fn(MDOC_ARGS)
1108: {
1.25 schwarze 1109: struct mdoc_node *nn;
1.1 kristaps 1110: const char *cp;
1111:
1.25 schwarze 1112: nn = n->child;
1113:
1114: if (NULL == nn || MDOC_TEXT != nn->type)
1115: return(0);
1116:
1117: /* .Fn "struct type *name" "char *arg" */
1.1 kristaps 1118:
1.25 schwarze 1119: cp = strrchr(nn->string, ' ');
1.1 kristaps 1120: if (NULL == cp)
1.25 schwarze 1121: cp = nn->string;
1.1 kristaps 1122:
1123: /* Strip away pointer symbol. */
1124:
1125: while ('*' == *cp)
1126: cp++;
1127:
1.25 schwarze 1128: /* Store the function name. */
1129:
1.1 kristaps 1130: buf_append(buf, cp);
1.8 schwarze 1131: hash_put(hash, buf, TYPE_Fn);
1.25 schwarze 1132:
1133: /* Store the function type. */
1134:
1135: if (nn->string < cp) {
1136: buf->len = 0;
1137: buf_appendb(buf, nn->string, cp - nn->string);
1138: buf_appendb(buf, "", 1);
1139: hash_put(hash, buf, TYPE_Ft);
1140: }
1141:
1142: /* Store the arguments. */
1143:
1144: for (nn = nn->next; nn; nn = nn->next) {
1145: if (MDOC_TEXT != nn->type)
1146: continue;
1147: buf->len = 0;
1148: buf_append(buf, nn->string);
1149: hash_put(hash, buf, TYPE_Fa);
1150: }
1151:
1152: return(0);
1.1 kristaps 1153: }
1154:
1155: /* ARGSUSED */
1.25 schwarze 1156: static int
1.1 kristaps 1157: pmdoc_St(MDOC_ARGS)
1158: {
1.25 schwarze 1159:
1.1 kristaps 1160: if (NULL == n->child || MDOC_TEXT != n->child->type)
1.25 schwarze 1161: return(0);
1.1 kristaps 1162:
1163: buf_append(buf, n->child->string);
1.25 schwarze 1164: return(1);
1.1 kristaps 1165: }
1166:
1167: /* ARGSUSED */
1.25 schwarze 1168: static int
1.1 kristaps 1169: pmdoc_Xr(MDOC_ARGS)
1170: {
1171:
1172: if (NULL == (n = n->child))
1.25 schwarze 1173: return(0);
1.1 kristaps 1174:
1175: buf_appendb(buf, n->string, strlen(n->string));
1176:
1177: if (NULL != (n = n->next)) {
1178: buf_appendb(buf, ".", 1);
1179: buf_appendb(buf, n->string, strlen(n->string) + 1);
1180: } else
1181: buf_appendb(buf, ".", 2);
1182:
1.25 schwarze 1183: return(1);
1.1 kristaps 1184: }
1185:
1186: /* ARGSUSED */
1.25 schwarze 1187: static int
1.1 kristaps 1188: pmdoc_Nd(MDOC_ARGS)
1189: {
1190:
1191: if (MDOC_BODY != n->type)
1.25 schwarze 1192: return(0);
1.1 kristaps 1193:
1194: buf_appendmdoc(dbuf, n->child, 1);
1.25 schwarze 1195: return(1);
1.1 kristaps 1196: }
1197:
1198: /* ARGSUSED */
1.25 schwarze 1199: static int
1200: pmdoc_Nm(MDOC_ARGS)
1.1 kristaps 1201: {
1202:
1.25 schwarze 1203: if (SEC_NAME == n->sec)
1204: return(1);
1205: else if (SEC_SYNOPSIS != n->sec || MDOC_HEAD != n->type)
1206: return(0);
1.1 kristaps 1207:
1.25 schwarze 1208: if (NULL == n->child)
1209: buf_append(buf, m->name);
1.1 kristaps 1210:
1.25 schwarze 1211: return(1);
1.1 kristaps 1212: }
1213:
1214: /* ARGSUSED */
1.25 schwarze 1215: static int
1216: pmdoc_Sh(MDOC_ARGS)
1.1 kristaps 1217: {
1218:
1.25 schwarze 1219: return(SEC_CUSTOM == n->sec && MDOC_HEAD == n->type);
1.1 kristaps 1220: }
1221:
1222: static void
1.9 kristaps 1223: hash_put(DB *db, const struct buf *buf, uint64_t mask)
1.1 kristaps 1224: {
1.37 schwarze 1225: uint64_t oldmask;
1.1 kristaps 1226: DBT key, val;
1227: int rc;
1228:
1229: if (buf->len < 2)
1230: return;
1231:
1232: key.data = buf->cp;
1233: key.size = buf->len;
1234:
1235: if ((rc = (*db->get)(db, &key, &val, 0)) < 0) {
1236: perror("hash");
1237: exit((int)MANDOCLEVEL_SYSERR);
1.37 schwarze 1238: } else if (0 == rc) {
1239: assert(sizeof(uint64_t) == val.size);
1240: memcpy(&oldmask, val.data, val.size);
1241: mask |= oldmask;
1242: }
1.1 kristaps 1243:
1244: val.data = &mask;
1.9 kristaps 1245: val.size = sizeof(uint64_t);
1.1 kristaps 1246:
1247: if ((rc = (*db->put)(db, &key, &val, 0)) < 0) {
1248: perror("hash");
1249: exit((int)MANDOCLEVEL_SYSERR);
1250: }
1251: }
1252:
1253: static void
1254: dbt_put(DB *db, const char *dbn, DBT *key, DBT *val)
1255: {
1256:
1257: assert(key->size);
1258: assert(val->size);
1259:
1260: if (0 == (*db->put)(db, key, val, 0))
1261: return;
1262:
1263: perror(dbn);
1264: exit((int)MANDOCLEVEL_SYSERR);
1265: /* NOTREACHED */
1266: }
1267:
1268: /*
1269: * Call out to per-macro handlers after clearing the persistent database
1270: * key. If the macro sets the database key, flush it to the database.
1271: */
1272: static void
1273: pmdoc_node(MDOC_ARGS)
1274: {
1275:
1276: if (NULL == n)
1277: return;
1278:
1279: switch (n->type) {
1280: case (MDOC_HEAD):
1281: /* FALLTHROUGH */
1282: case (MDOC_BODY):
1283: /* FALLTHROUGH */
1284: case (MDOC_TAIL):
1285: /* FALLTHROUGH */
1286: case (MDOC_BLOCK):
1287: /* FALLTHROUGH */
1288: case (MDOC_ELEM):
1.25 schwarze 1289: buf->len = 0;
1290:
1291: /*
1292: * Both NULL handlers and handlers returning true
1293: * request using the data. Only skip the element
1294: * when the handler returns false.
1295: */
1296:
1297: if (NULL != mdocs[n->tok].fp &&
1298: 0 == (*mdocs[n->tok].fp)(hash, buf, dbuf, n, m))
1.1 kristaps 1299: break;
1300:
1.25 schwarze 1301: /*
1302: * For many macros, use the text from all children.
1303: * Set zero flags for macros not needing this.
1304: * In that case, the handler must fill the buffer.
1305: */
1306:
1307: if (MDOCF_CHILD & mdocs[n->tok].flags)
1308: buf_appendmdoc(buf, n->child, 0);
1309:
1310: /*
1311: * Cover the most common case:
1312: * Automatically stage one string per element.
1313: * Set a zero mask for macros not needing this.
1314: * Additional staging can be done in the handler.
1315: */
1316:
1317: if (mdocs[n->tok].mask)
1318: hash_put(hash, buf, mdocs[n->tok].mask);
1.1 kristaps 1319: break;
1320: default:
1321: break;
1322: }
1323:
1324: pmdoc_node(hash, buf, dbuf, n->child, m);
1325: pmdoc_node(hash, buf, dbuf, n->next, m);
1326: }
1327:
1328: static int
1329: pman_node(MAN_ARGS)
1330: {
1331: const struct man_node *head, *body;
1.46 kristaps 1332: char *start, *sv, *title;
1333: size_t sz, titlesz;
1.1 kristaps 1334:
1335: if (NULL == n)
1336: return(0);
1337:
1338: /*
1339: * We're only searching for one thing: the first text child in
1340: * the BODY of a NAME section. Since we don't keep track of
1341: * sections in -man, run some hoops to find out whether we're in
1342: * the correct section or not.
1343: */
1344:
1345: if (MAN_BODY == n->type && MAN_SH == n->tok) {
1346: body = n;
1347: assert(body->parent);
1348: if (NULL != (head = body->parent->head) &&
1349: 1 == head->nchild &&
1350: NULL != (head = (head->child)) &&
1351: MAN_TEXT == head->type &&
1352: 0 == strcmp(head->string, "NAME") &&
1353: NULL != (body = body->child) &&
1354: MAN_TEXT == body->type) {
1355:
1.46 kristaps 1356: title = NULL;
1357: titlesz = 0;
1358: /*
1359: * Suck the entire NAME section into memory.
1360: * Yes, we might run away.
1361: * But too many manuals have big, spread-out
1362: * NAME sections over many lines.
1363: */
1364: for ( ; NULL != body; body = body->next) {
1365: if (MAN_TEXT != body->type)
1366: break;
1367: if (0 == (sz = strlen(body->string)))
1368: continue;
1369: title = mandoc_realloc
1370: (title, titlesz + sz + 1);
1371: memcpy(title + titlesz, body->string, sz);
1372: titlesz += sz + 1;
1373: title[(int)titlesz - 1] = ' ';
1374: }
1375: if (NULL == title)
1376: return(0);
1377:
1378: title = mandoc_realloc(title, titlesz + 1);
1379: title[(int)titlesz] = '\0';
1380:
1381: /* Skip leading space. */
1382:
1383: sv = title;
1384: while (isspace((unsigned char)*sv))
1385: sv++;
1386:
1387: if (0 == (sz = strlen(sv))) {
1388: free(title);
1389: return(0);
1390: }
1391:
1392: /* Erase trailing space. */
1393:
1394: start = &sv[sz - 1];
1395: while (start > sv && isspace((unsigned char)*start))
1396: *start-- = '\0';
1397:
1398: if (start == sv) {
1399: free(title);
1400: return(0);
1401: }
1402:
1403: start = sv;
1.1 kristaps 1404:
1405: /*
1406: * Go through a special heuristic dance here.
1407: * This is why -man manuals are great!
1408: * (I'm being sarcastic: my eyes are bleeding.)
1409: * Conventionally, one or more manual names are
1410: * comma-specified prior to a whitespace, then a
1411: * dash, then a description. Try to puzzle out
1412: * the name parts here.
1413: */
1414:
1415: for ( ;; ) {
1416: sz = strcspn(start, " ,");
1417: if ('\0' == start[(int)sz])
1418: break;
1419:
1420: buf->len = 0;
1421: buf_appendb(buf, start, sz);
1422: buf_appendb(buf, "", 1);
1423:
1.8 schwarze 1424: hash_put(hash, buf, TYPE_Nm);
1.1 kristaps 1425:
1426: if (' ' == start[(int)sz]) {
1427: start += (int)sz + 1;
1428: break;
1429: }
1430:
1431: assert(',' == start[(int)sz]);
1432: start += (int)sz + 1;
1433: while (' ' == *start)
1434: start++;
1435: }
1436:
1437: buf->len = 0;
1438:
1439: if (sv == start) {
1440: buf_append(buf, start);
1.46 kristaps 1441: free(title);
1.1 kristaps 1442: return(1);
1443: }
1444:
1.46 kristaps 1445: while (isspace((unsigned char)*start))
1.1 kristaps 1446: start++;
1447:
1448: if (0 == strncmp(start, "-", 1))
1449: start += 1;
1.43 kristaps 1450: else if (0 == strncmp(start, "\\-\\-", 4))
1451: start += 4;
1.1 kristaps 1452: else if (0 == strncmp(start, "\\-", 2))
1453: start += 2;
1454: else if (0 == strncmp(start, "\\(en", 4))
1455: start += 4;
1456: else if (0 == strncmp(start, "\\(em", 4))
1457: start += 4;
1458:
1459: while (' ' == *start)
1460: start++;
1461:
1462: sz = strlen(start) + 1;
1463: buf_appendb(dbuf, start, sz);
1464: buf_appendb(buf, start, sz);
1465:
1.8 schwarze 1466: hash_put(hash, buf, TYPE_Nd);
1.46 kristaps 1467: free(title);
1.1 kristaps 1468: }
1469: }
1470:
1.7 schwarze 1471: for (n = n->child; n; n = n->next)
1472: if (pman_node(hash, buf, dbuf, n))
1473: return(1);
1.1 kristaps 1474:
1475: return(0);
1476: }
1477:
1.14 schwarze 1478: /*
1479: * Parse a formatted manual page.
1480: * By necessity, this involves rather crude guesswork.
1481: */
1482: static void
1.49.2.2! schwarze 1483: pformatted(DB *hash, struct buf *buf,
! 1484: struct buf *dbuf, const struct of *of)
1.14 schwarze 1485: {
1486: FILE *stream;
1.43 kristaps 1487: char *line, *p, *title;
1488: size_t len, plen, titlesz;
1.14 schwarze 1489:
1490: if (NULL == (stream = fopen(of->fname, "r"))) {
1.49.2.2! schwarze 1491: if (warnings)
! 1492: perror(of->fname);
1.14 schwarze 1493: return;
1494: }
1495:
1496: /*
1497: * Always use the title derived from the filename up front,
1498: * do not even try to find it in the file. This also makes
1499: * sure we don't end up with an orphan index record, even if
1500: * the file content turns out to be completely unintelligible.
1501: */
1502:
1503: buf->len = 0;
1504: buf_append(buf, of->title);
1505: hash_put(hash, buf, TYPE_Nm);
1506:
1.31 schwarze 1507: /* Skip to first blank line. */
1.14 schwarze 1508:
1.28 kristaps 1509: while (NULL != (line = fgetln(stream, &len)))
1.31 schwarze 1510: if ('\n' == *line)
1.28 kristaps 1511: break;
1512:
1.31 schwarze 1513: /*
1514: * Assume the first line that is not indented
1515: * is the first section header. Skip to it.
1.28 kristaps 1516: */
1517:
1518: while (NULL != (line = fgetln(stream, &len)))
1.31 schwarze 1519: if ('\n' != *line && ' ' != *line)
1.28 kristaps 1520: break;
1.43 kristaps 1521:
1522: /*
1523: * Read up until the next section into a buffer.
1524: * Strip the leading and trailing newline from each read line,
1525: * appending a trailing space.
1526: * Ignore empty (whitespace-only) lines.
1527: */
1528:
1529: titlesz = 0;
1530: title = NULL;
1531:
1532: while (NULL != (line = fgetln(stream, &len))) {
1533: if (' ' != *line || '\n' != line[(int)len - 1])
1534: break;
1535: while (len > 0 && isspace((unsigned char)*line)) {
1536: line++;
1537: len--;
1538: }
1539: if (1 == len)
1540: continue;
1541: title = mandoc_realloc(title, titlesz + len);
1542: memcpy(title + titlesz, line, len);
1543: titlesz += len;
1544: title[(int)titlesz - 1] = ' ';
1545: }
1546:
1.49.2.2! schwarze 1547:
1.14 schwarze 1548: /*
1.31 schwarze 1549: * If no page content can be found, or the input line
1550: * is already the next section header, or there is no
1551: * trailing newline, reuse the page title as the page
1552: * description.
1.14 schwarze 1553: */
1554:
1.43 kristaps 1555: if (NULL == title || '\0' == *title) {
1.49.2.2! schwarze 1556: if (warnings)
! 1557: fprintf(stderr, "%s: cannot find NAME section\n",
! 1558: of->fname);
1.14 schwarze 1559: buf_appendb(dbuf, buf->cp, buf->size);
1560: hash_put(hash, buf, TYPE_Nd);
1561: fclose(stream);
1.43 kristaps 1562: free(title);
1.14 schwarze 1563: return;
1564: }
1565:
1.43 kristaps 1566: title = mandoc_realloc(title, titlesz + 1);
1567: title[(int)titlesz] = '\0';
1.28 kristaps 1568:
1.31 schwarze 1569: /*
1570: * Skip to the first dash.
1.28 kristaps 1571: * Use the remaining line as the description (no more than 70
1572: * bytes).
1.14 schwarze 1573: */
1574:
1.43 kristaps 1575: if (NULL != (p = strstr(title, "- "))) {
1.30 kristaps 1576: for (p += 2; ' ' == *p || '\b' == *p; p++)
1.28 kristaps 1577: /* Skip to next word. */ ;
1.38 schwarze 1578: } else {
1.49.2.2! schwarze 1579: if (warnings)
! 1580: fprintf(stderr, "%s: no dash in title line\n",
! 1581: of->fname);
1.43 kristaps 1582: p = title;
1.38 schwarze 1583: }
1.28 kristaps 1584:
1.43 kristaps 1585: plen = strlen(p);
1.29 kristaps 1586:
1587: /* Strip backspace-encoding from line. */
1588:
1589: while (NULL != (line = memchr(p, '\b', plen))) {
1590: len = line - p;
1591: if (0 == len) {
1592: memmove(line, line + 1, plen--);
1593: continue;
1594: }
1595: memmove(line - 1, line + 1, plen - len);
1596: plen -= 2;
1.14 schwarze 1597: }
1598:
1.28 kristaps 1599: buf_appendb(dbuf, p, plen + 1);
1.14 schwarze 1600: buf->len = 0;
1.28 kristaps 1601: buf_appendb(buf, p, plen + 1);
1.14 schwarze 1602: hash_put(hash, buf, TYPE_Nd);
1.28 kristaps 1603: fclose(stream);
1.43 kristaps 1604: free(title);
1.14 schwarze 1605: }
1606:
1.5 kristaps 1607: static void
1.49.2.2! schwarze 1608: ofile_argbuild(int argc, char *argv[], struct of **of,
! 1609: const char *basedir)
1.5 kristaps 1610: {
1.49.2.1 schwarze 1611: char buf[PATH_MAX];
1.49.2.2! schwarze 1612: char pbuf[PATH_MAX];
1.41 kristaps 1613: const char *sec, *arch, *title;
1.49.2.2! schwarze 1614: char *relpath, *p;
1.14 schwarze 1615: int i, src_form;
1.5 kristaps 1616: struct of *nof;
1617:
1618: for (i = 0; i < argc; i++) {
1.49.2.2! schwarze 1619: if (NULL == (relpath = realpath(argv[i], pbuf))) {
! 1620: perror(argv[i]);
! 1621: continue;
! 1622: }
! 1623: if (NULL != basedir) {
! 1624: if (strstr(pbuf, basedir) != pbuf) {
! 1625: fprintf(stderr, "%s: file outside "
! 1626: "base directory %s\n",
! 1627: pbuf, basedir);
! 1628: continue;
! 1629: }
! 1630: relpath = pbuf + strlen(basedir);
! 1631: }
1.12 schwarze 1632:
1633: /*
1634: * Try to infer the manual section, architecture and
1635: * page title from the path, assuming it looks like
1.14 schwarze 1636: * man*[/<arch>]/<title>.<section> or
1637: * cat<section>[/<arch>]/<title>.0
1.12 schwarze 1638: */
1639:
1.49.2.2! schwarze 1640: if (strlcpy(buf, relpath, sizeof(buf)) >= sizeof(buf)) {
! 1641: fprintf(stderr, "%s: path too long\n", relpath);
1.12 schwarze 1642: continue;
1643: }
1.38 schwarze 1644: sec = arch = title = "";
1.14 schwarze 1645: src_form = 0;
1.12 schwarze 1646: p = strrchr(buf, '\0');
1647: while (p-- > buf) {
1.38 schwarze 1648: if ('\0' == *sec && '.' == *p) {
1.12 schwarze 1649: sec = p + 1;
1650: *p = '\0';
1.14 schwarze 1651: if ('0' == *sec)
1652: src_form |= MANDOC_FORM;
1653: else if ('1' <= *sec && '9' >= *sec)
1654: src_form |= MANDOC_SRC;
1.12 schwarze 1655: continue;
1656: }
1657: if ('/' != *p)
1658: continue;
1.38 schwarze 1659: if ('\0' == *title) {
1.12 schwarze 1660: title = p + 1;
1661: *p = '\0';
1662: continue;
1663: }
1.24 schwarze 1664: if (0 == strncmp("man", p + 1, 3))
1.14 schwarze 1665: src_form |= MANDOC_SRC;
1.24 schwarze 1666: else if (0 == strncmp("cat", p + 1, 3))
1.14 schwarze 1667: src_form |= MANDOC_FORM;
1.24 schwarze 1668: else
1.12 schwarze 1669: arch = p + 1;
1670: break;
1671: }
1.38 schwarze 1672: if ('\0' == *title) {
1.49.2.2! schwarze 1673: if (warnings)
! 1674: fprintf(stderr,
! 1675: "%s: cannot deduce title "
! 1676: "from filename\n",
! 1677: relpath);
1.12 schwarze 1678: title = buf;
1.38 schwarze 1679: }
1.12 schwarze 1680:
1681: /*
1682: * Build the file structure.
1683: */
1684:
1.5 kristaps 1685: nof = mandoc_calloc(1, sizeof(struct of));
1.49.2.2! schwarze 1686: nof->fname = mandoc_strdup(relpath);
1.38 schwarze 1687: nof->sec = mandoc_strdup(sec);
1688: nof->arch = mandoc_strdup(arch);
1.12 schwarze 1689: nof->title = mandoc_strdup(title);
1.14 schwarze 1690: nof->src_form = src_form;
1.12 schwarze 1691:
1692: /*
1693: * Add the structure to the list.
1694: */
1695:
1.5 kristaps 1696: if (NULL == *of) {
1697: *of = nof;
1698: (*of)->first = nof;
1699: } else {
1700: nof->first = (*of)->first;
1701: (*of)->next = nof;
1702: *of = nof;
1703: }
1704: }
1705: }
1706:
1.4 kristaps 1707: /*
1708: * Recursively build up a list of files to parse.
1709: * We use this instead of ftw() and so on because I don't want global
1710: * variables hanging around.
1.44 kristaps 1711: * This ignores the mandocdb.db and mandocdb.index files, but assumes that
1.4 kristaps 1712: * everything else is a manual.
1713: * Pass in a pointer to a NULL structure for the first invocation.
1714: */
1.35 kristaps 1715: static void
1.12 schwarze 1716: ofile_dirbuild(const char *dir, const char* psec, const char *parch,
1.49.2.2! schwarze 1717: int p_src_form, struct of **of)
1.4 kristaps 1718: {
1.49.2.1 schwarze 1719: char buf[PATH_MAX];
1.5 kristaps 1720: size_t sz;
1.4 kristaps 1721: DIR *d;
1.12 schwarze 1722: const char *fn, *sec, *arch;
1.14 schwarze 1723: char *p, *q, *suffix;
1.4 kristaps 1724: struct of *nof;
1725: struct dirent *dp;
1.14 schwarze 1726: int src_form;
1.4 kristaps 1727:
1728: if (NULL == (d = opendir(dir))) {
1.49.2.2! schwarze 1729: if (warnings)
! 1730: perror(dir);
1.38 schwarze 1731: return;
1.4 kristaps 1732: }
1733:
1734: while (NULL != (dp = readdir(d))) {
1735: fn = dp->d_name;
1.12 schwarze 1736:
1737: if ('.' == *fn)
1738: continue;
1739:
1.14 schwarze 1740: src_form = p_src_form;
1741:
1.4 kristaps 1742: if (DT_DIR == dp->d_type) {
1.12 schwarze 1743: sec = psec;
1744: arch = parch;
1745:
1746: /*
1747: * By default, only use directories called:
1.14 schwarze 1748: * man<section>/[<arch>/] or
1749: * cat<section>/[<arch>/]
1.12 schwarze 1750: */
1751:
1.38 schwarze 1752: if ('\0' == *sec) {
1.14 schwarze 1753: if(0 == strncmp("man", fn, 3)) {
1754: src_form |= MANDOC_SRC;
1.12 schwarze 1755: sec = fn + 3;
1.14 schwarze 1756: } else if (0 == strncmp("cat", fn, 3)) {
1757: src_form |= MANDOC_FORM;
1758: sec = fn + 3;
1.38 schwarze 1759: } else {
1.49.2.2! schwarze 1760: if (warnings) fprintf(stderr,
! 1761: "%s/%s: bad section\n",
! 1762: dir, fn);
1.38 schwarze 1763: if (use_all)
1764: sec = fn;
1765: else
1766: continue;
1767: }
1768: } else if ('\0' == *arch) {
1769: if (NULL != strchr(fn, '.')) {
1.49.2.2! schwarze 1770: if (warnings) fprintf(stderr,
! 1771: "%s/%s: bad architecture\n",
! 1772: dir, fn);
1.38 schwarze 1773: if (0 == use_all)
1774: continue;
1775: }
1776: arch = fn;
1777: } else {
1.49.2.2! schwarze 1778: if (warnings) fprintf(stderr, "%s/%s: "
! 1779: "excessive subdirectory\n", dir, fn);
1.38 schwarze 1780: if (0 == use_all)
1.12 schwarze 1781: continue;
1.38 schwarze 1782: }
1.5 kristaps 1783:
1784: buf[0] = '\0';
1.49.2.1 schwarze 1785: strlcat(buf, dir, PATH_MAX);
1786: strlcat(buf, "/", PATH_MAX);
1787: sz = strlcat(buf, fn, PATH_MAX);
1.5 kristaps 1788:
1.49.2.1 schwarze 1789: if (PATH_MAX <= sz) {
1.49.2.2! schwarze 1790: if (warnings) fprintf(stderr, "%s/%s: "
! 1791: "path too long\n", dir, fn);
1.38 schwarze 1792: continue;
1.12 schwarze 1793: }
1.38 schwarze 1794:
1.49.2.2! schwarze 1795: ofile_dirbuild(buf, sec, arch, src_form, of);
1.38 schwarze 1796: continue;
1.35 kristaps 1797: }
1.12 schwarze 1798:
1.38 schwarze 1799: if (DT_REG != dp->d_type) {
1.49.2.2! schwarze 1800: if (warnings)
! 1801: fprintf(stderr,
! 1802: "%s/%s: not a regular file\n",
! 1803: dir, fn);
1.38 schwarze 1804: continue;
1805: }
1806: if (!strcmp(MANDOC_DB, fn) || !strcmp(MANDOC_IDX, fn))
1.12 schwarze 1807: continue;
1.38 schwarze 1808: if ('\0' == *psec) {
1.49.2.2! schwarze 1809: if (warnings)
! 1810: fprintf(stderr,
! 1811: "%s/%s: file outside section\n",
! 1812: dir, fn);
1.38 schwarze 1813: if (0 == use_all)
1814: continue;
1815: }
1.12 schwarze 1816:
1817: /*
1818: * By default, skip files where the file name suffix
1819: * does not agree with the section directory
1820: * they are located in.
1821: */
1822:
1823: suffix = strrchr(fn, '.');
1.38 schwarze 1824: if (NULL == suffix) {
1.49.2.2! schwarze 1825: if (warnings)
! 1826: fprintf(stderr,
! 1827: "%s/%s: no filename suffix\n",
! 1828: dir, fn);
1.38 schwarze 1829: if (0 == use_all)
1.5 kristaps 1830: continue;
1.38 schwarze 1831: } else if ((MANDOC_SRC & src_form &&
1832: strcmp(suffix + 1, psec)) ||
1.14 schwarze 1833: (MANDOC_FORM & src_form &&
1.38 schwarze 1834: strcmp(suffix + 1, "0"))) {
1.49.2.2! schwarze 1835: if (warnings)
! 1836: fprintf(stderr,
! 1837: "%s/%s: wrong filename suffix\n",
! 1838: dir, fn);
1.38 schwarze 1839: if (0 == use_all)
1840: continue;
1.14 schwarze 1841: if ('0' == suffix[1])
1842: src_form |= MANDOC_FORM;
1843: else if ('1' <= suffix[1] && '9' >= suffix[1])
1844: src_form |= MANDOC_SRC;
1845: }
1846:
1847: /*
1848: * Skip formatted manuals if a source version is
1849: * available. Ignore the age: it is very unlikely
1850: * that people install newer formatted base manuals
1851: * when they used to have source manuals before,
1852: * and in ports, old manuals get removed on update.
1853: */
1854: if (0 == use_all && MANDOC_FORM & src_form &&
1.38 schwarze 1855: '\0' != *psec) {
1.14 schwarze 1856: buf[0] = '\0';
1.49.2.1 schwarze 1857: strlcat(buf, dir, PATH_MAX);
1.14 schwarze 1858: p = strrchr(buf, '/');
1.38 schwarze 1859: if ('\0' != *parch && NULL != p)
1.32 schwarze 1860: for (p--; p > buf; p--)
1861: if ('/' == *p)
1862: break;
1.14 schwarze 1863: if (NULL == p)
1864: p = buf;
1865: else
1866: p++;
1867: if (0 == strncmp("cat", p, 3))
1868: memcpy(p, "man", 3);
1.49.2.1 schwarze 1869: strlcat(buf, "/", PATH_MAX);
1870: sz = strlcat(buf, fn, PATH_MAX);
1871: if (sz >= PATH_MAX) {
1.49.2.2! schwarze 1872: if (warnings) fprintf(stderr,
! 1873: "%s/%s: path too long\n",
! 1874: dir, fn);
1.5 kristaps 1875: continue;
1.14 schwarze 1876: }
1877: q = strrchr(buf, '.');
1878: if (NULL != q && p < q++) {
1879: *q = '\0';
1.49.2.1 schwarze 1880: sz = strlcat(buf, psec, PATH_MAX);
1881: if (sz >= PATH_MAX) {
1.49.2.2! schwarze 1882: if (warnings) fprintf(stderr,
! 1883: "%s/%s: path too long\n",
! 1884: dir, fn);
1.14 schwarze 1885: continue;
1886: }
1.35 kristaps 1887: if (0 == access(buf, R_OK))
1.14 schwarze 1888: continue;
1889: }
1.5 kristaps 1890: }
1.4 kristaps 1891:
1.38 schwarze 1892: buf[0] = '\0';
1.35 kristaps 1893: assert('.' == dir[0]);
1.38 schwarze 1894: if ('/' == dir[1]) {
1.49.2.1 schwarze 1895: strlcat(buf, dir + 2, PATH_MAX);
1896: strlcat(buf, "/", PATH_MAX);
1.38 schwarze 1897: }
1.49.2.1 schwarze 1898: sz = strlcat(buf, fn, PATH_MAX);
1899: if (sz >= PATH_MAX) {
1.49.2.2! schwarze 1900: if (warnings) fprintf(stderr,
! 1901: "%s/%s: path too long\n", dir, fn);
1.14 schwarze 1902: continue;
1.5 kristaps 1903: }
1904:
1.4 kristaps 1905: nof = mandoc_calloc(1, sizeof(struct of));
1.5 kristaps 1906: nof->fname = mandoc_strdup(buf);
1.38 schwarze 1907: nof->sec = mandoc_strdup(psec);
1908: nof->arch = mandoc_strdup(parch);
1.14 schwarze 1909: nof->src_form = src_form;
1.12 schwarze 1910:
1911: /*
1912: * Remember the file name without the extension,
1913: * to be used as the page title in the database.
1914: */
1915:
1916: if (NULL != suffix)
1917: *suffix = '\0';
1918: nof->title = mandoc_strdup(fn);
1.5 kristaps 1919:
1.14 schwarze 1920: /*
1921: * Add the structure to the list.
1922: */
1.41 kristaps 1923:
1.4 kristaps 1924: if (NULL == *of) {
1925: *of = nof;
1926: (*of)->first = nof;
1927: } else {
1.5 kristaps 1928: nof->first = (*of)->first;
1.4 kristaps 1929: (*of)->next = nof;
1930: *of = nof;
1931: }
1932: }
1933:
1.7 schwarze 1934: closedir(d);
1.4 kristaps 1935: }
1936:
1937: static void
1938: ofile_free(struct of *of)
1939: {
1940: struct of *nof;
1941:
1.41 kristaps 1942: if (NULL != of)
1943: of = of->first;
1944:
1945: while (NULL != of) {
1.4 kristaps 1946: nof = of->next;
1947: free(of->fname);
1.12 schwarze 1948: free(of->sec);
1949: free(of->arch);
1950: free(of->title);
1.4 kristaps 1951: free(of);
1952: of = nof;
1953: }
1.1 kristaps 1954: }
CVSweb