]>
git.cameronkatri.com Git - mandoc.git/blob - mandocdb.c
1 /* $Id: mandocdb.c,v 1.6 2011/09/17 13:54:27 schwarze Exp $ */
3 * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 #include <sys/param.h>
42 #define MANDOC_DB "mandoc.db"
43 #define MANDOC_IDX "mandoc.index"
44 #define MANDOC_BUFSZ BUFSIZ
45 #define MANDOC_SLOP 1024
47 /* Bit-fields. See mandocdb.8. */
49 #define TYPE_NAME 0x01
50 #define TYPE_FUNCTION 0x02
51 #define TYPE_UTILITY 0x04
52 #define TYPE_INCLUDES 0x08
53 #define TYPE_VARIABLE 0x10
54 #define TYPE_STANDARD 0x20
55 #define TYPE_AUTHOR 0x40
56 #define TYPE_CONFIG 0x80
57 #define TYPE_DESC 0x100
58 #define TYPE_XREF 0x200
59 #define TYPE_PATH 0x400
60 #define TYPE_ENV 0x800
61 #define TYPE_ERR 0x1000
63 /* Tiny list for files. No need to bring in QUEUE. */
66 char *fname
; /* heap-allocated */
67 struct of
*next
; /* NULL for last one */
68 struct of
*first
; /* first in list */
71 /* Buffer for storing growable data. */
75 size_t len
; /* current length */
76 size_t size
; /* total buffer size */
79 /* Operation we're going to perform. */
82 OP_NEW
= 0, /* new database */
83 OP_UPDATE
, /* delete/add entries in existing database */
84 OP_DELETE
/* delete entries from existing database */
87 #define MAN_ARGS DB *hash, \
90 const struct man_node *n
91 #define MDOC_ARGS DB *hash, \
94 const struct mdoc_node *n, \
95 const struct mdoc_meta *m
97 static void buf_appendmdoc(struct buf
*,
98 const struct mdoc_node
*, int);
99 static void buf_append(struct buf
*, const char *);
100 static void buf_appendb(struct buf
*,
101 const void *, size_t);
102 static void dbt_put(DB
*, const char *, DBT
*, DBT
*);
103 static void hash_put(DB
*, const struct buf
*, int);
104 static void hash_reset(DB
**);
105 static void index_merge(const struct of
*, struct mparse
*,
106 struct buf
*, struct buf
*,
107 DB
*, DB
*, const char *,
108 DB
*, const char *, int,
109 recno_t
, const recno_t
*, size_t);
110 static void index_prune(const struct of
*, DB
*,
111 const char *, DB
*, const char *,
112 int, recno_t
*, recno_t
**, size_t *);
113 static void ofile_argbuild(char *[], int, int, struct of
**);
114 static int ofile_dirbuild(const char *, int, struct of
**);
115 static void ofile_free(struct of
*);
116 static int pman_node(MAN_ARGS
);
117 static void pmdoc_node(MDOC_ARGS
);
118 static void pmdoc_An(MDOC_ARGS
);
119 static void pmdoc_Cd(MDOC_ARGS
);
120 static void pmdoc_Er(MDOC_ARGS
);
121 static void pmdoc_Ev(MDOC_ARGS
);
122 static void pmdoc_Fd(MDOC_ARGS
);
123 static void pmdoc_In(MDOC_ARGS
);
124 static void pmdoc_Fn(MDOC_ARGS
);
125 static void pmdoc_Fo(MDOC_ARGS
);
126 static void pmdoc_Nd(MDOC_ARGS
);
127 static void pmdoc_Nm(MDOC_ARGS
);
128 static void pmdoc_Pa(MDOC_ARGS
);
129 static void pmdoc_St(MDOC_ARGS
);
130 static void pmdoc_Vt(MDOC_ARGS
);
131 static void pmdoc_Xr(MDOC_ARGS
);
132 static void usage(void);
134 typedef void (*pmdoc_nf
)(MDOC_ARGS
);
136 static const pmdoc_nf mdocs
[MDOC_MAX
] = {
261 static const char *progname
;
264 main(int argc
, char *argv
[])
266 struct mparse
*mp
; /* parse sequence */
267 enum op op
; /* current operation */
269 char ibuf
[MAXPATHLEN
], /* index fname */
270 fbuf
[MAXPATHLEN
]; /* btree fname */
271 int verb
, /* output verbosity */
273 DB
*idx
, /* index database */
274 *db
, /* keyword database */
275 *hash
; /* temporary keyword hashtable */
276 BTREEINFO info
; /* btree configuration */
277 recno_t maxrec
; /* supremum of all records */
278 recno_t
*recs
; /* buffer of empty records */
280 recsz
, /* buffer size of recs */
281 reccur
; /* valid number of recs */
282 struct buf buf
, /* keyword buffer */
283 dbuf
; /* description buffer */
284 struct of
*of
; /* list of files for processing */
288 progname
= strrchr(argv
[0], '/');
289 if (progname
== NULL
)
305 while (-1 != (ch
= getopt(argc
, argv
, "d:u:v")))
320 return((int)MANDOCLEVEL_BADARG
);
326 memset(&info
, 0, sizeof(BTREEINFO
));
329 mp
= mparse_alloc(MPARSE_AUTO
, MANDOCLEVEL_FATAL
, NULL
, NULL
);
331 memset(&buf
, 0, sizeof(struct buf
));
332 memset(&dbuf
, 0, sizeof(struct buf
));
334 buf
.size
= dbuf
.size
= MANDOC_BUFSZ
;
336 buf
.cp
= mandoc_malloc(buf
.size
);
337 dbuf
.cp
= mandoc_malloc(dbuf
.size
);
339 flags
= OP_NEW
== op
? O_CREAT
|O_TRUNC
|O_RDWR
: O_CREAT
|O_RDWR
;
341 if (OP_UPDATE
== op
|| OP_DELETE
== op
) {
342 ibuf
[0] = fbuf
[0] = '\0';
344 strlcat(fbuf
, dir
, MAXPATHLEN
);
345 strlcat(fbuf
, "/", MAXPATHLEN
);
346 sz1
= strlcat(fbuf
, MANDOC_DB
, MAXPATHLEN
);
348 strlcat(ibuf
, dir
, MAXPATHLEN
);
349 strlcat(ibuf
, "/", MAXPATHLEN
);
350 sz2
= strlcat(ibuf
, MANDOC_IDX
, MAXPATHLEN
);
352 if (sz1
>= MAXPATHLEN
|| sz2
>= MAXPATHLEN
) {
353 fprintf(stderr
, "%s: Path too long\n", dir
);
354 exit((int)MANDOCLEVEL_BADARG
);
357 db
= dbopen(fbuf
, flags
, 0644, DB_BTREE
, &info
);
358 idx
= dbopen(ibuf
, flags
, 0644, DB_RECNO
, NULL
);
362 exit((int)MANDOCLEVEL_SYSERR
);
363 } else if (NULL
== db
) {
365 exit((int)MANDOCLEVEL_SYSERR
);
369 printf("%s: Opened\n", fbuf
);
370 printf("%s: Opened\n", ibuf
);
373 ofile_argbuild(argv
, argc
, verb
, &of
);
379 index_prune(of
, db
, fbuf
, idx
, ibuf
, verb
,
380 &maxrec
, &recs
, &recsz
);
383 index_merge(of
, mp
, &dbuf
, &buf
, hash
,
384 db
, fbuf
, idx
, ibuf
, verb
,
385 maxrec
, recs
, reccur
);
390 for (i
= 0; i
< argc
; i
++) {
391 ibuf
[0] = fbuf
[0] = '\0';
393 strlcat(fbuf
, argv
[i
], MAXPATHLEN
);
394 strlcat(fbuf
, "/", MAXPATHLEN
);
395 sz1
= strlcat(fbuf
, MANDOC_DB
, MAXPATHLEN
);
397 strlcat(ibuf
, argv
[i
], MAXPATHLEN
);
398 strlcat(ibuf
, "/", MAXPATHLEN
);
399 sz2
= strlcat(ibuf
, MANDOC_IDX
, MAXPATHLEN
);
401 if (sz1
>= MAXPATHLEN
|| sz2
>= MAXPATHLEN
) {
402 fprintf(stderr
, "%s: Path too long\n", argv
[i
]);
403 exit((int)MANDOCLEVEL_BADARG
);
406 db
= dbopen(fbuf
, flags
, 0644, DB_BTREE
, &info
);
407 idx
= dbopen(ibuf
, flags
, 0644, DB_RECNO
, NULL
);
411 exit((int)MANDOCLEVEL_SYSERR
);
412 } else if (NULL
== db
) {
414 exit((int)MANDOCLEVEL_SYSERR
);
418 printf("%s: Truncated\n", fbuf
);
419 printf("%s: Truncated\n", ibuf
);
425 if ( ! ofile_dirbuild(argv
[i
], verb
, &of
))
426 exit((int)MANDOCLEVEL_SYSERR
);
433 index_merge(of
, mp
, &dbuf
, &buf
, hash
, db
, fbuf
,
434 idx
, ibuf
, verb
, maxrec
, recs
, reccur
);
443 (*hash
->close
)(hash
);
452 return(MANDOCLEVEL_OK
);
456 index_merge(const struct of
*of
, struct mparse
*mp
,
457 struct buf
*dbuf
, struct buf
*buf
,
458 DB
*hash
, DB
*db
, const char *dbf
,
459 DB
*idx
, const char *idxf
, int verb
,
460 recno_t maxrec
, const recno_t
*recs
, size_t reccur
)
467 const char *fn
, *msec
, *mtitle
, *arch
;
472 for (rec
= 0; of
; of
= of
->next
) {
476 rec
= recs
[(int)reccur
];
477 } else if (maxrec
> 0) {
486 if (mparse_readfd(mp
, -1, fn
) >= MANDOCLEVEL_FATAL
) {
487 fprintf(stderr
, "%s: Parse failure\n", fn
);
491 mparse_result(mp
, &mdoc
, &man
);
492 if (NULL
== mdoc
&& NULL
== man
)
495 msec
= NULL
!= mdoc
?
496 mdoc_meta(mdoc
)->msec
: man_meta(man
)->msec
;
497 mtitle
= NULL
!= mdoc
?
498 mdoc_meta(mdoc
)->title
: man_meta(man
)->title
;
499 arch
= NULL
!= mdoc
?
500 mdoc_meta(mdoc
)->arch
: NULL
;
506 * The index record value consists of a nil-terminated
507 * filename, a nil-terminated manual section, and a
508 * nil-terminated description. Since the description
509 * may not be set, we set a sentinel to see if we're
510 * going to write a nil byte in its place.
514 buf_appendb(dbuf
, fn
, strlen(fn
) + 1);
515 buf_appendb(dbuf
, msec
, strlen(msec
) + 1);
516 buf_appendb(dbuf
, mtitle
, strlen(mtitle
) + 1);
517 buf_appendb(dbuf
, arch
, strlen(arch
) + 1);
521 /* Fix the record number in the btree value. */
524 pmdoc_node(hash
, buf
, dbuf
,
525 mdoc_node(mdoc
), mdoc_meta(mdoc
));
527 pman_node(hash
, buf
, dbuf
, man_node(man
));
530 * Copy from the in-memory hashtable of pending keywords
534 memset(vbuf
, 0, sizeof(uint32_t));
535 memcpy(vbuf
+ 4, &rec
, sizeof(uint32_t));
538 while (0 == (ch
= (*hash
->seq
)(hash
, &key
, &val
, seq
))) {
541 memcpy(vbuf
, val
.data
, sizeof(uint32_t));
542 val
.size
= sizeof(vbuf
);
546 printf("%s: Added keyword: %s\n",
547 fn
, (char *)key
.data
);
548 dbt_put(db
, dbf
, &key
, &val
);
552 exit((int)MANDOCLEVEL_SYSERR
);
556 * Apply to the index. If we haven't had a description
557 * set, put an empty one in now.
561 buf_appendb(dbuf
, "", 1);
564 key
.size
= sizeof(recno_t
);
567 val
.size
= dbuf
->len
;
570 printf("%s: Added index\n", fn
);
571 dbt_put(idx
, idxf
, &key
, &val
);
576 * Scan through all entries in the index file `idx' and prune those
577 * entries in `ofile'.
578 * Pruning consists of removing from `db', then invalidating the entry
579 * in `idx' (zeroing its value size).
582 index_prune(const struct of
*ofile
, DB
*db
, const char *dbf
,
583 DB
*idx
, const char *idxf
, int verb
,
584 recno_t
*maxrec
, recno_t
**recs
, size_t *recsz
)
595 while (0 == (ch
= (*idx
->seq
)(idx
, &key
, &val
, seq
))) {
597 *maxrec
= *(recno_t
*)key
.data
;
599 if (reccur
>= *recsz
) {
600 *recsz
+= MANDOC_SLOP
;
601 *recs
= mandoc_realloc(*recs
,
602 *recsz
* sizeof(recno_t
));
604 (*recs
)[(int)reccur
] = *maxrec
;
609 fn
= (char *)val
.data
;
610 for (of
= ofile
; of
; of
= of
->next
)
611 if (0 == strcmp(fn
, of
->fname
))
618 while (0 == (ch
= (*db
->seq
)(db
, &key
, &val
, sseq
))) {
620 assert(8 == val
.size
);
621 if (*maxrec
!= *(recno_t
*)(val
.data
+ 4))
624 printf("%s: Deleted keyword: %s\n",
625 fn
, (char *)key
.data
);
626 ch
= (*db
->del
)(db
, &key
, R_CURSOR
);
632 exit((int)MANDOCLEVEL_SYSERR
);
636 printf("%s: Deleted index\n", fn
);
639 ch
= (*idx
->put
)(idx
, &key
, &val
, R_CURSOR
);
642 exit((int)MANDOCLEVEL_SYSERR
);
645 if (reccur
>= *recsz
) {
646 *recsz
+= MANDOC_SLOP
;
647 *recs
= mandoc_realloc
648 (*recs
, *recsz
* sizeof(recno_t
));
651 (*recs
)[(int)reccur
] = *maxrec
;
658 * Grow the buffer (if necessary) and copy in a binary string.
661 buf_appendb(struct buf
*buf
, const void *cp
, size_t sz
)
664 /* Overshoot by MANDOC_BUFSZ. */
666 while (buf
->len
+ sz
>= buf
->size
) {
667 buf
->size
= buf
->len
+ sz
+ MANDOC_BUFSZ
;
668 buf
->cp
= mandoc_realloc(buf
->cp
, buf
->size
);
671 memcpy(buf
->cp
+ (int)buf
->len
, cp
, sz
);
676 * Append a nil-terminated string to the buffer.
677 * This can be invoked multiple times.
678 * The buffer string will be nil-terminated.
679 * If invoked multiple times, a space is put between strings.
682 buf_append(struct buf
*buf
, const char *cp
)
686 if (0 == (sz
= strlen(cp
)))
690 buf
->cp
[(int)buf
->len
- 1] = ' ';
692 buf_appendb(buf
, cp
, sz
+ 1);
696 * Recursively add all text from a given node.
697 * This is optimised for general mdoc nodes in this context, which do
698 * not consist of subexpressions and having a recursive call for n->next
700 * The "f" variable should be 0 unless called from pmdoc_Nd for the
701 * description buffer, which does not start at the beginning of the
705 buf_appendmdoc(struct buf
*buf
, const struct mdoc_node
*n
, int f
)
708 for ( ; n
; n
= n
->next
) {
710 buf_appendmdoc(buf
, n
->child
, f
);
712 if (MDOC_TEXT
== n
->type
&& f
) {
714 buf_appendb(buf
, n
->string
,
715 strlen(n
->string
) + 1);
716 } else if (MDOC_TEXT
== n
->type
)
717 buf_append(buf
, n
->string
);
727 if (SEC_AUTHORS
!= n
->sec
)
730 buf_appendmdoc(buf
, n
->child
, 0);
731 hash_put(hash
, buf
, TYPE_AUTHOR
);
739 if (NULL
!= (hash
= *db
))
740 (*hash
->close
)(hash
);
742 *db
= dbopen(NULL
, O_CREAT
|O_RDWR
, 0644, DB_HASH
, NULL
);
745 exit((int)MANDOCLEVEL_SYSERR
);
753 const char *start
, *end
;
756 if (SEC_SYNOPSIS
!= n
->sec
)
758 if (NULL
== (n
= n
->child
) || MDOC_TEXT
!= n
->type
)
762 * Only consider those `Fd' macro fields that begin with an
763 * "inclusion" token (versus, e.g., #define).
765 if (strcmp("#include", n
->string
))
768 if (NULL
== (n
= n
->next
) || MDOC_TEXT
!= n
->type
)
772 * Strip away the enclosing angle brackets and make sure we're
777 if ('<' == *start
|| '"' == *start
)
780 if (0 == (sz
= strlen(start
)))
783 end
= &start
[(int)sz
- 1];
784 if ('>' == *end
|| '"' == *end
)
787 assert(end
>= start
);
789 buf_appendb(buf
, start
, (size_t)(end
- start
+ 1));
790 buf_appendb(buf
, "", 1);
792 hash_put(hash
, buf
, TYPE_INCLUDES
);
800 if (SEC_SYNOPSIS
!= n
->sec
)
803 buf_appendmdoc(buf
, n
->child
, 0);
804 hash_put(hash
, buf
, TYPE_CONFIG
);
812 if (SEC_SYNOPSIS
!= n
->sec
)
814 if (NULL
== n
->child
|| MDOC_TEXT
!= n
->child
->type
)
817 buf_append(buf
, n
->child
->string
);
818 hash_put(hash
, buf
, TYPE_INCLUDES
);
827 if (SEC_SYNOPSIS
!= n
->sec
)
829 if (NULL
== n
->child
|| MDOC_TEXT
!= n
->child
->type
)
832 /* .Fn "struct type *arg" "foo" */
834 cp
= strrchr(n
->child
->string
, ' ');
836 cp
= n
->child
->string
;
838 /* Strip away pointer symbol. */
844 hash_put(hash
, buf
, TYPE_FUNCTION
);
852 if (SEC_STANDARDS
!= n
->sec
)
854 if (NULL
== n
->child
|| MDOC_TEXT
!= n
->child
->type
)
857 buf_append(buf
, n
->child
->string
);
858 hash_put(hash
, buf
, TYPE_STANDARD
);
866 if (NULL
== (n
= n
->child
))
869 buf_appendb(buf
, n
->string
, strlen(n
->string
));
871 if (NULL
!= (n
= n
->next
)) {
872 buf_appendb(buf
, ".", 1);
873 buf_appendb(buf
, n
->string
, strlen(n
->string
) + 1);
875 buf_appendb(buf
, ".", 2);
877 hash_put(hash
, buf
, TYPE_XREF
);
887 if (SEC_SYNOPSIS
!= n
->sec
)
889 if (MDOC_Vt
== n
->tok
&& MDOC_BODY
!= n
->type
)
891 if (NULL
== n
->last
|| MDOC_TEXT
!= n
->last
->type
)
895 * Strip away leading pointer symbol '*' and trailing ';'.
898 start
= n
->last
->string
;
900 while ('*' == *start
)
903 if (0 == (sz
= strlen(start
)))
906 if (';' == start
[(int)sz
- 1])
912 buf_appendb(buf
, start
, sz
);
913 buf_appendb(buf
, "", 1);
914 hash_put(hash
, buf
, TYPE_VARIABLE
);
922 if (SEC_SYNOPSIS
!= n
->sec
|| MDOC_HEAD
!= n
->type
)
924 if (NULL
== n
->child
|| MDOC_TEXT
!= n
->child
->type
)
927 buf_append(buf
, n
->child
->string
);
928 hash_put(hash
, buf
, TYPE_FUNCTION
);
937 if (MDOC_BODY
!= n
->type
)
940 buf_appendmdoc(dbuf
, n
->child
, 1);
941 buf_appendmdoc(buf
, n
->child
, 0);
943 hash_put(hash
, buf
, TYPE_DESC
);
951 if (SEC_ERRORS
!= n
->sec
)
954 buf_appendmdoc(buf
, n
->child
, 0);
955 hash_put(hash
, buf
, TYPE_ERR
);
963 if (SEC_ENVIRONMENT
!= n
->sec
)
966 buf_appendmdoc(buf
, n
->child
, 0);
967 hash_put(hash
, buf
, TYPE_ENV
);
975 if (SEC_FILES
!= n
->sec
)
978 buf_appendmdoc(buf
, n
->child
, 0);
979 hash_put(hash
, buf
, TYPE_PATH
);
987 if (SEC_NAME
== n
->sec
) {
988 buf_appendmdoc(buf
, n
->child
, 0);
989 hash_put(hash
, buf
, TYPE_NAME
);
991 } else if (SEC_SYNOPSIS
!= n
->sec
|| MDOC_HEAD
!= n
->type
)
994 if (NULL
== n
->child
)
995 buf_append(buf
, m
->name
);
997 buf_appendmdoc(buf
, n
->child
, 0);
998 hash_put(hash
, buf
, TYPE_UTILITY
);
1002 hash_put(DB
*db
, const struct buf
*buf
, int mask
)
1011 key
.size
= buf
->len
;
1013 if ((rc
= (*db
->get
)(db
, &key
, &val
, 0)) < 0) {
1015 exit((int)MANDOCLEVEL_SYSERR
);
1017 mask
|= *(int *)val
.data
;
1020 val
.size
= sizeof(int);
1022 if ((rc
= (*db
->put
)(db
, &key
, &val
, 0)) < 0) {
1024 exit((int)MANDOCLEVEL_SYSERR
);
1029 dbt_put(DB
*db
, const char *dbn
, DBT
*key
, DBT
*val
)
1035 if (0 == (*db
->put
)(db
, key
, val
, 0))
1039 exit((int)MANDOCLEVEL_SYSERR
);
1044 * Call out to per-macro handlers after clearing the persistent database
1045 * key. If the macro sets the database key, flush it to the database.
1048 pmdoc_node(MDOC_ARGS
)
1064 if (NULL
== mdocs
[n
->tok
])
1068 (*mdocs
[n
->tok
])(hash
, buf
, dbuf
, n
, m
);
1074 pmdoc_node(hash
, buf
, dbuf
, n
->child
, m
);
1075 pmdoc_node(hash
, buf
, dbuf
, n
->next
, m
);
1081 const struct man_node
*head
, *body
;
1082 const char *start
, *sv
;
1089 * We're only searching for one thing: the first text child in
1090 * the BODY of a NAME section. Since we don't keep track of
1091 * sections in -man, run some hoops to find out whether we're in
1092 * the correct section or not.
1095 if (MAN_BODY
== n
->type
&& MAN_SH
== n
->tok
) {
1097 assert(body
->parent
);
1098 if (NULL
!= (head
= body
->parent
->head
) &&
1099 1 == head
->nchild
&&
1100 NULL
!= (head
= (head
->child
)) &&
1101 MAN_TEXT
== head
->type
&&
1102 0 == strcmp(head
->string
, "NAME") &&
1103 NULL
!= (body
= body
->child
) &&
1104 MAN_TEXT
== body
->type
) {
1106 assert(body
->string
);
1107 start
= sv
= body
->string
;
1110 * Go through a special heuristic dance here.
1111 * This is why -man manuals are great!
1112 * (I'm being sarcastic: my eyes are bleeding.)
1113 * Conventionally, one or more manual names are
1114 * comma-specified prior to a whitespace, then a
1115 * dash, then a description. Try to puzzle out
1116 * the name parts here.
1120 sz
= strcspn(start
, " ,");
1121 if ('\0' == start
[(int)sz
])
1125 buf_appendb(buf
, start
, sz
);
1126 buf_appendb(buf
, "", 1);
1128 hash_put(hash
, buf
, TYPE_NAME
);
1130 if (' ' == start
[(int)sz
]) {
1131 start
+= (int)sz
+ 1;
1135 assert(',' == start
[(int)sz
]);
1136 start
+= (int)sz
+ 1;
1137 while (' ' == *start
)
1144 buf_append(buf
, start
);
1148 while (' ' == *start
)
1151 if (0 == strncmp(start
, "-", 1))
1153 else if (0 == strncmp(start
, "\\-", 2))
1155 else if (0 == strncmp(start
, "\\(en", 4))
1157 else if (0 == strncmp(start
, "\\(em", 4))
1160 while (' ' == *start
)
1163 sz
= strlen(start
) + 1;
1164 buf_appendb(dbuf
, start
, sz
);
1165 buf_appendb(buf
, start
, sz
);
1167 hash_put(hash
, buf
, TYPE_DESC
);
1171 if (pman_node(hash
, buf
, dbuf
, n
->child
))
1173 if (pman_node(hash
, buf
, dbuf
, n
->next
))
1180 ofile_argbuild(char *argv
[], int argc
, int verb
, struct of
**of
)
1185 for (i
= 0; i
< argc
; i
++) {
1186 nof
= mandoc_calloc(1, sizeof(struct of
));
1187 nof
->fname
= strdup(argv
[i
]);
1189 printf("%s: Scheduling\n", argv
[i
]);
1194 nof
->first
= (*of
)->first
;
1202 * Recursively build up a list of files to parse.
1203 * We use this instead of ftw() and so on because I don't want global
1204 * variables hanging around.
1205 * This ignores the mandoc.db and mandoc.index files, but assumes that
1206 * everything else is a manual.
1207 * Pass in a pointer to a NULL structure for the first invocation.
1210 ofile_dirbuild(const char *dir
, int verb
, struct of
**of
)
1212 char buf
[MAXPATHLEN
];
1219 if (NULL
== (d
= opendir(dir
))) {
1224 while (NULL
!= (dp
= readdir(d
))) {
1226 if (DT_DIR
== dp
->d_type
) {
1227 if (0 == strcmp(".", fn
))
1229 if (0 == strcmp("..", fn
))
1233 strlcat(buf
, dir
, MAXPATHLEN
);
1234 strlcat(buf
, "/", MAXPATHLEN
);
1235 sz
= strlcat(buf
, fn
, MAXPATHLEN
);
1237 if (sz
< MAXPATHLEN
) {
1238 if ( ! ofile_dirbuild(buf
, verb
, of
))
1241 } else if (sz
< MAXPATHLEN
)
1244 fprintf(stderr
, "%s: Path too long\n", dir
);
1247 if (DT_REG
!= dp
->d_type
)
1250 if (0 == strcmp(MANDOC_DB
, fn
) ||
1251 0 == strcmp(MANDOC_IDX
, fn
))
1255 strlcat(buf
, dir
, MAXPATHLEN
);
1256 strlcat(buf
, "/", MAXPATHLEN
);
1257 sz
= strlcat(buf
, fn
, MAXPATHLEN
);
1258 if (sz
>= MAXPATHLEN
) {
1259 fprintf(stderr
, "%s: Path too long\n", dir
);
1263 nof
= mandoc_calloc(1, sizeof(struct of
));
1264 nof
->fname
= mandoc_strdup(buf
);
1267 printf("%s: Scheduling\n", buf
);
1273 nof
->first
= (*of
)->first
;
1283 ofile_free(struct of
*of
)
1299 fprintf(stderr
, "usage: %s [-v] "
1300 "[-d dir [files...] |"
1301 " -u dir [files...] |"
1302 " dir...]\n", progname
);