Annotation of mandoc/main.c, Revision 1.18
1.18 ! kristaps 1: /* $Id: main.c,v 1.17 2009/03/26 16:23:22 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.18 ! kristaps 223: if (NULL == mdoc)
! 224: errx(1, "memory exhausted");
1.10 kristaps 225: break;
226: }
1.1 kristaps 227:
1.4 kristaps 228: /*
1.16 kristaps 229: * Main loop around available files.
1.4 kristaps 230: */
1.1 kristaps 231:
1.4 kristaps 232: if (NULL == *argv) {
1.5 kristaps 233: curp.file = "<stdin>";
1.4 kristaps 234: rc = 0;
1.16 kristaps 235: c = fdesc(&blk, &ln, "stdin", STDIN_FILENO, man, mdoc);
1.10 kristaps 236:
1.4 kristaps 237: if (c && NULL == outrun)
238: rc = 1;
1.11 kristaps 239: else if (c && outrun && (*outrun)(outdata, man, mdoc))
1.4 kristaps 240: rc = 1;
241: } else {
242: while (*argv) {
1.5 kristaps 243: curp.file = *argv;
1.10 kristaps 244: c = file(&blk, &ln, *argv, man, mdoc);
1.4 kristaps 245: if ( ! c)
246: break;
1.11 kristaps 247: if (outrun && ! (*outrun)(outdata, man, mdoc))
1.4 kristaps 248: break;
1.10 kristaps 249: if (man)
250: man_reset(man);
1.18 ! kristaps 251: if (mdoc && ! mdoc_reset(mdoc)) {
! 252: warnx("memory exhausted");
! 253: break;
! 254: }
1.4 kristaps 255: argv++;
256: }
257: rc = NULL == *argv;
1.1 kristaps 258: }
259:
1.5 kristaps 260: if (blk.buf)
261: free(blk.buf);
262: if (ln.buf)
263: free(ln.buf);
1.1 kristaps 264: if (outfree)
265: (*outfree)(outdata);
1.10 kristaps 266: if (mdoc)
267: mdoc_free(mdoc);
268: if (man)
269: man_free(man);
1.1 kristaps 270:
271: return(rc ? EXIT_SUCCESS : EXIT_FAILURE);
272: }
273:
274:
275: __dead static void
276: version(void)
277: {
278:
279: (void)printf("%s %s\n", __progname, VERSION);
1.18 ! kristaps 280: exit(EXIT_SUCCESS);
1.1 kristaps 281: }
282:
283:
284: __dead static void
285: usage(void)
286: {
287:
1.12 kristaps 288: (void)fprintf(stderr, "usage: %s [-V] [-foption...] "
289: "[-mformat] [-Toutput] [-Werr...]\n",
290: __progname);
1.18 ! kristaps 291: exit(EXIT_FAILURE);
1.1 kristaps 292: }
293:
294:
295: static int
1.10 kristaps 296: file(struct buf *blk, struct buf *ln, const char *file,
297: struct man *man, struct mdoc *mdoc)
1.1 kristaps 298: {
299: int fd, c;
300:
301: if (-1 == (fd = open(file, O_RDONLY, 0))) {
302: warn("%s", file);
303: return(0);
304: }
305:
1.10 kristaps 306: c = fdesc(blk, ln, file, fd, man, mdoc);
1.1 kristaps 307:
308: if (-1 == close(fd))
309: warn("%s", file);
310:
311: return(c);
312: }
313:
314:
315: static int
1.5 kristaps 316: fdesc(struct buf *blk, struct buf *ln,
1.10 kristaps 317: const char *f, int fd,
318: struct man *man, struct mdoc *mdoc)
1.1 kristaps 319: {
320: size_t sz;
321: ssize_t ssz;
322: struct stat st;
323: int j, i, pos, lnn;
1.10 kristaps 324:
325: assert( ! (man && mdoc));
1.1 kristaps 326:
327: /*
328: * Two buffers: ln and buf. buf is the input buffer, optimised
329: * for each file's block size. ln is a line buffer. Both
330: * growable, hence passed in by ptr-ptr.
331: */
332:
1.6 kristaps 333: sz = BUFSIZ;
334:
335: if (-1 == fstat(fd, &st))
1.1 kristaps 336: warnx("%s", f);
1.6 kristaps 337: else if ((size_t)st.st_blksize > sz)
338: sz = st.st_blksize;
1.1 kristaps 339:
1.5 kristaps 340: if (sz > blk->sz) {
341: blk->buf = realloc(blk->buf, sz);
342: if (NULL == blk->buf)
1.1 kristaps 343: err(1, "realloc");
1.5 kristaps 344: blk->sz = sz;
1.1 kristaps 345: }
346:
347: /*
348: * Fill buf with file blocksize and parse newlines into ln.
349: */
350:
351: for (lnn = 1, pos = 0; ; ) {
1.5 kristaps 352: if (-1 == (ssz = read(fd, blk->buf, sz))) {
1.1 kristaps 353: warn("%s", f);
354: return(0);
355: } else if (0 == ssz)
356: break;
357:
358: for (i = 0; i < (int)ssz; i++) {
1.5 kristaps 359: if (pos >= (int)ln->sz) {
360: ln->sz += 256; /* Step-size. */
361: ln->buf = realloc(ln->buf, ln->sz);
362: if (NULL == ln->buf)
1.1 kristaps 363: err(1, "realloc");
364: }
365:
1.5 kristaps 366: if ('\n' != blk->buf[i]) {
367: ln->buf[pos++] = blk->buf[i];
1.1 kristaps 368: continue;
369: }
370:
371: /* Check for CPP-escaped newline. */
372:
1.5 kristaps 373: if (pos > 0 && '\\' == ln->buf[pos - 1]) {
1.1 kristaps 374: for (j = pos - 1; j >= 0; j--)
1.5 kristaps 375: if ('\\' != ln->buf[j])
1.1 kristaps 376: break;
377:
378: if ( ! ((pos - j) % 2)) {
379: pos--;
380: lnn++;
381: continue;
382: }
383: }
384:
1.5 kristaps 385: ln->buf[pos] = 0;
1.10 kristaps 386: if (mdoc && ! mdoc_parseln(mdoc, lnn, ln->buf))
387: return(0);
388: if (man && ! man_parseln(man, lnn, ln->buf))
1.1 kristaps 389: return(0);
390: lnn++;
1.9 kristaps 391: pos = 0;
1.1 kristaps 392: }
393: }
394:
1.10 kristaps 395: if (mdoc)
396: return(mdoc_endparse(mdoc));
397:
398: return(man_endparse(man));
399: }
400:
401:
402: static int
403: moptions(enum intt *tflags, char *arg)
404: {
405:
1.17 kristaps 406: if (0 == strcmp(arg, "doc"))
1.10 kristaps 407: *tflags = INTT_MDOC;
1.17 kristaps 408: else if (0 == strcmp(arg, "an"))
1.10 kristaps 409: *tflags = INTT_MAN;
410: else {
411: warnx("bad argument: -m%s", arg);
412: return(0);
413: }
414:
415: return(1);
1.1 kristaps 416: }
417:
418:
419: static int
420: toptions(enum outt *tflags, char *arg)
421: {
422:
423: if (0 == strcmp(arg, "ascii"))
424: *tflags = OUTT_ASCII;
425: else if (0 == strcmp(arg, "lint"))
426: *tflags = OUTT_LINT;
427: else if (0 == strcmp(arg, "tree"))
428: *tflags = OUTT_TREE;
429: else {
430: warnx("bad argument: -T%s", arg);
431: return(0);
432: }
433:
434: return(1);
435: }
436:
437:
438: /*
439: * Parse out the options for [-fopt...] setting compiler options. These
440: * can be comma-delimited or called again.
441: */
442: static int
443: foptions(int *fflags, char *arg)
444: {
445: char *v;
446: char *toks[4];
447:
448: toks[0] = "ign-scope";
449: toks[1] = "ign-escape";
450: toks[2] = "ign-macro";
451: toks[3] = NULL;
452:
453: while (*arg)
454: switch (getsubopt(&arg, toks, &v)) {
455: case (0):
1.15 kristaps 456: *fflags |= IGN_SCOPE;
1.1 kristaps 457: break;
458: case (1):
1.15 kristaps 459: *fflags |= IGN_ESCAPE;
1.1 kristaps 460: break;
461: case (2):
1.15 kristaps 462: *fflags |= IGN_MACRO;
1.1 kristaps 463: break;
464: default:
465: warnx("bad argument: -f%s", arg);
466: return(0);
467: }
468:
469: return(1);
470: }
471:
472:
473: /*
474: * Parse out the options for [-Werr...], which sets warning modes.
475: * These can be comma-delimited or called again.
476: */
477: static int
478: woptions(int *wflags, char *arg)
479: {
480: char *v;
481: char *toks[5];
482:
483: toks[0] = "all";
484: toks[1] = "compat";
485: toks[2] = "syntax";
486: toks[3] = "error";
487: toks[4] = NULL;
488:
489: while (*arg)
490: switch (getsubopt(&arg, toks, &v)) {
491: case (0):
492: *wflags |= WARN_WALL;
493: break;
494: case (1):
495: *wflags |= WARN_WCOMPAT;
496: break;
497: case (2):
498: *wflags |= WARN_WSYNTAX;
499: break;
500: case (3):
501: *wflags |= WARN_WERR;
502: break;
503: default:
504: warnx("bad argument: -W%s", arg);
505: return(0);
506: }
507:
508: return(1);
509: }
510:
511:
1.2 kristaps 512: /* ARGSUSED */
1.1 kristaps 513: static int
514: merr(void *arg, int line, int col, const char *msg)
515: {
1.5 kristaps 516: struct curparse *curp;
517:
518: curp = (struct curparse *)arg;
1.1 kristaps 519:
1.5 kristaps 520: warnx("%s:%d: error: %s (column %d)",
521: curp->file, line, msg, col);
1.1 kristaps 522: return(0);
523: }
524:
525:
526: static int
1.14 kristaps 527: mdocwarn(void *arg, int line, int col,
1.1 kristaps 528: enum mdoc_warn type, const char *msg)
529: {
1.5 kristaps 530: struct curparse *curp;
1.1 kristaps 531: char *wtype;
532:
1.5 kristaps 533: curp = (struct curparse *)arg;
1.1 kristaps 534: wtype = NULL;
535:
536: switch (type) {
537: case (WARN_COMPAT):
538: wtype = "compat";
1.5 kristaps 539: if (curp->wflags & WARN_WCOMPAT)
1.1 kristaps 540: break;
541: return(1);
542: case (WARN_SYNTAX):
543: wtype = "syntax";
1.5 kristaps 544: if (curp->wflags & WARN_WSYNTAX)
1.1 kristaps 545: break;
546: return(1);
547: }
548:
549: assert(wtype);
1.5 kristaps 550: warnx("%s:%d: %s warning: %s (column %d)",
551: curp->file, line, wtype, msg, col);
1.1 kristaps 552:
1.5 kristaps 553: if ( ! (curp->wflags & WARN_WERR))
1.1 kristaps 554: return(1);
555:
556: warnx("%s: considering warnings as errors",
557: __progname);
558: return(0);
559: }
560:
561:
1.14 kristaps 562: static int
563: manwarn(void *arg, int line, int col, const char *msg)
564: {
565: struct curparse *curp;
566:
567: curp = (struct curparse *)arg;
568:
569: if ( ! (curp->wflags & WARN_WSYNTAX))
570: return(1);
571:
572: warnx("%s:%d: syntax warning: %s (column %d)",
573: curp->file, line, msg, col);
574:
575: if ( ! (curp->wflags & WARN_WERR))
576: return(1);
577:
578: warnx("%s: considering warnings as errors",
579: __progname);
580: return(0);
581: }
CVSweb