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