1 /* $Id: mandocdb.c,v 1.49.2.7 2013/10/02 21:03:26 schwarze Exp $ */
3 * Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2011, 2012 Ingo Schwarze <schwarze@openbsd.org>
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.
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.
22 #include <sys/types.h>
37 #if defined(__linux__) || defined(__sun)
40 #elif defined(__APPLE__)
41 # include <libkern/OSByteOrder.h>
44 # include <sys/endian.h>
58 #define MANDOC_BUFSZ BUFSIZ
59 #define MANDOC_SLOP 1024
61 #define MANDOC_SRC 0x1
62 #define MANDOC_FORM 0x2
64 /* Access to the mandoc database on disk. */
67 char idxn
[PATH_MAX
]; /* index db filename */
68 char dbn
[PATH_MAX
]; /* keyword db filename */
69 DB
*idx
; /* index recno database */
70 DB
*db
; /* keyword btree database */
73 /* Stack of temporarily unused index records. */
76 recno_t
*stack
; /* pointer to a malloc'ed array */
77 size_t size
; /* number of allocated slots */
78 size_t cur
; /* current number of empty records */
79 recno_t last
; /* last record number in the index */
82 /* Tiny list for files. No need to bring in QUEUE. */
85 char *fname
; /* heap-allocated */
90 struct of
*next
; /* NULL for last one */
91 struct of
*first
; /* first in list */
94 /* Buffer for storing growable data. */
98 size_t len
; /* current length */
99 size_t size
; /* total buffer size */
102 /* Operation we're going to perform. */
105 OP_DEFAULT
= 0, /* new dbs from dir list or default config */
106 OP_CONFFILE
, /* new databases from custom config file */
107 OP_UPDATE
, /* delete/add entries in existing database */
108 OP_DELETE
, /* delete entries from existing database */
109 OP_TEST
/* change no databases, report potential problems */
112 #define MAN_ARGS DB *hash, \
115 const struct man_node *n
116 #define MDOC_ARGS DB *hash, \
119 const struct mdoc_node *n, \
120 const struct mdoc_meta *m
122 static void buf_appendmdoc(struct buf
*,
123 const struct mdoc_node
*, int);
124 static void buf_append(struct buf
*, const char *);
125 static void buf_appendb(struct buf
*,
126 const void *, size_t);
127 static void dbt_put(DB
*, const char *, DBT
*, DBT
*);
128 static void hash_put(DB
*, const struct buf
*, uint64_t);
129 static void hash_reset(DB
**);
130 static void index_merge(const struct of
*, struct mparse
*,
131 struct buf
*, struct buf
*, DB
*,
132 struct mdb
*, struct recs
*);
133 static void index_prune(const struct of
*, struct mdb
*,
135 static void ofile_argbuild(int, char *[], struct of
**,
137 static void ofile_dirbuild(const char *, const char *,
138 const char *, int, struct of
**);
139 static void ofile_free(struct of
*);
140 static void pformatted(DB
*, struct buf
*,
141 struct buf
*, const struct of
*);
142 static int pman_node(MAN_ARGS
);
143 static void pmdoc_node(MDOC_ARGS
);
144 static int pmdoc_head(MDOC_ARGS
);
145 static int pmdoc_body(MDOC_ARGS
);
146 static int pmdoc_Fd(MDOC_ARGS
);
147 static int pmdoc_In(MDOC_ARGS
);
148 static int pmdoc_Fn(MDOC_ARGS
);
149 static int pmdoc_Nd(MDOC_ARGS
);
150 static int pmdoc_Nm(MDOC_ARGS
);
151 static int pmdoc_Sh(MDOC_ARGS
);
152 static int pmdoc_St(MDOC_ARGS
);
153 static int pmdoc_Xr(MDOC_ARGS
);
155 #define MDOCF_CHILD 0x01 /* Automatically index child nodes. */
157 struct mdoc_handler
{
158 int (*fp
)(MDOC_ARGS
); /* Optional handler. */
159 uint64_t mask
; /* Set unless handler returns 0. */
160 int flags
; /* For use by pmdoc_node. */
163 static const struct mdoc_handler mdocs
[MDOC_MAX
] = {
164 { NULL
, 0, 0 }, /* Ap */
165 { NULL
, 0, 0 }, /* Dd */
166 { NULL
, 0, 0 }, /* Dt */
167 { NULL
, 0, 0 }, /* Os */
168 { pmdoc_Sh
, TYPE_Sh
, MDOCF_CHILD
}, /* Sh */
169 { pmdoc_head
, TYPE_Ss
, MDOCF_CHILD
}, /* Ss */
170 { NULL
, 0, 0 }, /* Pp */
171 { NULL
, 0, 0 }, /* D1 */
172 { NULL
, 0, 0 }, /* Dl */
173 { NULL
, 0, 0 }, /* Bd */
174 { NULL
, 0, 0 }, /* Ed */
175 { NULL
, 0, 0 }, /* Bl */
176 { NULL
, 0, 0 }, /* El */
177 { NULL
, 0, 0 }, /* It */
178 { NULL
, 0, 0 }, /* Ad */
179 { NULL
, TYPE_An
, MDOCF_CHILD
}, /* An */
180 { NULL
, TYPE_Ar
, MDOCF_CHILD
}, /* Ar */
181 { NULL
, TYPE_Cd
, MDOCF_CHILD
}, /* Cd */
182 { NULL
, TYPE_Cm
, MDOCF_CHILD
}, /* Cm */
183 { NULL
, TYPE_Dv
, MDOCF_CHILD
}, /* Dv */
184 { NULL
, TYPE_Er
, MDOCF_CHILD
}, /* Er */
185 { NULL
, TYPE_Ev
, MDOCF_CHILD
}, /* Ev */
186 { NULL
, 0, 0 }, /* Ex */
187 { NULL
, TYPE_Fa
, MDOCF_CHILD
}, /* Fa */
188 { pmdoc_Fd
, TYPE_In
, 0 }, /* Fd */
189 { NULL
, TYPE_Fl
, MDOCF_CHILD
}, /* Fl */
190 { pmdoc_Fn
, 0, 0 }, /* Fn */
191 { NULL
, TYPE_Ft
, MDOCF_CHILD
}, /* Ft */
192 { NULL
, TYPE_Ic
, MDOCF_CHILD
}, /* Ic */
193 { pmdoc_In
, TYPE_In
, 0 }, /* In */
194 { NULL
, TYPE_Li
, MDOCF_CHILD
}, /* Li */
195 { pmdoc_Nd
, TYPE_Nd
, MDOCF_CHILD
}, /* Nd */
196 { pmdoc_Nm
, TYPE_Nm
, MDOCF_CHILD
}, /* Nm */
197 { NULL
, 0, 0 }, /* Op */
198 { NULL
, 0, 0 }, /* Ot */
199 { NULL
, TYPE_Pa
, MDOCF_CHILD
}, /* Pa */
200 { NULL
, 0, 0 }, /* Rv */
201 { pmdoc_St
, TYPE_St
, 0 }, /* St */
202 { NULL
, TYPE_Va
, MDOCF_CHILD
}, /* Va */
203 { pmdoc_body
, TYPE_Va
, MDOCF_CHILD
}, /* Vt */
204 { pmdoc_Xr
, TYPE_Xr
, 0 }, /* Xr */
205 { NULL
, 0, 0 }, /* %A */
206 { NULL
, 0, 0 }, /* %B */
207 { NULL
, 0, 0 }, /* %D */
208 { NULL
, 0, 0 }, /* %I */
209 { NULL
, 0, 0 }, /* %J */
210 { NULL
, 0, 0 }, /* %N */
211 { NULL
, 0, 0 }, /* %O */
212 { NULL
, 0, 0 }, /* %P */
213 { NULL
, 0, 0 }, /* %R */
214 { NULL
, 0, 0 }, /* %T */
215 { NULL
, 0, 0 }, /* %V */
216 { NULL
, 0, 0 }, /* Ac */
217 { NULL
, 0, 0 }, /* Ao */
218 { NULL
, 0, 0 }, /* Aq */
219 { NULL
, TYPE_At
, MDOCF_CHILD
}, /* At */
220 { NULL
, 0, 0 }, /* Bc */
221 { NULL
, 0, 0 }, /* Bf */
222 { NULL
, 0, 0 }, /* Bo */
223 { NULL
, 0, 0 }, /* Bq */
224 { NULL
, TYPE_Bsx
, MDOCF_CHILD
}, /* Bsx */
225 { NULL
, TYPE_Bx
, MDOCF_CHILD
}, /* Bx */
226 { NULL
, 0, 0 }, /* Db */
227 { NULL
, 0, 0 }, /* Dc */
228 { NULL
, 0, 0 }, /* Do */
229 { NULL
, 0, 0 }, /* Dq */
230 { NULL
, 0, 0 }, /* Ec */
231 { NULL
, 0, 0 }, /* Ef */
232 { NULL
, TYPE_Em
, MDOCF_CHILD
}, /* Em */
233 { NULL
, 0, 0 }, /* Eo */
234 { NULL
, TYPE_Fx
, MDOCF_CHILD
}, /* Fx */
235 { NULL
, TYPE_Ms
, MDOCF_CHILD
}, /* Ms */
236 { NULL
, 0, 0 }, /* No */
237 { NULL
, 0, 0 }, /* Ns */
238 { NULL
, TYPE_Nx
, MDOCF_CHILD
}, /* Nx */
239 { NULL
, TYPE_Ox
, MDOCF_CHILD
}, /* Ox */
240 { NULL
, 0, 0 }, /* Pc */
241 { NULL
, 0, 0 }, /* Pf */
242 { NULL
, 0, 0 }, /* Po */
243 { NULL
, 0, 0 }, /* Pq */
244 { NULL
, 0, 0 }, /* Qc */
245 { NULL
, 0, 0 }, /* Ql */
246 { NULL
, 0, 0 }, /* Qo */
247 { NULL
, 0, 0 }, /* Qq */
248 { NULL
, 0, 0 }, /* Re */
249 { NULL
, 0, 0 }, /* Rs */
250 { NULL
, 0, 0 }, /* Sc */
251 { NULL
, 0, 0 }, /* So */
252 { NULL
, 0, 0 }, /* Sq */
253 { NULL
, 0, 0 }, /* Sm */
254 { NULL
, 0, 0 }, /* Sx */
255 { NULL
, TYPE_Sy
, MDOCF_CHILD
}, /* Sy */
256 { NULL
, TYPE_Tn
, MDOCF_CHILD
}, /* Tn */
257 { NULL
, 0, 0 }, /* Ux */
258 { NULL
, 0, 0 }, /* Xc */
259 { NULL
, 0, 0 }, /* Xo */
260 { pmdoc_head
, TYPE_Fn
, 0 }, /* Fo */
261 { NULL
, 0, 0 }, /* Fc */
262 { NULL
, 0, 0 }, /* Oo */
263 { NULL
, 0, 0 }, /* Oc */
264 { NULL
, 0, 0 }, /* Bk */
265 { NULL
, 0, 0 }, /* Ek */
266 { NULL
, 0, 0 }, /* Bt */
267 { NULL
, 0, 0 }, /* Hf */
268 { NULL
, 0, 0 }, /* Fr */
269 { NULL
, 0, 0 }, /* Ud */
270 { NULL
, TYPE_Lb
, MDOCF_CHILD
}, /* Lb */
271 { NULL
, 0, 0 }, /* Lp */
272 { NULL
, TYPE_Lk
, MDOCF_CHILD
}, /* Lk */
273 { NULL
, TYPE_Mt
, MDOCF_CHILD
}, /* Mt */
274 { NULL
, 0, 0 }, /* Brq */
275 { NULL
, 0, 0 }, /* Bro */
276 { NULL
, 0, 0 }, /* Brc */
277 { NULL
, 0, 0 }, /* %C */
278 { NULL
, 0, 0 }, /* Es */
279 { NULL
, 0, 0 }, /* En */
280 { NULL
, TYPE_Dx
, MDOCF_CHILD
}, /* Dx */
281 { NULL
, 0, 0 }, /* %Q */
282 { NULL
, 0, 0 }, /* br */
283 { NULL
, 0, 0 }, /* sp */
284 { NULL
, 0, 0 }, /* %U */
285 { NULL
, 0, 0 }, /* Ta */
288 static const char *progname
;
289 static int use_all
; /* Use all directories and files. */
290 static int verb
; /* Output verbosity level. */
291 static int warnings
; /* Potential problems in manuals. */
294 main(int argc
, char *argv
[])
296 struct mparse
*mp
; /* parse sequence */
297 struct manpaths dirs
;
300 enum op op
; /* current operation */
305 DB
*hash
; /* temporary keyword hashtable */
306 BTREEINFO info
; /* btree configuration */
307 size_t sz1
, sz2
, ipath
;
308 struct buf buf
, /* keyword buffer */
309 dbuf
; /* description buffer */
310 struct of
*of
; /* list of files for processing */
314 progname
= strrchr(argv
[0], '/');
315 if (progname
== NULL
)
320 memset(&dirs
, 0, sizeof(struct manpaths
));
321 memset(&mdb
, 0, sizeof(struct mdb
));
322 memset(&recs
, 0, sizeof(struct recs
));
330 while (-1 != (ch
= getopt(argc
, argv
, "aC:d:tu:vW")))
338 "-C: conflicting options\n");
347 "-d: conflicting options\n");
354 dup2(STDOUT_FILENO
, STDERR_FILENO
);
357 "-t: conflicting options\n");
367 "-u: conflicting options\n");
386 if (OP_CONFFILE
== op
&& argc
> 0) {
387 fprintf(stderr
, "-C: too many arguments\n");
391 memset(&info
, 0, sizeof(BTREEINFO
));
395 mp
= mparse_alloc(MPARSE_AUTO
, MANDOCLEVEL_FATAL
, NULL
, NULL
, NULL
);
397 memset(&buf
, 0, sizeof(struct buf
));
398 memset(&dbuf
, 0, sizeof(struct buf
));
400 buf
.size
= dbuf
.size
= MANDOC_BUFSZ
;
402 buf
.cp
= mandoc_malloc(buf
.size
);
403 dbuf
.cp
= mandoc_malloc(dbuf
.size
);
406 ofile_argbuild(argc
, argv
, &of
, NULL
);
409 index_merge(of
, mp
, &dbuf
, &buf
, hash
, &mdb
, &recs
);
413 if (OP_UPDATE
== op
|| OP_DELETE
== op
) {
414 if (NULL
== realpath(dir
, pbuf
)) {
416 exit((int)MANDOCLEVEL_BADARG
);
418 if (strlcat(pbuf
, "/", PATH_MAX
) >= PATH_MAX
) {
419 fprintf(stderr
, "%s: path too long\n", pbuf
);
420 exit((int)MANDOCLEVEL_BADARG
);
423 strlcat(mdb
.dbn
, pbuf
, PATH_MAX
);
424 sz1
= strlcat(mdb
.dbn
, MANDOC_DB
, PATH_MAX
);
426 strlcat(mdb
.idxn
, pbuf
, PATH_MAX
);
427 sz2
= strlcat(mdb
.idxn
, MANDOC_IDX
, PATH_MAX
);
429 if (sz1
>= PATH_MAX
|| sz2
>= PATH_MAX
) {
430 fprintf(stderr
, "%s: path too long\n", mdb
.idxn
);
431 exit((int)MANDOCLEVEL_BADARG
);
434 flags
= O_CREAT
| O_RDWR
;
435 mdb
.db
= dbopen(mdb
.dbn
, flags
, 0644, DB_BTREE
, &info
);
436 mdb
.idx
= dbopen(mdb
.idxn
, flags
, 0644, DB_RECNO
, NULL
);
438 if (NULL
== mdb
.db
) {
440 exit((int)MANDOCLEVEL_SYSERR
);
441 } else if (NULL
== mdb
.idx
) {
443 exit((int)MANDOCLEVEL_SYSERR
);
446 ofile_argbuild(argc
, argv
, &of
, pbuf
);
451 index_prune(of
, &mdb
, &recs
);
454 * Go to the root of the respective manual tree.
455 * This must work or no manuals may be found (they're
456 * indexed relative to the root).
459 if (OP_UPDATE
== op
) {
460 if (-1 == chdir(dir
)) {
462 exit((int)MANDOCLEVEL_SYSERR
);
464 index_merge(of
, mp
, &dbuf
, &buf
, hash
,
472 * Configure the directories we're going to scan.
473 * If we have command-line arguments, use them.
474 * If not, we use man(1)'s method (see mandocdb.8).
478 dirs
.paths
= mandoc_calloc(argc
, sizeof(char *));
480 for (i
= 0; i
< argc
; i
++) {
481 if (NULL
== (cp
= realpath(argv
[i
], pbuf
))) {
485 dirs
.paths
[i
] = mandoc_strdup(cp
);
488 manpath_parse(&dirs
, dir
, NULL
, NULL
);
490 for (ipath
= 0; ipath
< dirs
.sz
; ipath
++) {
493 * Go to the root of the respective manual tree.
494 * This must work or no manuals may be found:
495 * They are indexed relative to the root.
498 if (-1 == chdir(dirs
.paths
[ipath
])) {
499 perror(dirs
.paths
[ipath
]);
500 exit((int)MANDOCLEVEL_SYSERR
);
503 /* Create a new database in two temporary files. */
505 flags
= O_CREAT
| O_EXCL
| O_RDWR
;
506 while (NULL
== mdb
.db
) {
507 strlcpy(mdb
.dbn
, MANDOC_DB
, PATH_MAX
);
508 strlcat(mdb
.dbn
, ".XXXXXXXXXX", PATH_MAX
);
509 if (NULL
== mktemp(mdb
.dbn
)) {
511 exit((int)MANDOCLEVEL_SYSERR
);
513 mdb
.db
= dbopen(mdb
.dbn
, flags
, 0644,
515 if (NULL
== mdb
.db
&& EEXIST
!= errno
) {
517 exit((int)MANDOCLEVEL_SYSERR
);
520 while (NULL
== mdb
.idx
) {
521 strlcpy(mdb
.idxn
, MANDOC_IDX
, PATH_MAX
);
522 strlcat(mdb
.idxn
, ".XXXXXXXXXX", PATH_MAX
);
523 if (NULL
== mktemp(mdb
.idxn
)) {
526 exit((int)MANDOCLEVEL_SYSERR
);
528 mdb
.idx
= dbopen(mdb
.idxn
, flags
, 0644,
530 if (NULL
== mdb
.idx
&& EEXIST
!= errno
) {
533 exit((int)MANDOCLEVEL_SYSERR
);
538 * Search for manuals and fill the new database.
541 ofile_dirbuild(".", "", "", 0, &of
);
544 index_merge(of
, mp
, &dbuf
, &buf
, hash
,
550 (*mdb
.db
->close
)(mdb
.db
);
551 (*mdb
.idx
->close
)(mdb
.idx
);
556 * Replace the old database with the new one.
557 * This is not perfectly atomic,
558 * but i cannot think of a better way.
561 if (-1 == rename(mdb
.dbn
, MANDOC_DB
)) {
565 exit((int)MANDOCLEVEL_SYSERR
);
567 if (-1 == rename(mdb
.idxn
, MANDOC_IDX
)) {
572 exit((int)MANDOCLEVEL_SYSERR
);
578 (*mdb
.db
->close
)(mdb
.db
);
580 (*mdb
.idx
->close
)(mdb
.idx
);
582 (*hash
->close
)(hash
);
592 return(MANDOCLEVEL_OK
);
596 "usage: %s [-avvv] [-C file] | dir ... | -t file ...\n"
597 " -d dir [file ...] | "
598 "-u dir [file ...]\n",
601 return((int)MANDOCLEVEL_BADARG
);
605 index_merge(const struct of
*of
, struct mparse
*mp
,
606 struct buf
*dbuf
, struct buf
*buf
, DB
*hash
,
607 struct mdb
*mdb
, struct recs
*recs
)
612 DB
*files
; /* temporary file name table */
615 const char *fn
, *msec
, *march
, *mtitle
;
629 for (of
= of
->first
; of
; of
= of
->next
) {
633 * Try interpreting the file as mdoc(7) or man(7)
634 * source code, unless it is already known to be
635 * formatted. Fall back to formatted mode.
642 if ((MANDOC_SRC
& of
->src_form
||
643 ! (MANDOC_FORM
& of
->src_form
)) &&
644 MANDOCLEVEL_FATAL
> mparse_readfd(mp
, -1, fn
))
645 mparse_result(mp
, &mdoc
, &man
);
648 msec
= mdoc_meta(mdoc
)->msec
;
649 march
= mdoc_meta(mdoc
)->arch
;
652 mtitle
= mdoc_meta(mdoc
)->title
;
653 } else if (NULL
!= man
) {
654 msec
= man_meta(man
)->msec
;
656 mtitle
= man_meta(man
)->title
;
664 * Check whether the manual section given in a file
665 * agrees with the directory where the file is located.
666 * Some manuals have suffixes like (3p) on their
667 * section number either inside the file or in the
668 * directory name, some are linked into more than one
669 * section, like encrypt(1) = makekey(8). Do not skip
670 * manuals for such reasons.
677 if (strcasecmp(msec
, of
->sec
))
678 fprintf(stderr
, "%s: "
679 "section \"%s\" manual "
680 "in \"%s\" directory\n",
684 * Manual page directories exist for each kernel
685 * architecture as returned by machine(1).
686 * However, many manuals only depend on the
687 * application architecture as returned by arch(1).
688 * For example, some (2/ARM) manuals are shared
689 * across the "armish" and "zaurus" kernel
691 * A few manuals are even shared across completely
692 * different architectures, for example fdformat(1)
693 * on amd64, i386, sparc, and sparc64.
694 * Thus, warn about architecture mismatches,
695 * but don't skip manuals for this reason.
701 if (strcasecmp(march
, of
->arch
))
702 fprintf(stderr
, "%s: "
703 "architecture \"%s\" manual "
704 "in \"%s\" directory\n",
705 fn
, march
, of
->arch
);
708 * By default, skip a file if the title given
709 * in the file disagrees with the file name.
710 * Do not warn, this happens for all MLINKs.
716 if (strcasecmp(mtitle
, of
->title
))
721 * Build a title string for the file. If it matches
722 * the location of the file, remember the title as
723 * found; else, remember it as missing.
728 buf_appendb(buf
, mtitle
, strlen(mtitle
));
729 buf_appendb(buf
, "(", 1);
730 buf_appendb(buf
, msec
, strlen(msec
));
731 if ('\0' != *march
) {
732 buf_appendb(buf
, "/", 1);
733 buf_appendb(buf
, march
, strlen(march
));
735 buf_appendb(buf
, ")", 2);
736 for (p
= buf
->cp
; '\0' != *p
; p
++)
745 ch
= (*files
->get
)(files
, &key
, &val
, 0);
748 exit((int)MANDOCLEVEL_SYSERR
);
750 val
.data
= (void *)fn
;
751 val
.size
= strlen(fn
) + 1;
755 if (NULL
!= val
.data
&&
756 (*files
->put
)(files
, &key
, &val
, 0) < 0) {
758 exit((int)MANDOCLEVEL_SYSERR
);
762 if (skip
&& !use_all
)
766 * The index record value consists of a nil-terminated
767 * filename, a nil-terminated manual section, and a
768 * nil-terminated description. Use the actual
769 * location of the file, such that the user can find
770 * it with man(1). Since the description may not be
771 * set, we set a sentinel to see if we're going to
772 * write a nil byte in its place.
776 type
= mdoc
? 'd' : (man
? 'a' : 'c');
777 buf_appendb(dbuf
, &type
, 1);
778 buf_appendb(dbuf
, fn
, strlen(fn
) + 1);
779 buf_appendb(dbuf
, of
->sec
, strlen(of
->sec
) + 1);
780 buf_appendb(dbuf
, of
->title
, strlen(of
->title
) + 1);
781 buf_appendb(dbuf
, of
->arch
, strlen(of
->arch
) + 1);
786 * Collect keyword/mask pairs.
787 * Each pair will become a new btree node.
792 pmdoc_node(hash
, buf
, dbuf
,
793 mdoc_node(mdoc
), mdoc_meta(mdoc
));
795 pman_node(hash
, buf
, dbuf
, man_node(man
));
797 pformatted(hash
, buf
, dbuf
, of
);
799 /* Test mode, do not access any database. */
801 if (NULL
== mdb
->db
|| NULL
== mdb
->idx
)
805 * Make sure the file name is always registered
806 * as an .Nm search key.
809 buf_append(buf
, of
->title
);
810 hash_put(hash
, buf
, TYPE_Nm
);
813 * Reclaim an empty index record, if available.
814 * Use its record number for all new btree nodes.
819 rec
= recs
->stack
[(int)recs
->cur
];
820 } else if (recs
->last
> 0) {
825 vbuf
[1] = htobe64(rec
);
828 * Copy from the in-memory hashtable of pending
829 * keyword/mask pairs into the database.
833 while (0 == (ch
= (*hash
->seq
)(hash
, &key
, &val
, seq
))) {
835 assert(sizeof(uint64_t) == val
.size
);
836 memcpy(&mask
, val
.data
, val
.size
);
837 vbuf
[0] = htobe64(mask
);
838 val
.size
= sizeof(vbuf
);
840 dbt_put(mdb
->db
, mdb
->dbn
, &key
, &val
);
846 exit((int)MANDOCLEVEL_SYSERR
);
850 * Apply to the index. If we haven't had a description
851 * set, put an empty one in now.
855 buf_appendb(dbuf
, "", 1);
858 key
.size
= sizeof(recno_t
);
861 val
.size
= dbuf
->len
;
864 printf("%s: adding to index\n", fn
);
866 dbt_put(mdb
->idx
, mdb
->idxn
, &key
, &val
);
870 * Iterate the remembered file titles and check that
871 * all files can be found by their main title.
876 while (0 == (*files
->seq
)(files
, &key
, &val
, seq
)) {
879 fprintf(stderr
, "%s: probably "
880 "unreachable, title is %s\n",
881 (char *)val
.data
, (char *)key
.data
);
883 (*files
->close
)(files
);
888 * Scan through all entries in the index file `idx' and prune those
889 * entries in `ofile'.
890 * Pruning consists of removing from `db', then invalidating the entry
891 * in `idx' (zeroing its value size).
894 index_prune(const struct of
*ofile
, struct mdb
*mdb
, struct recs
*recs
)
905 while (0 == (ch
= (*mdb
->idx
->seq
)(mdb
->idx
, &key
, &val
, seq
))) {
907 assert(sizeof(recno_t
) == key
.size
);
908 memcpy(&recs
->last
, key
.data
, key
.size
);
910 /* Deleted records are zero-sized. Skip them. */
916 * Make sure we're sane.
917 * Read past our mdoc/man/cat type to the next string,
918 * then make sure it's bounded by a NUL.
919 * Failing any of these, we go into our error handler.
922 fn
= (char *)val
.data
+ 1;
923 if (NULL
== memchr(fn
, '\0', val
.size
- 1))
927 * Search for the file in those we care about.
928 * XXX: build this into a tree. Too slow.
931 for (of
= ofile
->first
; of
; of
= of
->next
)
932 if (0 == strcmp(fn
, of
->fname
))
939 * Search through the keyword database, throwing out all
940 * references to our file.
944 while (0 == (ch
= (*mdb
->db
->seq
)(mdb
->db
,
945 &key
, &val
, sseq
))) {
947 if (sizeof(vbuf
) != val
.size
)
950 memcpy(vbuf
, val
.data
, val
.size
);
951 if (recs
->last
!= betoh64(vbuf
[1]))
954 if ((ch
= (*mdb
->db
->del
)(mdb
->db
,
955 &key
, R_CURSOR
)) < 0)
961 exit((int)MANDOCLEVEL_SYSERR
);
962 } else if (1 != ch
) {
963 fprintf(stderr
, "%s: corrupt database\n",
965 exit((int)MANDOCLEVEL_SYSERR
);
969 printf("%s: deleting from index\n", fn
);
972 ch
= (*mdb
->idx
->put
)(mdb
->idx
, &key
, &val
, R_CURSOR
);
977 if (recs
->cur
>= recs
->size
) {
978 recs
->size
+= MANDOC_SLOP
;
979 recs
->stack
= mandoc_realloc(recs
->stack
,
980 recs
->size
* sizeof(recno_t
));
983 recs
->stack
[(int)recs
->cur
] = recs
->last
;
989 exit((int)MANDOCLEVEL_SYSERR
);
990 } else if (1 != ch
) {
991 fprintf(stderr
, "%s: corrupt index\n", mdb
->idxn
);
992 exit((int)MANDOCLEVEL_SYSERR
);
999 * Grow the buffer (if necessary) and copy in a binary string.
1002 buf_appendb(struct buf
*buf
, const void *cp
, size_t sz
)
1005 /* Overshoot by MANDOC_BUFSZ. */
1007 while (buf
->len
+ sz
>= buf
->size
) {
1008 buf
->size
= buf
->len
+ sz
+ MANDOC_BUFSZ
;
1009 buf
->cp
= mandoc_realloc(buf
->cp
, buf
->size
);
1012 memcpy(buf
->cp
+ (int)buf
->len
, cp
, sz
);
1017 * Append a nil-terminated string to the buffer.
1018 * This can be invoked multiple times.
1019 * The buffer string will be nil-terminated.
1020 * If invoked multiple times, a space is put between strings.
1023 buf_append(struct buf
*buf
, const char *cp
)
1027 if (0 == (sz
= strlen(cp
)))
1031 buf
->cp
[(int)buf
->len
- 1] = ' ';
1033 buf_appendb(buf
, cp
, sz
+ 1);
1037 * Recursively add all text from a given node.
1038 * This is optimised for general mdoc nodes in this context, which do
1039 * not consist of subexpressions and having a recursive call for n->next
1040 * would be wasteful.
1041 * The "f" variable should be 0 unless called from pmdoc_Nd for the
1042 * description buffer, which does not start at the beginning of the
1046 buf_appendmdoc(struct buf
*buf
, const struct mdoc_node
*n
, int f
)
1049 for ( ; n
; n
= n
->next
) {
1051 buf_appendmdoc(buf
, n
->child
, f
);
1053 if (MDOC_TEXT
== n
->type
&& f
) {
1055 buf_appendb(buf
, n
->string
,
1056 strlen(n
->string
) + 1);
1057 } else if (MDOC_TEXT
== n
->type
)
1058 buf_append(buf
, n
->string
);
1068 if (NULL
!= (hash
= *db
))
1069 (*hash
->close
)(hash
);
1071 *db
= dbopen(NULL
, O_CREAT
|O_RDWR
, 0644, DB_HASH
, NULL
);
1074 exit((int)MANDOCLEVEL_SYSERR
);
1080 pmdoc_head(MDOC_ARGS
)
1083 return(MDOC_HEAD
== n
->type
);
1088 pmdoc_body(MDOC_ARGS
)
1091 return(MDOC_BODY
== n
->type
);
1098 const char *start
, *end
;
1101 if (SEC_SYNOPSIS
!= n
->sec
)
1103 if (NULL
== (n
= n
->child
) || MDOC_TEXT
!= n
->type
)
1107 * Only consider those `Fd' macro fields that begin with an
1108 * "inclusion" token (versus, e.g., #define).
1110 if (strcmp("#include", n
->string
))
1113 if (NULL
== (n
= n
->next
) || MDOC_TEXT
!= n
->type
)
1117 * Strip away the enclosing angle brackets and make sure we're
1122 if ('<' == *start
|| '"' == *start
)
1125 if (0 == (sz
= strlen(start
)))
1128 end
= &start
[(int)sz
- 1];
1129 if ('>' == *end
|| '"' == *end
)
1132 assert(end
>= start
);
1134 buf_appendb(buf
, start
, (size_t)(end
- start
+ 1));
1135 buf_appendb(buf
, "", 1);
1144 if (NULL
== n
->child
|| MDOC_TEXT
!= n
->child
->type
)
1147 buf_append(buf
, n
->child
->string
);
1155 struct mdoc_node
*nn
;
1160 if (NULL
== nn
|| MDOC_TEXT
!= nn
->type
)
1163 /* .Fn "struct type *name" "char *arg" */
1165 cp
= strrchr(nn
->string
, ' ');
1169 /* Strip away pointer symbol. */
1174 /* Store the function name. */
1176 buf_append(buf
, cp
);
1177 hash_put(hash
, buf
, TYPE_Fn
);
1179 /* Store the function type. */
1181 if (nn
->string
< cp
) {
1183 buf_appendb(buf
, nn
->string
, cp
- nn
->string
);
1184 buf_appendb(buf
, "", 1);
1185 hash_put(hash
, buf
, TYPE_Ft
);
1188 /* Store the arguments. */
1190 for (nn
= nn
->next
; nn
; nn
= nn
->next
) {
1191 if (MDOC_TEXT
!= nn
->type
)
1194 buf_append(buf
, nn
->string
);
1195 hash_put(hash
, buf
, TYPE_Fa
);
1206 if (NULL
== n
->child
|| MDOC_TEXT
!= n
->child
->type
)
1209 buf_append(buf
, n
->child
->string
);
1218 if (NULL
== (n
= n
->child
))
1221 buf_appendb(buf
, n
->string
, strlen(n
->string
));
1223 if (NULL
!= (n
= n
->next
)) {
1224 buf_appendb(buf
, ".", 1);
1225 buf_appendb(buf
, n
->string
, strlen(n
->string
) + 1);
1227 buf_appendb(buf
, ".", 2);
1237 if (MDOC_BODY
!= n
->type
)
1240 buf_appendmdoc(dbuf
, n
->child
, 1);
1249 if (SEC_NAME
== n
->sec
)
1251 else if (SEC_SYNOPSIS
!= n
->sec
|| MDOC_HEAD
!= n
->type
)
1254 if (NULL
== n
->child
)
1255 buf_append(buf
, m
->name
);
1265 return(SEC_CUSTOM
== n
->sec
&& MDOC_HEAD
== n
->type
);
1269 hash_put(DB
*db
, const struct buf
*buf
, uint64_t mask
)
1279 key
.size
= buf
->len
;
1281 if ((rc
= (*db
->get
)(db
, &key
, &val
, 0)) < 0) {
1283 exit((int)MANDOCLEVEL_SYSERR
);
1284 } else if (0 == rc
) {
1285 assert(sizeof(uint64_t) == val
.size
);
1286 memcpy(&oldmask
, val
.data
, val
.size
);
1291 val
.size
= sizeof(uint64_t);
1293 if ((rc
= (*db
->put
)(db
, &key
, &val
, 0)) < 0) {
1295 exit((int)MANDOCLEVEL_SYSERR
);
1300 dbt_put(DB
*db
, const char *dbn
, DBT
*key
, DBT
*val
)
1306 if (0 == (*db
->put
)(db
, key
, val
, 0))
1310 exit((int)MANDOCLEVEL_SYSERR
);
1315 * Call out to per-macro handlers after clearing the persistent database
1316 * key. If the macro sets the database key, flush it to the database.
1319 pmdoc_node(MDOC_ARGS
)
1338 * Both NULL handlers and handlers returning true
1339 * request using the data. Only skip the element
1340 * when the handler returns false.
1343 if (NULL
!= mdocs
[n
->tok
].fp
&&
1344 0 == (*mdocs
[n
->tok
].fp
)(hash
, buf
, dbuf
, n
, m
))
1348 * For many macros, use the text from all children.
1349 * Set zero flags for macros not needing this.
1350 * In that case, the handler must fill the buffer.
1353 if (MDOCF_CHILD
& mdocs
[n
->tok
].flags
)
1354 buf_appendmdoc(buf
, n
->child
, 0);
1357 * Cover the most common case:
1358 * Automatically stage one string per element.
1359 * Set a zero mask for macros not needing this.
1360 * Additional staging can be done in the handler.
1363 if (mdocs
[n
->tok
].mask
)
1364 hash_put(hash
, buf
, mdocs
[n
->tok
].mask
);
1370 pmdoc_node(hash
, buf
, dbuf
, n
->child
, m
);
1371 pmdoc_node(hash
, buf
, dbuf
, n
->next
, m
);
1377 const struct man_node
*head
, *body
;
1378 char *start
, *sv
, *title
;
1385 * We're only searching for one thing: the first text child in
1386 * the BODY of a NAME section. Since we don't keep track of
1387 * sections in -man, run some hoops to find out whether we're in
1388 * the correct section or not.
1391 if (MAN_BODY
== n
->type
&& MAN_SH
== n
->tok
) {
1393 assert(body
->parent
);
1394 if (NULL
!= (head
= body
->parent
->head
) &&
1395 1 == head
->nchild
&&
1396 NULL
!= (head
= (head
->child
)) &&
1397 MAN_TEXT
== head
->type
&&
1398 0 == strcmp(head
->string
, "NAME") &&
1399 NULL
!= (body
= body
->child
) &&
1400 MAN_TEXT
== body
->type
) {
1405 * Suck the entire NAME section into memory.
1406 * Yes, we might run away.
1407 * But too many manuals have big, spread-out
1408 * NAME sections over many lines.
1410 for ( ; NULL
!= body
; body
= body
->next
) {
1411 if (MAN_TEXT
!= body
->type
)
1413 if (0 == (sz
= strlen(body
->string
)))
1415 title
= mandoc_realloc
1416 (title
, titlesz
+ sz
+ 1);
1417 memcpy(title
+ titlesz
, body
->string
, sz
);
1419 title
[(int)titlesz
- 1] = ' ';
1424 title
= mandoc_realloc(title
, titlesz
+ 1);
1425 title
[(int)titlesz
] = '\0';
1427 /* Skip leading space. */
1430 while (isspace((unsigned char)*sv
))
1433 if (0 == (sz
= strlen(sv
))) {
1438 /* Erase trailing space. */
1440 start
= &sv
[sz
- 1];
1441 while (start
> sv
&& isspace((unsigned char)*start
))
1452 * Go through a special heuristic dance here.
1453 * This is why -man manuals are great!
1454 * (I'm being sarcastic: my eyes are bleeding.)
1455 * Conventionally, one or more manual names are
1456 * comma-specified prior to a whitespace, then a
1457 * dash, then a description. Try to puzzle out
1458 * the name parts here.
1462 sz
= strcspn(start
, " ,");
1463 if ('\0' == start
[(int)sz
])
1467 buf_appendb(buf
, start
, sz
);
1468 buf_appendb(buf
, "", 1);
1470 hash_put(hash
, buf
, TYPE_Nm
);
1472 if (' ' == start
[(int)sz
]) {
1473 start
+= (int)sz
+ 1;
1477 assert(',' == start
[(int)sz
]);
1478 start
+= (int)sz
+ 1;
1479 while (' ' == *start
)
1486 buf_append(buf
, start
);
1491 while (isspace((unsigned char)*start
))
1494 if (0 == strncmp(start
, "-", 1))
1496 else if (0 == strncmp(start
, "\\-\\-", 4))
1498 else if (0 == strncmp(start
, "\\-", 2))
1500 else if (0 == strncmp(start
, "\\(en", 4))
1502 else if (0 == strncmp(start
, "\\(em", 4))
1505 while (' ' == *start
)
1508 sz
= strlen(start
) + 1;
1509 buf_appendb(dbuf
, start
, sz
);
1510 buf_appendb(buf
, start
, sz
);
1512 hash_put(hash
, buf
, TYPE_Nd
);
1517 for (n
= n
->child
; n
; n
= n
->next
)
1518 if (pman_node(hash
, buf
, dbuf
, n
))
1525 * Parse a formatted manual page.
1526 * By necessity, this involves rather crude guesswork.
1529 pformatted(DB
*hash
, struct buf
*buf
,
1530 struct buf
*dbuf
, const struct of
*of
)
1533 char *line
, *p
, *title
;
1534 size_t len
, plen
, titlesz
;
1536 if (NULL
== (stream
= fopen(of
->fname
, "r"))) {
1543 * Always use the title derived from the filename up front,
1544 * do not even try to find it in the file. This also makes
1545 * sure we don't end up with an orphan index record, even if
1546 * the file content turns out to be completely unintelligible.
1550 buf_append(buf
, of
->title
);
1551 hash_put(hash
, buf
, TYPE_Nm
);
1553 /* Skip to first blank line. */
1555 while (NULL
!= (line
= fgetln(stream
, &len
)))
1560 * Assume the first line that is not indented
1561 * is the first section header. Skip to it.
1564 while (NULL
!= (line
= fgetln(stream
, &len
)))
1565 if ('\n' != *line
&& ' ' != *line
)
1569 * Read up until the next section into a buffer.
1570 * Strip the leading and trailing newline from each read line,
1571 * appending a trailing space.
1572 * Ignore empty (whitespace-only) lines.
1578 while (NULL
!= (line
= fgetln(stream
, &len
))) {
1579 if (' ' != *line
|| '\n' != line
[(int)len
- 1])
1581 while (len
> 0 && isspace((unsigned char)*line
)) {
1587 title
= mandoc_realloc(title
, titlesz
+ len
);
1588 memcpy(title
+ titlesz
, line
, len
);
1590 title
[(int)titlesz
- 1] = ' ';
1595 * If no page content can be found, or the input line
1596 * is already the next section header, or there is no
1597 * trailing newline, reuse the page title as the page
1601 if (NULL
== title
|| '\0' == *title
) {
1603 fprintf(stderr
, "%s: cannot find NAME section\n",
1605 buf_appendb(dbuf
, buf
->cp
, buf
->size
);
1606 hash_put(hash
, buf
, TYPE_Nd
);
1612 title
= mandoc_realloc(title
, titlesz
+ 1);
1613 title
[(int)titlesz
] = '\0';
1616 * Skip to the first dash.
1617 * Use the remaining line as the description (no more than 70
1621 if (NULL
!= (p
= strstr(title
, "- "))) {
1622 for (p
+= 2; ' ' == *p
|| '\b' == *p
; p
++)
1623 /* Skip to next word. */ ;
1626 fprintf(stderr
, "%s: no dash in title line\n",
1633 /* Strip backspace-encoding from line. */
1635 while (NULL
!= (line
= memchr(p
, '\b', plen
))) {
1638 memmove(line
, line
+ 1, plen
--);
1641 memmove(line
- 1, line
+ 1, plen
- len
);
1645 buf_appendb(dbuf
, p
, plen
+ 1);
1647 buf_appendb(buf
, p
, plen
+ 1);
1648 hash_put(hash
, buf
, TYPE_Nd
);
1654 ofile_argbuild(int argc
, char *argv
[], struct of
**of
,
1655 const char *basedir
)
1658 char pbuf
[PATH_MAX
];
1659 const char *sec
, *arch
, *title
;
1664 for (i
= 0; i
< argc
; i
++) {
1665 if (NULL
== (relpath
= realpath(argv
[i
], pbuf
))) {
1669 if (NULL
!= basedir
) {
1670 if (strstr(pbuf
, basedir
) != pbuf
) {
1671 fprintf(stderr
, "%s: file outside "
1672 "base directory %s\n",
1676 relpath
= pbuf
+ strlen(basedir
);
1680 * Try to infer the manual section, architecture and
1681 * page title from the path, assuming it looks like
1682 * man*[/<arch>]/<title>.<section> or
1683 * cat<section>[/<arch>]/<title>.0
1686 if (strlcpy(buf
, relpath
, sizeof(buf
)) >= sizeof(buf
)) {
1687 fprintf(stderr
, "%s: path too long\n", relpath
);
1690 sec
= arch
= title
= "";
1692 p
= strrchr(buf
, '\0');
1694 if ('\0' == *sec
&& '.' == *p
) {
1698 src_form
|= MANDOC_FORM
;
1699 else if ('1' <= *sec
&& '9' >= *sec
)
1700 src_form
|= MANDOC_SRC
;
1705 if ('\0' == *title
) {
1710 if (0 == strncmp("man", p
+ 1, 3))
1711 src_form
|= MANDOC_SRC
;
1712 else if (0 == strncmp("cat", p
+ 1, 3))
1713 src_form
|= MANDOC_FORM
;
1718 if ('\0' == *title
) {
1721 "%s: cannot deduce title "
1728 * Build the file structure.
1731 nof
= mandoc_calloc(1, sizeof(struct of
));
1732 nof
->fname
= mandoc_strdup(relpath
);
1733 nof
->sec
= mandoc_strdup(sec
);
1734 nof
->arch
= mandoc_strdup(arch
);
1735 nof
->title
= mandoc_strdup(title
);
1736 nof
->src_form
= src_form
;
1739 * Add the structure to the list.
1746 nof
->first
= (*of
)->first
;
1754 * Recursively build up a list of files to parse.
1755 * We use this instead of ftw() and so on because I don't want global
1756 * variables hanging around.
1757 * This ignores the mandoc.db and mandoc.index files, but assumes that
1758 * everything else is a manual.
1759 * Pass in a pointer to a NULL structure for the first invocation.
1762 ofile_dirbuild(const char *dir
, const char* psec
, const char *parch
,
1763 int p_src_form
, struct of
**of
)
1771 const char *fn
, *sec
, *arch
;
1772 char *p
, *q
, *suffix
;
1777 if (NULL
== (d
= opendir(dir
))) {
1783 while (NULL
!= (dp
= readdir(d
))) {
1789 src_form
= p_src_form
;
1792 stat(dp
->d_name
, &sb
);
1793 if (S_IFDIR
& sb
.st_mode
) {
1795 if (DT_DIR
== dp
->d_type
) {
1801 * By default, only use directories called:
1802 * man<section>/[<arch>/] or
1803 * cat<section>/[<arch>/]
1807 if(0 == strncmp("man", fn
, 3)) {
1808 src_form
|= MANDOC_SRC
;
1810 } else if (0 == strncmp("cat", fn
, 3)) {
1811 src_form
|= MANDOC_FORM
;
1814 if (warnings
) fprintf(stderr
,
1815 "%s/%s: bad section\n",
1822 } else if ('\0' == *arch
) {
1823 if (NULL
!= strchr(fn
, '.')) {
1824 if (warnings
) fprintf(stderr
,
1825 "%s/%s: bad architecture\n",
1832 if (warnings
) fprintf(stderr
, "%s/%s: "
1833 "excessive subdirectory\n", dir
, fn
);
1839 strlcat(buf
, dir
, PATH_MAX
);
1840 strlcat(buf
, "/", PATH_MAX
);
1841 sz
= strlcat(buf
, fn
, PATH_MAX
);
1843 if (PATH_MAX
<= sz
) {
1844 if (warnings
) fprintf(stderr
, "%s/%s: "
1845 "path too long\n", dir
, fn
);
1849 ofile_dirbuild(buf
, sec
, arch
, src_form
, of
);
1854 if (0 == S_IFREG
& sb
.st_mode
) {
1856 if (DT_REG
!= dp
->d_type
) {
1860 "%s/%s: not a regular file\n",
1864 if (!strcmp(MANDOC_DB
, fn
) || !strcmp(MANDOC_IDX
, fn
))
1866 if ('\0' == *psec
) {
1869 "%s/%s: file outside section\n",
1876 * By default, skip files where the file name suffix
1877 * does not agree with the section directory
1878 * they are located in.
1881 suffix
= strrchr(fn
, '.');
1882 if (NULL
== suffix
) {
1885 "%s/%s: no filename suffix\n",
1889 } else if ((MANDOC_SRC
& src_form
&&
1890 strcmp(suffix
+ 1, psec
)) ||
1891 (MANDOC_FORM
& src_form
&&
1892 strcmp(suffix
+ 1, "0"))) {
1895 "%s/%s: wrong filename suffix\n",
1899 if ('0' == suffix
[1])
1900 src_form
|= MANDOC_FORM
;
1901 else if ('1' <= suffix
[1] && '9' >= suffix
[1])
1902 src_form
|= MANDOC_SRC
;
1906 * Skip formatted manuals if a source version is
1907 * available. Ignore the age: it is very unlikely
1908 * that people install newer formatted base manuals
1909 * when they used to have source manuals before,
1910 * and in ports, old manuals get removed on update.
1912 if (0 == use_all
&& MANDOC_FORM
& src_form
&&
1915 strlcat(buf
, dir
, PATH_MAX
);
1916 p
= strrchr(buf
, '/');
1917 if ('\0' != *parch
&& NULL
!= p
)
1918 for (p
--; p
> buf
; p
--)
1925 if (0 == strncmp("cat", p
, 3))
1926 memcpy(p
, "man", 3);
1927 strlcat(buf
, "/", PATH_MAX
);
1928 sz
= strlcat(buf
, fn
, PATH_MAX
);
1929 if (sz
>= PATH_MAX
) {
1930 if (warnings
) fprintf(stderr
,
1931 "%s/%s: path too long\n",
1935 q
= strrchr(buf
, '.');
1936 if (NULL
!= q
&& p
< q
++) {
1938 sz
= strlcat(buf
, psec
, PATH_MAX
);
1939 if (sz
>= PATH_MAX
) {
1940 if (warnings
) fprintf(stderr
,
1941 "%s/%s: path too long\n",
1945 if (0 == access(buf
, R_OK
))
1951 assert('.' == dir
[0]);
1952 if ('/' == dir
[1]) {
1953 strlcat(buf
, dir
+ 2, PATH_MAX
);
1954 strlcat(buf
, "/", PATH_MAX
);
1956 sz
= strlcat(buf
, fn
, PATH_MAX
);
1957 if (sz
>= PATH_MAX
) {
1958 if (warnings
) fprintf(stderr
,
1959 "%s/%s: path too long\n", dir
, fn
);
1963 nof
= mandoc_calloc(1, sizeof(struct of
));
1964 nof
->fname
= mandoc_strdup(buf
);
1965 nof
->sec
= mandoc_strdup(psec
);
1966 nof
->arch
= mandoc_strdup(parch
);
1967 nof
->src_form
= src_form
;
1970 * Remember the file name without the extension,
1971 * to be used as the page title in the database.
1976 nof
->title
= mandoc_strdup(fn
);
1979 * Add the structure to the list.
1986 nof
->first
= (*of
)->first
;
1996 ofile_free(struct of
*of
)
2003 while (NULL
!= of
) {