Annotation of mandoc/mdocml.c, Revision 1.42
1.42 ! kristaps 1: /* $Id: mdocml.c,v 1.41 2009/01/12 16:39:57 kristaps Exp $ */
1.1 kristaps 2: /*
3: * Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
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: */
1.21 kristaps 19: #include <sys/stat.h>
1.1 kristaps 20: #include <sys/param.h>
21:
22: #include <assert.h>
1.21 kristaps 23: #include <fcntl.h>
1.1 kristaps 24: #include <err.h>
25: #include <getopt.h>
26: #include <stdio.h>
27: #include <stdlib.h>
28: #include <string.h>
29: #include <unistd.h>
30:
1.21 kristaps 31: #include "mdoc.h"
1.1 kristaps 32:
1.21 kristaps 33: #define MD_LINE_SZ (256)
1.2 kristaps 34:
1.21 kristaps 35: struct md_parse {
36: int warn;
37: #define MD_WARN_ALL (1 << 0)
38: #define MD_WARN_ERR (1 << 1)
39: int dbg;
40: struct mdoc *mdoc;
41: char *buf;
42: u_long bufsz;
43: char *name;
44: int fd;
45: int lnn;
46: char *line;
47: };
1.17 kristaps 48:
1.9 kristaps 49: static void usage(void);
50:
1.21 kristaps 51: static int parse_begin(struct md_parse *);
52: static int parse_leave(struct md_parse *, int);
53: static int io_begin(struct md_parse *);
54: static int io_leave(struct md_parse *, int);
55: static int buf_begin(struct md_parse *);
56: static int buf_leave(struct md_parse *, int);
57:
58: static int msg_err(void *, int, int, enum mdoc_err);
59: static int msg_warn(void *, int, int, enum mdoc_warn);
1.37 kristaps 60: static void msg_msg(void *, int, int, const char *);
1.1 kristaps 61:
1.19 kristaps 62: #ifdef __linux__
63: extern int getsubopt(char **, char *const *, char **);
64: #endif
65:
1.1 kristaps 66: int
67: main(int argc, char *argv[])
68: {
69: int c;
1.21 kristaps 70: struct md_parse parser;
71: char *opts, *v;
1.18 kristaps 72: #define ALL 0
73: #define ERROR 1
74: char *toks[] = { "all", "error", NULL };
1.1 kristaps 75:
76: extern char *optarg;
77: extern int optind;
78:
1.21 kristaps 79: (void)memset(&parser, 0, sizeof(struct md_parse));
1.17 kristaps 80:
1.21 kristaps 81: while (-1 != (c = getopt(argc, argv, "vW:")))
1.1 kristaps 82: switch (c) {
1.13 kristaps 83: case ('v'):
1.21 kristaps 84: parser.dbg++;
1.13 kristaps 85: break;
86: case ('W'):
1.18 kristaps 87: opts = optarg;
88: while (*opts)
89: switch (getsubopt(&opts, toks, &v)) {
90: case (ALL):
1.21 kristaps 91: parser.warn |= MD_WARN_ALL;
1.18 kristaps 92: break;
93: case (ERROR):
1.21 kristaps 94: parser.warn |= MD_WARN_ERR;
1.18 kristaps 95: break;
96: default:
97: usage();
98: return(1);
99: }
1.13 kristaps 100: break;
1.1 kristaps 101: default:
102: usage();
103: return(1);
104: }
105:
106: argv += optind;
1.4 kristaps 107: argc -= optind;
1.1 kristaps 108:
1.21 kristaps 109: parser.name = "-";
1.4 kristaps 110: if (1 == argc)
1.21 kristaps 111: parser.name = *argv++;
112:
113: if ( ! io_begin(&parser))
114: return(EXIT_FAILURE);
1.1 kristaps 115:
1.21 kristaps 116: return(EXIT_SUCCESS);
1.1 kristaps 117: }
118:
119:
120: static int
1.21 kristaps 121: io_leave(struct md_parse *p, int code)
1.1 kristaps 122: {
123:
1.21 kristaps 124: if (-1 == p->fd || STDIN_FILENO == p->fd)
125: return(code);
126:
127: if (-1 == close(p->fd)) {
128: warn("%s", p->name);
129: code = 0;
1.4 kristaps 130: }
1.21 kristaps 131: return(code);
132: }
133:
134:
135: static int
136: io_begin(struct md_parse *p)
137: {
138:
139: p->fd = STDIN_FILENO;
140: if (0 != strncmp(p->name, "-", 1))
141: if (-1 == (p->fd = open(p->name, O_RDONLY, 0))) {
142: warn("%s", p->name);
143: return(io_leave(p, 0));
144: }
1.1 kristaps 145:
1.21 kristaps 146: return(io_leave(p, buf_begin(p)));
1.1 kristaps 147: }
148:
149:
150: static int
1.21 kristaps 151: buf_leave(struct md_parse *p, int code)
1.1 kristaps 152: {
1.4 kristaps 153:
1.21 kristaps 154: if (p->buf)
155: free(p->buf);
156: return(code);
157: }
1.1 kristaps 158:
159:
1.21 kristaps 160: static int
161: buf_begin(struct md_parse *p)
162: {
163: struct stat st;
1.1 kristaps 164:
1.21 kristaps 165: if (-1 == fstat(p->fd, &st)) {
166: warn("%s", p->name);
167: return(1);
168: }
169:
170: p->bufsz = MAX(st.st_blksize, BUFSIZ);
171:
172: if (NULL == (p->buf = malloc(p->bufsz))) {
173: warn("malloc");
174: return(buf_leave(p, 0));
175: }
176:
177: return(buf_leave(p, parse_begin(p)));
178: }
179:
180:
181: static void
182: print_node(const struct mdoc_node *n, int indent)
183: {
1.26 kristaps 184: const char *p, *t;
185: int i, j;
186: size_t argc, sz;
187: char **params;
188: struct mdoc_arg *argv;
1.24 kristaps 189:
190: argv = NULL;
191: argc = 0;
1.26 kristaps 192: params = NULL;
193: sz = 0;
1.21 kristaps 194:
1.42 ! kristaps 195: t = mdoc_type2a(n->type);
! 196:
1.21 kristaps 197: switch (n->type) {
198: case (MDOC_TEXT):
199: assert(NULL == n->child);
1.25 kristaps 200: p = n->data.text.string;
1.21 kristaps 201: break;
202: case (MDOC_BODY):
1.40 kristaps 203: p = mdoc_macronames[n->tok];
1.21 kristaps 204: break;
205: case (MDOC_HEAD):
1.40 kristaps 206: p = mdoc_macronames[n->tok];
1.21 kristaps 207: break;
1.34 kristaps 208: case (MDOC_TAIL):
1.40 kristaps 209: p = mdoc_macronames[n->tok];
1.34 kristaps 210: break;
1.21 kristaps 211: case (MDOC_ELEM):
1.40 kristaps 212: p = mdoc_macronames[n->tok];
1.24 kristaps 213: argv = n->data.elem.argv;
214: argc = n->data.elem.argc;
1.21 kristaps 215: break;
216: case (MDOC_BLOCK):
1.40 kristaps 217: p = mdoc_macronames[n->tok];
1.24 kristaps 218: argv = n->data.block.argv;
219: argc = n->data.block.argc;
1.21 kristaps 220: break;
1.38 kristaps 221: case (MDOC_ROOT):
222: p = "root";
223: break;
1.22 kristaps 224: default:
225: abort();
226: /* NOTREACHED */
1.21 kristaps 227: }
228:
229: for (i = 0; i < indent; i++)
230: (void)printf(" ");
1.24 kristaps 231: (void)printf("%s (%s)", p, t);
232:
233: for (i = 0; i < (int)argc; i++) {
234: (void)printf(" -%s", mdoc_argnames[argv[i].arg]);
1.42 ! kristaps 235: if (argv[i].sz > 0)
1.41 kristaps 236: (void)printf(" [");
1.24 kristaps 237: for (j = 0; j < (int)argv[i].sz; j++)
1.41 kristaps 238: (void)printf(" [%s]", argv[i].value[j]);
1.42 ! kristaps 239: if (argv[i].sz > 0)
1.41 kristaps 240: (void)printf(" ]");
1.24 kristaps 241: }
242:
1.26 kristaps 243: for (i = 0; i < (int)sz; i++)
1.41 kristaps 244: (void)printf(" [%s]", params[i]);
1.26 kristaps 245:
1.39 kristaps 246: (void)printf(" %d:%d\n", n->line, n->pos);
1.21 kristaps 247:
248: if (n->child)
249: print_node(n->child, indent + 1);
250: if (n->next)
251: print_node(n->next, indent);
252: }
1.1 kristaps 253:
254:
1.21 kristaps 255: static int
256: parse_leave(struct md_parse *p, int code)
257: {
258: const struct mdoc_node *n;
259:
1.36 kristaps 260: if (NULL == p->mdoc)
261: return(code);
262:
263: if ( ! mdoc_endparse(p->mdoc))
264: code = 0;
265: if ((n = mdoc_result(p->mdoc)))
266: print_node(n, 0);
267:
1.38 kristaps 268: mdoc_free(p->mdoc);
269:
1.21 kristaps 270: return(code);
271: }
272:
273:
274: static int
275: parse_begin(struct md_parse *p)
276: {
277: ssize_t sz, i;
278: size_t pos;
279: char line[256], sv[256];
280: struct mdoc_cb cb;
281:
282: cb.mdoc_err = msg_err;
283: cb.mdoc_warn = msg_warn;
284: cb.mdoc_msg = msg_msg;
285:
286: if (NULL == (p->mdoc = mdoc_alloc(p, &cb)))
287: return(parse_leave(p, 0));
288:
289: p->lnn = 1;
290: p->line = sv;
291:
292: for (pos = 0; ; ) {
293: if (-1 == (sz = read(p->fd, p->buf, p->bufsz))) {
294: warn("%s", p->name);
295: return(parse_leave(p, 0));
296: } else if (0 == sz)
297: break;
298:
299: for (i = 0; i < sz; i++) {
300: if ('\n' != p->buf[i]) {
301: if (pos < sizeof(line)) {
1.23 kristaps 302: sv[(int)pos] = p->buf[(int)i];
303: line[(int)pos++] =
304: p->buf[(int)i];
1.21 kristaps 305: continue;
306: }
307: warnx("%s: line %d too long",
308: p->name, p->lnn);
309: return(parse_leave(p, 0));
310: }
311:
312: line[(int)pos] = sv[(int)pos] = 0;
1.36 kristaps 313: if ( ! mdoc_parseln(p->mdoc, p->lnn, line))
1.21 kristaps 314: return(parse_leave(p, 0));
1.1 kristaps 315:
1.21 kristaps 316: p->lnn++;
317: pos = 0;
1.4 kristaps 318: }
1.21 kristaps 319: }
1.1 kristaps 320:
1.21 kristaps 321: return(parse_leave(p, 1));
1.4 kristaps 322: }
1.1 kristaps 323:
324:
1.4 kristaps 325: static int
1.37 kristaps 326: msg_err(void *arg, int line, int col, enum mdoc_err type)
1.21 kristaps 327: {
1.37 kristaps 328: char *lit;
1.21 kristaps 329: struct md_parse *p;
330:
331: p = (struct md_parse *)arg;
332:
1.37 kristaps 333: lit = NULL;
1.21 kristaps 334:
335: switch (type) {
1.35 kristaps 336: case (ERR_SYNTAX_NOTEXT):
337: lit = "syntax: context-free text disallowed";
338: break;
1.21 kristaps 339: case (ERR_SYNTAX_QUOTE):
1.24 kristaps 340: lit = "syntax: disallowed argument quotation";
341: break;
342: case (ERR_SYNTAX_UNQUOTE):
1.21 kristaps 343: lit = "syntax: unterminated quotation";
344: break;
345: case (ERR_SYNTAX_WS):
346: lit = "syntax: whitespace in argument";
347: break;
1.25 kristaps 348: case (ERR_SYNTAX_ARGFORM):
1.37 kristaps 349: lit = "syntax: macro arguments malformed";
1.23 kristaps 350: break;
1.28 kristaps 351: case (ERR_SYNTAX_NOPUNCT):
1.37 kristaps 352: lit = "syntax: macro doesn't understand punctuation";
1.28 kristaps 353: break;
1.25 kristaps 354: case (ERR_SYNTAX_ARG):
1.37 kristaps 355: lit = "syntax: unknown argument for macro";
1.24 kristaps 356: break;
1.21 kristaps 357: case (ERR_SCOPE_BREAK):
1.42 ! kristaps 358: lit = "scope: macro breaks prior scope";
1.24 kristaps 359: break;
360: case (ERR_SCOPE_NOCTX):
1.37 kristaps 361: lit = "scope: closure macro has no context";
1.21 kristaps 362: break;
1.25 kristaps 363: case (ERR_SCOPE_NONEST):
1.37 kristaps 364: lit = "scope: macro may not be nested in the current context";
1.25 kristaps 365: break;
1.21 kristaps 366: case (ERR_MACRO_NOTSUP):
1.35 kristaps 367: lit = "macro not supported";
1.21 kristaps 368: break;
369: case (ERR_MACRO_NOTCALL):
1.37 kristaps 370: lit = "macro not callable";
1.21 kristaps 371: break;
1.23 kristaps 372: case (ERR_SEC_PROLOGUE):
1.37 kristaps 373: lit = "macro cannot be called in the prologue";
1.23 kristaps 374: break;
375: case (ERR_SEC_NPROLOGUE):
1.37 kristaps 376: lit = "macro called outside of prologue";
1.23 kristaps 377: break;
1.28 kristaps 378: case (ERR_ARGS_EQ0):
1.37 kristaps 379: lit = "macro expects zero arguments";
1.28 kristaps 380: break;
1.29 kristaps 381: case (ERR_ARGS_EQ1):
1.37 kristaps 382: lit = "macro expects one argument";
1.29 kristaps 383: break;
1.21 kristaps 384: case (ERR_ARGS_GE1):
1.37 kristaps 385: lit = "macro expects one or more arguments";
1.21 kristaps 386: break;
1.28 kristaps 387: case (ERR_ARGS_LE2):
1.37 kristaps 388: lit = "macro expects two or fewer arguments";
1.28 kristaps 389: break;
1.36 kristaps 390: case (ERR_ARGS_LE8):
1.37 kristaps 391: lit = "macro expects eight or fewer arguments";
1.36 kristaps 392: break;
1.23 kristaps 393: case (ERR_ARGS_MANY):
1.37 kristaps 394: lit = "macro has too many arguments";
1.23 kristaps 395: break;
396: case (ERR_SEC_PROLOGUE_OO):
1.37 kristaps 397: lit = "prologue macro is out-of-order";
1.23 kristaps 398: break;
399: case (ERR_SEC_PROLOGUE_REP):
1.37 kristaps 400: lit = "prologue macro repeated";
1.23 kristaps 401: break;
402: case (ERR_SEC_NAME):
403: lit = "`NAME' section must be first";
404: break;
1.24 kristaps 405: case (ERR_SYNTAX_ARGVAL):
406: lit = "syntax: expected value for macro argument";
407: break;
1.25 kristaps 408: case (ERR_SYNTAX_ARGBAD):
1.39 kristaps 409: lit = "syntax: invalid value(s) for macro argument";
410: break;
411: case (ERR_SYNTAX_ARGMISS):
412: lit = "syntax: missing required argument(s) for macro";
1.25 kristaps 413: break;
1.24 kristaps 414: case (ERR_SYNTAX_ARGMANY):
415: lit = "syntax: too many values for macro argument";
416: break;
1.39 kristaps 417: case (ERR_SYNTAX_CHILDBAD):
418: lit = "syntax: invalid child for parent macro";
419: break;
1.40 kristaps 420: case (ERR_SYNTAX_PARENTBAD):
421: lit = "syntax: invalid parent for macro";
422: break;
1.32 kristaps 423: case (ERR_SYNTAX_CHILDHEAD):
424: lit = "syntax: expected only block-header section";
425: break;
426: case (ERR_SYNTAX_CHILDBODY):
427: lit = "syntax: expected only a block-body section";
428: break;
429: case (ERR_SYNTAX_EMPTYHEAD):
430: lit = "syntax: block-header section may not be empty";
431: break;
432: case (ERR_SYNTAX_EMPTYBODY):
433: lit = "syntax: block-body section may not be empty";
1.31 kristaps 434: break;
1.21 kristaps 435: default:
436: abort();
437: /* NOTREACHED */
438: }
439:
1.39 kristaps 440: (void)fprintf(stderr, "%s:%d: error: %s (column %d)\n",
441: p->name, line, lit, col);
1.21 kristaps 442: return(0);
443: }
444:
445:
446: static void
1.37 kristaps 447: msg_msg(void *arg, int line, int col, const char *msg)
1.4 kristaps 448: {
1.21 kristaps 449: struct md_parse *p;
450:
451: p = (struct md_parse *)arg;
452:
453: if (p->dbg < 2)
454: return;
455:
1.39 kristaps 456: (void)printf("%s:%d: %s (column %d)\n",
457: p->name, line, msg, col);
1.1 kristaps 458: }
459:
460:
461: static int
1.37 kristaps 462: msg_warn(void *arg, int line, int col, enum mdoc_warn type)
1.1 kristaps 463: {
1.37 kristaps 464: char *lit;
1.21 kristaps 465: struct md_parse *p;
466: extern char *__progname;
1.1 kristaps 467:
1.21 kristaps 468: p = (struct md_parse *)arg;
1.1 kristaps 469:
1.21 kristaps 470: if ( ! (p->warn & MD_WARN_ALL))
1.1 kristaps 471: return(1);
1.21 kristaps 472:
1.37 kristaps 473: lit = NULL;
1.21 kristaps 474:
475: switch (type) {
476: case (WARN_SYNTAX_WS_EOLN):
477: lit = "syntax: whitespace at end-of-line";
478: break;
1.25 kristaps 479: case (WARN_SYNTAX_QUOTED):
480: lit = "syntax: quotation mark starting string";
481: break;
1.21 kristaps 482: case (WARN_SYNTAX_MACLIKE):
483: lit = "syntax: macro-like argument";
484: break;
1.24 kristaps 485: case (WARN_SYNTAX_ARGLIKE):
486: lit = "syntax: argument-like value";
487: break;
1.32 kristaps 488: case (WARN_SYNTAX_EMPTYBODY):
1.40 kristaps 489: lit = "syntax: macro suggests non-empty block-body section";
490: break;
491: case (WARN_SYNTAX_EMPTYHEAD):
492: lit = "syntax: macro suggests non-empty block-head section";
493: break;
494: case (WARN_SYNTAX_NOBODY):
495: lit = "syntax: macro suggests empty block-body section";
1.32 kristaps 496: break;
1.23 kristaps 497: case (WARN_SEC_OO):
498: lit = "section is out of conventional order";
499: break;
1.38 kristaps 500: case (WARN_SEC_REP):
501: lit = "section repeated";
502: break;
1.21 kristaps 503: case (WARN_ARGS_GE1):
1.37 kristaps 504: lit = "macro suggests one or more arguments";
1.21 kristaps 505: break;
1.25 kristaps 506: case (WARN_ARGS_EQ0):
1.37 kristaps 507: lit = "macro suggests zero arguments";
1.25 kristaps 508: break;
509: case (WARN_IGN_AFTER_BLK):
1.37 kristaps 510: lit = "ignore: macro ignored after block macro";
1.25 kristaps 511: break;
1.30 kristaps 512: case (WARN_IGN_OBSOLETE):
1.37 kristaps 513: lit = "ignore: macro is obsolete";
1.30 kristaps 514: break;
1.25 kristaps 515: case (WARN_IGN_BEFORE_BLK):
1.37 kristaps 516: lit = "ignore: macro before block macro ignored";
1.25 kristaps 517: break;
1.27 kristaps 518: case (WARN_COMPAT_TROFF):
1.37 kristaps 519: lit = "compat: macro behaves differently in troff and nroff";
1.27 kristaps 520: break;
1.21 kristaps 521: default:
522: abort();
523: /* NOTREACHED */
1.1 kristaps 524: }
525:
1.21 kristaps 526:
1.39 kristaps 527: (void)fprintf(stderr, "%s:%d: warning: %s (column %d)\n",
528: p->name, line, lit, col);
1.21 kristaps 529:
530: if (p->warn & MD_WARN_ERR) {
531: (void)fprintf(stderr, "%s: considering warnings as "
532: "errors\n", __progname);
533: return(0);
1.1 kristaps 534: }
535:
1.21 kristaps 536: return(1);
1.1 kristaps 537: }
538:
539:
540: static void
541: usage(void)
542: {
543: extern char *__progname;
544:
1.21 kristaps 545: (void)fprintf(stderr, "usage: %s [-v] [-Wwarn...] [infile]\n",
1.19 kristaps 546: __progname);
1.1 kristaps 547: }
1.18 kristaps 548:
CVSweb