Annotation of src/usr.bin/mandoc/man.c, Revision 1.55
1.55 ! schwarze 1: /* $Id: man.c,v 1.54 2011/01/16 02:56:47 schwarze Exp $ */
1.1 kristaps 2: /*
1.52 schwarze 3: * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
1.1 kristaps 4: *
5: * Permission to use, copy, modify, and distribute this software for any
1.2 schwarze 6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
1.1 kristaps 8: *
1.2 schwarze 9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1.1 kristaps 16: */
1.14 schwarze 17: #include <sys/types.h>
18:
1.1 kristaps 19: #include <assert.h>
20: #include <stdarg.h>
21: #include <stdlib.h>
22: #include <stdio.h>
23: #include <string.h>
24:
1.33 schwarze 25: #include "mandoc.h"
1.1 kristaps 26: #include "libman.h"
1.16 schwarze 27: #include "libmandoc.h"
1.1 kristaps 28:
29: const char *const __man_macronames[MAN_MAX] = {
1.3 schwarze 30: "br", "TH", "SH", "SS",
1.1 kristaps 31: "TP", "LP", "PP", "P",
32: "IP", "HP", "SM", "SB",
33: "BI", "IB", "BR", "RB",
34: "R", "B", "I", "IR",
1.50 schwarze 35: "RI", "na", "sp", "nf",
36: "fi", "RE", "RS", "DT",
37: "UC", "PD", "AT", "in",
1.52 schwarze 38: "ft"
1.1 kristaps 39: };
40:
41: const char * const *man_macronames = __man_macronames;
42:
1.54 schwarze 43: static struct man_node *man_node_alloc(struct man *, int, int,
1.22 schwarze 44: enum man_type, enum mant);
1.1 kristaps 45: static int man_node_append(struct man *,
46: struct man_node *);
1.22 schwarze 47: static void man_node_free(struct man_node *);
48: static void man_node_unlink(struct man *,
49: struct man_node *);
1.31 schwarze 50: static int man_ptext(struct man *, int, char *, int);
51: static int man_pmacro(struct man *, int, char *, int);
1.1 kristaps 52: static void man_free1(struct man *);
1.16 schwarze 53: static void man_alloc1(struct man *);
1.52 schwarze 54: static int man_descope(struct man *, int, int);
1.1 kristaps 55:
56:
57: const struct man_node *
58: man_node(const struct man *m)
59: {
60:
1.53 schwarze 61: assert( ! (MAN_HALT & m->flags));
62: return(m->first);
1.1 kristaps 63: }
64:
65:
66: const struct man_meta *
67: man_meta(const struct man *m)
68: {
69:
1.53 schwarze 70: assert( ! (MAN_HALT & m->flags));
71: return(&m->meta);
1.1 kristaps 72: }
73:
74:
1.16 schwarze 75: void
1.1 kristaps 76: man_reset(struct man *man)
77: {
78:
79: man_free1(man);
1.16 schwarze 80: man_alloc1(man);
1.1 kristaps 81: }
82:
83:
84: void
85: man_free(struct man *man)
86: {
87:
88: man_free1(man);
89: free(man);
90: }
91:
92:
93: struct man *
1.40 schwarze 94: man_alloc(struct regset *regs, void *data, mandocmsg msg)
1.1 kristaps 95: {
96: struct man *p;
97:
1.16 schwarze 98: p = mandoc_calloc(1, sizeof(struct man));
1.1 kristaps 99:
1.13 schwarze 100: man_hash_init();
1.1 kristaps 101: p->data = data;
1.33 schwarze 102: p->msg = msg;
1.35 schwarze 103: p->regs = regs;
1.16 schwarze 104:
105: man_alloc1(p);
1.1 kristaps 106: return(p);
107: }
108:
109:
110: int
111: man_endparse(struct man *m)
112: {
113:
1.53 schwarze 114: assert( ! (MAN_HALT & m->flags));
115: if (man_macroend(m))
1.1 kristaps 116: return(1);
117: m->flags |= MAN_HALT;
118: return(0);
119: }
120:
121:
122: int
1.31 schwarze 123: man_parseln(struct man *m, int ln, char *buf, int offs)
1.1 kristaps 124: {
1.25 schwarze 125:
1.54 schwarze 126: m->flags |= MAN_NEWLINE;
127:
1.53 schwarze 128: assert( ! (MAN_HALT & m->flags));
1.31 schwarze 129: return(('.' == buf[offs] || '\'' == buf[offs]) ?
130: man_pmacro(m, ln, buf, offs) :
131: man_ptext(m, ln, buf, offs));
1.1 kristaps 132: }
133:
134:
135: static void
136: man_free1(struct man *man)
137: {
138:
139: if (man->first)
1.22 schwarze 140: man_node_delete(man, man->first);
1.1 kristaps 141: if (man->meta.title)
142: free(man->meta.title);
143: if (man->meta.source)
144: free(man->meta.source);
1.34 schwarze 145: if (man->meta.rawdate)
146: free(man->meta.rawdate);
1.1 kristaps 147: if (man->meta.vol)
148: free(man->meta.vol);
1.29 schwarze 149: if (man->meta.msec)
150: free(man->meta.msec);
1.1 kristaps 151: }
152:
153:
1.16 schwarze 154: static void
1.1 kristaps 155: man_alloc1(struct man *m)
156: {
157:
1.16 schwarze 158: memset(&m->meta, 0, sizeof(struct man_meta));
1.1 kristaps 159: m->flags = 0;
1.16 schwarze 160: m->last = mandoc_calloc(1, sizeof(struct man_node));
1.1 kristaps 161: m->first = m->last;
162: m->last->type = MAN_ROOT;
1.22 schwarze 163: m->last->tok = MAN_MAX;
1.1 kristaps 164: m->next = MAN_NEXT_CHILD;
165: }
166:
167:
168: static int
169: man_node_append(struct man *man, struct man_node *p)
170: {
171:
172: assert(man->last);
173: assert(man->first);
174: assert(MAN_ROOT != p->type);
175:
176: switch (man->next) {
177: case (MAN_NEXT_SIBLING):
178: man->last->next = p;
179: p->prev = man->last;
180: p->parent = man->last->parent;
181: break;
182: case (MAN_NEXT_CHILD):
183: man->last->child = p;
184: p->parent = man->last;
185: break;
186: default:
187: abort();
188: /* NOTREACHED */
189: }
1.5 schwarze 190:
1.22 schwarze 191: assert(p->parent);
1.5 schwarze 192: p->parent->nchild++;
1.1 kristaps 193:
1.9 schwarze 194: if ( ! man_valid_pre(man, p))
195: return(0);
196:
197: switch (p->type) {
198: case (MAN_HEAD):
199: assert(MAN_BLOCK == p->parent->type);
200: p->parent->head = p;
201: break;
202: case (MAN_BODY):
203: assert(MAN_BLOCK == p->parent->type);
204: p->parent->body = p;
205: break;
206: default:
207: break;
208: }
209:
1.1 kristaps 210: man->last = p;
211:
212: switch (p->type) {
1.52 schwarze 213: case (MAN_TBL):
214: /* FALLTHROUGH */
1.1 kristaps 215: case (MAN_TEXT):
216: if ( ! man_valid_post(man))
217: return(0);
218: break;
219: default:
220: break;
221: }
222:
223: return(1);
224: }
225:
226:
227: static struct man_node *
1.54 schwarze 228: man_node_alloc(struct man *m, int line, int pos,
229: enum man_type type, enum mant tok)
1.1 kristaps 230: {
231: struct man_node *p;
232:
1.16 schwarze 233: p = mandoc_calloc(1, sizeof(struct man_node));
1.1 kristaps 234: p->line = line;
235: p->pos = pos;
236: p->type = type;
237: p->tok = tok;
1.54 schwarze 238:
239: if (MAN_NEWLINE & m->flags)
240: p->flags |= MAN_LINE;
241: m->flags &= ~MAN_NEWLINE;
1.1 kristaps 242: return(p);
243: }
244:
245:
246: int
1.22 schwarze 247: man_elem_alloc(struct man *m, int line, int pos, enum mant tok)
1.1 kristaps 248: {
249: struct man_node *p;
250:
1.54 schwarze 251: p = man_node_alloc(m, line, pos, MAN_ELEM, tok);
1.10 schwarze 252: if ( ! man_node_append(m, p))
253: return(0);
254: m->next = MAN_NEXT_CHILD;
255: return(1);
1.1 kristaps 256: }
257:
258:
259: int
1.22 schwarze 260: man_head_alloc(struct man *m, int line, int pos, enum mant tok)
1.9 schwarze 261: {
262: struct man_node *p;
263:
1.54 schwarze 264: p = man_node_alloc(m, line, pos, MAN_HEAD, tok);
1.9 schwarze 265: if ( ! man_node_append(m, p))
266: return(0);
267: m->next = MAN_NEXT_CHILD;
268: return(1);
269: }
270:
271:
272: int
1.22 schwarze 273: man_body_alloc(struct man *m, int line, int pos, enum mant tok)
1.9 schwarze 274: {
275: struct man_node *p;
276:
1.54 schwarze 277: p = man_node_alloc(m, line, pos, MAN_BODY, tok);
1.9 schwarze 278: if ( ! man_node_append(m, p))
279: return(0);
280: m->next = MAN_NEXT_CHILD;
281: return(1);
282: }
283:
284:
285: int
1.22 schwarze 286: man_block_alloc(struct man *m, int line, int pos, enum mant tok)
1.9 schwarze 287: {
288: struct man_node *p;
289:
1.54 schwarze 290: p = man_node_alloc(m, line, pos, MAN_BLOCK, tok);
1.9 schwarze 291: if ( ! man_node_append(m, p))
292: return(0);
293: m->next = MAN_NEXT_CHILD;
294: return(1);
295: }
296:
1.27 schwarze 297: int
298: man_word_alloc(struct man *m, int line, int pos, const char *word)
1.1 kristaps 299: {
1.10 schwarze 300: struct man_node *n;
1.27 schwarze 301: size_t sv, len;
302:
303: len = strlen(word);
1.1 kristaps 304:
1.54 schwarze 305: n = man_node_alloc(m, line, pos, MAN_TEXT, MAN_MAX);
1.16 schwarze 306: n->string = mandoc_malloc(len + 1);
1.27 schwarze 307: sv = strlcpy(n->string, word, len + 1);
1.10 schwarze 308:
309: /* Prohibit truncation. */
310: assert(sv < len + 1);
311:
312: if ( ! man_node_append(m, n))
1.1 kristaps 313: return(0);
1.27 schwarze 314:
1.10 schwarze 315: m->next = MAN_NEXT_SIBLING;
316: return(1);
317: }
318:
319:
1.22 schwarze 320: /*
321: * Free all of the resources held by a node. This does NOT unlink a
322: * node from its context; for that, see man_node_unlink().
323: */
324: static void
1.1 kristaps 325: man_node_free(struct man_node *p)
326: {
327:
328: if (p->string)
329: free(p->string);
330: free(p);
331: }
332:
333:
334: void
1.22 schwarze 335: man_node_delete(struct man *m, struct man_node *p)
1.1 kristaps 336: {
337:
1.22 schwarze 338: while (p->child)
339: man_node_delete(m, p->child);
340:
341: man_node_unlink(m, p);
1.1 kristaps 342: man_node_free(p);
343: }
344:
345:
1.52 schwarze 346: int
347: man_addspan(struct man *m, const struct tbl_span *sp)
348: {
1.55 ! schwarze 349: struct man_node *n;
1.52 schwarze 350:
1.53 schwarze 351: assert( ! (MAN_HALT & m->flags));
1.55 ! schwarze 352:
! 353: n = man_node_alloc(m, sp->line, 0, MAN_TBL, MAN_MAX);
! 354: n->span = sp;
! 355:
! 356: if ( ! man_node_append(m, n))
1.52 schwarze 357: return(0);
1.55 ! schwarze 358:
! 359: m->next = MAN_NEXT_SIBLING;
! 360: return(man_descope(m, sp->line, 0));
1.52 schwarze 361: }
362:
363: static int
364: man_descope(struct man *m, int line, int offs)
365: {
366: /*
367: * Co-ordinate what happens with having a next-line scope open:
368: * first close out the element scope (if applicable), then close
369: * out the block scope (also if applicable).
370: */
371:
372: if (MAN_ELINE & m->flags) {
373: m->flags &= ~MAN_ELINE;
374: if ( ! man_unscope(m, m->last->parent, MANDOCERR_MAX))
375: return(0);
376: }
377:
378: if ( ! (MAN_BLINE & m->flags))
379: return(1);
380: m->flags &= ~MAN_BLINE;
381:
382: if ( ! man_unscope(m, m->last->parent, MANDOCERR_MAX))
383: return(0);
384: return(man_body_alloc(m, line, offs, m->last->tok));
385: }
386:
387:
1.1 kristaps 388: static int
1.31 schwarze 389: man_ptext(struct man *m, int line, char *buf, int offs)
1.1 kristaps 390: {
1.27 schwarze 391: int i;
1.26 schwarze 392:
393: /* Ignore bogus comments. */
394:
1.31 schwarze 395: if ('\\' == buf[offs] &&
396: '.' == buf[offs + 1] &&
1.51 schwarze 397: '"' == buf[offs + 2]) {
398: man_pmsg(m, line, offs, MANDOCERR_BADCOMMENT);
399: return(1);
400: }
1.10 schwarze 401:
402: /* Literal free-form text whitespace is preserved. */
403:
404: if (MAN_LITERAL & m->flags) {
1.31 schwarze 405: if ( ! man_word_alloc(m, line, offs, buf + offs))
1.10 schwarze 406: return(0);
1.52 schwarze 407: return(man_descope(m, line, offs));
1.10 schwarze 408: }
409:
1.27 schwarze 410: /* Pump blank lines directly into the backend. */
1.10 schwarze 411:
1.31 schwarze 412: for (i = offs; ' ' == buf[i]; i++)
1.10 schwarze 413: /* Skip leading whitespace. */ ;
1.18 schwarze 414:
415: if ('\0' == buf[i]) {
1.27 schwarze 416: /* Allocate a blank entry. */
1.31 schwarze 417: if ( ! man_word_alloc(m, line, offs, ""))
1.10 schwarze 418: return(0);
1.52 schwarze 419: return(man_descope(m, line, offs));
1.10 schwarze 420: }
1.1 kristaps 421:
1.27 schwarze 422: /*
423: * Warn if the last un-escaped character is whitespace. Then
424: * strip away the remaining spaces (tabs stay!).
425: */
1.18 schwarze 426:
1.27 schwarze 427: i = (int)strlen(buf);
428: assert(i);
1.18 schwarze 429:
1.27 schwarze 430: if (' ' == buf[i - 1] || '\t' == buf[i - 1]) {
431: if (i > 1 && '\\' != buf[i - 2])
1.51 schwarze 432: man_pmsg(m, line, i - 1, MANDOCERR_EOLNSPACE);
1.18 schwarze 433:
1.27 schwarze 434: for (--i; i && ' ' == buf[i]; i--)
435: /* Spin back to non-space. */ ;
1.10 schwarze 436:
1.27 schwarze 437: /* Jump ahead of escaped whitespace. */
438: i += '\\' == buf[i] ? 2 : 1;
1.18 schwarze 439:
1.27 schwarze 440: buf[i] = '\0';
1.10 schwarze 441: }
1.9 schwarze 442:
1.31 schwarze 443: if ( ! man_word_alloc(m, line, offs, buf + offs))
1.1 kristaps 444: return(0);
1.28 schwarze 445:
446: /*
447: * End-of-sentence check. If the last character is an unescaped
448: * EOS character, then flag the node as being the end of a
449: * sentence. The front-end will know how to interpret this.
450: */
451:
452: assert(i);
1.37 schwarze 453: if (mandoc_eos(buf, (size_t)i, 0))
1.28 schwarze 454: m->last->flags |= MAN_EOS;
1.10 schwarze 455:
1.52 schwarze 456: return(man_descope(m, line, offs));
1.1 kristaps 457: }
458:
459:
1.53 schwarze 460: static int
1.31 schwarze 461: man_pmacro(struct man *m, int ln, char *buf, int offs)
1.1 kristaps 462: {
1.24 schwarze 463: int i, j, ppos;
1.22 schwarze 464: enum mant tok;
1.10 schwarze 465: char mac[5];
466: struct man_node *n;
1.1 kristaps 467:
468: /* Comments and empties are quickly ignored. */
469:
1.31 schwarze 470: offs++;
471:
472: if ('\0' == buf[offs])
1.17 schwarze 473: return(1);
1.1 kristaps 474:
1.31 schwarze 475: i = offs;
1.1 kristaps 476:
1.23 schwarze 477: /*
478: * Skip whitespace between the control character and initial
479: * text. "Whitespace" is both spaces and tabs.
480: */
1.27 schwarze 481:
1.23 schwarze 482: if (' ' == buf[i] || '\t' == buf[i]) {
1.1 kristaps 483: i++;
1.23 schwarze 484: while (buf[i] && (' ' == buf[i] || '\t' == buf[i]))
1.1 kristaps 485: i++;
1.18 schwarze 486: if ('\0' == buf[i])
1.1 kristaps 487: goto out;
488: }
489:
490: ppos = i;
491:
1.39 schwarze 492: /*
493: * Copy the first word into a nil-terminated buffer.
494: * Stop copying when a tab, space, or eoln is encountered.
495: */
1.1 kristaps 496:
1.39 schwarze 497: j = 0;
498: while (j < 4 && '\0' != buf[i] && ' ' != buf[i] && '\t' != buf[i])
499: mac[j++] = buf[i++];
1.17 schwarze 500: mac[j] = '\0';
1.1 kristaps 501:
1.40 schwarze 502: tok = (j > 0 && j < 4) ? man_hash_find(mac) : MAN_MAX;
503: if (MAN_MAX == tok) {
1.44 schwarze 504: man_vmsg(m, MANDOCERR_MACRO, ln, ppos, "%s", buf + ppos - 1);
1.1 kristaps 505: return(1);
506: }
507:
508: /* The macro is sane. Jump to the next word. */
509:
510: while (buf[i] && ' ' == buf[i])
511: i++;
1.18 schwarze 512:
1.27 schwarze 513: /*
514: * Trailing whitespace. Note that tabs are allowed to be passed
515: * into the parser as "text", so we only warn about spaces here.
516: */
1.18 schwarze 517:
518: if ('\0' == buf[i] && ' ' == buf[i - 1])
1.51 schwarze 519: man_pmsg(m, ln, i - 1, MANDOCERR_EOLNSPACE);
1.1 kristaps 520:
1.21 schwarze 521: /*
1.46 schwarze 522: * Remove prior ELINE macro, as it's being clobbered by a new
1.21 schwarze 523: * macro. Note that NSCOPED macros do not close out ELINE
524: * macros---they don't print text---so we let those slip by.
525: */
526:
1.22 schwarze 527: if ( ! (MAN_NSCOPED & man_macros[tok].flags) &&
1.21 schwarze 528: m->flags & MAN_ELINE) {
1.10 schwarze 529: n = m->last;
1.46 schwarze 530: assert(MAN_TEXT != n->type);
1.21 schwarze 531:
1.49 schwarze 532: /* Remove repeated NSCOPED macros causing ELINE. */
533:
1.46 schwarze 534: if (MAN_NSCOPED & man_macros[n->tok].flags)
535: n = n->parent;
1.21 schwarze 536:
1.46 schwarze 537: man_vmsg(m, MANDOCERR_LINESCOPE, n->line, n->pos,
1.49 schwarze 538: "%s", man_macronames[n->tok]);
1.10 schwarze 539:
1.22 schwarze 540: man_node_delete(m, n);
1.10 schwarze 541: m->flags &= ~MAN_ELINE;
542: }
543:
1.24 schwarze 544: /*
545: * Save the fact that we're in the next-line for a block. In
546: * this way, embedded roff instructions can "remember" state
547: * when they exit.
548: */
549:
550: if (MAN_BLINE & m->flags)
551: m->flags |= MAN_BPLINE;
552:
553: /* Call to handler... */
1.1 kristaps 554:
1.22 schwarze 555: assert(man_macros[tok].fp);
556: if ( ! (*man_macros[tok].fp)(m, tok, ln, ppos, &i, buf))
1.1 kristaps 557: goto err;
558:
559: out:
1.21 schwarze 560: /*
561: * We weren't in a block-line scope when entering the
562: * above-parsed macro, so return.
563: */
564:
1.24 schwarze 565: if ( ! (MAN_BPLINE & m->flags)) {
1.21 schwarze 566: m->flags &= ~MAN_ILINE;
1.9 schwarze 567: return(1);
1.21 schwarze 568: }
1.24 schwarze 569: m->flags &= ~MAN_BPLINE;
1.21 schwarze 570:
571: /*
572: * If we're in a block scope, then allow this macro to slip by
573: * without closing scope around it.
574: */
575:
576: if (MAN_ILINE & m->flags) {
577: m->flags &= ~MAN_ILINE;
578: return(1);
579: }
1.9 schwarze 580:
581: /*
582: * If we've opened a new next-line element scope, then return
583: * now, as the next line will close out the block scope.
584: */
585:
586: if (MAN_ELINE & m->flags)
587: return(1);
588:
589: /* Close out the block scope opened in the prior line. */
1.1 kristaps 590:
1.9 schwarze 591: assert(MAN_BLINE & m->flags);
592: m->flags &= ~MAN_BLINE;
1.1 kristaps 593:
1.33 schwarze 594: if ( ! man_unscope(m, m->last->parent, MANDOCERR_MAX))
1.9 schwarze 595: return(0);
1.31 schwarze 596: return(man_body_alloc(m, ln, offs, m->last->tok));
1.1 kristaps 597:
598: err: /* Error out. */
599:
600: m->flags |= MAN_HALT;
601: return(0);
602: }
603:
604:
605: int
1.33 schwarze 606: man_vmsg(struct man *man, enum mandocerr t,
607: int ln, int pos, const char *fmt, ...)
1.1 kristaps 608: {
609: char buf[256];
610: va_list ap;
611:
612: va_start(ap, fmt);
1.33 schwarze 613: vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
1.1 kristaps 614: va_end(ap);
1.33 schwarze 615: return((*man->msg)(t, man->data, ln, pos, buf));
1.21 schwarze 616: }
617:
618:
1.22 schwarze 619: /*
620: * Unlink a node from its context. If "m" is provided, the last parse
621: * point will also be adjusted accordingly.
622: */
623: static void
1.21 schwarze 624: man_node_unlink(struct man *m, struct man_node *n)
625: {
626:
1.22 schwarze 627: /* Adjust siblings. */
628:
629: if (n->prev)
1.21 schwarze 630: n->prev->next = n->next;
1.22 schwarze 631: if (n->next)
632: n->next->prev = n->prev;
633:
634: /* Adjust parent. */
635:
636: if (n->parent) {
637: n->parent->nchild--;
638: if (n->parent->child == n)
639: n->parent->child = n->prev ? n->prev : n->next;
640: }
641:
642: /* Adjust parse point, if applicable. */
643:
644: if (m && m->last == n) {
645: /*XXX: this can occur when bailing from validation. */
646: /*assert(NULL == n->next);*/
647: if (n->prev) {
1.21 schwarze 648: m->last = n->prev;
649: m->next = MAN_NEXT_SIBLING;
1.22 schwarze 650: } else {
1.21 schwarze 651: m->last = n->parent;
652: m->next = MAN_NEXT_CHILD;
653: }
654: }
655:
1.22 schwarze 656: if (m && m->first == n)
657: m->first = NULL;
1.4 schwarze 658: }