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