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