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