Annotation of mandoc/mmain.c, Revision 1.6
1.6 ! kristaps 1: /* $Id: mmain.c,v 1.5 2009/03/06 14:13:47 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: */
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 "mmain.h"
32:
33: #define MD_LINE_SZ (256) /* Max input line size. */
34:
35: struct mmain {
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.2 kristaps 45: char *in; /* Input file name. */
1.1 kristaps 46: int fdin; /* Input file desc. */
1.6 ! kristaps 47: int pflags; /* Parse flags. */
1.1 kristaps 48: };
49:
50: extern char *__progname;
51:
1.6 ! kristaps 52: static int optswarn(struct mmain *, char *);
! 53: static int optsopt(struct mmain *, char *);
1.1 kristaps 54: static int parse(struct mmain *);
55: static void msg_msg(void *, int, int, const char *);
56: static int msg_err(void *, int, int, const char *);
57: static int msg_warn(void *, int, int,
58: enum mdoc_warn, const char *);
59:
60: #ifdef __linux__
61: extern int getsubopt(char **, char *const *, char **);
1.3 kristaps 62: extern size_t strlcpy(char *, const char *, size_t);
63: extern size_t strlcat(char *, const char *, size_t);
1.1 kristaps 64: #endif
65:
66:
1.2 kristaps 67: /*
68: * Print our and our caller's usage message.
69: */
1.1 kristaps 70: void
71: mmain_usage(const char *help)
72: {
73:
74: warnx("usage: %s %s%s[-v] [-Wwarn...] [infile]", __progname,
75: help ? help : "", help ? " " : "");
76: }
77:
78:
1.2 kristaps 79: /*
80: * Allocate the convenience library and initialise some values.
81: */
1.1 kristaps 82: struct mmain *
83: mmain_alloc(void)
84: {
85: struct mmain *p;
86:
87: if (NULL == (p = calloc(1, sizeof(struct mmain))))
88: err(1, "malloc");
89:
1.2 kristaps 90: p->in = "-";
1.1 kristaps 91: p->fdin = STDIN_FILENO;
92:
93: return(p);
94: }
95:
96:
1.2 kristaps 97: /*
98: * Parse command-line options. Accepts a small (<28 char) opstring "u"
99: * parameter (e.g. "ho:") or NULL, a corresponding "help" string (e.g.
100: * "[-h] [-o output]" or NULL, a callback function for parsed arguments
101: * and an opaque pointer argument for that function.
102: */
1.1 kristaps 103: int
1.2 kristaps 104: mmain_getopt(struct mmain *p, int argc, char *argv[],
105: const char *help, const char *u, void *arg,
106: int (*getopt_cb)(void *, int, const char *))
1.1 kristaps 107: {
1.2 kristaps 108: int c;
109: char opts[32]; /* XXX */
110: size_t sz;
111:
112: extern int optind;
1.1 kristaps 113:
1.6 ! kristaps 114: sz = strlcpy(opts, "VvW:f:", 32);
1.2 kristaps 115: assert(sz < 32);
1.1 kristaps 116:
1.2 kristaps 117: if (u) {
118: sz = strlcat(opts, u, 32);
119: assert(sz < 32);
120: }
1.1 kristaps 121:
1.3 kristaps 122: optind = 1;
123:
1.2 kristaps 124: /* LINTED */
125: while (-1 != (c = getopt(argc, argv, opts)))
1.1 kristaps 126: switch (c) {
1.6 ! kristaps 127: case ('f'):
! 128: if ( ! optsopt(p, optarg))
! 129: return(-1);
! 130: break;
1.1 kristaps 131: case ('v'):
132: p->dbg++;
133: break;
1.5 kristaps 134: case ('V'):
135: (void)printf("%s %s\n", __progname, VERSION);
136: return(0);
1.1 kristaps 137: case ('W'):
1.6 ! kristaps 138: if ( ! optswarn(p, optarg))
1.5 kristaps 139: return(-1);
1.1 kristaps 140: break;
141: case ('?'):
1.2 kristaps 142: mmain_usage(help);
1.5 kristaps 143: return(-1);
1.1 kristaps 144: default:
1.2 kristaps 145: assert(getopt_cb);
146: if ((*getopt_cb)(arg, c, optarg))
147: break;
1.5 kristaps 148: return(-1);
1.1 kristaps 149: }
150:
151: argv += optind;
1.2 kristaps 152: if ((argc -= optind) > 0)
153: p->in = *argv++;
1.1 kristaps 154:
1.2 kristaps 155: return(1);
1.1 kristaps 156: }
157:
158:
1.4 kristaps 159: dead_pre void
160: mmain_exit(struct mmain *p, int code)
1.1 kristaps 161: {
162:
163: if (p->mdoc)
164: mdoc_free(p->mdoc);
165: free(p);
166: exit(code);
167: }
168:
169:
170: struct mdoc *
171: mmain_mdoc(struct mmain *p)
172: {
173: struct stat st;
174: int c;
175: struct mdoc_cb cb;
176:
177: cb.mdoc_err = msg_err;
178: cb.mdoc_warn = msg_warn;
179: cb.mdoc_msg = msg_msg;
180:
181: if (0 != strcmp(p->in, "-"))
182: if (-1 == (p->fdin = open(p->in, O_RDONLY, 0))) {
183: warn("%s", p->in);
184: return(0);
185: }
186:
187: /* Allocate a buffer to be BUFSIZ/block size. */
188:
189: if (-1 == fstat(p->fdin, &st)) {
190: warn("%s", p->in);
191: p->bufsz = BUFSIZ;
192: } else
193: p->bufsz = MAX(st.st_blksize, BUFSIZ);
194:
195: p->buf = malloc(p->bufsz);
196: if (NULL == p->buf)
197: err(1, "malloc");
198:
199: /* Allocate the parser. */
200:
201: p->mdoc = mdoc_alloc(p, &cb);
202:
203: /* Parse the input file. */
204:
205: c = parse(p);
206: free(p->buf);
207:
208: if (STDIN_FILENO != p->fdin)
209: if (-1 == close(p->fdin))
210: warn("%s", p->in);
211:
212: return(c ? p->mdoc : NULL);
213: }
214:
215:
216: static int
1.6 ! kristaps 217: optsopt(struct mmain *p, char *arg)
! 218: {
! 219: char *v;
! 220: char *toks[] = { "ignore-scope", NULL };
! 221:
! 222: while (*arg)
! 223: switch (getsubopt(&arg, toks, &v)) {
! 224: case (0):
! 225: p->pflags |= MDOC_IGN_SCOPE;
! 226: break;
! 227: default:
! 228: return(0);
! 229: }
! 230:
! 231: return(1);
! 232: }
! 233:
! 234:
! 235: static int
! 236: optswarn(struct mmain *p, char *arg)
1.1 kristaps 237: {
238: char *v;
239: char *toks[] = { "all", "compat",
240: "syntax", "error", NULL };
241:
242: while (*arg)
243: switch (getsubopt(&arg, toks, &v)) {
244: case (0):
245: p->warn |= MD_WARN_ALL;
246: break;
247: case (1):
248: p->warn |= MD_WARN_COMPAT;
249: break;
250: case (2):
251: p->warn |= MD_WARN_SYNTAX;
252: break;
253: case (3):
254: p->warn |= MD_WARN_ERR;
255: break;
256: default:
257: return(0);
258: }
259:
260: return(1);
261: }
262:
263:
264: static int
265: parse(struct mmain *p)
266: {
267: ssize_t sz, i;
268: size_t pos;
269: char line[MD_LINE_SZ];
270: int lnn;
271:
272: /*
273: * This is a little more complicated than fgets. TODO: have
274: * some benchmarks that show it's faster (note that I want to
275: * check many, many manuals simultaneously, so speed is
276: * important). Fill a buffer (sized to the block size) with a
277: * single read, then parse \n-terminated lines into a line
278: * buffer, which is passed to the parser. Hard-code the line
279: * buffer to a particular size -- a reasonable assumption.
280: */
281:
282: for (lnn = 1, pos = 0; ; ) {
283: if (-1 == (sz = read(p->fdin, p->buf, p->bufsz))) {
284: warn("%s", p->in);
285: return(0);
286: } else if (0 == sz)
287: break;
288:
289: for (i = 0; i < sz; i++) {
290: if ('\n' != p->buf[i]) {
291: if (pos < sizeof(line)) {
292: line[(int)pos++] = p->buf[(int)i];
293: continue;
294: }
295: warnx("%s: line %d too long", p->in, lnn);
296: return(0);
297: }
298:
299: line[(int)pos] = 0;
300: if ( ! mdoc_parseln(p->mdoc, lnn, line))
301: return(0);
302:
303: lnn++;
304: pos = 0;
305: }
306: }
307:
308: return(mdoc_endparse(p->mdoc));
309: }
310:
311:
312: static int
313: msg_err(void *arg, int line, int col, const char *msg)
314: {
315: struct mmain *p;
316:
317: p = (struct mmain *)arg;
318:
319: warnx("%s:%d: error: %s (column %d)",
320: p->in, line, msg, col);
321: return(0);
322: }
323:
324:
325: static void
326: msg_msg(void *arg, int line, int col, const char *msg)
327: {
328: struct mmain *p;
329:
330: p = (struct mmain *)arg;
331:
332: if (0 == p->dbg)
333: return;
334:
335: warnx("%s:%d: debug: %s (column %d)",
336: p->in, line, msg, col);
337: }
338:
339:
340: static int
341: msg_warn(void *arg, int line, int col,
342: enum mdoc_warn type, const char *msg)
343: {
344: struct mmain *p;
345:
346: p = (struct mmain *)arg;
347:
348: switch (type) {
349: case (WARN_COMPAT):
350: if (p->warn & MD_WARN_COMPAT)
351: break;
352: return(1);
353: case (WARN_SYNTAX):
354: if (p->warn & MD_WARN_SYNTAX)
355: break;
356: return(1);
357: }
358:
359: warnx("%s:%d: warning: %s (column %d)",
360: p->in, line, msg, col);
361:
362: if ( ! (p->warn & MD_WARN_ERR))
363: return(1);
364:
365: warnx("%s: considering warnings as errors", __progname);
366: return(0);
367: }
CVSweb