]> git.cameronkatri.com Git - mandoc.git/blob - mmain.c
Adding mdoclint.1 manual.
[mandoc.git] / mmain.c
1 /* $Id: mmain.c,v 1.3 2009/02/23 12:45:19 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, "vW:", 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 ('W'):
129 if ( ! getsopts(p, optarg))
130 return(0);
131 break;
132 case ('?'):
133 mmain_usage(help);
134 return(0);
135 default:
136 assert(getopt_cb);
137 if ((*getopt_cb)(arg, c, optarg))
138 break;
139 return(0);
140 }
141
142 argv += optind;
143 if ((argc -= optind) > 0)
144 p->in = *argv++;
145
146 return(1);
147 }
148
149
150 __dead void
151 mmain_exit(struct mmain *p, int code)
152 {
153
154 if (p->mdoc)
155 mdoc_free(p->mdoc);
156 free(p);
157 exit(code);
158 }
159
160
161 struct mdoc *
162 mmain_mdoc(struct mmain *p)
163 {
164 struct stat st;
165 int c;
166 struct mdoc_cb cb;
167
168 cb.mdoc_err = msg_err;
169 cb.mdoc_warn = msg_warn;
170 cb.mdoc_msg = msg_msg;
171
172 if (0 != strcmp(p->in, "-"))
173 if (-1 == (p->fdin = open(p->in, O_RDONLY, 0))) {
174 warn("%s", p->in);
175 return(0);
176 }
177
178 /* Allocate a buffer to be BUFSIZ/block size. */
179
180 if (-1 == fstat(p->fdin, &st)) {
181 warn("%s", p->in);
182 p->bufsz = BUFSIZ;
183 } else
184 p->bufsz = MAX(st.st_blksize, BUFSIZ);
185
186 p->buf = malloc(p->bufsz);
187 if (NULL == p->buf)
188 err(1, "malloc");
189
190 /* Allocate the parser. */
191
192 p->mdoc = mdoc_alloc(p, &cb);
193
194 /* Parse the input file. */
195
196 c = parse(p);
197 free(p->buf);
198
199 if (STDIN_FILENO != p->fdin)
200 if (-1 == close(p->fdin))
201 warn("%s", p->in);
202
203 return(c ? p->mdoc : NULL);
204 }
205
206
207 static int
208 getsopts(struct mmain *p, char *arg)
209 {
210 char *v;
211 char *toks[] = { "all", "compat",
212 "syntax", "error", NULL };
213
214 while (*arg)
215 switch (getsubopt(&arg, toks, &v)) {
216 case (0):
217 p->warn |= MD_WARN_ALL;
218 break;
219 case (1):
220 p->warn |= MD_WARN_COMPAT;
221 break;
222 case (2):
223 p->warn |= MD_WARN_SYNTAX;
224 break;
225 case (3):
226 p->warn |= MD_WARN_ERR;
227 break;
228 default:
229 return(0);
230 }
231
232 return(1);
233 }
234
235
236 static int
237 parse(struct mmain *p)
238 {
239 ssize_t sz, i;
240 size_t pos;
241 char line[MD_LINE_SZ];
242 int lnn;
243
244 /*
245 * This is a little more complicated than fgets. TODO: have
246 * some benchmarks that show it's faster (note that I want to
247 * check many, many manuals simultaneously, so speed is
248 * important). Fill a buffer (sized to the block size) with a
249 * single read, then parse \n-terminated lines into a line
250 * buffer, which is passed to the parser. Hard-code the line
251 * buffer to a particular size -- a reasonable assumption.
252 */
253
254 for (lnn = 1, pos = 0; ; ) {
255 if (-1 == (sz = read(p->fdin, p->buf, p->bufsz))) {
256 warn("%s", p->in);
257 return(0);
258 } else if (0 == sz)
259 break;
260
261 for (i = 0; i < sz; i++) {
262 if ('\n' != p->buf[i]) {
263 if (pos < sizeof(line)) {
264 line[(int)pos++] = p->buf[(int)i];
265 continue;
266 }
267 warnx("%s: line %d too long", p->in, lnn);
268 return(0);
269 }
270
271 line[(int)pos] = 0;
272 if ( ! mdoc_parseln(p->mdoc, lnn, line))
273 return(0);
274
275 lnn++;
276 pos = 0;
277 }
278 }
279
280 return(mdoc_endparse(p->mdoc));
281 }
282
283
284 static int
285 msg_err(void *arg, int line, int col, const char *msg)
286 {
287 struct mmain *p;
288
289 p = (struct mmain *)arg;
290
291 warnx("%s:%d: error: %s (column %d)",
292 p->in, line, msg, col);
293 return(0);
294 }
295
296
297 static void
298 msg_msg(void *arg, int line, int col, const char *msg)
299 {
300 struct mmain *p;
301
302 p = (struct mmain *)arg;
303
304 if (0 == p->dbg)
305 return;
306
307 warnx("%s:%d: debug: %s (column %d)",
308 p->in, line, msg, col);
309 }
310
311
312 static int
313 msg_warn(void *arg, int line, int col,
314 enum mdoc_warn type, const char *msg)
315 {
316 struct mmain *p;
317
318 p = (struct mmain *)arg;
319
320 switch (type) {
321 case (WARN_COMPAT):
322 if (p->warn & MD_WARN_COMPAT)
323 break;
324 return(1);
325 case (WARN_SYNTAX):
326 if (p->warn & MD_WARN_SYNTAX)
327 break;
328 return(1);
329 }
330
331 warnx("%s:%d: warning: %s (column %d)",
332 p->in, line, msg, col);
333
334 if ( ! (p->warn & MD_WARN_ERR))
335 return(1);
336
337 warnx("%s: considering warnings as errors", __progname);
338 return(0);
339 }