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