]> git.cameronkatri.com Git - mandoc.git/blob - mmain.c
Strings abstracted into dynamically-created C files.
[mandoc.git] / mmain.c
1 /* $Id: mmain.c,v 1.5 2009/03/06 14:13:47 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 };
48
49 extern char *__progname;
50
51 static int getsopts(struct mmain *, char *);
52 static int parse(struct mmain *);
53 static void msg_msg(void *, int, int, const char *);
54 static int msg_err(void *, int, int, const char *);
55 static int msg_warn(void *, int, int,
56 enum mdoc_warn, const char *);
57
58 #ifdef __linux__
59 extern int getsubopt(char **, char *const *, char **);
60 extern size_t strlcpy(char *, const char *, size_t);
61 extern size_t strlcat(char *, const char *, size_t);
62 #endif
63
64
65 /*
66 * Print our and our caller's usage message.
67 */
68 void
69 mmain_usage(const char *help)
70 {
71
72 warnx("usage: %s %s%s[-v] [-Wwarn...] [infile]", __progname,
73 help ? help : "", help ? " " : "");
74 }
75
76
77 /*
78 * Allocate the convenience library and initialise some values.
79 */
80 struct mmain *
81 mmain_alloc(void)
82 {
83 struct mmain *p;
84
85 if (NULL == (p = calloc(1, sizeof(struct mmain))))
86 err(1, "malloc");
87
88 p->in = "-";
89 p->fdin = STDIN_FILENO;
90
91 return(p);
92 }
93
94
95 /*
96 * Parse command-line options. Accepts a small (<28 char) opstring "u"
97 * parameter (e.g. "ho:") or NULL, a corresponding "help" string (e.g.
98 * "[-h] [-o output]" or NULL, a callback function for parsed arguments
99 * and an opaque pointer argument for that function.
100 */
101 int
102 mmain_getopt(struct mmain *p, int argc, char *argv[],
103 const char *help, const char *u, void *arg,
104 int (*getopt_cb)(void *, int, const char *))
105 {
106 int c;
107 char opts[32]; /* XXX */
108 size_t sz;
109
110 extern int optind;
111
112 sz = strlcpy(opts, "VvW:", 32);
113 assert(sz < 32);
114
115 if (u) {
116 sz = strlcat(opts, u, 32);
117 assert(sz < 32);
118 }
119
120 optind = 1;
121
122 /* LINTED */
123 while (-1 != (c = getopt(argc, argv, opts)))
124 switch (c) {
125 case ('v'):
126 p->dbg++;
127 break;
128 case ('V'):
129 (void)printf("%s %s\n", __progname, VERSION);
130 return(0);
131 case ('W'):
132 if ( ! getsopts(p, optarg))
133 return(-1);
134 break;
135 case ('?'):
136 mmain_usage(help);
137 return(-1);
138 default:
139 assert(getopt_cb);
140 if ((*getopt_cb)(arg, c, optarg))
141 break;
142 return(-1);
143 }
144
145 argv += optind;
146 if ((argc -= optind) > 0)
147 p->in = *argv++;
148
149 return(1);
150 }
151
152
153 dead_pre void
154 mmain_exit(struct mmain *p, int code)
155 {
156
157 if (p->mdoc)
158 mdoc_free(p->mdoc);
159 free(p);
160 exit(code);
161 }
162
163
164 struct mdoc *
165 mmain_mdoc(struct mmain *p)
166 {
167 struct stat st;
168 int c;
169 struct mdoc_cb cb;
170
171 cb.mdoc_err = msg_err;
172 cb.mdoc_warn = msg_warn;
173 cb.mdoc_msg = msg_msg;
174
175 if (0 != strcmp(p->in, "-"))
176 if (-1 == (p->fdin = open(p->in, O_RDONLY, 0))) {
177 warn("%s", p->in);
178 return(0);
179 }
180
181 /* Allocate a buffer to be BUFSIZ/block size. */
182
183 if (-1 == fstat(p->fdin, &st)) {
184 warn("%s", p->in);
185 p->bufsz = BUFSIZ;
186 } else
187 p->bufsz = MAX(st.st_blksize, BUFSIZ);
188
189 p->buf = malloc(p->bufsz);
190 if (NULL == p->buf)
191 err(1, "malloc");
192
193 /* Allocate the parser. */
194
195 p->mdoc = mdoc_alloc(p, &cb);
196
197 /* Parse the input file. */
198
199 c = parse(p);
200 free(p->buf);
201
202 if (STDIN_FILENO != p->fdin)
203 if (-1 == close(p->fdin))
204 warn("%s", p->in);
205
206 return(c ? p->mdoc : NULL);
207 }
208
209
210 static int
211 getsopts(struct mmain *p, char *arg)
212 {
213 char *v;
214 char *toks[] = { "all", "compat",
215 "syntax", "error", NULL };
216
217 while (*arg)
218 switch (getsubopt(&arg, toks, &v)) {
219 case (0):
220 p->warn |= MD_WARN_ALL;
221 break;
222 case (1):
223 p->warn |= MD_WARN_COMPAT;
224 break;
225 case (2):
226 p->warn |= MD_WARN_SYNTAX;
227 break;
228 case (3):
229 p->warn |= MD_WARN_ERR;
230 break;
231 default:
232 return(0);
233 }
234
235 return(1);
236 }
237
238
239 static int
240 parse(struct mmain *p)
241 {
242 ssize_t sz, i;
243 size_t pos;
244 char line[MD_LINE_SZ];
245 int lnn;
246
247 /*
248 * This is a little more complicated than fgets. TODO: have
249 * some benchmarks that show it's faster (note that I want to
250 * check many, many manuals simultaneously, so speed is
251 * important). Fill a buffer (sized to the block size) with a
252 * single read, then parse \n-terminated lines into a line
253 * buffer, which is passed to the parser. Hard-code the line
254 * buffer to a particular size -- a reasonable assumption.
255 */
256
257 for (lnn = 1, pos = 0; ; ) {
258 if (-1 == (sz = read(p->fdin, p->buf, p->bufsz))) {
259 warn("%s", p->in);
260 return(0);
261 } else if (0 == sz)
262 break;
263
264 for (i = 0; i < sz; i++) {
265 if ('\n' != p->buf[i]) {
266 if (pos < sizeof(line)) {
267 line[(int)pos++] = p->buf[(int)i];
268 continue;
269 }
270 warnx("%s: line %d too long", p->in, lnn);
271 return(0);
272 }
273
274 line[(int)pos] = 0;
275 if ( ! mdoc_parseln(p->mdoc, lnn, line))
276 return(0);
277
278 lnn++;
279 pos = 0;
280 }
281 }
282
283 return(mdoc_endparse(p->mdoc));
284 }
285
286
287 static int
288 msg_err(void *arg, int line, int col, const char *msg)
289 {
290 struct mmain *p;
291
292 p = (struct mmain *)arg;
293
294 warnx("%s:%d: error: %s (column %d)",
295 p->in, line, msg, col);
296 return(0);
297 }
298
299
300 static void
301 msg_msg(void *arg, int line, int col, const char *msg)
302 {
303 struct mmain *p;
304
305 p = (struct mmain *)arg;
306
307 if (0 == p->dbg)
308 return;
309
310 warnx("%s:%d: debug: %s (column %d)",
311 p->in, line, msg, col);
312 }
313
314
315 static int
316 msg_warn(void *arg, int line, int col,
317 enum mdoc_warn type, const char *msg)
318 {
319 struct mmain *p;
320
321 p = (struct mmain *)arg;
322
323 switch (type) {
324 case (WARN_COMPAT):
325 if (p->warn & MD_WARN_COMPAT)
326 break;
327 return(1);
328 case (WARN_SYNTAX):
329 if (p->warn & MD_WARN_SYNTAX)
330 break;
331 return(1);
332 }
333
334 warnx("%s:%d: warning: %s (column %d)",
335 p->in, line, msg, col);
336
337 if ( ! (p->warn & MD_WARN_ERR))
338 return(1);
339
340 warnx("%s: considering warnings as errors", __progname);
341 return(0);
342 }