Annotation of src/usr.bin/mandoc/mdoc.c, Revision 1.77
1.77 ! schwarze 1: /* $Id: mdoc.c,v 1.76 2011/01/01 17:38:11 schwarze Exp $ */
1.1 kristaps 2: /*
1.77 ! schwarze 3: * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
1.61 schwarze 4: * Copyright (c) 2010 Ingo Schwarze <schwarze@openbsd.org>
1.1 kristaps 5: *
6: * Permission to use, copy, modify, and distribute this software for any
1.3 schwarze 7: * purpose with or without fee is hereby granted, provided that the above
8: * copyright notice and this permission notice appear in all copies.
1.1 kristaps 9: *
1.3 schwarze 10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1.1 kristaps 17: */
1.28 schwarze 18: #include <sys/types.h>
19:
1.1 kristaps 20: #include <assert.h>
21: #include <stdarg.h>
22: #include <stdio.h>
23: #include <stdlib.h>
24: #include <string.h>
1.39 schwarze 25: #include <time.h>
1.1 kristaps 26:
1.54 schwarze 27: #include "mandoc.h"
1.1 kristaps 28: #include "libmdoc.h"
1.32 schwarze 29: #include "libmandoc.h"
1.1 kristaps 30:
31: const char *const __mdoc_macronames[MDOC_MAX] = {
1.7 schwarze 32: "Ap", "Dd", "Dt", "Os",
1.1 kristaps 33: "Sh", "Ss", "Pp", "D1",
34: "Dl", "Bd", "Ed", "Bl",
35: "El", "It", "Ad", "An",
36: "Ar", "Cd", "Cm", "Dv",
37: "Er", "Ev", "Ex", "Fa",
38: "Fd", "Fl", "Fn", "Ft",
39: "Ic", "In", "Li", "Nd",
40: "Nm", "Op", "Ot", "Pa",
41: "Rv", "St", "Va", "Vt",
42: /* LINTED */
1.33 schwarze 43: "Xr", "%A", "%B", "%D",
1.1 kristaps 44: /* LINTED */
1.33 schwarze 45: "%I", "%J", "%N", "%O",
1.1 kristaps 46: /* LINTED */
1.33 schwarze 47: "%P", "%R", "%T", "%V",
1.1 kristaps 48: "Ac", "Ao", "Aq", "At",
49: "Bc", "Bf", "Bo", "Bq",
50: "Bsx", "Bx", "Db", "Dc",
51: "Do", "Dq", "Ec", "Ef",
52: "Em", "Eo", "Fx", "Ms",
53: "No", "Ns", "Nx", "Ox",
54: "Pc", "Pf", "Po", "Pq",
55: "Qc", "Ql", "Qo", "Qq",
56: "Re", "Rs", "Sc", "So",
57: "Sq", "Sm", "Sx", "Sy",
58: "Tn", "Ux", "Xc", "Xo",
59: "Fo", "Fc", "Oo", "Oc",
60: "Bk", "Ek", "Bt", "Hf",
1.7 schwarze 61: "Fr", "Ud", "Lb", "Lp",
62: "Lk", "Mt", "Brq", "Bro",
1.1 kristaps 63: /* LINTED */
1.33 schwarze 64: "Brc", "%C", "Es", "En",
1.1 kristaps 65: /* LINTED */
1.33 schwarze 66: "Dx", "%Q", "br", "sp",
1.31 schwarze 67: /* LINTED */
1.77 ! schwarze 68: "%U", "Ta"
1.1 kristaps 69: };
70:
71: const char *const __mdoc_argnames[MDOC_ARG_MAX] = {
72: "split", "nosplit", "ragged",
73: "unfilled", "literal", "file",
74: "offset", "bullet", "dash",
75: "hyphen", "item", "enum",
76: "tag", "diag", "hang",
77: "ohang", "inset", "column",
78: "width", "compact", "std",
79: "filled", "words", "emphasis",
1.30 schwarze 80: "symbolic", "nested", "centered"
1.1 kristaps 81: };
82:
83: const char * const *mdoc_macronames = __mdoc_macronames;
84: const char * const *mdoc_argnames = __mdoc_argnames;
85:
1.40 schwarze 86: static void mdoc_node_free(struct mdoc_node *);
87: static void mdoc_node_unlink(struct mdoc *,
88: struct mdoc_node *);
1.1 kristaps 89: static void mdoc_free1(struct mdoc *);
1.32 schwarze 90: static void mdoc_alloc1(struct mdoc *);
1.1 kristaps 91: static struct mdoc_node *node_alloc(struct mdoc *, int, int,
1.37 schwarze 92: enum mdoct, enum mdoc_type);
1.1 kristaps 93: static int node_append(struct mdoc *,
94: struct mdoc_node *);
1.53 schwarze 95: static int mdoc_ptext(struct mdoc *, int, char *, int);
96: static int mdoc_pmacro(struct mdoc *, int, char *, int);
1.77 ! schwarze 97: static int mdoc_span_alloc(struct mdoc *,
! 98: const struct tbl_span *);
1.46 schwarze 99:
1.1 kristaps 100:
101: const struct mdoc_node *
102: mdoc_node(const struct mdoc *m)
103: {
104:
105: return(MDOC_HALT & m->flags ? NULL : m->first);
106: }
107:
108:
109: const struct mdoc_meta *
110: mdoc_meta(const struct mdoc *m)
111: {
112:
113: return(MDOC_HALT & m->flags ? NULL : &m->meta);
114: }
115:
116:
1.8 schwarze 117: /*
118: * Frees volatile resources (parse tree, meta-data, fields).
119: */
1.1 kristaps 120: static void
121: mdoc_free1(struct mdoc *mdoc)
122: {
123:
124: if (mdoc->first)
1.40 schwarze 125: mdoc_node_delete(mdoc, mdoc->first);
1.1 kristaps 126: if (mdoc->meta.title)
127: free(mdoc->meta.title);
128: if (mdoc->meta.os)
129: free(mdoc->meta.os);
130: if (mdoc->meta.name)
131: free(mdoc->meta.name);
132: if (mdoc->meta.arch)
133: free(mdoc->meta.arch);
134: if (mdoc->meta.vol)
135: free(mdoc->meta.vol);
1.50 schwarze 136: if (mdoc->meta.msec)
137: free(mdoc->meta.msec);
1.1 kristaps 138: }
139:
140:
1.8 schwarze 141: /*
142: * Allocate all volatile resources (parse tree, meta-data, fields).
143: */
1.32 schwarze 144: static void
1.1 kristaps 145: mdoc_alloc1(struct mdoc *mdoc)
146: {
147:
1.32 schwarze 148: memset(&mdoc->meta, 0, sizeof(struct mdoc_meta));
1.1 kristaps 149: mdoc->flags = 0;
1.9 schwarze 150: mdoc->lastnamed = mdoc->lastsec = SEC_NONE;
1.32 schwarze 151: mdoc->last = mandoc_calloc(1, sizeof(struct mdoc_node));
1.1 kristaps 152: mdoc->first = mdoc->last;
153: mdoc->last->type = MDOC_ROOT;
154: mdoc->next = MDOC_NEXT_CHILD;
155: }
156:
157:
158: /*
1.8 schwarze 159: * Free up volatile resources (see mdoc_free1()) then re-initialises the
160: * data with mdoc_alloc1(). After invocation, parse data has been reset
161: * and the parser is ready for re-invocation on a new tree; however,
162: * cross-parse non-volatile data is kept intact.
1.1 kristaps 163: */
1.32 schwarze 164: void
1.1 kristaps 165: mdoc_reset(struct mdoc *mdoc)
166: {
167:
168: mdoc_free1(mdoc);
1.32 schwarze 169: mdoc_alloc1(mdoc);
1.1 kristaps 170: }
171:
172:
173: /*
1.8 schwarze 174: * Completely free up all volatile and non-volatile parse resources.
175: * After invocation, the pointer is no longer usable.
1.1 kristaps 176: */
177: void
178: mdoc_free(struct mdoc *mdoc)
179: {
180:
181: mdoc_free1(mdoc);
182: free(mdoc);
183: }
184:
185:
1.8 schwarze 186: /*
187: * Allocate volatile and non-volatile parse resources.
188: */
1.1 kristaps 189: struct mdoc *
1.65 schwarze 190: mdoc_alloc(struct regset *regs, void *data, mandocmsg msg)
1.1 kristaps 191: {
192: struct mdoc *p;
193:
1.32 schwarze 194: p = mandoc_calloc(1, sizeof(struct mdoc));
195:
1.54 schwarze 196: p->msg = msg;
1.1 kristaps 197: p->data = data;
1.58 schwarze 198: p->regs = regs;
1.1 kristaps 199:
1.32 schwarze 200: mdoc_hash_init();
201: mdoc_alloc1(p);
202: return(p);
1.1 kristaps 203: }
204:
205:
206: /*
207: * Climb back up the parse tree, validating open scopes. Mostly calls
1.8 schwarze 208: * through to macro_end() in macro.c.
1.1 kristaps 209: */
210: int
211: mdoc_endparse(struct mdoc *m)
212: {
213:
214: if (MDOC_HALT & m->flags)
215: return(0);
216: else if (mdoc_macroend(m))
217: return(1);
218: m->flags |= MDOC_HALT;
219: return(0);
220: }
221:
1.77 ! schwarze 222: int
! 223: mdoc_addspan(struct mdoc *m, const struct tbl_span *sp)
! 224: {
! 225:
! 226: if (MDOC_HALT & m->flags)
! 227: return(0);
! 228:
! 229: /* No text before an initial macro. */
! 230:
! 231: if (SEC_NONE == m->lastnamed) {
! 232: /* FIXME: grab from span. */
! 233: mdoc_pmsg(m, 0, 0, MANDOCERR_NOTEXT);
! 234: return(1);
! 235: }
! 236:
! 237: return(mdoc_span_alloc(m, sp));
! 238: }
! 239:
1.1 kristaps 240:
241: /*
242: * Main parse routine. Parses a single line -- really just hands off to
1.45 schwarze 243: * the macro (mdoc_pmacro()) or text parser (mdoc_ptext()).
1.1 kristaps 244: */
245: int
1.53 schwarze 246: mdoc_parseln(struct mdoc *m, int ln, char *buf, int offs)
1.1 kristaps 247: {
248:
249: if (MDOC_HALT & m->flags)
250: return(0);
251:
1.47 schwarze 252: m->flags |= MDOC_NEWLINE;
1.60 schwarze 253:
254: /*
255: * Let the roff nS register switch SYNOPSIS mode early,
256: * such that the parser knows at all times
257: * whether this mode is on or off.
258: * Note that this mode is also switched by the Sh macro.
259: */
260: if (m->regs->regs[(int)REG_nS].set) {
261: if (m->regs->regs[(int)REG_nS].v.u)
262: m->flags |= MDOC_SYNOPSIS;
263: else
264: m->flags &= ~MDOC_SYNOPSIS;
265: }
266:
1.53 schwarze 267: return(('.' == buf[offs] || '\'' == buf[offs]) ?
268: mdoc_pmacro(m, ln, buf, offs) :
269: mdoc_ptext(m, ln, buf, offs));
1.1 kristaps 270: }
271:
272:
273: int
1.54 schwarze 274: mdoc_vmsg(struct mdoc *mdoc, enum mandocerr t,
275: int ln, int pos, const char *fmt, ...)
1.1 kristaps 276: {
277: char buf[256];
278: va_list ap;
279:
280: va_start(ap, fmt);
1.54 schwarze 281: vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
1.1 kristaps 282: va_end(ap);
1.17 schwarze 283:
1.54 schwarze 284: return((*mdoc->msg)(t, mdoc->data, ln, pos, buf));
1.5 schwarze 285: }
286:
287:
288: int
1.58 schwarze 289: mdoc_macro(MACRO_PROT_ARGS)
1.1 kristaps 290: {
1.46 schwarze 291: assert(tok < MDOC_MAX);
292:
293: /* If we're in the body, deny prologue calls. */
1.37 schwarze 294:
1.1 kristaps 295: if (MDOC_PROLOGUE & mdoc_macros[tok].flags &&
1.76 schwarze 296: MDOC_PBODY & m->flags) {
297: mdoc_pmsg(m, line, ppos, MANDOCERR_BADBODY);
298: return(1);
299: }
1.46 schwarze 300:
301: /* If we're in the prologue, deny "body" macros. */
302:
1.1 kristaps 303: if ( ! (MDOC_PROLOGUE & mdoc_macros[tok].flags) &&
1.39 schwarze 304: ! (MDOC_PBODY & m->flags)) {
1.76 schwarze 305: mdoc_pmsg(m, line, ppos, MANDOCERR_BADPROLOG);
1.71 schwarze 306: if (NULL == m->meta.msec)
307: m->meta.msec = mandoc_strdup("1");
1.39 schwarze 308: if (NULL == m->meta.title)
1.56 schwarze 309: m->meta.title = mandoc_strdup("UNKNOWN");
1.39 schwarze 310: if (NULL == m->meta.vol)
1.56 schwarze 311: m->meta.vol = mandoc_strdup("LOCAL");
1.39 schwarze 312: if (NULL == m->meta.os)
1.56 schwarze 313: m->meta.os = mandoc_strdup("LOCAL");
1.39 schwarze 314: if (0 == m->meta.date)
315: m->meta.date = time(NULL);
316: m->flags |= MDOC_PBODY;
317: }
1.1 kristaps 318:
1.58 schwarze 319: return((*mdoc_macros[tok].fp)(m, tok, line, ppos, pos, buf));
1.1 kristaps 320: }
321:
322:
323: static int
324: node_append(struct mdoc *mdoc, struct mdoc_node *p)
325: {
326:
327: assert(mdoc->last);
328: assert(mdoc->first);
329: assert(MDOC_ROOT != p->type);
330:
331: switch (mdoc->next) {
332: case (MDOC_NEXT_SIBLING):
333: mdoc->last->next = p;
334: p->prev = mdoc->last;
335: p->parent = mdoc->last->parent;
336: break;
337: case (MDOC_NEXT_CHILD):
338: mdoc->last->child = p;
339: p->parent = mdoc->last;
340: break;
341: default:
342: abort();
343: /* NOTREACHED */
344: }
345:
1.10 schwarze 346: p->parent->nchild++;
347:
1.75 schwarze 348: /*
349: * Copy over the normalised-data pointer of our parent. Not
350: * everybody has one, but copying a null pointer is fine.
351: */
352:
353: switch (p->type) {
354: case (MDOC_BODY):
355: /* FALLTHROUGH */
356: case (MDOC_TAIL):
357: /* FALLTHROUGH */
358: case (MDOC_HEAD):
359: p->norm = p->parent->norm;
360: break;
361: default:
362: break;
363: }
364:
1.1 kristaps 365: if ( ! mdoc_valid_pre(mdoc, p))
366: return(0);
367:
368: switch (p->type) {
369: case (MDOC_HEAD):
370: assert(MDOC_BLOCK == p->parent->type);
371: p->parent->head = p;
372: break;
373: case (MDOC_TAIL):
374: assert(MDOC_BLOCK == p->parent->type);
375: p->parent->tail = p;
376: break;
377: case (MDOC_BODY):
1.59 schwarze 378: if (p->end)
379: break;
1.1 kristaps 380: assert(MDOC_BLOCK == p->parent->type);
381: p->parent->body = p;
382: break;
383: default:
384: break;
385: }
386:
387: mdoc->last = p;
388:
389: switch (p->type) {
1.77 ! schwarze 390: case (MDOC_TBL):
! 391: /* FALLTHROUGH */
1.1 kristaps 392: case (MDOC_TEXT):
393: if ( ! mdoc_valid_post(mdoc))
394: return(0);
395: break;
396: default:
397: break;
398: }
399:
400: return(1);
401: }
402:
403:
404: static struct mdoc_node *
1.37 schwarze 405: node_alloc(struct mdoc *m, int line, int pos,
406: enum mdoct tok, enum mdoc_type type)
1.1 kristaps 407: {
408: struct mdoc_node *p;
409:
1.32 schwarze 410: p = mandoc_calloc(1, sizeof(struct mdoc_node));
1.19 schwarze 411: p->sec = m->lastsec;
1.1 kristaps 412: p->line = line;
413: p->pos = pos;
414: p->tok = tok;
1.37 schwarze 415: p->type = type;
1.58 schwarze 416:
417: /* Flag analysis. */
418:
1.60 schwarze 419: if (MDOC_SYNOPSIS & m->flags)
420: p->flags |= MDOC_SYNPRETTY;
421: else
422: p->flags &= ~MDOC_SYNPRETTY;
1.47 schwarze 423: if (MDOC_NEWLINE & m->flags)
424: p->flags |= MDOC_LINE;
425: m->flags &= ~MDOC_NEWLINE;
1.58 schwarze 426:
1.1 kristaps 427: return(p);
428: }
429:
430:
431: int
1.37 schwarze 432: mdoc_tail_alloc(struct mdoc *m, int line, int pos, enum mdoct tok)
1.1 kristaps 433: {
434: struct mdoc_node *p;
435:
1.19 schwarze 436: p = node_alloc(m, line, pos, tok, MDOC_TAIL);
1.25 schwarze 437: if ( ! node_append(m, p))
438: return(0);
439: m->next = MDOC_NEXT_CHILD;
440: return(1);
1.1 kristaps 441: }
442:
443:
444: int
1.37 schwarze 445: mdoc_head_alloc(struct mdoc *m, int line, int pos, enum mdoct tok)
1.1 kristaps 446: {
447: struct mdoc_node *p;
448:
1.19 schwarze 449: assert(m->first);
450: assert(m->last);
1.1 kristaps 451:
1.19 schwarze 452: p = node_alloc(m, line, pos, tok, MDOC_HEAD);
1.25 schwarze 453: if ( ! node_append(m, p))
454: return(0);
455: m->next = MDOC_NEXT_CHILD;
456: return(1);
1.1 kristaps 457: }
458:
459:
460: int
1.37 schwarze 461: mdoc_body_alloc(struct mdoc *m, int line, int pos, enum mdoct tok)
1.1 kristaps 462: {
463: struct mdoc_node *p;
464:
1.19 schwarze 465: p = node_alloc(m, line, pos, tok, MDOC_BODY);
1.25 schwarze 466: if ( ! node_append(m, p))
467: return(0);
468: m->next = MDOC_NEXT_CHILD;
1.59 schwarze 469: return(1);
470: }
471:
472:
473: int
474: mdoc_endbody_alloc(struct mdoc *m, int line, int pos, enum mdoct tok,
475: struct mdoc_node *body, enum mdoc_endbody end)
476: {
477: struct mdoc_node *p;
478:
479: p = node_alloc(m, line, pos, tok, MDOC_BODY);
480: p->pending = body;
481: p->end = end;
482: if ( ! node_append(m, p))
483: return(0);
484: m->next = MDOC_NEXT_SIBLING;
1.25 schwarze 485: return(1);
1.1 kristaps 486: }
487:
488:
489: int
1.19 schwarze 490: mdoc_block_alloc(struct mdoc *m, int line, int pos,
1.37 schwarze 491: enum mdoct tok, struct mdoc_arg *args)
1.1 kristaps 492: {
493: struct mdoc_node *p;
494:
1.19 schwarze 495: p = node_alloc(m, line, pos, tok, MDOC_BLOCK);
1.4 schwarze 496: p->args = args;
497: if (p->args)
1.1 kristaps 498: (args->refcnt)++;
1.75 schwarze 499:
500: switch (tok) {
501: case (MDOC_Bd):
502: /* FALLTHROUGH */
503: case (MDOC_Bf):
504: /* FALLTHROUGH */
505: case (MDOC_Bl):
506: /* FALLTHROUGH */
507: case (MDOC_Rs):
508: p->norm = mandoc_calloc(1, sizeof(union mdoc_data));
509: break;
510: default:
511: break;
512: }
513:
1.25 schwarze 514: if ( ! node_append(m, p))
515: return(0);
516: m->next = MDOC_NEXT_CHILD;
517: return(1);
1.1 kristaps 518: }
519:
520:
521: int
1.19 schwarze 522: mdoc_elem_alloc(struct mdoc *m, int line, int pos,
1.37 schwarze 523: enum mdoct tok, struct mdoc_arg *args)
1.1 kristaps 524: {
525: struct mdoc_node *p;
526:
1.19 schwarze 527: p = node_alloc(m, line, pos, tok, MDOC_ELEM);
1.4 schwarze 528: p->args = args;
529: if (p->args)
1.1 kristaps 530: (args->refcnt)++;
1.75 schwarze 531:
532: switch (tok) {
533: case (MDOC_An):
534: p->norm = mandoc_calloc(1, sizeof(union mdoc_data));
535: break;
536: default:
537: break;
538: }
539:
1.25 schwarze 540: if ( ! node_append(m, p))
541: return(0);
542: m->next = MDOC_NEXT_CHILD;
543: return(1);
1.1 kristaps 544: }
545:
1.77 ! schwarze 546: static int
! 547: mdoc_span_alloc(struct mdoc *m, const struct tbl_span *sp)
! 548: {
! 549: struct mdoc_node *n;
! 550:
! 551: /* FIXME: grab from tbl_span. */
! 552: n = node_alloc(m, 0, 0, MDOC_MAX, MDOC_TBL);
! 553: n->span = sp;
! 554:
! 555: if ( ! node_append(m, n))
! 556: return(0);
! 557:
! 558: m->next = MDOC_NEXT_SIBLING;
! 559: return(1);
! 560: }
! 561:
1.1 kristaps 562:
1.46 schwarze 563: int
564: mdoc_word_alloc(struct mdoc *m, int line, int pos, const char *p)
1.1 kristaps 565: {
1.19 schwarze 566: struct mdoc_node *n;
1.46 schwarze 567: size_t sv, len;
568:
569: len = strlen(p);
1.1 kristaps 570:
1.46 schwarze 571: n = node_alloc(m, line, pos, MDOC_MAX, MDOC_TEXT);
1.32 schwarze 572: n->string = mandoc_malloc(len + 1);
1.19 schwarze 573: sv = strlcpy(n->string, p, len + 1);
574:
575: /* Prohibit truncation. */
576: assert(sv < len + 1);
577:
1.25 schwarze 578: if ( ! node_append(m, n))
579: return(0);
1.46 schwarze 580:
1.25 schwarze 581: m->next = MDOC_NEXT_SIBLING;
582: return(1);
1.19 schwarze 583: }
584:
585:
1.61 schwarze 586: static void
1.1 kristaps 587: mdoc_node_free(struct mdoc_node *p)
588: {
1.61 schwarze 589:
1.75 schwarze 590: if (MDOC_BLOCK == p->type || MDOC_ELEM == p->type)
591: free(p->norm);
1.1 kristaps 592: if (p->string)
593: free(p->string);
594: if (p->args)
595: mdoc_argv_free(p->args);
596: free(p);
597: }
598:
599:
1.40 schwarze 600: static void
601: mdoc_node_unlink(struct mdoc *m, struct mdoc_node *n)
602: {
603:
604: /* Adjust siblings. */
605:
606: if (n->prev)
607: n->prev->next = n->next;
608: if (n->next)
609: n->next->prev = n->prev;
610:
611: /* Adjust parent. */
612:
613: if (n->parent) {
614: n->parent->nchild--;
615: if (n->parent->child == n)
616: n->parent->child = n->prev ? n->prev : n->next;
1.72 schwarze 617: if (n->parent->last == n)
618: n->parent->last = n->prev ? n->prev : NULL;
1.40 schwarze 619: }
620:
621: /* Adjust parse point, if applicable. */
622:
623: if (m && m->last == n) {
624: if (n->prev) {
625: m->last = n->prev;
626: m->next = MDOC_NEXT_SIBLING;
627: } else {
628: m->last = n->parent;
629: m->next = MDOC_NEXT_CHILD;
630: }
631: }
632:
633: if (m && m->first == n)
634: m->first = NULL;
635: }
636:
637:
1.1 kristaps 638: void
1.40 schwarze 639: mdoc_node_delete(struct mdoc *m, struct mdoc_node *p)
1.1 kristaps 640: {
641:
1.40 schwarze 642: while (p->child) {
643: assert(p->nchild);
644: mdoc_node_delete(m, p->child);
645: }
646: assert(0 == p->nchild);
1.1 kristaps 647:
1.40 schwarze 648: mdoc_node_unlink(m, p);
1.1 kristaps 649: mdoc_node_free(p);
650: }
651:
652:
653: /*
654: * Parse free-form text, that is, a line that does not begin with the
655: * control character.
656: */
657: static int
1.53 schwarze 658: mdoc_ptext(struct mdoc *m, int line, char *buf, int offs)
1.1 kristaps 659: {
1.56 schwarze 660: char *c, *ws, *end;
661: struct mdoc_node *n;
1.44 schwarze 662:
663: /* Ignore bogus comments. */
664:
1.53 schwarze 665: if ('\\' == buf[offs] &&
666: '.' == buf[offs + 1] &&
1.76 schwarze 667: '"' == buf[offs + 2]) {
668: mdoc_pmsg(m, line, offs, MANDOCERR_BADCOMMENT);
669: return(1);
670: }
1.1 kristaps 671:
1.46 schwarze 672: /* No text before an initial macro. */
673:
1.76 schwarze 674: if (SEC_NONE == m->lastnamed) {
675: mdoc_pmsg(m, line, offs, MANDOCERR_NOTEXT);
676: return(1);
677: }
1.46 schwarze 678:
1.56 schwarze 679: assert(m->last);
680: n = m->last;
681:
682: /*
683: * Divert directly to list processing if we're encountering a
684: * columnar MDOC_BLOCK with or without a prior MDOC_BLOCK entry
685: * (a MDOC_BODY means it's already open, in which case we should
686: * process within its context in the normal way).
687: */
688:
689: if (MDOC_Bl == n->tok && MDOC_BODY == n->type &&
1.75 schwarze 690: LIST_column == n->norm->Bl.type) {
1.56 schwarze 691: /* `Bl' is open without any children. */
692: m->flags |= MDOC_FREECOL;
693: return(mdoc_macro(m, MDOC_It, line, offs, &offs, buf));
694: }
695:
696: if (MDOC_It == n->tok && MDOC_BLOCK == n->type &&
697: NULL != n->parent &&
698: MDOC_Bl == n->parent->tok &&
1.75 schwarze 699: LIST_column == n->parent->norm->Bl.type) {
1.56 schwarze 700: /* `Bl' has block-level `It' children. */
701: m->flags |= MDOC_FREECOL;
702: return(mdoc_macro(m, MDOC_It, line, offs, &offs, buf));
703: }
704:
1.52 schwarze 705: /*
706: * Search for the beginning of unescaped trailing whitespace (ws)
707: * and for the first character not to be output (end).
708: */
1.56 schwarze 709:
710: /* FIXME: replace with strcspn(). */
1.52 schwarze 711: ws = NULL;
1.53 schwarze 712: for (c = end = buf + offs; *c; c++) {
1.52 schwarze 713: switch (*c) {
1.55 schwarze 714: case '-':
715: if (mandoc_hyph(buf + offs, c))
716: *c = ASCII_HYPH;
1.56 schwarze 717: ws = NULL;
1.55 schwarze 718: break;
1.52 schwarze 719: case ' ':
720: if (NULL == ws)
721: ws = c;
722: continue;
723: case '\t':
724: /*
725: * Always warn about trailing tabs,
726: * even outside literal context,
727: * where they should be put on the next line.
728: */
729: if (NULL == ws)
730: ws = c;
731: /*
732: * Strip trailing tabs in literal context only;
733: * outside, they affect the next line.
734: */
735: if (MDOC_LITERAL & m->flags)
736: continue;
737: break;
738: case '\\':
739: /* Skip the escaped character, too, if any. */
740: if (c[1])
741: c++;
742: /* FALLTHROUGH */
743: default:
744: ws = NULL;
745: break;
746: }
747: end = c + 1;
748: }
749: *end = '\0';
1.19 schwarze 750:
1.52 schwarze 751: if (ws)
1.76 schwarze 752: mdoc_pmsg(m, line, (int)(ws-buf), MANDOCERR_EOLNSPACE);
1.34 schwarze 753:
1.53 schwarze 754: if ('\0' == buf[offs] && ! (MDOC_LITERAL & m->flags)) {
1.76 schwarze 755: mdoc_pmsg(m, line, (int)(c-buf), MANDOCERR_NOBLANKLN);
1.46 schwarze 756:
1.40 schwarze 757: /*
1.66 schwarze 758: * Insert a `sp' in the case of a blank line. Technically,
1.46 schwarze 759: * blank lines aren't allowed, but enough manuals assume this
760: * behaviour that we want to work around it.
1.40 schwarze 761: */
1.66 schwarze 762: if ( ! mdoc_elem_alloc(m, line, offs, MDOC_sp, NULL))
1.42 schwarze 763: return(0);
1.46 schwarze 764:
1.42 schwarze 765: m->next = MDOC_NEXT_SIBLING;
766: return(1);
1.38 schwarze 767: }
1.1 kristaps 768:
1.53 schwarze 769: if ( ! mdoc_word_alloc(m, line, offs, buf+offs))
1.1 kristaps 770: return(0);
771:
1.52 schwarze 772: if (MDOC_LITERAL & m->flags)
773: return(1);
774:
1.35 schwarze 775: /*
1.48 schwarze 776: * End-of-sentence check. If the last character is an unescaped
777: * EOS character, then flag the node as being the end of a
778: * sentence. The front-end will know how to interpret this.
1.35 schwarze 779: */
780:
1.52 schwarze 781: assert(buf < end);
1.48 schwarze 782:
1.62 schwarze 783: if (mandoc_eos(buf+offs, (size_t)(end-buf-offs), 0))
1.48 schwarze 784: m->last->flags |= MDOC_EOS;
785:
1.1 kristaps 786: return(1);
787: }
1.19 schwarze 788:
1.1 kristaps 789:
790: /*
791: * Parse a macro line, that is, a line beginning with the control
792: * character.
793: */
1.61 schwarze 794: static int
1.53 schwarze 795: mdoc_pmacro(struct mdoc *m, int ln, char *buf, int offs)
1.1 kristaps 796: {
1.56 schwarze 797: enum mdoct tok;
798: int i, j, sv;
799: char mac[5];
800: struct mdoc_node *n;
1.1 kristaps 801:
1.7 schwarze 802: /* Empty lines are ignored. */
1.1 kristaps 803:
1.53 schwarze 804: offs++;
805:
806: if ('\0' == buf[offs])
1.1 kristaps 807: return(1);
808:
1.53 schwarze 809: i = offs;
1.24 schwarze 810:
1.63 schwarze 811: /* Accept tabs/whitespace after the initial control char. */
1.24 schwarze 812:
1.63 schwarze 813: if (' ' == buf[i] || '\t' == buf[i]) {
1.24 schwarze 814: i++;
1.63 schwarze 815: while (buf[i] && (' ' == buf[i] || '\t' == buf[i]))
1.1 kristaps 816: i++;
1.34 schwarze 817: if ('\0' == buf[i])
1.1 kristaps 818: return(1);
819: }
820:
1.47 schwarze 821: sv = i;
822:
1.63 schwarze 823: /*
1.64 schwarze 824: * Copy the first word into a nil-terminated buffer.
825: * Stop copying when a tab, space, or eoln is encountered.
1.63 schwarze 826: */
1.1 kristaps 827:
1.64 schwarze 828: j = 0;
829: while (j < 4 && '\0' != buf[i] && ' ' != buf[i] && '\t' != buf[i])
830: mac[j++] = buf[i++];
1.53 schwarze 831: mac[j] = '\0';
1.1 kristaps 832:
1.65 schwarze 833: tok = (j > 1 || j < 4) ? mdoc_hash_find(mac) : MDOC_MAX;
834: if (MDOC_MAX == tok) {
1.69 schwarze 835: mdoc_vmsg(m, MANDOCERR_MACRO, ln, sv, "%s", buf + sv - 1);
1.1 kristaps 836: return(1);
837: }
838:
1.63 schwarze 839: /* Disregard the first trailing tab, if applicable. */
840:
841: if ('\t' == buf[i])
842: i++;
843:
844: /* Jump to the next non-whitespace word. */
1.1 kristaps 845:
846: while (buf[i] && ' ' == buf[i])
847: i++;
1.34 schwarze 848:
1.46 schwarze 849: /*
850: * Trailing whitespace. Note that tabs are allowed to be passed
851: * into the parser as "text", so we only warn about spaces here.
852: */
1.34 schwarze 853:
854: if ('\0' == buf[i] && ' ' == buf[i - 1])
1.76 schwarze 855: mdoc_pmsg(m, ln, i - 1, MANDOCERR_EOLNSPACE);
1.1 kristaps 856:
1.56 schwarze 857: /*
858: * If an initial macro or a list invocation, divert directly
859: * into macro processing.
860: */
861:
862: if (NULL == m->last || MDOC_It == tok || MDOC_El == tok) {
863: if ( ! mdoc_macro(m, tok, ln, sv, &i, buf))
864: goto err;
865: return(1);
866: }
867:
868: n = m->last;
869: assert(m->last);
870:
871: /*
872: * If the first macro of a `Bl -column', open an `It' block
873: * context around the parsed macro.
874: */
875:
876: if (MDOC_Bl == n->tok && MDOC_BODY == n->type &&
1.75 schwarze 877: LIST_column == n->norm->Bl.type) {
1.56 schwarze 878: m->flags |= MDOC_FREECOL;
1.58 schwarze 879: if ( ! mdoc_macro(m, MDOC_It, ln, sv, &sv, buf))
1.56 schwarze 880: goto err;
881: return(1);
882: }
883:
884: /*
885: * If we're following a block-level `It' within a `Bl -column'
886: * context (perhaps opened in the above block or in ptext()),
887: * then open an `It' block context around the parsed macro.
1.24 schwarze 888: */
1.56 schwarze 889:
890: if (MDOC_It == n->tok && MDOC_BLOCK == n->type &&
891: NULL != n->parent &&
892: MDOC_Bl == n->parent->tok &&
1.75 schwarze 893: LIST_column == n->parent->norm->Bl.type) {
1.56 schwarze 894: m->flags |= MDOC_FREECOL;
895: if ( ! mdoc_macro(m, MDOC_It, ln, sv, &sv, buf))
896: goto err;
897: return(1);
898: }
899:
900: /* Normal processing of a macro. */
901:
1.47 schwarze 902: if ( ! mdoc_macro(m, tok, ln, sv, &i, buf))
1.1 kristaps 903: goto err;
904:
905: return(1);
906:
907: err: /* Error out. */
908:
909: m->flags |= MDOC_HALT;
910: return(0);
911: }
1.24 schwarze 912:
913: