Annotation of mandoc/mmain.c, Revision 1.14
1.14 ! kristaps 1: /* $Id: mmain.c,v 1.13 2009/03/16 22:19:19 kristaps Exp $ */
1.1 kristaps 2: /*
1.14 ! kristaps 3: * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@openbsd.org>
1.1 kristaps 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:
1.8 kristaps 33: #define MD_LINE_SZ (256) /* Input line step-size. */
1.1 kristaps 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. */
1.9 kristaps 44: size_t bufsz; /* Input buffer size. */
1.12 kristaps 45: const 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.12 kristaps 52: static void usage(const char *, const char *);
1.6 kristaps 53: static int optswarn(struct mmain *, char *);
54: static int optsopt(struct mmain *, char *);
1.1 kristaps 55: static int parse(struct mmain *);
56: static void msg_msg(void *, int, int, const char *);
57: static int msg_err(void *, int, int, const char *);
58: static int msg_warn(void *, int, int,
59: enum mdoc_warn, const char *);
60:
1.2 kristaps 61: /*
62: * Print our and our caller's usage message.
63: */
1.1 kristaps 64: void
1.12 kristaps 65: usage(const char *help, const char *args)
1.1 kristaps 66: {
67:
1.12 kristaps 68: warnx("usage: %s %s%s[-v] [-foption...] [-Wwarn...]%s%s",
69: __progname,
70: help ? help : "", help ? " " : "",
71: args ? " " : "", args ? args : "");
1.1 kristaps 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:
86: return(p);
87: }
88:
89:
1.2 kristaps 90: /*
91: * Parse command-line options. Accepts a small (<28 char) opstring "u"
92: * parameter (e.g. "ho:") or NULL, a corresponding "help" string (e.g.
93: * "[-h] [-o output]" or NULL, a callback function for parsed arguments
94: * and an opaque pointer argument for that function.
95: */
1.1 kristaps 96: int
1.2 kristaps 97: mmain_getopt(struct mmain *p, int argc, char *argv[],
1.12 kristaps 98: const char *help, const char *args,
99: const char *u, void *arg,
100: int (*getopt_cb)(void *, int, char *))
1.1 kristaps 101: {
1.2 kristaps 102: int c;
103: char opts[32]; /* XXX */
104: size_t sz;
105:
106: extern int optind;
1.1 kristaps 107:
1.6 kristaps 108: sz = strlcpy(opts, "VvW:f:", 32);
1.2 kristaps 109: assert(sz < 32);
1.1 kristaps 110:
1.2 kristaps 111: if (u) {
112: sz = strlcat(opts, u, 32);
113: assert(sz < 32);
114: }
1.1 kristaps 115:
1.3 kristaps 116: optind = 1;
117:
1.2 kristaps 118: /* LINTED */
119: while (-1 != (c = getopt(argc, argv, opts)))
1.1 kristaps 120: switch (c) {
1.6 kristaps 121: case ('f'):
122: if ( ! optsopt(p, optarg))
1.12 kristaps 123: mmain_exit(p, 1);
1.6 kristaps 124: break;
1.1 kristaps 125: case ('v'):
126: p->dbg++;
127: break;
1.5 kristaps 128: case ('V'):
129: (void)printf("%s %s\n", __progname, VERSION);
1.12 kristaps 130: mmain_exit(p, 0);
131: /* NOTREACHED */
1.1 kristaps 132: case ('W'):
1.6 kristaps 133: if ( ! optswarn(p, optarg))
1.12 kristaps 134: mmain_exit(p, 1);
1.1 kristaps 135: break;
136: case ('?'):
1.12 kristaps 137: usage(help, args);
138: mmain_exit(p, 1);
139: /* NOTREACHED */
1.1 kristaps 140: default:
1.2 kristaps 141: assert(getopt_cb);
142: if ((*getopt_cb)(arg, c, optarg))
143: break;
1.12 kristaps 144: mmain_exit(p, 1);
145: /* NOTREACHED */
1.1 kristaps 146: }
147:
1.12 kristaps 148: return(optind);
149: }
150:
151:
152: void
153: mmain_reset(struct mmain *p)
154: {
1.1 kristaps 155:
1.12 kristaps 156: if (p->mdoc)
157: mdoc_free(p->mdoc);
158: p->mdoc = NULL;
1.1 kristaps 159: }
160:
161:
1.12 kristaps 162: void
163: mmain_free(struct mmain *p)
1.1 kristaps 164: {
165:
166: if (p->mdoc)
167: mdoc_free(p->mdoc);
168: free(p);
1.12 kristaps 169: }
170:
171:
1.13 kristaps 172: __dead void
1.12 kristaps 173: mmain_exit(struct mmain *p, int code)
174: {
175:
176: mmain_free(p);
1.1 kristaps 177: exit(code);
178: }
179:
180:
1.12 kristaps 181: void
182: mmain_prepare(struct mmain *p, const char *in)
1.1 kristaps 183: {
184: struct stat st;
185:
1.12 kristaps 186: p->in = in;
187: p->fdin = STDIN_FILENO;
1.1 kristaps 188:
189: if (0 != strcmp(p->in, "-"))
190: if (-1 == (p->fdin = open(p->in, O_RDONLY, 0))) {
191: warn("%s", p->in);
1.12 kristaps 192: mmain_exit(p, 1);
1.1 kristaps 193: }
194:
195: /* Allocate a buffer to be BUFSIZ/block size. */
196:
197: if (-1 == fstat(p->fdin, &st)) {
198: warn("%s", p->in);
199: p->bufsz = BUFSIZ;
200: } else
1.9 kristaps 201: p->bufsz = (size_t)MAX(st.st_blksize, BUFSIZ);
1.1 kristaps 202:
203: p->buf = malloc(p->bufsz);
204: if (NULL == p->buf)
205: err(1, "malloc");
1.12 kristaps 206: }
207:
208:
209: struct mdoc *
210: mmain_process(struct mmain *p)
211: {
212: int c;
213: struct mdoc_cb cb;
214:
215: /* XXX - in mmain_alloc.*/
216: cb.mdoc_err = msg_err;
217: cb.mdoc_warn = msg_warn;
218: cb.mdoc_msg = msg_msg;
1.1 kristaps 219:
220: /* Allocate the parser. */
221:
1.7 kristaps 222: p->mdoc = mdoc_alloc(p, p->pflags, &cb);
1.1 kristaps 223:
224: /* Parse the input file. */
225:
226: c = parse(p);
227: free(p->buf);
228:
229: if (STDIN_FILENO != p->fdin)
230: if (-1 == close(p->fdin))
231: warn("%s", p->in);
232:
233: return(c ? p->mdoc : NULL);
234: }
235:
236:
1.12 kristaps 237: struct mdoc *
238: mmain_mdoc(struct mmain *p, const char *in)
239: {
240:
241: mmain_prepare(p, in);
242: return(mmain_process(p));
243: }
244:
245:
1.1 kristaps 246: static int
1.6 kristaps 247: optsopt(struct mmain *p, char *arg)
248: {
249: char *v;
1.10 kristaps 250: char *toks[] = { "ign-scope", "ign-escape",
251: "ign-macro", NULL };
1.6 kristaps 252:
253: while (*arg)
254: switch (getsubopt(&arg, toks, &v)) {
255: case (0):
256: p->pflags |= MDOC_IGN_SCOPE;
257: break;
1.7 kristaps 258: case (1):
259: p->pflags |= MDOC_IGN_ESCAPE;
260: break;
1.10 kristaps 261: case (2):
262: p->pflags |= MDOC_IGN_MACRO;
263: break;
1.6 kristaps 264: default:
1.10 kristaps 265: warnx("unknown -f argument");
1.6 kristaps 266: return(0);
267: }
268:
269: return(1);
270: }
271:
272:
273: static int
274: optswarn(struct mmain *p, char *arg)
1.1 kristaps 275: {
276: char *v;
277: char *toks[] = { "all", "compat",
278: "syntax", "error", NULL };
279:
280: while (*arg)
281: switch (getsubopt(&arg, toks, &v)) {
282: case (0):
283: p->warn |= MD_WARN_ALL;
284: break;
285: case (1):
286: p->warn |= MD_WARN_COMPAT;
287: break;
288: case (2):
289: p->warn |= MD_WARN_SYNTAX;
290: break;
291: case (3):
292: p->warn |= MD_WARN_ERR;
293: break;
294: default:
1.10 kristaps 295: warnx("unknown -W argument");
1.1 kristaps 296: return(0);
297: }
298:
299: return(1);
300: }
301:
302:
303: static int
304: parse(struct mmain *p)
305: {
1.8 kristaps 306: ssize_t sz;
1.10 kristaps 307: int j, i, pos, len, lnn;
308: char *ln;
1.1 kristaps 309:
1.10 kristaps 310: for (ln = NULL, lnn = 1, len = pos = 0; ; ) {
1.1 kristaps 311: if (-1 == (sz = read(p->fdin, p->buf, p->bufsz))) {
312: warn("%s", p->in);
313: return(0);
314: } else if (0 == sz)
315: break;
316:
1.8 kristaps 317: for (i = 0; i < (int)sz; i++) {
318: if (pos >= len) {
319: len += MD_LINE_SZ;
1.10 kristaps 320: ln = realloc(ln, (size_t)len);
321: if (NULL == ln)
1.8 kristaps 322: err(1, "realloc");
323: }
324:
1.1 kristaps 325: if ('\n' != p->buf[i]) {
1.10 kristaps 326: ln[pos++] = p->buf[i];
1.8 kristaps 327: continue;
1.1 kristaps 328: }
1.8 kristaps 329:
1.10 kristaps 330: /* Check for escaped newline. */
331:
332: if (pos > 0 && '\\' == ln[pos - 1]) {
333: for (j = pos - 1; j >= 0; j--)
334: if ('\\' != ln[j])
335: break;
336:
337: if ( ! ((pos - j) % 2)) {
338: pos--;
339: lnn++;
340: continue;
341: }
342: }
343:
344: ln[pos] = 0;
1.11 kristaps 345: if ( ! mdoc_parseln(p->mdoc, lnn, ln)) {
346: free(ln);
1.1 kristaps 347: return(0);
1.11 kristaps 348: }
1.1 kristaps 349: lnn++;
350: pos = 0;
351: }
352: }
353:
1.11 kristaps 354: if (ln)
355: free(ln);
1.10 kristaps 356: if (pos > 0)
357: warnx("%s: file not eof-terminated", p->in);
1.1 kristaps 358: return(mdoc_endparse(p->mdoc));
359: }
360:
361:
362: static int
363: msg_err(void *arg, int line, int col, const char *msg)
364: {
365: struct mmain *p;
366:
367: p = (struct mmain *)arg;
368:
369: warnx("%s:%d: error: %s (column %d)",
370: p->in, line, msg, col);
371: return(0);
372: }
373:
374:
375: static void
376: msg_msg(void *arg, int line, int col, const char *msg)
377: {
378: struct mmain *p;
379:
380: p = (struct mmain *)arg;
381:
382: if (0 == p->dbg)
383: return;
384:
385: warnx("%s:%d: debug: %s (column %d)",
386: p->in, line, msg, col);
387: }
388:
389:
390: static int
391: msg_warn(void *arg, int line, int col,
392: enum mdoc_warn type, const char *msg)
393: {
394: struct mmain *p;
395:
396: p = (struct mmain *)arg;
397:
398: switch (type) {
399: case (WARN_COMPAT):
400: if (p->warn & MD_WARN_COMPAT)
401: break;
402: return(1);
403: case (WARN_SYNTAX):
404: if (p->warn & MD_WARN_SYNTAX)
405: break;
406: return(1);
407: }
408:
409: warnx("%s:%d: warning: %s (column %d)",
410: p->in, line, msg, col);
411:
412: if ( ! (p->warn & MD_WARN_ERR))
413: return(1);
414:
415: warnx("%s: considering warnings as errors", __progname);
416: return(0);
417: }
CVSweb