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