]> git.cameronkatri.com Git - mandoc.git/blob - mmain.c
Segmentation-fault fix in mdocterm.
[mandoc.git] / mmain.c
1 /* $Id: mmain.c,v 1.6 2009/03/08 11:41:22 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) /* 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. */
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] [-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 = 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, &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[] = { "ignore-scope", NULL };
221
222 while (*arg)
223 switch (getsubopt(&arg, toks, &v)) {
224 case (0):
225 p->pflags |= MDOC_IGN_SCOPE;
226 break;
227 default:
228 return(0);
229 }
230
231 return(1);
232 }
233
234
235 static int
236 optswarn(struct mmain *p, char *arg)
237 {
238 char *v;
239 char *toks[] = { "all", "compat",
240 "syntax", "error", NULL };
241
242 while (*arg)
243 switch (getsubopt(&arg, toks, &v)) {
244 case (0):
245 p->warn |= MD_WARN_ALL;
246 break;
247 case (1):
248 p->warn |= MD_WARN_COMPAT;
249 break;
250 case (2):
251 p->warn |= MD_WARN_SYNTAX;
252 break;
253 case (3):
254 p->warn |= MD_WARN_ERR;
255 break;
256 default:
257 return(0);
258 }
259
260 return(1);
261 }
262
263
264 static int
265 parse(struct mmain *p)
266 {
267 ssize_t sz, i;
268 size_t pos;
269 char line[MD_LINE_SZ];
270 int lnn;
271
272 /*
273 * This is a little more complicated than fgets. TODO: have
274 * some benchmarks that show it's faster (note that I want to
275 * check many, many manuals simultaneously, so speed is
276 * important). Fill a buffer (sized to the block size) with a
277 * single read, then parse \n-terminated lines into a line
278 * buffer, which is passed to the parser. Hard-code the line
279 * buffer to a particular size -- a reasonable assumption.
280 */
281
282 for (lnn = 1, pos = 0; ; ) {
283 if (-1 == (sz = read(p->fdin, p->buf, p->bufsz))) {
284 warn("%s", p->in);
285 return(0);
286 } else if (0 == sz)
287 break;
288
289 for (i = 0; i < sz; i++) {
290 if ('\n' != p->buf[i]) {
291 if (pos < sizeof(line)) {
292 line[(int)pos++] = p->buf[(int)i];
293 continue;
294 }
295 warnx("%s: line %d too long", p->in, lnn);
296 return(0);
297 }
298
299 line[(int)pos] = 0;
300 if ( ! mdoc_parseln(p->mdoc, lnn, line))
301 return(0);
302
303 lnn++;
304 pos = 0;
305 }
306 }
307
308 return(mdoc_endparse(p->mdoc));
309 }
310
311
312 static int
313 msg_err(void *arg, int line, int col, const char *msg)
314 {
315 struct mmain *p;
316
317 p = (struct mmain *)arg;
318
319 warnx("%s:%d: error: %s (column %d)",
320 p->in, line, msg, col);
321 return(0);
322 }
323
324
325 static void
326 msg_msg(void *arg, int line, int col, const char *msg)
327 {
328 struct mmain *p;
329
330 p = (struct mmain *)arg;
331
332 if (0 == p->dbg)
333 return;
334
335 warnx("%s:%d: debug: %s (column %d)",
336 p->in, line, msg, col);
337 }
338
339
340 static int
341 msg_warn(void *arg, int line, int col,
342 enum mdoc_warn type, const char *msg)
343 {
344 struct mmain *p;
345
346 p = (struct mmain *)arg;
347
348 switch (type) {
349 case (WARN_COMPAT):
350 if (p->warn & MD_WARN_COMPAT)
351 break;
352 return(1);
353 case (WARN_SYNTAX):
354 if (p->warn & MD_WARN_SYNTAX)
355 break;
356 return(1);
357 }
358
359 warnx("%s:%d: warning: %s (column %d)",
360 p->in, line, msg, col);
361
362 if ( ! (p->warn & MD_WARN_ERR))
363 return(1);
364
365 warnx("%s: considering warnings as errors", __progname);
366 return(0);
367 }