Annotation of mandoc/mdocml.c, Revision 1.54
1.54 ! kristaps 1: /* $Id: mdocml.c,v 1.53 2009/02/20 23:35:36 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.44 kristaps 33: #define MD_LINE_SZ (256) /* Max input line size. */
1.2 kristaps 34:
1.54 ! kristaps 35: typedef int (*md_print)(const struct mdoc_node *,
! 36: const struct mdoc_meta *);
! 37:
1.21 kristaps 38: struct md_parse {
1.47 kristaps 39: int warn; /* Warning flags. */
40: #define MD_WARN_SYNTAX (1 << 0) /* Show syntax warnings. */
41: #define MD_WARN_COMPAT (1 << 1) /* Show compat warnings. */
42: #define MD_WARN_ALL (0x03) /* Show all warnings. */
43: #define MD_WARN_ERR (1 << 2) /* Make warnings->errors. */
44: int dbg; /* Debug level. */
45: struct mdoc *mdoc; /* Active parser. */
46: char *buf; /* Input buffer. */
47: u_long bufsz; /* Input buffer size. */
1.51 kristaps 48: char *in; /* Input file name. */
49: int fdin; /* Input file desc. */
1.54 ! kristaps 50: md_print fp;
1.21 kristaps 51: };
1.17 kristaps 52:
1.47 kristaps 53: extern char *__progname;
54:
1.51 kristaps 55: static void usage(void);
1.44 kristaps 56:
1.51 kristaps 57: static int parse_opts(struct md_parse *, int, char *[]);
58: static int parse_subopts(struct md_parse *, char *);
1.9 kristaps 59:
1.47 kristaps 60: static int parse_begin(struct md_parse *);
61: static int parse_leave(struct md_parse *, int);
62: static int io_begin(struct md_parse *);
63: static int io_leave(struct md_parse *, int);
64: static int buf_begin(struct md_parse *);
65: static int buf_leave(struct md_parse *, int);
66:
67: static void msg_msg(void *, int, int, const char *);
68: static int msg_err(void *, int, int, const char *);
69: static int msg_warn(void *, int, int,
1.44 kristaps 70: enum mdoc_warn, const char *);
1.1 kristaps 71:
1.19 kristaps 72: #ifdef __linux__
1.47 kristaps 73: extern int getsubopt(char **, char *const *, char **);
1.19 kristaps 74: #endif
75:
1.1 kristaps 76: int
77: main(int argc, char *argv[])
78: {
1.21 kristaps 79: struct md_parse parser;
1.47 kristaps 80:
1.21 kristaps 81: (void)memset(&parser, 0, sizeof(struct md_parse));
1.17 kristaps 82:
1.51 kristaps 83: if ( ! parse_opts(&parser, argc, argv))
84: return(EXIT_FAILURE);
1.21 kristaps 85: if ( ! io_begin(&parser))
86: return(EXIT_FAILURE);
1.1 kristaps 87:
1.21 kristaps 88: return(EXIT_SUCCESS);
1.1 kristaps 89: }
90:
91:
92: static int
1.21 kristaps 93: io_leave(struct md_parse *p, int code)
1.1 kristaps 94: {
95:
1.51 kristaps 96: if (-1 == p->fdin || STDIN_FILENO == p->fdin)
1.21 kristaps 97: return(code);
98:
1.51 kristaps 99: if (-1 == close(p->fdin)) {
100: warn("%s", p->in);
1.21 kristaps 101: code = 0;
1.4 kristaps 102: }
1.21 kristaps 103: return(code);
104: }
105:
106:
107: static int
1.51 kristaps 108: parse_subopts(struct md_parse *p, char *arg)
109: {
110: char *v;
111: char *toks[] = { "all", "compat",
112: "syntax", "error", NULL };
113:
114: /*
115: * Future -Wxxx levels and so on should be here. For now we
116: * only recognise syntax and compat warnings as categories,
117: * beyond the usually "all" and "error" (make warn error out).
118: */
119:
120: while (*arg)
121: switch (getsubopt(&arg, toks, &v)) {
122: case (0):
123: p->warn |= MD_WARN_ALL;
124: break;
125: case (1):
126: p->warn |= MD_WARN_COMPAT;
127: break;
128: case (2):
129: p->warn |= MD_WARN_SYNTAX;
130: break;
131: case (3):
132: p->warn |= MD_WARN_ERR;
133: break;
134: default:
135: usage();
136: return(0);
137: }
138:
139: return(1);
140: }
141:
142:
143: static int
144: parse_opts(struct md_parse *p, int argc, char *argv[])
145: {
146: int c;
147:
148: extern char *optarg;
149: extern int optind;
150:
1.54 ! kristaps 151: extern int termprint(const struct mdoc_node *,
! 152: const struct mdoc_meta *);
! 153: extern int treeprint(const struct mdoc_node *,
! 154: const struct mdoc_meta *);
! 155:
1.51 kristaps 156: p->in = "-";
157:
1.54 ! kristaps 158: while (-1 != (c = getopt(argc, argv, "f:vW:")))
1.51 kristaps 159: switch (c) {
1.54 ! kristaps 160: case ('f'):
! 161: if (0 == strcmp(optarg, "tree")) {
! 162: p->fp = treeprint;
! 163: break;
! 164: } else if (0 == strcmp(optarg, "term")) {
! 165: p->fp = termprint;
! 166: break;
! 167: }
! 168: warnx("unknown filter: %s", optarg);
! 169: return(0);
1.51 kristaps 170: case ('v'):
171: p->dbg++;
172: break;
173: case ('W'):
174: if ( ! parse_subopts(p, optarg))
175: return(0);
176: break;
177: default:
178: usage();
179: return(0);
180: }
181:
182: argv += optind;
183: if (0 == (argc -= optind))
184: return(1);
185:
186: p->in = *argv++;
187: return(1);
188: }
189:
190:
191: static int
1.21 kristaps 192: io_begin(struct md_parse *p)
193: {
194:
1.51 kristaps 195: p->fdin = STDIN_FILENO;
196: if (0 != strncmp(p->in, "-", 1))
197: if (-1 == (p->fdin = open(p->in, O_RDONLY, 0))) {
198: warn("%s", p->in);
1.21 kristaps 199: return(io_leave(p, 0));
200: }
1.1 kristaps 201:
1.21 kristaps 202: return(io_leave(p, buf_begin(p)));
1.1 kristaps 203: }
204:
205:
206: static int
1.21 kristaps 207: buf_leave(struct md_parse *p, int code)
1.1 kristaps 208: {
1.4 kristaps 209:
1.21 kristaps 210: if (p->buf)
211: free(p->buf);
212: return(code);
213: }
1.1 kristaps 214:
215:
1.21 kristaps 216: static int
217: buf_begin(struct md_parse *p)
218: {
219: struct stat st;
1.1 kristaps 220:
1.51 kristaps 221: if (-1 == fstat(p->fdin, &st)) {
222: warn("%s", p->in);
223: return(0);
1.21 kristaps 224: }
225:
1.51 kristaps 226: /*
227: * Try to intuit the fastest way of sucking down buffered data
228: * by using either the block buffer size or the hard-coded one.
229: * This is inspired by bin/cat.c.
230: */
231:
1.21 kristaps 232: p->bufsz = MAX(st.st_blksize, BUFSIZ);
233:
234: if (NULL == (p->buf = malloc(p->bufsz))) {
235: warn("malloc");
236: return(buf_leave(p, 0));
237: }
238:
239: return(buf_leave(p, parse_begin(p)));
240: }
241:
242:
243: static int
244: parse_leave(struct md_parse *p, int code)
245: {
1.54 ! kristaps 246: md_print fp;
1.21 kristaps 247:
1.36 kristaps 248: if (NULL == p->mdoc)
249: return(code);
250:
251: if ( ! mdoc_endparse(p->mdoc))
252: code = 0;
1.51 kristaps 253:
1.54 ! kristaps 254: if (code && (fp = p->fp)) {
! 255: if ( ! (*fp)(mdoc_node(p->mdoc), mdoc_meta(p->mdoc)))
! 256: code = 0;
! 257: }
1.36 kristaps 258:
1.38 kristaps 259: mdoc_free(p->mdoc);
1.21 kristaps 260: return(code);
261: }
262:
263:
264: static int
265: parse_begin(struct md_parse *p)
266: {
267: ssize_t sz, i;
268: size_t pos;
1.44 kristaps 269: char line[MD_LINE_SZ];
1.21 kristaps 270: struct mdoc_cb cb;
1.43 kristaps 271: int lnn;
1.21 kristaps 272:
273: cb.mdoc_err = msg_err;
274: cb.mdoc_warn = msg_warn;
275: cb.mdoc_msg = msg_msg;
276:
277: if (NULL == (p->mdoc = mdoc_alloc(p, &cb)))
278: return(parse_leave(p, 0));
279:
1.51 kristaps 280: /*
281: * This is a little more complicated than fgets. TODO: have
282: * some benchmarks that show it's faster (note that I want to
283: * check many, many manuals simultaneously, so speed is
284: * important). Fill a buffer (sized to the block size) with a
285: * single read, then parse \n-terminated lines into a line
286: * buffer, which is passed to the parser. Hard-code the line
287: * buffer to a particular size -- a reasonable assumption.
288: */
289:
1.43 kristaps 290: for (lnn = 1, pos = 0; ; ) {
1.51 kristaps 291: if (-1 == (sz = read(p->fdin, p->buf, p->bufsz))) {
292: warn("%s", p->in);
1.21 kristaps 293: return(parse_leave(p, 0));
294: } else if (0 == sz)
295: break;
296:
297: for (i = 0; i < sz; i++) {
298: if ('\n' != p->buf[i]) {
299: if (pos < sizeof(line)) {
1.44 kristaps 300: line[(int)pos++] = p->buf[(int)i];
1.21 kristaps 301: continue;
302: }
1.51 kristaps 303: warnx("%s: line %d too long", p->in, lnn);
1.21 kristaps 304: return(parse_leave(p, 0));
305: }
306:
1.44 kristaps 307: line[(int)pos] = 0;
1.43 kristaps 308: if ( ! mdoc_parseln(p->mdoc, lnn, line))
1.21 kristaps 309: return(parse_leave(p, 0));
1.1 kristaps 310:
1.43 kristaps 311: lnn++;
1.21 kristaps 312: pos = 0;
1.4 kristaps 313: }
1.21 kristaps 314: }
1.1 kristaps 315:
1.21 kristaps 316: return(parse_leave(p, 1));
1.4 kristaps 317: }
1.1 kristaps 318:
319:
1.4 kristaps 320: static int
1.44 kristaps 321: msg_err(void *arg, int line, int col, const char *msg)
1.21 kristaps 322: {
323: struct md_parse *p;
324:
325: p = (struct md_parse *)arg;
326:
1.51 kristaps 327: warnx("%s:%d: error: %s (column %d)",
328: p->in, line, msg, col);
1.21 kristaps 329: return(0);
330: }
331:
332:
333: static void
1.37 kristaps 334: msg_msg(void *arg, int line, int col, const char *msg)
1.4 kristaps 335: {
1.21 kristaps 336: struct md_parse *p;
337:
338: p = (struct md_parse *)arg;
339:
1.44 kristaps 340: if (0 == p->dbg)
1.21 kristaps 341: return;
342:
1.51 kristaps 343: warnx("%s:%d: debug: %s (column %d)",
344: p->in, line, msg, col);
1.1 kristaps 345: }
346:
347:
348: static int
1.44 kristaps 349: msg_warn(void *arg, int line, int col,
350: enum mdoc_warn type, const char *msg)
1.1 kristaps 351: {
1.21 kristaps 352: struct md_parse *p;
1.1 kristaps 353:
1.21 kristaps 354: p = (struct md_parse *)arg;
1.1 kristaps 355:
1.44 kristaps 356: switch (type) {
357: case (WARN_COMPAT):
358: if (p->warn & MD_WARN_COMPAT)
359: break;
360: return(1);
361: case (WARN_SYNTAX):
362: if (p->warn & MD_WARN_SYNTAX)
363: break;
1.1 kristaps 364: return(1);
365: }
366:
1.51 kristaps 367: warnx("%s:%d: warning: %s (column %d)",
368: p->in, line, msg, col);
1.21 kristaps 369:
1.44 kristaps 370: if ( ! (p->warn & MD_WARN_ERR))
371: return(1);
1.21 kristaps 372:
1.51 kristaps 373: warnx("%s: considering warnings as errors", __progname);
1.44 kristaps 374: return(0);
1.1 kristaps 375: }
376:
377:
378: static void
379: usage(void)
380: {
381:
1.54 ! kristaps 382: warnx("usage: %s [-ffilter] [-v] [-Wwarn...] [infile]",
! 383: __progname);
1.1 kristaps 384: }
1.18 kristaps 385:
CVSweb