Annotation of mandoc/main.c, Revision 1.20
1.20 ! kristaps 1: /* $Id: main.c,v 1.19 2009/04/02 16:26:35 kristaps Exp $ */
1.1 kristaps 2: /*
3: * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@openbsd.org>
4: *
5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the
7: * above copyright notice and this permission notice appear in all
8: * copies.
9: *
10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11: * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12: * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13: * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14: * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15: * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16: * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17: * PERFORMANCE OF THIS SOFTWARE.
18: */
19: #include <sys/stat.h>
20:
21: #include <assert.h>
22: #include <err.h>
23: #include <fcntl.h>
24: #include <stdio.h>
25: #include <stdlib.h>
26: #include <string.h>
27: #include <unistd.h>
28:
29: #include "mdoc.h"
1.10 kristaps 30: #include "man.h"
1.1 kristaps 31:
1.16 kristaps 32: /* Account for FreeBSD and Linux in our declarations. */
33:
1.3 kristaps 34: #ifdef __linux__
35: extern int getsubopt(char **, char * const *, char **);
36: # ifndef __dead
37: # define __dead __attribute__((__noreturn__))
38: # endif
1.13 kristaps 39: #elif defined(__dead2)
1.10 kristaps 40: # ifndef __dead
41: # define __dead __dead2
42: # endif
1.3 kristaps 43: #endif
44:
1.5 kristaps 45: struct buf {
46: char *buf;
47: size_t sz;
48: };
49:
1.19 kristaps 50: enum intt {
51: INTT_AUTO,
52: INTT_MDOC,
53: INTT_MAN
54: };
55:
56: enum outt {
57: OUTT_ASCII = 0,
58: OUTT_TREE,
59: OUTT_LINT
60: };
61:
1.5 kristaps 62: struct curparse {
63: const char *file;
1.19 kristaps 64: int fd;
1.5 kristaps 65: int wflags;
1.1 kristaps 66: #define WARN_WALL 0x03 /* All-warnings mask. */
67: #define WARN_WCOMPAT (1 << 0) /* Compatibility warnings. */
68: #define WARN_WSYNTAX (1 << 1) /* Syntax warnings. */
69: #define WARN_WERR (1 << 2) /* Warnings->errors. */
1.19 kristaps 70: int fflags;
71: enum intt inttype;
72: struct man *man;
73: struct mdoc *mdoc;
1.5 kristaps 74: };
1.1 kristaps 75:
1.16 kristaps 76: #define IGN_SCOPE (1 << 0) /* Ignore scope errors. */
77: #define IGN_ESCAPE (1 << 1) /* Ignore bad escapes. */
78: #define IGN_MACRO (1 << 2) /* Ignore unknown macros. */
1.20 ! kristaps 79: #define NO_IGN_MACRO (1 << 3)
1.15 kristaps 80:
1.11 kristaps 81: typedef int (*out_run)(void *, const struct man *,
82: const struct mdoc *);
1.1 kristaps 83: typedef void (*out_free)(void *);
84:
85: extern char *__progname;
86:
87: extern void *ascii_alloc(void);
1.11 kristaps 88: extern int terminal_run(void *, const struct man *,
89: const struct mdoc *);
90: extern int tree_run(void *, const struct man *,
91: const struct mdoc *);
1.1 kristaps 92: extern void terminal_free(void *);
93:
94: static int foptions(int *, char *);
95: static int toptions(enum outt *, char *);
1.10 kristaps 96: static int moptions(enum intt *, char *);
1.1 kristaps 97: static int woptions(int *, char *);
98: static int merr(void *, int, int, const char *);
1.14 kristaps 99: static int manwarn(void *, int, int, const char *);
100: static int mdocwarn(void *, int, int,
1.1 kristaps 101: enum mdoc_warn, const char *);
1.19 kristaps 102: static int fstdin(struct buf *, struct buf *,
103: struct curparse *);
104: static int ffile(struct buf *, struct buf *,
105: const char *, struct curparse *);
1.5 kristaps 106: static int fdesc(struct buf *, struct buf *,
1.19 kristaps 107: struct curparse *);
108: static int pset(const char *, size_t, struct curparse *,
109: struct man **, struct mdoc **);
110: static struct man *man_init(struct curparse *);
111: static struct mdoc *mdoc_init(struct curparse *);
1.10 kristaps 112: __dead static void version(void);
113: __dead static void usage(void);
1.1 kristaps 114:
115:
116: int
117: main(int argc, char *argv[])
118: {
1.19 kristaps 119: int c, rc;
1.1 kristaps 120: void *outdata;
121: enum outt outtype;
1.5 kristaps 122: struct buf ln, blk;
1.1 kristaps 123: out_run outrun;
124: out_free outfree;
1.5 kristaps 125: struct curparse curp;
1.1 kristaps 126:
127: outtype = OUTT_ASCII;
128:
1.5 kristaps 129: bzero(&curp, sizeof(struct curparse));
130:
1.19 kristaps 131: curp.inttype = INTT_AUTO;
132:
1.1 kristaps 133: /* LINTED */
1.10 kristaps 134: while (-1 != (c = getopt(argc, argv, "f:m:VW:T:")))
1.1 kristaps 135: switch (c) {
136: case ('f'):
1.19 kristaps 137: if ( ! foptions(&curp.fflags, optarg))
1.1 kristaps 138: return(0);
139: break;
1.10 kristaps 140: case ('m'):
1.19 kristaps 141: if ( ! moptions(&curp.inttype, optarg))
1.10 kristaps 142: return(0);
143: break;
1.1 kristaps 144: case ('T'):
145: if ( ! toptions(&outtype, optarg))
146: return(0);
147: break;
148: case ('W'):
1.5 kristaps 149: if ( ! woptions(&curp.wflags, optarg))
1.1 kristaps 150: return(0);
151: break;
152: case ('V'):
153: version();
154: /* NOTREACHED */
155: default:
156: usage();
157: /* NOTREACHED */
158: }
159:
160: argc -= optind;
161: argv += optind;
162:
163: /*
1.16 kristaps 164: * Allocate the appropriate front-end. Note that utf8, latin1
165: * (both not yet implemented) and ascii all resolve to the
166: * terminal front-end with different encodings (see terminal.c).
167: * Not all frontends have cleanup or alloc routines.
1.1 kristaps 168: */
169:
170: switch (outtype) {
171: case (OUTT_TREE):
172: outdata = NULL;
173: outrun = tree_run;
174: outfree = NULL;
175: break;
176: case (OUTT_LINT):
177: outdata = NULL;
178: outrun = NULL;
179: outfree = NULL;
180: break;
181: default:
182: outdata = ascii_alloc();
183: outrun = terminal_run;
184: outfree = terminal_free;
185: break;
186: }
187:
1.16 kristaps 188: /* Configure buffers. */
189:
1.5 kristaps 190: bzero(&ln, sizeof(struct buf));
191: bzero(&blk, sizeof(struct buf));
1.1 kristaps 192:
1.4 kristaps 193: /*
1.16 kristaps 194: * Main loop around available files.
1.4 kristaps 195: */
1.1 kristaps 196:
1.4 kristaps 197: if (NULL == *argv) {
198: rc = 0;
1.19 kristaps 199: c = fstdin(&blk, &ln, &curp);
1.10 kristaps 200:
1.4 kristaps 201: if (c && NULL == outrun)
202: rc = 1;
1.19 kristaps 203: /*else if (c && outrun && (*outrun)(outdata, curp.man, curp.mdoc))
204: rc = 1;*/
1.4 kristaps 205: } else {
206: while (*argv) {
1.19 kristaps 207: c = ffile(&blk, &ln, *argv, &curp);
1.4 kristaps 208: if ( ! c)
209: break;
1.19 kristaps 210: /*if (outrun && ! (*outrun)(outdata, curp.man, curp.mdoc))
211: break;*/
212: if (curp.man)
213: man_reset(curp.man);
214: if (curp.mdoc && ! mdoc_reset(curp.mdoc)) {
1.18 kristaps 215: warnx("memory exhausted");
216: break;
217: }
1.4 kristaps 218: argv++;
219: }
220: rc = NULL == *argv;
1.1 kristaps 221: }
222:
1.5 kristaps 223: if (blk.buf)
224: free(blk.buf);
225: if (ln.buf)
226: free(ln.buf);
1.1 kristaps 227: if (outfree)
228: (*outfree)(outdata);
1.19 kristaps 229: if (curp.mdoc)
230: mdoc_free(curp.mdoc);
231: if (curp.man)
232: man_free(curp.man);
1.1 kristaps 233:
234: return(rc ? EXIT_SUCCESS : EXIT_FAILURE);
235: }
236:
237:
238: __dead static void
239: version(void)
240: {
241:
242: (void)printf("%s %s\n", __progname, VERSION);
1.18 kristaps 243: exit(EXIT_SUCCESS);
1.1 kristaps 244: }
245:
246:
247: __dead static void
248: usage(void)
249: {
250:
1.12 kristaps 251: (void)fprintf(stderr, "usage: %s [-V] [-foption...] "
252: "[-mformat] [-Toutput] [-Werr...]\n",
253: __progname);
1.18 kristaps 254: exit(EXIT_FAILURE);
1.1 kristaps 255: }
256:
257:
1.19 kristaps 258: static struct man *
259: man_init(struct curparse *curp)
260: {
261: int pflags;
262: struct man *man;
263: struct man_cb mancb;
264:
265: mancb.man_err = merr;
266: mancb.man_warn = manwarn;
267:
1.20 ! kristaps 268: /* Set command defaults. */
! 269: pflags = MAN_IGN_MACRO;
1.19 kristaps 270:
271: if (curp->fflags & IGN_MACRO)
272: pflags |= MAN_IGN_MACRO;
1.20 ! kristaps 273: if (curp->fflags & NO_IGN_MACRO)
! 274: pflags &= ~MAN_IGN_MACRO;
1.19 kristaps 275:
276: if (NULL == (man = man_alloc(curp, pflags, &mancb)))
277: warnx("memory exhausted");
278:
279: return(man);
280: }
281:
282:
283: static struct mdoc *
284: mdoc_init(struct curparse *curp)
285: {
286: int pflags;
287: struct mdoc *mdoc;
288: struct mdoc_cb mdoccb;
289:
290: mdoccb.mdoc_msg = NULL;
291: mdoccb.mdoc_err = merr;
292: mdoccb.mdoc_warn = mdocwarn;
293:
294: pflags = 0;
295:
296: if (curp->fflags & IGN_SCOPE)
297: pflags |= MDOC_IGN_SCOPE;
298: if (curp->fflags & IGN_ESCAPE)
299: pflags |= MDOC_IGN_ESCAPE;
300: if (curp->fflags & IGN_MACRO)
301: pflags |= MDOC_IGN_MACRO;
302:
303: if (NULL == (mdoc = mdoc_alloc(curp, pflags, &mdoccb)))
304: warnx("memory allocated");
305:
306: return(mdoc);
307: }
308:
309:
310: static int
311: fstdin(struct buf *blk, struct buf *ln, struct curparse *curp)
312: {
313:
314: curp->file = "<stdin>";
315: curp->fd = STDIN_FILENO;
316: return(fdesc(blk, ln, curp));
317: }
318:
319:
1.1 kristaps 320: static int
1.19 kristaps 321: ffile(struct buf *blk, struct buf *ln,
322: const char *file, struct curparse *curp)
1.1 kristaps 323: {
1.19 kristaps 324: int c;
1.1 kristaps 325:
1.19 kristaps 326: curp->file = file;
327: if (-1 == (curp->fd = open(curp->file, O_RDONLY, 0))) {
328: warn("%s", curp->file);
1.1 kristaps 329: return(0);
330: }
331:
1.19 kristaps 332: c = fdesc(blk, ln, curp);
1.1 kristaps 333:
1.19 kristaps 334: if (-1 == close(curp->fd))
335: warn("%s", curp->file);
1.1 kristaps 336:
337: return(c);
338: }
339:
340:
341: static int
1.19 kristaps 342: fdesc(struct buf *blk, struct buf *ln, struct curparse *curp)
1.1 kristaps 343: {
344: size_t sz;
345: ssize_t ssz;
346: struct stat st;
347: int j, i, pos, lnn;
1.19 kristaps 348: struct man *man;
349: struct mdoc *mdoc;
1.10 kristaps 350:
1.19 kristaps 351: sz = BUFSIZ;
352: man = NULL;
353: mdoc = NULL;
1.1 kristaps 354:
355: /*
1.19 kristaps 356: * Two buffers: ln and buf. buf is the input buffer optimised
357: * here for each file's block size. ln is a line buffer. Both
1.1 kristaps 358: * growable, hence passed in by ptr-ptr.
359: */
360:
1.19 kristaps 361: if (-1 == fstat(curp->fd, &st))
362: warnx("%s", curp->file);
1.6 kristaps 363: else if ((size_t)st.st_blksize > sz)
364: sz = st.st_blksize;
1.1 kristaps 365:
1.5 kristaps 366: if (sz > blk->sz) {
367: blk->buf = realloc(blk->buf, sz);
1.19 kristaps 368: if (NULL == blk->buf) {
369: warn("realloc");
370: return(0);
371: }
1.5 kristaps 372: blk->sz = sz;
1.1 kristaps 373: }
374:
1.19 kristaps 375: /* Fill buf with file blocksize. */
1.1 kristaps 376:
1.19 kristaps 377: for (lnn = 0, pos = 0; ; ) {
378: if (-1 == (ssz = read(curp->fd, blk->buf, sz))) {
379: warn("%s", curp->file);
1.1 kristaps 380: return(0);
381: } else if (0 == ssz)
382: break;
383:
1.19 kristaps 384: /* Parse the read block into partial or full lines. */
385:
1.1 kristaps 386: for (i = 0; i < (int)ssz; i++) {
1.5 kristaps 387: if (pos >= (int)ln->sz) {
388: ln->sz += 256; /* Step-size. */
389: ln->buf = realloc(ln->buf, ln->sz);
1.19 kristaps 390: if (NULL == ln->buf) {
391: warn("realloc");
392: return(0);
393: }
1.1 kristaps 394: }
395:
1.5 kristaps 396: if ('\n' != blk->buf[i]) {
397: ln->buf[pos++] = blk->buf[i];
1.1 kristaps 398: continue;
399: }
400:
401: /* Check for CPP-escaped newline. */
402:
1.5 kristaps 403: if (pos > 0 && '\\' == ln->buf[pos - 1]) {
1.1 kristaps 404: for (j = pos - 1; j >= 0; j--)
1.5 kristaps 405: if ('\\' != ln->buf[j])
1.1 kristaps 406: break;
407:
408: if ( ! ((pos - j) % 2)) {
409: pos--;
410: lnn++;
411: continue;
412: }
413: }
414:
1.5 kristaps 415: ln->buf[pos] = 0;
1.19 kristaps 416: lnn++;
417:
418: /*
419: * If no manual parser has been assigned, then
420: * try to assign one in pset(), which may do
421: * nothing at all. After this, parse the manual
422: * line accordingly.
423: */
424:
425: if ( ! (man || mdoc) && ! pset(ln->buf,
426: pos, curp, &man, &mdoc))
1.10 kristaps 427: return(0);
1.19 kristaps 428:
429: pos = 0;
430:
1.10 kristaps 431: if (man && ! man_parseln(man, lnn, ln->buf))
1.1 kristaps 432: return(0);
1.19 kristaps 433: if (mdoc && ! mdoc_parseln(mdoc, lnn, ln->buf))
434: return(0);
1.1 kristaps 435: }
436: }
437:
1.19 kristaps 438: /* Note that a parser may not have been assigned, yet. */
439:
1.10 kristaps 440: if (mdoc)
441: return(mdoc_endparse(mdoc));
1.19 kristaps 442: if (man)
443: return(man_endparse(man));
444:
445: warnx("%s: not a manual", curp->file);
446: return(0);
447: }
448:
449:
450: static int
451: pset(const char *buf, size_t pos, struct curparse *curp,
452: struct man **man, struct mdoc **mdoc)
453: {
454:
455: /*
456: * Try to intuit which kind of manual parser should be used. If
457: * passed in by command-line (-man, -mdoc), then use that
458: * explicitly. If passed as -mandoc, then try to guess from the
459: * line: either skip comments, use -mdoc when finding `.Dt', or
460: * default to -man, which is more lenient.
461: */
462:
463: if (pos >= 3 && 0 == memcmp(buf, ".\\\"", 3))
464: return(1);
1.10 kristaps 465:
1.19 kristaps 466: switch (curp->inttype) {
467: case (INTT_MDOC):
468: if (NULL == curp->mdoc)
469: curp->mdoc = mdoc_init(curp);
470: if (NULL == (*mdoc = curp->mdoc))
471: return(0);
472: warnx("inheriting -mdoc parser");
473: return(1);
474: case (INTT_MAN):
475: if (NULL == curp->man)
476: curp->man = man_init(curp);
477: if (NULL == (*man = curp->man))
478: return(0);
479: warnx("inheriting -man parser");
480: return(1);
481: default:
482: break;
483: }
484:
485: if (pos >= 3 && 0 == memcmp(buf, ".Dd", 3)) {
486: if (NULL == curp->mdoc)
487: curp->mdoc = mdoc_init(curp);
488: if (NULL == (*mdoc = curp->mdoc))
489: return(0);
490: return(1);
491: }
492:
493: if (NULL == curp->man)
494: curp->man = man_init(curp);
495: if (NULL == (*man = curp->man))
496: return(0);
497: return(1);
1.10 kristaps 498: }
499:
500:
501: static int
502: moptions(enum intt *tflags, char *arg)
503: {
504:
1.17 kristaps 505: if (0 == strcmp(arg, "doc"))
1.10 kristaps 506: *tflags = INTT_MDOC;
1.19 kristaps 507: else if (0 == strcmp(arg, "andoc"))
508: *tflags = INTT_AUTO;
1.17 kristaps 509: else if (0 == strcmp(arg, "an"))
1.10 kristaps 510: *tflags = INTT_MAN;
511: else {
512: warnx("bad argument: -m%s", arg);
513: return(0);
514: }
515:
516: return(1);
1.1 kristaps 517: }
518:
519:
520: static int
521: toptions(enum outt *tflags, char *arg)
522: {
523:
524: if (0 == strcmp(arg, "ascii"))
525: *tflags = OUTT_ASCII;
526: else if (0 == strcmp(arg, "lint"))
527: *tflags = OUTT_LINT;
528: else if (0 == strcmp(arg, "tree"))
529: *tflags = OUTT_TREE;
530: else {
531: warnx("bad argument: -T%s", arg);
532: return(0);
533: }
534:
535: return(1);
536: }
537:
538:
539: /*
540: * Parse out the options for [-fopt...] setting compiler options. These
541: * can be comma-delimited or called again.
542: */
543: static int
544: foptions(int *fflags, char *arg)
545: {
546: char *v;
1.20 ! kristaps 547: char *toks[5];
1.1 kristaps 548:
549: toks[0] = "ign-scope";
550: toks[1] = "ign-escape";
551: toks[2] = "ign-macro";
1.20 ! kristaps 552: toks[4] = "no-ign-macro";
! 553: toks[5] = NULL;
1.1 kristaps 554:
555: while (*arg)
556: switch (getsubopt(&arg, toks, &v)) {
557: case (0):
1.15 kristaps 558: *fflags |= IGN_SCOPE;
1.1 kristaps 559: break;
560: case (1):
1.15 kristaps 561: *fflags |= IGN_ESCAPE;
1.1 kristaps 562: break;
563: case (2):
1.15 kristaps 564: *fflags |= IGN_MACRO;
1.1 kristaps 565: break;
1.20 ! kristaps 566: case (3):
! 567: *fflags |= NO_IGN_MACRO;
! 568: break;
1.1 kristaps 569: default:
570: warnx("bad argument: -f%s", arg);
571: return(0);
572: }
573:
574: return(1);
575: }
576:
577:
578: /*
579: * Parse out the options for [-Werr...], which sets warning modes.
580: * These can be comma-delimited or called again.
581: */
582: static int
583: woptions(int *wflags, char *arg)
584: {
585: char *v;
586: char *toks[5];
587:
588: toks[0] = "all";
589: toks[1] = "compat";
590: toks[2] = "syntax";
591: toks[3] = "error";
592: toks[4] = NULL;
593:
594: while (*arg)
595: switch (getsubopt(&arg, toks, &v)) {
596: case (0):
597: *wflags |= WARN_WALL;
598: break;
599: case (1):
600: *wflags |= WARN_WCOMPAT;
601: break;
602: case (2):
603: *wflags |= WARN_WSYNTAX;
604: break;
605: case (3):
606: *wflags |= WARN_WERR;
607: break;
608: default:
609: warnx("bad argument: -W%s", arg);
610: return(0);
611: }
612:
613: return(1);
614: }
615:
616:
1.2 kristaps 617: /* ARGSUSED */
1.1 kristaps 618: static int
619: merr(void *arg, int line, int col, const char *msg)
620: {
1.5 kristaps 621: struct curparse *curp;
622:
623: curp = (struct curparse *)arg;
1.1 kristaps 624:
1.5 kristaps 625: warnx("%s:%d: error: %s (column %d)",
626: curp->file, line, msg, col);
1.1 kristaps 627: return(0);
628: }
629:
630:
631: static int
1.14 kristaps 632: mdocwarn(void *arg, int line, int col,
1.1 kristaps 633: enum mdoc_warn type, const char *msg)
634: {
1.5 kristaps 635: struct curparse *curp;
1.1 kristaps 636: char *wtype;
637:
1.5 kristaps 638: curp = (struct curparse *)arg;
1.1 kristaps 639: wtype = NULL;
640:
641: switch (type) {
642: case (WARN_COMPAT):
643: wtype = "compat";
1.5 kristaps 644: if (curp->wflags & WARN_WCOMPAT)
1.1 kristaps 645: break;
646: return(1);
647: case (WARN_SYNTAX):
648: wtype = "syntax";
1.5 kristaps 649: if (curp->wflags & WARN_WSYNTAX)
1.1 kristaps 650: break;
651: return(1);
652: }
653:
654: assert(wtype);
1.5 kristaps 655: warnx("%s:%d: %s warning: %s (column %d)",
656: curp->file, line, wtype, msg, col);
1.1 kristaps 657:
1.5 kristaps 658: if ( ! (curp->wflags & WARN_WERR))
1.1 kristaps 659: return(1);
660:
661: warnx("%s: considering warnings as errors",
662: __progname);
663: return(0);
664: }
665:
666:
1.14 kristaps 667: static int
668: manwarn(void *arg, int line, int col, const char *msg)
669: {
670: struct curparse *curp;
671:
672: curp = (struct curparse *)arg;
673:
674: if ( ! (curp->wflags & WARN_WSYNTAX))
675: return(1);
676:
677: warnx("%s:%d: syntax warning: %s (column %d)",
678: curp->file, line, msg, col);
679:
680: if ( ! (curp->wflags & WARN_WERR))
681: return(1);
682:
683: warnx("%s: considering warnings as errors",
684: __progname);
685: return(0);
686: }
CVSweb