Annotation of mandoc/mdocml.c, Revision 1.52
1.52 ! kristaps 1: /* $Id: mdocml.c,v 1.51 2009/02/20 07:43:15 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: {
1.52 ! kristaps 227: extern int termprint(const struct mdoc_node *,
! 228: const struct mdoc_meta *);
1.21 kristaps 229:
1.36 kristaps 230: if (NULL == p->mdoc)
231: return(code);
232:
233: if ( ! mdoc_endparse(p->mdoc))
234: code = 0;
1.51 kristaps 235:
236: /* TODO */
1.52 ! kristaps 237: if (code && ! termprint(mdoc_node(p->mdoc), mdoc_meta(p->mdoc)))
1.51 kristaps 238: code = 0;
1.36 kristaps 239:
1.38 kristaps 240: mdoc_free(p->mdoc);
1.21 kristaps 241: return(code);
242: }
243:
244:
245: static int
246: parse_begin(struct md_parse *p)
247: {
248: ssize_t sz, i;
249: size_t pos;
1.44 kristaps 250: char line[MD_LINE_SZ];
1.21 kristaps 251: struct mdoc_cb cb;
1.43 kristaps 252: int lnn;
1.21 kristaps 253:
254: cb.mdoc_err = msg_err;
255: cb.mdoc_warn = msg_warn;
256: cb.mdoc_msg = msg_msg;
257:
258: if (NULL == (p->mdoc = mdoc_alloc(p, &cb)))
259: return(parse_leave(p, 0));
260:
1.51 kristaps 261: /*
262: * This is a little more complicated than fgets. TODO: have
263: * some benchmarks that show it's faster (note that I want to
264: * check many, many manuals simultaneously, so speed is
265: * important). Fill a buffer (sized to the block size) with a
266: * single read, then parse \n-terminated lines into a line
267: * buffer, which is passed to the parser. Hard-code the line
268: * buffer to a particular size -- a reasonable assumption.
269: */
270:
1.43 kristaps 271: for (lnn = 1, pos = 0; ; ) {
1.51 kristaps 272: if (-1 == (sz = read(p->fdin, p->buf, p->bufsz))) {
273: warn("%s", p->in);
1.21 kristaps 274: return(parse_leave(p, 0));
275: } else if (0 == sz)
276: break;
277:
278: for (i = 0; i < sz; i++) {
279: if ('\n' != p->buf[i]) {
280: if (pos < sizeof(line)) {
1.44 kristaps 281: line[(int)pos++] = p->buf[(int)i];
1.21 kristaps 282: continue;
283: }
1.51 kristaps 284: warnx("%s: line %d too long", p->in, lnn);
1.21 kristaps 285: return(parse_leave(p, 0));
286: }
287:
1.44 kristaps 288: line[(int)pos] = 0;
1.43 kristaps 289: if ( ! mdoc_parseln(p->mdoc, lnn, line))
1.21 kristaps 290: return(parse_leave(p, 0));
1.1 kristaps 291:
1.43 kristaps 292: lnn++;
1.21 kristaps 293: pos = 0;
1.4 kristaps 294: }
1.21 kristaps 295: }
1.1 kristaps 296:
1.21 kristaps 297: return(parse_leave(p, 1));
1.4 kristaps 298: }
1.1 kristaps 299:
300:
1.4 kristaps 301: static int
1.44 kristaps 302: msg_err(void *arg, int line, int col, const char *msg)
1.21 kristaps 303: {
304: struct md_parse *p;
305:
306: p = (struct md_parse *)arg;
307:
1.51 kristaps 308: warnx("%s:%d: error: %s (column %d)",
309: p->in, line, msg, col);
1.21 kristaps 310: return(0);
311: }
312:
313:
314: static void
1.37 kristaps 315: msg_msg(void *arg, int line, int col, const char *msg)
1.4 kristaps 316: {
1.21 kristaps 317: struct md_parse *p;
318:
319: p = (struct md_parse *)arg;
320:
1.44 kristaps 321: if (0 == p->dbg)
1.21 kristaps 322: return;
323:
1.51 kristaps 324: warnx("%s:%d: debug: %s (column %d)",
325: p->in, line, msg, col);
1.1 kristaps 326: }
327:
328:
329: static int
1.44 kristaps 330: msg_warn(void *arg, int line, int col,
331: enum mdoc_warn type, const char *msg)
1.1 kristaps 332: {
1.21 kristaps 333: struct md_parse *p;
1.1 kristaps 334:
1.21 kristaps 335: p = (struct md_parse *)arg;
1.1 kristaps 336:
1.44 kristaps 337: switch (type) {
338: case (WARN_COMPAT):
339: if (p->warn & MD_WARN_COMPAT)
340: break;
341: return(1);
342: case (WARN_SYNTAX):
343: if (p->warn & MD_WARN_SYNTAX)
344: break;
1.1 kristaps 345: return(1);
346: }
347:
1.51 kristaps 348: warnx("%s:%d: warning: %s (column %d)",
349: p->in, line, msg, col);
1.21 kristaps 350:
1.44 kristaps 351: if ( ! (p->warn & MD_WARN_ERR))
352: return(1);
1.21 kristaps 353:
1.51 kristaps 354: warnx("%s: considering warnings as errors", __progname);
1.44 kristaps 355: return(0);
1.1 kristaps 356: }
357:
358:
359: static void
360: usage(void)
361: {
362:
1.51 kristaps 363: warnx("usage: %s [-v] [-Wwarn...] [infile]", __progname);
1.1 kristaps 364: }
1.18 kristaps 365:
CVSweb