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