Annotation of mandoc/main.c, Revision 1.21
1.21 ! kristaps 1: /* $Id: main.c,v 1.20 2009/04/02 16:37:40 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:
1.20 kristaps 271: if (curp->fflags & NO_IGN_MACRO)
272: pflags &= ~MAN_IGN_MACRO;
1.19 kristaps 273:
274: if (NULL == (man = man_alloc(curp, pflags, &mancb)))
275: warnx("memory exhausted");
276:
277: return(man);
278: }
279:
280:
281: static struct mdoc *
282: mdoc_init(struct curparse *curp)
283: {
284: int pflags;
285: struct mdoc *mdoc;
286: struct mdoc_cb mdoccb;
287:
288: mdoccb.mdoc_msg = NULL;
289: mdoccb.mdoc_err = merr;
290: mdoccb.mdoc_warn = mdocwarn;
291:
292: pflags = 0;
293:
294: if (curp->fflags & IGN_SCOPE)
295: pflags |= MDOC_IGN_SCOPE;
296: if (curp->fflags & IGN_ESCAPE)
297: pflags |= MDOC_IGN_ESCAPE;
298: if (curp->fflags & IGN_MACRO)
299: pflags |= MDOC_IGN_MACRO;
300:
301: if (NULL == (mdoc = mdoc_alloc(curp, pflags, &mdoccb)))
302: warnx("memory allocated");
303:
304: return(mdoc);
305: }
306:
307:
308: static int
309: fstdin(struct buf *blk, struct buf *ln, struct curparse *curp)
310: {
311:
312: curp->file = "<stdin>";
313: curp->fd = STDIN_FILENO;
314: return(fdesc(blk, ln, curp));
315: }
316:
317:
1.1 kristaps 318: static int
1.19 kristaps 319: ffile(struct buf *blk, struct buf *ln,
320: const char *file, struct curparse *curp)
1.1 kristaps 321: {
1.19 kristaps 322: int c;
1.1 kristaps 323:
1.19 kristaps 324: curp->file = file;
325: if (-1 == (curp->fd = open(curp->file, O_RDONLY, 0))) {
326: warn("%s", curp->file);
1.1 kristaps 327: return(0);
328: }
329:
1.19 kristaps 330: c = fdesc(blk, ln, curp);
1.1 kristaps 331:
1.19 kristaps 332: if (-1 == close(curp->fd))
333: warn("%s", curp->file);
1.1 kristaps 334:
335: return(c);
336: }
337:
338:
339: static int
1.19 kristaps 340: fdesc(struct buf *blk, struct buf *ln, struct curparse *curp)
1.1 kristaps 341: {
342: size_t sz;
343: ssize_t ssz;
344: struct stat st;
345: int j, i, pos, lnn;
1.19 kristaps 346: struct man *man;
347: struct mdoc *mdoc;
1.10 kristaps 348:
1.19 kristaps 349: sz = BUFSIZ;
350: man = NULL;
351: mdoc = NULL;
1.1 kristaps 352:
353: /*
1.19 kristaps 354: * Two buffers: ln and buf. buf is the input buffer optimised
355: * here for each file's block size. ln is a line buffer. Both
1.1 kristaps 356: * growable, hence passed in by ptr-ptr.
357: */
358:
1.19 kristaps 359: if (-1 == fstat(curp->fd, &st))
360: warnx("%s", curp->file);
1.6 kristaps 361: else if ((size_t)st.st_blksize > sz)
362: sz = st.st_blksize;
1.1 kristaps 363:
1.5 kristaps 364: if (sz > blk->sz) {
365: blk->buf = realloc(blk->buf, sz);
1.19 kristaps 366: if (NULL == blk->buf) {
367: warn("realloc");
368: return(0);
369: }
1.5 kristaps 370: blk->sz = sz;
1.1 kristaps 371: }
372:
1.19 kristaps 373: /* Fill buf with file blocksize. */
1.1 kristaps 374:
1.19 kristaps 375: for (lnn = 0, pos = 0; ; ) {
376: if (-1 == (ssz = read(curp->fd, blk->buf, sz))) {
377: warn("%s", curp->file);
1.1 kristaps 378: return(0);
379: } else if (0 == ssz)
380: break;
381:
1.19 kristaps 382: /* Parse the read block into partial or full lines. */
383:
1.1 kristaps 384: for (i = 0; i < (int)ssz; i++) {
1.5 kristaps 385: if (pos >= (int)ln->sz) {
386: ln->sz += 256; /* Step-size. */
387: ln->buf = realloc(ln->buf, ln->sz);
1.19 kristaps 388: if (NULL == ln->buf) {
389: warn("realloc");
390: return(0);
391: }
1.1 kristaps 392: }
393:
1.5 kristaps 394: if ('\n' != blk->buf[i]) {
395: ln->buf[pos++] = blk->buf[i];
1.1 kristaps 396: continue;
397: }
398:
399: /* Check for CPP-escaped newline. */
400:
1.5 kristaps 401: if (pos > 0 && '\\' == ln->buf[pos - 1]) {
1.1 kristaps 402: for (j = pos - 1; j >= 0; j--)
1.5 kristaps 403: if ('\\' != ln->buf[j])
1.1 kristaps 404: break;
405:
406: if ( ! ((pos - j) % 2)) {
407: pos--;
408: lnn++;
409: continue;
410: }
411: }
412:
1.5 kristaps 413: ln->buf[pos] = 0;
1.19 kristaps 414: lnn++;
415:
416: /*
417: * If no manual parser has been assigned, then
418: * try to assign one in pset(), which may do
419: * nothing at all. After this, parse the manual
420: * line accordingly.
421: */
422:
423: if ( ! (man || mdoc) && ! pset(ln->buf,
424: pos, curp, &man, &mdoc))
1.10 kristaps 425: return(0);
1.19 kristaps 426:
427: pos = 0;
428:
1.10 kristaps 429: if (man && ! man_parseln(man, lnn, ln->buf))
1.1 kristaps 430: return(0);
1.19 kristaps 431: if (mdoc && ! mdoc_parseln(mdoc, lnn, ln->buf))
432: return(0);
1.1 kristaps 433: }
434: }
435:
1.19 kristaps 436: /* Note that a parser may not have been assigned, yet. */
437:
1.10 kristaps 438: if (mdoc)
439: return(mdoc_endparse(mdoc));
1.19 kristaps 440: if (man)
441: return(man_endparse(man));
442:
443: warnx("%s: not a manual", curp->file);
444: return(0);
445: }
446:
447:
448: static int
449: pset(const char *buf, size_t pos, struct curparse *curp,
450: struct man **man, struct mdoc **mdoc)
451: {
452:
453: /*
454: * Try to intuit which kind of manual parser should be used. If
455: * passed in by command-line (-man, -mdoc), then use that
456: * explicitly. If passed as -mandoc, then try to guess from the
457: * line: either skip comments, use -mdoc when finding `.Dt', or
458: * default to -man, which is more lenient.
459: */
460:
461: if (pos >= 3 && 0 == memcmp(buf, ".\\\"", 3))
462: return(1);
1.10 kristaps 463:
1.19 kristaps 464: switch (curp->inttype) {
465: case (INTT_MDOC):
466: if (NULL == curp->mdoc)
467: curp->mdoc = mdoc_init(curp);
468: if (NULL == (*mdoc = curp->mdoc))
469: return(0);
470: warnx("inheriting -mdoc parser");
471: return(1);
472: case (INTT_MAN):
473: if (NULL == curp->man)
474: curp->man = man_init(curp);
475: if (NULL == (*man = curp->man))
476: return(0);
477: warnx("inheriting -man parser");
478: return(1);
479: default:
480: break;
481: }
482:
483: if (pos >= 3 && 0 == memcmp(buf, ".Dd", 3)) {
484: if (NULL == curp->mdoc)
485: curp->mdoc = mdoc_init(curp);
486: if (NULL == (*mdoc = curp->mdoc))
487: return(0);
488: return(1);
489: }
490:
491: if (NULL == curp->man)
492: curp->man = man_init(curp);
493: if (NULL == (*man = curp->man))
494: return(0);
495: return(1);
1.10 kristaps 496: }
497:
498:
499: static int
500: moptions(enum intt *tflags, char *arg)
501: {
502:
1.17 kristaps 503: if (0 == strcmp(arg, "doc"))
1.10 kristaps 504: *tflags = INTT_MDOC;
1.19 kristaps 505: else if (0 == strcmp(arg, "andoc"))
506: *tflags = INTT_AUTO;
1.17 kristaps 507: else if (0 == strcmp(arg, "an"))
1.10 kristaps 508: *tflags = INTT_MAN;
509: else {
510: warnx("bad argument: -m%s", arg);
511: return(0);
512: }
513:
514: return(1);
1.1 kristaps 515: }
516:
517:
518: static int
519: toptions(enum outt *tflags, char *arg)
520: {
521:
522: if (0 == strcmp(arg, "ascii"))
523: *tflags = OUTT_ASCII;
524: else if (0 == strcmp(arg, "lint"))
525: *tflags = OUTT_LINT;
526: else if (0 == strcmp(arg, "tree"))
527: *tflags = OUTT_TREE;
528: else {
529: warnx("bad argument: -T%s", arg);
530: return(0);
531: }
532:
533: return(1);
534: }
535:
536:
537: /*
538: * Parse out the options for [-fopt...] setting compiler options. These
539: * can be comma-delimited or called again.
540: */
541: static int
542: foptions(int *fflags, char *arg)
543: {
544: char *v;
1.20 kristaps 545: char *toks[5];
1.1 kristaps 546:
547: toks[0] = "ign-scope";
548: toks[1] = "ign-escape";
549: toks[2] = "ign-macro";
1.20 kristaps 550: toks[4] = "no-ign-macro";
551: toks[5] = NULL;
1.1 kristaps 552:
553: while (*arg)
554: switch (getsubopt(&arg, toks, &v)) {
555: case (0):
1.15 kristaps 556: *fflags |= IGN_SCOPE;
1.1 kristaps 557: break;
558: case (1):
1.15 kristaps 559: *fflags |= IGN_ESCAPE;
1.1 kristaps 560: break;
561: case (2):
1.15 kristaps 562: *fflags |= IGN_MACRO;
1.1 kristaps 563: break;
1.20 kristaps 564: case (3):
565: *fflags |= NO_IGN_MACRO;
566: break;
1.1 kristaps 567: default:
568: warnx("bad argument: -f%s", arg);
569: return(0);
570: }
571:
572: return(1);
573: }
574:
575:
576: /*
577: * Parse out the options for [-Werr...], which sets warning modes.
578: * These can be comma-delimited or called again.
579: */
580: static int
581: woptions(int *wflags, char *arg)
582: {
583: char *v;
584: char *toks[5];
585:
586: toks[0] = "all";
587: toks[1] = "compat";
588: toks[2] = "syntax";
589: toks[3] = "error";
590: toks[4] = NULL;
591:
592: while (*arg)
593: switch (getsubopt(&arg, toks, &v)) {
594: case (0):
595: *wflags |= WARN_WALL;
596: break;
597: case (1):
598: *wflags |= WARN_WCOMPAT;
599: break;
600: case (2):
601: *wflags |= WARN_WSYNTAX;
602: break;
603: case (3):
604: *wflags |= WARN_WERR;
605: break;
606: default:
607: warnx("bad argument: -W%s", arg);
608: return(0);
609: }
610:
611: return(1);
612: }
613:
614:
1.2 kristaps 615: /* ARGSUSED */
1.1 kristaps 616: static int
617: merr(void *arg, int line, int col, const char *msg)
618: {
1.5 kristaps 619: struct curparse *curp;
620:
621: curp = (struct curparse *)arg;
1.1 kristaps 622:
1.5 kristaps 623: warnx("%s:%d: error: %s (column %d)",
624: curp->file, line, msg, col);
1.1 kristaps 625: return(0);
626: }
627:
628:
629: static int
1.14 kristaps 630: mdocwarn(void *arg, int line, int col,
1.1 kristaps 631: enum mdoc_warn type, const char *msg)
632: {
1.5 kristaps 633: struct curparse *curp;
1.1 kristaps 634: char *wtype;
635:
1.5 kristaps 636: curp = (struct curparse *)arg;
1.1 kristaps 637: wtype = NULL;
638:
639: switch (type) {
640: case (WARN_COMPAT):
641: wtype = "compat";
1.5 kristaps 642: if (curp->wflags & WARN_WCOMPAT)
1.1 kristaps 643: break;
644: return(1);
645: case (WARN_SYNTAX):
646: wtype = "syntax";
1.5 kristaps 647: if (curp->wflags & WARN_WSYNTAX)
1.1 kristaps 648: break;
649: return(1);
650: }
651:
652: assert(wtype);
1.5 kristaps 653: warnx("%s:%d: %s warning: %s (column %d)",
654: curp->file, line, wtype, msg, col);
1.1 kristaps 655:
1.5 kristaps 656: if ( ! (curp->wflags & WARN_WERR))
1.1 kristaps 657: return(1);
658:
659: warnx("%s: considering warnings as errors",
660: __progname);
661: return(0);
662: }
663:
664:
1.14 kristaps 665: static int
666: manwarn(void *arg, int line, int col, const char *msg)
667: {
668: struct curparse *curp;
669:
670: curp = (struct curparse *)arg;
671:
672: if ( ! (curp->wflags & WARN_WSYNTAX))
673: return(1);
674:
675: warnx("%s:%d: syntax warning: %s (column %d)",
676: curp->file, line, msg, col);
677:
678: if ( ! (curp->wflags & WARN_WERR))
679: return(1);
680:
681: warnx("%s: considering warnings as errors",
682: __progname);
683: return(0);
684: }
CVSweb