Annotation of mandoc/mmain.c, Revision 1.4
1.4 ! kristaps 1: /* $Id: mmain.c,v 1.3 2009/02/23 12:45:19 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.2 kristaps 112: sz = strlcpy(opts, "vW:", 32);
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;
128: case ('W'):
129: if ( ! getsopts(p, optarg))
130: return(0);
131: break;
132: case ('?'):
1.2 kristaps 133: mmain_usage(help);
134: return(0);
1.1 kristaps 135: default:
1.2 kristaps 136: assert(getopt_cb);
137: if ((*getopt_cb)(arg, c, optarg))
138: break;
1.1 kristaps 139: return(0);
140: }
141:
142: argv += optind;
1.2 kristaps 143: if ((argc -= optind) > 0)
144: p->in = *argv++;
1.1 kristaps 145:
1.2 kristaps 146: return(1);
1.1 kristaps 147: }
148:
149:
1.4 ! kristaps 150: dead_pre void
! 151: mmain_exit(struct mmain *p, int code)
1.1 kristaps 152: {
153:
154: if (p->mdoc)
155: mdoc_free(p->mdoc);
156: free(p);
157: exit(code);
158: }
159:
160:
161: struct mdoc *
162: mmain_mdoc(struct mmain *p)
163: {
164: struct stat st;
165: int c;
166: struct mdoc_cb cb;
167:
168: cb.mdoc_err = msg_err;
169: cb.mdoc_warn = msg_warn;
170: cb.mdoc_msg = msg_msg;
171:
172: if (0 != strcmp(p->in, "-"))
173: if (-1 == (p->fdin = open(p->in, O_RDONLY, 0))) {
174: warn("%s", p->in);
175: return(0);
176: }
177:
178: /* Allocate a buffer to be BUFSIZ/block size. */
179:
180: if (-1 == fstat(p->fdin, &st)) {
181: warn("%s", p->in);
182: p->bufsz = BUFSIZ;
183: } else
184: p->bufsz = MAX(st.st_blksize, BUFSIZ);
185:
186: p->buf = malloc(p->bufsz);
187: if (NULL == p->buf)
188: err(1, "malloc");
189:
190: /* Allocate the parser. */
191:
192: p->mdoc = mdoc_alloc(p, &cb);
193:
194: /* Parse the input file. */
195:
196: c = parse(p);
197: free(p->buf);
198:
199: if (STDIN_FILENO != p->fdin)
200: if (-1 == close(p->fdin))
201: warn("%s", p->in);
202:
203: return(c ? p->mdoc : NULL);
204: }
205:
206:
207: static int
208: getsopts(struct mmain *p, char *arg)
209: {
210: char *v;
211: char *toks[] = { "all", "compat",
212: "syntax", "error", NULL };
213:
214: while (*arg)
215: switch (getsubopt(&arg, toks, &v)) {
216: case (0):
217: p->warn |= MD_WARN_ALL;
218: break;
219: case (1):
220: p->warn |= MD_WARN_COMPAT;
221: break;
222: case (2):
223: p->warn |= MD_WARN_SYNTAX;
224: break;
225: case (3):
226: p->warn |= MD_WARN_ERR;
227: break;
228: default:
229: return(0);
230: }
231:
232: return(1);
233: }
234:
235:
236: static int
237: parse(struct mmain *p)
238: {
239: ssize_t sz, i;
240: size_t pos;
241: char line[MD_LINE_SZ];
242: int lnn;
243:
244: /*
245: * This is a little more complicated than fgets. TODO: have
246: * some benchmarks that show it's faster (note that I want to
247: * check many, many manuals simultaneously, so speed is
248: * important). Fill a buffer (sized to the block size) with a
249: * single read, then parse \n-terminated lines into a line
250: * buffer, which is passed to the parser. Hard-code the line
251: * buffer to a particular size -- a reasonable assumption.
252: */
253:
254: for (lnn = 1, pos = 0; ; ) {
255: if (-1 == (sz = read(p->fdin, p->buf, p->bufsz))) {
256: warn("%s", p->in);
257: return(0);
258: } else if (0 == sz)
259: break;
260:
261: for (i = 0; i < sz; i++) {
262: if ('\n' != p->buf[i]) {
263: if (pos < sizeof(line)) {
264: line[(int)pos++] = p->buf[(int)i];
265: continue;
266: }
267: warnx("%s: line %d too long", p->in, lnn);
268: return(0);
269: }
270:
271: line[(int)pos] = 0;
272: if ( ! mdoc_parseln(p->mdoc, lnn, line))
273: return(0);
274:
275: lnn++;
276: pos = 0;
277: }
278: }
279:
280: return(mdoc_endparse(p->mdoc));
281: }
282:
283:
284: static int
285: msg_err(void *arg, int line, int col, const char *msg)
286: {
287: struct mmain *p;
288:
289: p = (struct mmain *)arg;
290:
291: warnx("%s:%d: error: %s (column %d)",
292: p->in, line, msg, col);
293: return(0);
294: }
295:
296:
297: static void
298: msg_msg(void *arg, int line, int col, const char *msg)
299: {
300: struct mmain *p;
301:
302: p = (struct mmain *)arg;
303:
304: if (0 == p->dbg)
305: return;
306:
307: warnx("%s:%d: debug: %s (column %d)",
308: p->in, line, msg, col);
309: }
310:
311:
312: static int
313: msg_warn(void *arg, int line, int col,
314: enum mdoc_warn type, const char *msg)
315: {
316: struct mmain *p;
317:
318: p = (struct mmain *)arg;
319:
320: switch (type) {
321: case (WARN_COMPAT):
322: if (p->warn & MD_WARN_COMPAT)
323: break;
324: return(1);
325: case (WARN_SYNTAX):
326: if (p->warn & MD_WARN_SYNTAX)
327: break;
328: return(1);
329: }
330:
331: warnx("%s:%d: warning: %s (column %d)",
332: p->in, line, msg, col);
333:
334: if ( ! (p->warn & MD_WARN_ERR))
335: return(1);
336:
337: warnx("%s: considering warnings as errors", __progname);
338: return(0);
339: }
CVSweb