Annotation of mandoc/main.c, Revision 1.16
1.16 ! kristaps 1: /* $Id: main.c,v 1.15 2009/03/25 21:03:13 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:
50: struct curparse {
51: const char *file;
52: int wflags;
1.1 kristaps 53: #define WARN_WALL 0x03 /* All-warnings mask. */
54: #define WARN_WCOMPAT (1 << 0) /* Compatibility warnings. */
55: #define WARN_WSYNTAX (1 << 1) /* Syntax warnings. */
56: #define WARN_WERR (1 << 2) /* Warnings->errors. */
1.5 kristaps 57: };
1.1 kristaps 58:
1.16 ! kristaps 59: #define IGN_SCOPE (1 << 0) /* Ignore scope errors. */
! 60: #define IGN_ESCAPE (1 << 1) /* Ignore bad escapes. */
! 61: #define IGN_MACRO (1 << 2) /* Ignore unknown macros. */
1.15 kristaps 62:
1.10 kristaps 63: enum intt {
64: INTT_MDOC = 0,
65: INTT_MAN
66: };
67:
68: enum outt {
69: OUTT_ASCII = 0,
1.1 kristaps 70: OUTT_TREE,
71: OUTT_LINT
72: };
73:
1.11 kristaps 74: typedef int (*out_run)(void *, const struct man *,
75: const struct mdoc *);
1.1 kristaps 76: typedef void (*out_free)(void *);
77:
78: extern char *__progname;
79:
80: extern void *ascii_alloc(void);
1.11 kristaps 81: extern int terminal_run(void *, const struct man *,
82: const struct mdoc *);
83: extern int tree_run(void *, const struct man *,
84: const struct mdoc *);
1.1 kristaps 85: extern void terminal_free(void *);
86:
87: static int foptions(int *, char *);
88: static int toptions(enum outt *, char *);
1.10 kristaps 89: static int moptions(enum intt *, char *);
1.1 kristaps 90: static int woptions(int *, char *);
91: static int merr(void *, int, int, const char *);
1.14 kristaps 92: static int manwarn(void *, int, int, const char *);
93: static int mdocwarn(void *, int, int,
1.1 kristaps 94: enum mdoc_warn, const char *);
1.10 kristaps 95: static int file(struct buf *, struct buf *,
96: const char *,
97: struct man *, struct mdoc *);
1.5 kristaps 98: static int fdesc(struct buf *, struct buf *,
1.10 kristaps 99: const char *, int,
100: struct man *, struct mdoc *);
101: __dead static void version(void);
102: __dead static void usage(void);
1.1 kristaps 103:
104:
105: int
106: main(int argc, char *argv[])
107: {
1.15 kristaps 108: int c, rc, fflags, pflags;
1.14 kristaps 109: struct mdoc_cb mdoccb;
110: struct man_cb mancb;
1.10 kristaps 111: struct man *man;
1.1 kristaps 112: struct mdoc *mdoc;
113: void *outdata;
114: enum outt outtype;
1.10 kristaps 115: enum intt inttype;
1.5 kristaps 116: struct buf ln, blk;
1.1 kristaps 117: out_run outrun;
118: out_free outfree;
1.5 kristaps 119: struct curparse curp;
1.1 kristaps 120:
1.7 kristaps 121: fflags = 0;
1.1 kristaps 122: outtype = OUTT_ASCII;
1.10 kristaps 123: inttype = INTT_MDOC;
1.1 kristaps 124:
1.5 kristaps 125: bzero(&curp, sizeof(struct curparse));
126:
1.1 kristaps 127: /* LINTED */
1.10 kristaps 128: while (-1 != (c = getopt(argc, argv, "f:m:VW:T:")))
1.1 kristaps 129: switch (c) {
130: case ('f'):
131: if ( ! foptions(&fflags, optarg))
132: return(0);
133: break;
1.10 kristaps 134: case ('m'):
135: if ( ! moptions(&inttype, optarg))
136: return(0);
137: break;
1.1 kristaps 138: case ('T'):
139: if ( ! toptions(&outtype, optarg))
140: return(0);
141: break;
142: case ('W'):
1.5 kristaps 143: if ( ! woptions(&curp.wflags, optarg))
1.1 kristaps 144: return(0);
145: break;
146: case ('V'):
147: version();
148: /* NOTREACHED */
149: default:
150: usage();
151: /* NOTREACHED */
152: }
153:
154: argc -= optind;
155: argv += optind;
156:
157: /*
1.16 ! kristaps 158: * Allocate the appropriate front-end. Note that utf8, latin1
! 159: * (both not yet implemented) and ascii all resolve to the
! 160: * terminal front-end with different encodings (see terminal.c).
! 161: * Not all frontends have cleanup or alloc routines.
1.1 kristaps 162: */
163:
164: switch (outtype) {
165: case (OUTT_TREE):
166: outdata = NULL;
167: outrun = tree_run;
168: outfree = NULL;
169: break;
170: case (OUTT_LINT):
171: outdata = NULL;
172: outrun = NULL;
173: outfree = NULL;
174: break;
175: default:
176: outdata = ascii_alloc();
177: outrun = terminal_run;
178: outfree = terminal_free;
179: break;
180: }
181:
182: /*
183: * All callbacks route into here, where we print them onto the
184: * screen. XXX - for now, no path for debugging messages.
185: */
186:
1.14 kristaps 187: mdoccb.mdoc_msg = NULL;
188: mdoccb.mdoc_err = merr;
189: mdoccb.mdoc_warn = mdocwarn;
190:
191: mancb.man_err = merr;
192: mancb.man_warn = manwarn;
1.1 kristaps 193:
1.16 ! kristaps 194: /* Configure buffers. */
! 195:
1.5 kristaps 196: bzero(&ln, sizeof(struct buf));
197: bzero(&blk, sizeof(struct buf));
1.1 kristaps 198:
1.10 kristaps 199: man = NULL;
200: mdoc = NULL;
1.15 kristaps 201: pflags = 0;
1.10 kristaps 202:
1.16 ! kristaps 203: /*
! 204: * Allocate the parser. There are two kinds of parser: libman
! 205: * and libmdoc. We must separately copy over the flags that
! 206: * we'll use internally.
! 207: */
! 208:
1.10 kristaps 209: switch (inttype) {
210: case (INTT_MAN):
1.15 kristaps 211: if (fflags & IGN_MACRO)
212: pflags |= MAN_IGN_MACRO;
213: man = man_alloc(&curp, pflags, &mancb);
1.10 kristaps 214: break;
215: default:
1.15 kristaps 216: if (fflags & IGN_SCOPE)
217: pflags |= MDOC_IGN_SCOPE;
218: if (fflags & IGN_ESCAPE)
219: pflags |= MDOC_IGN_ESCAPE;
220: if (fflags & IGN_MACRO)
221: pflags |= MDOC_IGN_MACRO;
222: mdoc = mdoc_alloc(&curp, pflags, &mdoccb);
1.10 kristaps 223: break;
224: }
1.1 kristaps 225:
1.4 kristaps 226: /*
1.16 ! kristaps 227: * Main loop around available files.
1.4 kristaps 228: */
1.1 kristaps 229:
1.4 kristaps 230: if (NULL == *argv) {
1.5 kristaps 231: curp.file = "<stdin>";
1.4 kristaps 232: rc = 0;
1.16 ! kristaps 233: c = fdesc(&blk, &ln, "stdin", STDIN_FILENO, man, mdoc);
1.10 kristaps 234:
1.4 kristaps 235: if (c && NULL == outrun)
236: rc = 1;
1.11 kristaps 237: else if (c && outrun && (*outrun)(outdata, man, mdoc))
1.4 kristaps 238: rc = 1;
239: } else {
240: while (*argv) {
1.5 kristaps 241: curp.file = *argv;
1.10 kristaps 242: c = file(&blk, &ln, *argv, man, mdoc);
1.4 kristaps 243: if ( ! c)
244: break;
1.11 kristaps 245: if (outrun && ! (*outrun)(outdata, man, mdoc))
1.4 kristaps 246: break;
1.10 kristaps 247: if (man)
248: man_reset(man);
249: if (mdoc)
250: mdoc_reset(mdoc);
1.4 kristaps 251: argv++;
252: }
253: rc = NULL == *argv;
1.1 kristaps 254: }
255:
1.5 kristaps 256: if (blk.buf)
257: free(blk.buf);
258: if (ln.buf)
259: free(ln.buf);
1.1 kristaps 260: if (outfree)
261: (*outfree)(outdata);
1.10 kristaps 262: if (mdoc)
263: mdoc_free(mdoc);
264: if (man)
265: man_free(man);
1.1 kristaps 266:
267: return(rc ? EXIT_SUCCESS : EXIT_FAILURE);
268: }
269:
270:
271: __dead static void
272: version(void)
273: {
274:
275: (void)printf("%s %s\n", __progname, VERSION);
276: exit(0);
277: /* NOTREACHED */
278: }
279:
280:
281: __dead static void
282: usage(void)
283: {
284:
1.12 kristaps 285: (void)fprintf(stderr, "usage: %s [-V] [-foption...] "
286: "[-mformat] [-Toutput] [-Werr...]\n",
287: __progname);
1.1 kristaps 288: exit(1);
289: /* NOTREACHED */
290: }
291:
292:
293: static int
1.10 kristaps 294: file(struct buf *blk, struct buf *ln, const char *file,
295: struct man *man, struct mdoc *mdoc)
1.1 kristaps 296: {
297: int fd, c;
298:
299: if (-1 == (fd = open(file, O_RDONLY, 0))) {
300: warn("%s", file);
301: return(0);
302: }
303:
1.10 kristaps 304: c = fdesc(blk, ln, file, fd, man, mdoc);
1.1 kristaps 305:
306: if (-1 == close(fd))
307: warn("%s", file);
308:
309: return(c);
310: }
311:
312:
313: static int
1.5 kristaps 314: fdesc(struct buf *blk, struct buf *ln,
1.10 kristaps 315: const char *f, int fd,
316: struct man *man, struct mdoc *mdoc)
1.1 kristaps 317: {
318: size_t sz;
319: ssize_t ssz;
320: struct stat st;
321: int j, i, pos, lnn;
1.10 kristaps 322:
323: assert( ! (man && mdoc));
1.1 kristaps 324:
325: /*
326: * Two buffers: ln and buf. buf is the input buffer, optimised
327: * for each file's block size. ln is a line buffer. Both
328: * growable, hence passed in by ptr-ptr.
329: */
330:
1.6 kristaps 331: sz = BUFSIZ;
332:
333: if (-1 == fstat(fd, &st))
1.1 kristaps 334: warnx("%s", f);
1.6 kristaps 335: else if ((size_t)st.st_blksize > sz)
336: sz = st.st_blksize;
1.1 kristaps 337:
1.5 kristaps 338: if (sz > blk->sz) {
339: blk->buf = realloc(blk->buf, sz);
340: if (NULL == blk->buf)
1.1 kristaps 341: err(1, "realloc");
1.5 kristaps 342: blk->sz = sz;
1.1 kristaps 343: }
344:
345: /*
346: * Fill buf with file blocksize and parse newlines into ln.
347: */
348:
349: for (lnn = 1, pos = 0; ; ) {
1.5 kristaps 350: if (-1 == (ssz = read(fd, blk->buf, sz))) {
1.1 kristaps 351: warn("%s", f);
352: return(0);
353: } else if (0 == ssz)
354: break;
355:
356: for (i = 0; i < (int)ssz; i++) {
1.5 kristaps 357: if (pos >= (int)ln->sz) {
358: ln->sz += 256; /* Step-size. */
359: ln->buf = realloc(ln->buf, ln->sz);
360: if (NULL == ln->buf)
1.1 kristaps 361: err(1, "realloc");
362: }
363:
1.5 kristaps 364: if ('\n' != blk->buf[i]) {
365: ln->buf[pos++] = blk->buf[i];
1.1 kristaps 366: continue;
367: }
368:
369: /* Check for CPP-escaped newline. */
370:
1.5 kristaps 371: if (pos > 0 && '\\' == ln->buf[pos - 1]) {
1.1 kristaps 372: for (j = pos - 1; j >= 0; j--)
1.5 kristaps 373: if ('\\' != ln->buf[j])
1.1 kristaps 374: break;
375:
376: if ( ! ((pos - j) % 2)) {
377: pos--;
378: lnn++;
379: continue;
380: }
381: }
382:
1.5 kristaps 383: ln->buf[pos] = 0;
1.10 kristaps 384: if (mdoc && ! mdoc_parseln(mdoc, lnn, ln->buf))
385: return(0);
386: if (man && ! man_parseln(man, lnn, ln->buf))
1.1 kristaps 387: return(0);
388: lnn++;
1.9 kristaps 389: pos = 0;
1.1 kristaps 390: }
391: }
392:
1.10 kristaps 393: if (mdoc)
394: return(mdoc_endparse(mdoc));
395:
396: return(man_endparse(man));
397: }
398:
399:
400: static int
401: moptions(enum intt *tflags, char *arg)
402: {
403:
404: if (0 == strcmp(arg, "mdoc"))
405: *tflags = INTT_MDOC;
406: else if (0 == strcmp(arg, "man"))
407: *tflags = INTT_MAN;
408: else {
409: warnx("bad argument: -m%s", arg);
410: return(0);
411: }
412:
413: return(1);
1.1 kristaps 414: }
415:
416:
417: static int
418: toptions(enum outt *tflags, char *arg)
419: {
420:
421: if (0 == strcmp(arg, "ascii"))
422: *tflags = OUTT_ASCII;
423: else if (0 == strcmp(arg, "lint"))
424: *tflags = OUTT_LINT;
425: else if (0 == strcmp(arg, "tree"))
426: *tflags = OUTT_TREE;
427: else {
428: warnx("bad argument: -T%s", arg);
429: return(0);
430: }
431:
432: return(1);
433: }
434:
435:
436: /*
437: * Parse out the options for [-fopt...] setting compiler options. These
438: * can be comma-delimited or called again.
439: */
440: static int
441: foptions(int *fflags, char *arg)
442: {
443: char *v;
444: char *toks[4];
445:
446: toks[0] = "ign-scope";
447: toks[1] = "ign-escape";
448: toks[2] = "ign-macro";
449: toks[3] = NULL;
450:
451: while (*arg)
452: switch (getsubopt(&arg, toks, &v)) {
453: case (0):
1.15 kristaps 454: *fflags |= IGN_SCOPE;
1.1 kristaps 455: break;
456: case (1):
1.15 kristaps 457: *fflags |= IGN_ESCAPE;
1.1 kristaps 458: break;
459: case (2):
1.15 kristaps 460: *fflags |= IGN_MACRO;
1.1 kristaps 461: break;
462: default:
463: warnx("bad argument: -f%s", arg);
464: return(0);
465: }
466:
467: return(1);
468: }
469:
470:
471: /*
472: * Parse out the options for [-Werr...], which sets warning modes.
473: * These can be comma-delimited or called again.
474: */
475: static int
476: woptions(int *wflags, char *arg)
477: {
478: char *v;
479: char *toks[5];
480:
481: toks[0] = "all";
482: toks[1] = "compat";
483: toks[2] = "syntax";
484: toks[3] = "error";
485: toks[4] = NULL;
486:
487: while (*arg)
488: switch (getsubopt(&arg, toks, &v)) {
489: case (0):
490: *wflags |= WARN_WALL;
491: break;
492: case (1):
493: *wflags |= WARN_WCOMPAT;
494: break;
495: case (2):
496: *wflags |= WARN_WSYNTAX;
497: break;
498: case (3):
499: *wflags |= WARN_WERR;
500: break;
501: default:
502: warnx("bad argument: -W%s", arg);
503: return(0);
504: }
505:
506: return(1);
507: }
508:
509:
1.2 kristaps 510: /* ARGSUSED */
1.1 kristaps 511: static int
512: merr(void *arg, int line, int col, const char *msg)
513: {
1.5 kristaps 514: struct curparse *curp;
515:
516: curp = (struct curparse *)arg;
1.1 kristaps 517:
1.5 kristaps 518: warnx("%s:%d: error: %s (column %d)",
519: curp->file, line, msg, col);
1.1 kristaps 520: return(0);
521: }
522:
523:
524: static int
1.14 kristaps 525: mdocwarn(void *arg, int line, int col,
1.1 kristaps 526: enum mdoc_warn type, const char *msg)
527: {
1.5 kristaps 528: struct curparse *curp;
1.1 kristaps 529: char *wtype;
530:
1.5 kristaps 531: curp = (struct curparse *)arg;
1.1 kristaps 532: wtype = NULL;
533:
534: switch (type) {
535: case (WARN_COMPAT):
536: wtype = "compat";
1.5 kristaps 537: if (curp->wflags & WARN_WCOMPAT)
1.1 kristaps 538: break;
539: return(1);
540: case (WARN_SYNTAX):
541: wtype = "syntax";
1.5 kristaps 542: if (curp->wflags & WARN_WSYNTAX)
1.1 kristaps 543: break;
544: return(1);
545: }
546:
547: assert(wtype);
1.5 kristaps 548: warnx("%s:%d: %s warning: %s (column %d)",
549: curp->file, line, wtype, msg, col);
1.1 kristaps 550:
1.5 kristaps 551: if ( ! (curp->wflags & WARN_WERR))
1.1 kristaps 552: return(1);
553:
554: warnx("%s: considering warnings as errors",
555: __progname);
556: return(0);
557: }
558:
559:
1.14 kristaps 560: static int
561: manwarn(void *arg, int line, int col, const char *msg)
562: {
563: struct curparse *curp;
564:
565: curp = (struct curparse *)arg;
566:
567: if ( ! (curp->wflags & WARN_WSYNTAX))
568: return(1);
569:
570: warnx("%s:%d: syntax warning: %s (column %d)",
571: curp->file, line, msg, col);
572:
573: if ( ! (curp->wflags & WARN_WERR))
574: return(1);
575:
576: warnx("%s: considering warnings as errors",
577: __progname);
578: return(0);
579: }
CVSweb