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