]> git.cameronkatri.com Git - mandoc.git/blob - mdoc.c
Memory-corruption fix.
[mandoc.git] / mdoc.c
1 /* $Id: mdoc.c,v 1.54 2009/03/08 12:40:27 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 <assert.h>
20 #include <ctype.h>
21 #include <err.h>
22 #include <stdarg.h>
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <string.h>
26
27 #include "private.h"
28
29 /*
30 * Main caller in the libmdoc library. This begins the parsing routine,
31 * handles allocation of data, and so forth. Most of the "work" is done
32 * in macro.c and validate.c.
33 */
34
35 static struct mdoc_node *mdoc_node_alloc(const struct mdoc *);
36 static int mdoc_node_append(struct mdoc *,
37 struct mdoc_node *);
38
39 static int parsetext(struct mdoc *, int, char *);
40 static int parsemacro(struct mdoc *, int, char *);
41
42
43 const char *const __mdoc_macronames[MDOC_MAX] = {
44 "\\\"", "Dd", "Dt", "Os",
45 "Sh", "Ss", "Pp", "D1",
46 "Dl", "Bd", "Ed", "Bl",
47 "El", "It", "Ad", "An",
48 "Ar", "Cd", "Cm", "Dv",
49 "Er", "Ev", "Ex", "Fa",
50 "Fd", "Fl", "Fn", "Ft",
51 "Ic", "In", "Li", "Nd",
52 "Nm", "Op", "Ot", "Pa",
53 "Rv", "St", "Va", "Vt",
54 /* LINTED */
55 "Xr", "\%A", "\%B", "\%D",
56 /* LINTED */
57 "\%I", "\%J", "\%N", "\%O",
58 /* LINTED */
59 "\%P", "\%R", "\%T", "\%V",
60 "Ac", "Ao", "Aq", "At",
61 "Bc", "Bf", "Bo", "Bq",
62 "Bsx", "Bx", "Db", "Dc",
63 "Do", "Dq", "Ec", "Ef",
64 "Em", "Eo", "Fx", "Ms",
65 "No", "Ns", "Nx", "Ox",
66 "Pc", "Pf", "Po", "Pq",
67 "Qc", "Ql", "Qo", "Qq",
68 "Re", "Rs", "Sc", "So",
69 "Sq", "Sm", "Sx", "Sy",
70 "Tn", "Ux", "Xc", "Xo",
71 "Fo", "Fc", "Oo", "Oc",
72 "Bk", "Ek", "Bt", "Hf",
73 "Fr", "Ud", "Lb",
74 };
75
76 const char *const __mdoc_argnames[MDOC_ARG_MAX] = {
77 "split", "nosplit", "ragged",
78 "unfilled", "literal", "file",
79 "offset", "bullet", "dash",
80 "hyphen", "item", "enum",
81 "tag", "diag", "hang",
82 "ohang", "inset", "column",
83 "width", "compact", "std",
84 "filled", "words", "emphasis",
85 "symbolic"
86 };
87
88 const char * const *mdoc_macronames = __mdoc_macronames;
89 const char * const *mdoc_argnames = __mdoc_argnames;
90
91
92 const struct mdoc_node *
93 mdoc_node(const struct mdoc *mdoc)
94 {
95
96 return(mdoc->first);
97 }
98
99
100 const struct mdoc_meta *
101 mdoc_meta(const struct mdoc *mdoc)
102 {
103
104 return(&mdoc->meta);
105 }
106
107
108 void
109 mdoc_free(struct mdoc *mdoc)
110 {
111
112 if (mdoc->first)
113 mdoc_node_freelist(mdoc->first);
114 if (mdoc->htab)
115 mdoc_tokhash_free(mdoc->htab);
116 if (mdoc->meta.title)
117 free(mdoc->meta.title);
118 if (mdoc->meta.os)
119 free(mdoc->meta.os);
120 if (mdoc->meta.name)
121 free(mdoc->meta.name);
122 if (mdoc->meta.arch)
123 free(mdoc->meta.arch);
124 if (mdoc->meta.vol)
125 free(mdoc->meta.vol);
126
127 free(mdoc);
128 }
129
130
131 struct mdoc *
132 mdoc_alloc(void *data, const struct mdoc_cb *cb)
133 {
134 struct mdoc *p;
135
136 p = xcalloc(1, sizeof(struct mdoc));
137
138 p->data = data;
139 if (cb)
140 (void)memcpy(&p->cb, cb, sizeof(struct mdoc_cb));
141
142 p->last = xcalloc(1, sizeof(struct mdoc_node));
143 p->last->type = MDOC_ROOT;
144 p->first = p->last;
145
146 p->next = MDOC_NEXT_CHILD;
147 p->htab = mdoc_tokhash_alloc();
148
149 return(p);
150 }
151
152
153 int
154 mdoc_endparse(struct mdoc *mdoc)
155 {
156
157 if (MDOC_HALT & mdoc->flags)
158 return(0);
159 if (NULL == mdoc->first)
160 return(1);
161
162 assert(mdoc->last);
163 if ( ! macro_end(mdoc)) {
164 mdoc->flags |= MDOC_HALT;
165 return(0);
166 }
167 return(1);
168 }
169
170
171 /*
172 * Main parse routine. Parses a single line -- really just hands off to
173 * the macro or text parser.
174 */
175 int
176 mdoc_parseln(struct mdoc *m, int ln, char *buf)
177 {
178
179 /* If in error-mode, then we parse no more. */
180
181 if (MDOC_HALT & m->flags)
182 return(0);
183
184 return('.' == *buf ? parsemacro(m, ln, buf) :
185 parsetext(m, ln, buf));
186 }
187
188
189 void
190 mdoc_vmsg(struct mdoc *mdoc, int ln, int pos, const char *fmt, ...)
191 {
192 char buf[256];
193 va_list ap;
194
195 if (NULL == mdoc->cb.mdoc_msg)
196 return;
197
198 va_start(ap, fmt);
199 (void)vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
200 va_end(ap);
201 (*mdoc->cb.mdoc_msg)(mdoc->data, ln, pos, buf);
202 }
203
204
205 int
206 mdoc_verr(struct mdoc *mdoc, int ln, int pos,
207 const char *fmt, ...)
208 {
209 char buf[256];
210 va_list ap;
211
212 if (NULL == mdoc->cb.mdoc_err)
213 return(0);
214
215 va_start(ap, fmt);
216 (void)vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
217 va_end(ap);
218 return((*mdoc->cb.mdoc_err)(mdoc->data, ln, pos, buf));
219 }
220
221
222 int
223 mdoc_vwarn(struct mdoc *mdoc, int ln, int pos,
224 enum mdoc_warn type, const char *fmt, ...)
225 {
226 char buf[256];
227 va_list ap;
228
229 if (NULL == mdoc->cb.mdoc_warn)
230 return(0);
231
232 va_start(ap, fmt);
233 (void)vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
234 va_end(ap);
235 return((*mdoc->cb.mdoc_warn)(mdoc->data, ln, pos, type, buf));
236 }
237
238
239 int
240 mdoc_macro(struct mdoc *m, int tok,
241 int ln, int pp, int *pos, char *buf)
242 {
243
244 /* FIXME - these should happen during validation. */
245
246 if (MDOC_PROLOGUE & mdoc_macros[tok].flags &&
247 SEC_PROLOGUE != m->lastnamed)
248 return(mdoc_perr(m, ln, pp,
249 "disallowed in document body"));
250
251 if ( ! (MDOC_PROLOGUE & mdoc_macros[tok].flags) &&
252 SEC_PROLOGUE == m->lastnamed)
253 return(mdoc_perr(m, ln, pp,
254 "disallowed in prologue"));
255
256 if (1 != pp && ! (MDOC_CALLABLE & mdoc_macros[tok].flags))
257 return(mdoc_perr(m, ln, pp, "not callable"));
258
259 return((*mdoc_macros[tok].fp)(m, tok, ln, pp, pos, buf));
260 }
261
262
263 static int
264 mdoc_node_append(struct mdoc *mdoc, struct mdoc_node *p)
265 {
266
267 assert(mdoc->last);
268 assert(mdoc->first);
269 assert(MDOC_ROOT != p->type);
270
271 switch (mdoc->next) {
272 case (MDOC_NEXT_SIBLING):
273 mdoc->last->next = p;
274 p->prev = mdoc->last;
275 p->parent = mdoc->last->parent;
276 break;
277 case (MDOC_NEXT_CHILD):
278 mdoc->last->child = p;
279 p->parent = mdoc->last;
280 break;
281 default:
282 abort();
283 /* NOTREACHED */
284 }
285
286 if ( ! mdoc_valid_pre(mdoc, p))
287 return(0);
288
289 switch (p->type) {
290 case (MDOC_HEAD):
291 assert(MDOC_BLOCK == p->parent->type);
292 p->parent->head = p;
293 break;
294 case (MDOC_TAIL):
295 assert(MDOC_BLOCK == p->parent->type);
296 p->parent->tail = p;
297 break;
298 case (MDOC_BODY):
299 assert(MDOC_BLOCK == p->parent->type);
300 p->parent->body = p;
301 break;
302 default:
303 break;
304 }
305
306 mdoc->last = p;
307 return(1);
308 }
309
310
311 static struct mdoc_node *
312 mdoc_node_alloc(const struct mdoc *mdoc)
313 {
314 struct mdoc_node *p;
315
316 p = xcalloc(1, sizeof(struct mdoc_node));
317 p->sec = mdoc->lastsec;
318
319 return(p);
320 }
321
322
323 int
324 mdoc_tail_alloc(struct mdoc *mdoc, int line, int pos, int tok)
325 {
326 struct mdoc_node *p;
327
328 assert(mdoc->first);
329 assert(mdoc->last);
330
331 p = mdoc_node_alloc(mdoc);
332
333 p->line = line;
334 p->pos = pos;
335 p->type = MDOC_TAIL;
336 p->tok = tok;
337
338 return(mdoc_node_append(mdoc, p));
339 }
340
341
342 int
343 mdoc_head_alloc(struct mdoc *mdoc, int line, int pos, int tok)
344 {
345 struct mdoc_node *p;
346
347 assert(mdoc->first);
348 assert(mdoc->last);
349
350 p = mdoc_node_alloc(mdoc);
351
352 p->line = line;
353 p->pos = pos;
354 p->type = MDOC_HEAD;
355 p->tok = tok;
356
357 return(mdoc_node_append(mdoc, p));
358 }
359
360
361 int
362 mdoc_body_alloc(struct mdoc *mdoc, int line, int pos, int tok)
363 {
364 struct mdoc_node *p;
365
366 assert(mdoc->first);
367 assert(mdoc->last);
368
369 p = mdoc_node_alloc(mdoc);
370
371 p->line = line;
372 p->pos = pos;
373 p->type = MDOC_BODY;
374 p->tok = tok;
375
376 return(mdoc_node_append(mdoc, p));
377 }
378
379
380 int
381 mdoc_root_alloc(struct mdoc *mdoc)
382 {
383 struct mdoc_node *p;
384
385 p = mdoc_node_alloc(mdoc);
386
387 p->type = MDOC_ROOT;
388
389 return(mdoc_node_append(mdoc, p));
390 }
391
392
393 int
394 mdoc_block_alloc(struct mdoc *mdoc, int line, int pos,
395 int tok, struct mdoc_arg *args)
396 {
397 struct mdoc_node *p;
398
399 p = mdoc_node_alloc(mdoc);
400
401 p->pos = pos;
402 p->line = line;
403 p->type = MDOC_BLOCK;
404 p->tok = tok;
405 p->args = args;
406
407 if (args)
408 (args->refcnt)++;
409
410 return(mdoc_node_append(mdoc, p));
411 }
412
413
414 int
415 mdoc_elem_alloc(struct mdoc *mdoc, int line, int pos,
416 int tok, struct mdoc_arg *args)
417 {
418 struct mdoc_node *p;
419
420 p = mdoc_node_alloc(mdoc);
421
422 p->line = line;
423 p->pos = pos;
424 p->type = MDOC_ELEM;
425 p->tok = tok;
426 p->args = args;
427
428 if (args)
429 (args->refcnt)++;
430
431 return(mdoc_node_append(mdoc, p));
432 }
433
434
435 int
436 mdoc_word_alloc(struct mdoc *mdoc,
437 int line, int pos, const char *word)
438 {
439 struct mdoc_node *p;
440
441 p = mdoc_node_alloc(mdoc);
442
443 p->line = line;
444 p->pos = pos;
445 p->type = MDOC_TEXT;
446 p->string = xstrdup(word);
447
448 return(mdoc_node_append(mdoc, p));
449 }
450
451
452 void
453 mdoc_node_free(struct mdoc_node *p)
454 {
455
456 if (p->string)
457 free(p->string);
458 if (p->args)
459 mdoc_argv_free(p->args);
460 free(p);
461 }
462
463
464 void
465 mdoc_node_freelist(struct mdoc_node *p)
466 {
467
468 if (p->child)
469 mdoc_node_freelist(p->child);
470 if (p->next)
471 mdoc_node_freelist(p->next);
472
473 mdoc_node_free(p);
474 }
475
476
477 /*
478 * Parse free-form text, that is, a line that does not begin with the
479 * control character.
480 */
481 static int
482 parsetext(struct mdoc *mdoc, int line, char *buf)
483 {
484
485 if (SEC_PROLOGUE == mdoc->lastnamed)
486 return(mdoc_perr(mdoc, line, 0,
487 "text disallowed in prologue"));
488
489 if ( ! mdoc_word_alloc(mdoc, line, 0, buf))
490 return(0);
491
492 mdoc->next = MDOC_NEXT_SIBLING;
493 return(1);
494 }
495
496
497 /*
498 * Parse a macro line, that is, a line beginning with the control
499 * character.
500 */
501 int
502 parsemacro(struct mdoc *m, int ln, char *buf)
503 {
504 int i, c;
505 char mac[5];
506
507 /* Comments are quickly ignored. */
508
509 if (buf[1] && '\\' == buf[1])
510 if (buf[2] && '\"' == buf[2])
511 return(1);
512
513 /* Copy the first word into a nil-terminated buffer. */
514
515 for (i = 1; i < 5; i++) {
516 if (0 == (mac[i - 1] = buf[i]))
517 break;
518 else if (isspace((unsigned char)buf[i]))
519 break;
520 }
521
522 /* FIXME: be able to skip unknown macro lines! */
523
524 mac[i - 1] = 0;
525
526 if (i == 5 || i <= 2) {
527 (void)mdoc_perr(m, ln, 1, "unknown macro: %s%s",
528 mac, i == 5 ? "..." : "");
529 goto err;
530 }
531
532 if (MDOC_MAX == (c = mdoc_tokhash_find(m->htab, mac))) {
533 (void)mdoc_perr(m, ln, 1, "unknown macro: %s", mac);
534 goto err;
535 }
536
537 /* The macro is sane. Jump to the next word. */
538
539 while (buf[i] && isspace((unsigned char)buf[i]))
540 i++;
541
542 /* Begin recursive parse sequence. */
543
544 if ( ! mdoc_macro(m, c, ln, 1, &i, buf))
545 goto err;
546
547 return(1);
548
549 err: /* Error out. */
550
551 m->flags |= MDOC_HALT;
552 return(0);
553 }