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