]> git.cameronkatri.com Git - mandoc.git/blob - mmain.c
Removed "regression" for OpenBSD (broken).
[mandoc.git] / mmain.c
1 /* $Id: mmain.c,v 1.11 2009/03/09 13:35:09 kristaps Exp $ */
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) /* Input line step-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 size_t bufsz; /* Input buffer size. */
45 char *in; /* Input file name. */
46 int fdin; /* Input file desc. */
47 int pflags; /* Parse flags. */
48 };
49
50 extern char *__progname;
51
52 static int optswarn(struct mmain *, char *);
53 static int optsopt(struct mmain *, char *);
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 **);
62 extern size_t strlcpy(char *, const char *, size_t);
63 extern size_t strlcat(char *, const char *, size_t);
64 #endif
65
66
67 /*
68 * Print our and our caller's usage message.
69 */
70 void
71 mmain_usage(const char *help)
72 {
73
74 warnx("usage: %s %s%s[-v] [-foption...] [-Wwarn...] [infile]", __progname,
75 help ? help : "", help ? " " : "");
76 }
77
78
79 /*
80 * Allocate the convenience library and initialise some values.
81 */
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
90 p->in = "-";
91 p->fdin = STDIN_FILENO;
92
93 return(p);
94 }
95
96
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 */
103 int
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 *))
107 {
108 int c;
109 char opts[32]; /* XXX */
110 size_t sz;
111
112 extern int optind;
113
114 sz = strlcpy(opts, "VvW:f:", 32);
115 assert(sz < 32);
116
117 if (u) {
118 sz = strlcat(opts, u, 32);
119 assert(sz < 32);
120 }
121
122 optind = 1;
123
124 /* LINTED */
125 while (-1 != (c = getopt(argc, argv, opts)))
126 switch (c) {
127 case ('f'):
128 if ( ! optsopt(p, optarg))
129 return(-1);
130 break;
131 case ('v'):
132 p->dbg++;
133 break;
134 case ('V'):
135 (void)printf("%s %s\n", __progname, VERSION);
136 return(0);
137 case ('W'):
138 if ( ! optswarn(p, optarg))
139 return(-1);
140 break;
141 case ('?'):
142 mmain_usage(help);
143 return(-1);
144 default:
145 assert(getopt_cb);
146 if ((*getopt_cb)(arg, c, optarg))
147 break;
148 return(-1);
149 }
150
151 argv += optind;
152 if ((argc -= optind) > 0)
153 p->in = *argv++;
154
155 return(1);
156 }
157
158
159 dead_pre void
160 mmain_exit(struct mmain *p, int code)
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
193 p->bufsz = (size_t)MAX(st.st_blksize, BUFSIZ);
194
195 p->buf = malloc(p->bufsz);
196 if (NULL == p->buf)
197 err(1, "malloc");
198
199 /* Allocate the parser. */
200
201 p->mdoc = mdoc_alloc(p, p->pflags, &cb);
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
217 optsopt(struct mmain *p, char *arg)
218 {
219 char *v;
220 char *toks[] = { "ign-scope", "ign-escape",
221 "ign-macro", NULL };
222
223 while (*arg)
224 switch (getsubopt(&arg, toks, &v)) {
225 case (0):
226 p->pflags |= MDOC_IGN_SCOPE;
227 break;
228 case (1):
229 p->pflags |= MDOC_IGN_ESCAPE;
230 break;
231 case (2):
232 p->pflags |= MDOC_IGN_MACRO;
233 break;
234 default:
235 warnx("unknown -f argument");
236 return(0);
237 }
238
239 return(1);
240 }
241
242
243 static int
244 optswarn(struct mmain *p, char *arg)
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:
265 warnx("unknown -W argument");
266 return(0);
267 }
268
269 return(1);
270 }
271
272
273 static int
274 parse(struct mmain *p)
275 {
276 ssize_t sz;
277 int j, i, pos, len, lnn;
278 char *ln;
279
280 for (ln = NULL, lnn = 1, len = pos = 0; ; ) {
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
287 for (i = 0; i < (int)sz; i++) {
288 if (pos >= len) {
289 len += MD_LINE_SZ;
290 ln = realloc(ln, (size_t)len);
291 if (NULL == ln)
292 err(1, "realloc");
293 }
294
295 if ('\n' != p->buf[i]) {
296 ln[pos++] = p->buf[i];
297 continue;
298 }
299
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;
315 if ( ! mdoc_parseln(p->mdoc, lnn, ln)) {
316 free(ln);
317 return(0);
318 }
319 lnn++;
320 pos = 0;
321 }
322 }
323
324 if (ln)
325 free(ln);
326 if (pos > 0)
327 warnx("%s: file not eof-terminated", p->in);
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 }