Annotation of src/usr.bin/mandoc/mdoc.c, Revision 1.135
1.135 ! schwarze 1: /* $OpenBSD: mdoc.c,v 1.134 2015/04/18 17:28:08 schwarze Exp $ */
1.1 kristaps 2: /*
1.77 schwarze 3: * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
1.126 schwarze 4: * Copyright (c) 2010, 2012-2015 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.130 schwarze 10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
1.3 schwarze 11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1.130 schwarze 12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
1.3 schwarze 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>
1.101 schwarze 21: #include <ctype.h>
1.1 kristaps 22: #include <stdarg.h>
23: #include <stdio.h>
24: #include <stdlib.h>
25: #include <string.h>
1.39 schwarze 26: #include <time.h>
1.1 kristaps 27:
1.128 schwarze 28: #include "mandoc_aux.h"
29: #include "mandoc.h"
30: #include "roff.h"
1.83 schwarze 31: #include "mdoc.h"
1.128 schwarze 32: #include "libmandoc.h"
1.1 kristaps 33: #include "libmdoc.h"
34:
1.106 schwarze 35: const char *const __mdoc_macronames[MDOC_MAX + 1] = {
1.7 schwarze 36: "Ap", "Dd", "Dt", "Os",
1.1 kristaps 37: "Sh", "Ss", "Pp", "D1",
38: "Dl", "Bd", "Ed", "Bl",
39: "El", "It", "Ad", "An",
40: "Ar", "Cd", "Cm", "Dv",
41: "Er", "Ev", "Ex", "Fa",
42: "Fd", "Fl", "Fn", "Ft",
43: "Ic", "In", "Li", "Nd",
44: "Nm", "Op", "Ot", "Pa",
45: "Rv", "St", "Va", "Vt",
1.33 schwarze 46: "Xr", "%A", "%B", "%D",
47: "%I", "%J", "%N", "%O",
48: "%P", "%R", "%T", "%V",
1.1 kristaps 49: "Ac", "Ao", "Aq", "At",
50: "Bc", "Bf", "Bo", "Bq",
51: "Bsx", "Bx", "Db", "Dc",
52: "Do", "Dq", "Ec", "Ef",
53: "Em", "Eo", "Fx", "Ms",
54: "No", "Ns", "Nx", "Ox",
55: "Pc", "Pf", "Po", "Pq",
56: "Qc", "Ql", "Qo", "Qq",
57: "Re", "Rs", "Sc", "So",
58: "Sq", "Sm", "Sx", "Sy",
59: "Tn", "Ux", "Xc", "Xo",
60: "Fo", "Fc", "Oo", "Oc",
61: "Bk", "Ek", "Bt", "Hf",
1.7 schwarze 62: "Fr", "Ud", "Lb", "Lp",
63: "Lk", "Mt", "Brq", "Bro",
1.33 schwarze 64: "Brc", "%C", "Es", "En",
65: "Dx", "%Q", "br", "sp",
1.106 schwarze 66: "%U", "Ta", "ll", "text",
1.1 kristaps 67: };
68:
1.103 schwarze 69: const char *const __mdoc_argnames[MDOC_ARG_MAX] = {
1.1 kristaps 70: "split", "nosplit", "ragged",
1.103 schwarze 71: "unfilled", "literal", "file",
72: "offset", "bullet", "dash",
73: "hyphen", "item", "enum",
74: "tag", "diag", "hang",
75: "ohang", "inset", "column",
76: "width", "compact", "std",
1.1 kristaps 77: "filled", "words", "emphasis",
1.30 schwarze 78: "symbolic", "nested", "centered"
1.1 kristaps 79: };
80:
81: const char * const *mdoc_macronames = __mdoc_macronames;
82: const char * const *mdoc_argnames = __mdoc_argnames;
83:
1.129 schwarze 84: static void mdoc_node_free(struct roff_node *);
1.131 schwarze 85: static void mdoc_node_unlink(struct roff_man *,
1.129 schwarze 86: struct roff_node *);
1.131 schwarze 87: static struct roff_node *node_alloc(struct roff_man *, int, int,
1.129 schwarze 88: int, enum roff_type);
1.131 schwarze 89: static void node_append(struct roff_man *, struct roff_node *);
90: static int mdoc_ptext(struct roff_man *, int, char *, int);
91: static int mdoc_pmacro(struct roff_man *, int, char *, int);
1.1 kristaps 92:
93:
1.123 schwarze 94: void
1.131 schwarze 95: mdoc_endparse(struct roff_man *mdoc)
1.1 kristaps 96: {
97:
1.121 schwarze 98: mdoc_macroend(mdoc);
1.81 schwarze 99: }
100:
1.122 schwarze 101: void
1.131 schwarze 102: mdoc_addeqn(struct roff_man *mdoc, const struct eqn *ep)
1.81 schwarze 103: {
1.129 schwarze 104: struct roff_node *n;
1.81 schwarze 105:
1.128 schwarze 106: n = node_alloc(mdoc, ep->ln, ep->pos, MDOC_MAX, ROFFT_EQN);
1.81 schwarze 107: n->eqn = ep;
1.116 schwarze 108: if (ep->ln > mdoc->last->line)
109: n->flags |= MDOC_LINE;
1.119 schwarze 110: node_append(mdoc, n);
1.131 schwarze 111: mdoc->next = ROFF_NEXT_SIBLING;
1.1 kristaps 112: }
113:
1.122 schwarze 114: void
1.131 schwarze 115: mdoc_addspan(struct roff_man *mdoc, const struct tbl_span *sp)
1.77 schwarze 116: {
1.129 schwarze 117: struct roff_node *n;
1.77 schwarze 118:
1.128 schwarze 119: n = node_alloc(mdoc, sp->line, 0, MDOC_MAX, ROFFT_TBL);
1.79 schwarze 120: n->span = sp;
1.119 schwarze 121: node_append(mdoc, n);
1.131 schwarze 122: mdoc->next = ROFF_NEXT_SIBLING;
1.77 schwarze 123: }
124:
1.1 kristaps 125: /*
126: * Main parse routine. Parses a single line -- really just hands off to
1.45 schwarze 127: * the macro (mdoc_pmacro()) or text parser (mdoc_ptext()).
1.1 kristaps 128: */
129: int
1.131 schwarze 130: mdoc_parseln(struct roff_man *mdoc, int ln, char *buf, int offs)
1.1 kristaps 131: {
132:
1.128 schwarze 133: if (mdoc->last->type != ROFFT_EQN || ln > mdoc->last->line)
1.117 schwarze 134: mdoc->flags |= MDOC_NEWLINE;
1.60 schwarze 135:
136: /*
137: * Let the roff nS register switch SYNOPSIS mode early,
138: * such that the parser knows at all times
139: * whether this mode is on or off.
140: * Note that this mode is also switched by the Sh macro.
141: */
1.94 schwarze 142: if (roff_getreg(mdoc->roff, "nS"))
143: mdoc->flags |= MDOC_SYNOPSIS;
144: else
145: mdoc->flags &= ~MDOC_SYNOPSIS;
1.60 schwarze 146:
1.93 schwarze 147: return(roff_getcontrol(mdoc->roff, buf, &offs) ?
1.103 schwarze 148: mdoc_pmacro(mdoc, ln, buf, offs) :
149: mdoc_ptext(mdoc, ln, buf, offs));
1.1 kristaps 150: }
151:
1.121 schwarze 152: void
1.58 schwarze 153: mdoc_macro(MACRO_PROT_ARGS)
1.1 kristaps 154: {
1.46 schwarze 155: assert(tok < MDOC_MAX);
156:
1.113 schwarze 157: if (mdoc->flags & MDOC_PBODY) {
158: if (tok == MDOC_Dt) {
159: mandoc_vmsg(MANDOCERR_DT_LATE,
160: mdoc->parse, line, ppos,
161: "Dt %s", buf + *pos);
1.121 schwarze 162: return;
1.113 schwarze 163: }
164: } else if ( ! (mdoc_macros[tok].flags & MDOC_PROLOGUE)) {
165: if (mdoc->meta.title == NULL) {
166: mandoc_vmsg(MANDOCERR_DT_NOTITLE,
167: mdoc->parse, line, ppos, "%s %s",
168: mdoc_macronames[tok], buf + *pos);
169: mdoc->meta.title = mandoc_strdup("UNTITLED");
170: }
1.93 schwarze 171: if (NULL == mdoc->meta.vol)
172: mdoc->meta.vol = mandoc_strdup("LOCAL");
173: mdoc->flags |= MDOC_PBODY;
1.39 schwarze 174: }
1.121 schwarze 175: (*mdoc_macros[tok].fp)(mdoc, tok, line, ppos, pos, buf);
1.1 kristaps 176: }
177:
178:
1.119 schwarze 179: static void
1.131 schwarze 180: node_append(struct roff_man *mdoc, struct roff_node *p)
1.1 kristaps 181: {
182:
183: assert(mdoc->last);
184: assert(mdoc->first);
1.128 schwarze 185: assert(p->type != ROFFT_ROOT);
1.1 kristaps 186:
187: switch (mdoc->next) {
1.131 schwarze 188: case ROFF_NEXT_SIBLING:
1.1 kristaps 189: mdoc->last->next = p;
190: p->prev = mdoc->last;
191: p->parent = mdoc->last->parent;
192: break;
1.131 schwarze 193: case ROFF_NEXT_CHILD:
1.1 kristaps 194: mdoc->last->child = p;
195: p->parent = mdoc->last;
196: break;
197: default:
198: abort();
199: /* NOTREACHED */
200: }
201:
1.10 schwarze 202: p->parent->nchild++;
203:
1.75 schwarze 204: /*
205: * Copy over the normalised-data pointer of our parent. Not
206: * everybody has one, but copying a null pointer is fine.
207: */
208:
209: switch (p->type) {
1.128 schwarze 210: case ROFFT_BODY:
1.92 schwarze 211: if (ENDBODY_NOT != p->end)
212: break;
1.75 schwarze 213: /* FALLTHROUGH */
1.128 schwarze 214: case ROFFT_TAIL:
1.75 schwarze 215: /* FALLTHROUGH */
1.128 schwarze 216: case ROFFT_HEAD:
1.75 schwarze 217: p->norm = p->parent->norm;
218: break;
219: default:
220: break;
221: }
222:
1.119 schwarze 223: mdoc_valid_pre(mdoc, p);
1.1 kristaps 224:
225: switch (p->type) {
1.128 schwarze 226: case ROFFT_HEAD:
227: assert(p->parent->type == ROFFT_BLOCK);
1.1 kristaps 228: p->parent->head = p;
229: break;
1.128 schwarze 230: case ROFFT_TAIL:
231: assert(p->parent->type == ROFFT_BLOCK);
1.1 kristaps 232: p->parent->tail = p;
233: break;
1.128 schwarze 234: case ROFFT_BODY:
1.59 schwarze 235: if (p->end)
236: break;
1.128 schwarze 237: assert(p->parent->type == ROFFT_BLOCK);
1.1 kristaps 238: p->parent->body = p;
239: break;
240: default:
241: break;
242: }
243:
244: mdoc->last = p;
245:
246: switch (p->type) {
1.128 schwarze 247: case ROFFT_TBL:
1.77 schwarze 248: /* FALLTHROUGH */
1.128 schwarze 249: case ROFFT_TEXT:
1.119 schwarze 250: mdoc_valid_post(mdoc);
1.1 kristaps 251: break;
252: default:
253: break;
254: }
255: }
256:
1.129 schwarze 257: static struct roff_node *
1.131 schwarze 258: node_alloc(struct roff_man *mdoc, int line, int pos,
1.129 schwarze 259: int tok, enum roff_type type)
1.1 kristaps 260: {
1.129 schwarze 261: struct roff_node *p;
1.1 kristaps 262:
1.129 schwarze 263: p = mandoc_calloc(1, sizeof(*p));
1.93 schwarze 264: p->sec = mdoc->lastsec;
1.1 kristaps 265: p->line = line;
266: p->pos = pos;
267: p->tok = tok;
1.37 schwarze 268: p->type = type;
1.58 schwarze 269:
270: /* Flag analysis. */
271:
1.93 schwarze 272: if (MDOC_SYNOPSIS & mdoc->flags)
1.60 schwarze 273: p->flags |= MDOC_SYNPRETTY;
274: else
275: p->flags &= ~MDOC_SYNPRETTY;
1.93 schwarze 276: if (MDOC_NEWLINE & mdoc->flags)
1.47 schwarze 277: p->flags |= MDOC_LINE;
1.93 schwarze 278: mdoc->flags &= ~MDOC_NEWLINE;
1.58 schwarze 279:
1.1 kristaps 280: return(p);
281: }
282:
1.120 schwarze 283: void
1.131 schwarze 284: mdoc_tail_alloc(struct roff_man *mdoc, int line, int pos, int tok)
1.1 kristaps 285: {
1.129 schwarze 286: struct roff_node *p;
1.1 kristaps 287:
1.128 schwarze 288: p = node_alloc(mdoc, line, pos, tok, ROFFT_TAIL);
1.119 schwarze 289: node_append(mdoc, p);
1.131 schwarze 290: mdoc->next = ROFF_NEXT_CHILD;
1.1 kristaps 291: }
292:
1.129 schwarze 293: struct roff_node *
1.131 schwarze 294: mdoc_head_alloc(struct roff_man *mdoc, int line, int pos, int tok)
1.1 kristaps 295: {
1.129 schwarze 296: struct roff_node *p;
1.1 kristaps 297:
1.93 schwarze 298: assert(mdoc->first);
299: assert(mdoc->last);
1.128 schwarze 300: p = node_alloc(mdoc, line, pos, tok, ROFFT_HEAD);
1.119 schwarze 301: node_append(mdoc, p);
1.131 schwarze 302: mdoc->next = ROFF_NEXT_CHILD;
1.120 schwarze 303: return(p);
1.1 kristaps 304: }
305:
1.129 schwarze 306: struct roff_node *
1.131 schwarze 307: mdoc_body_alloc(struct roff_man *mdoc, int line, int pos, int tok)
1.1 kristaps 308: {
1.129 schwarze 309: struct roff_node *p;
1.1 kristaps 310:
1.128 schwarze 311: p = node_alloc(mdoc, line, pos, tok, ROFFT_BODY);
1.119 schwarze 312: node_append(mdoc, p);
1.131 schwarze 313: mdoc->next = ROFF_NEXT_CHILD;
1.120 schwarze 314: return(p);
1.59 schwarze 315: }
316:
1.129 schwarze 317: struct roff_node *
1.131 schwarze 318: mdoc_endbody_alloc(struct roff_man *mdoc, int line, int pos, int tok,
1.129 schwarze 319: struct roff_node *body, enum mdoc_endbody end)
1.59 schwarze 320: {
1.129 schwarze 321: struct roff_node *p;
1.59 schwarze 322:
1.126 schwarze 323: body->flags |= MDOC_ENDED;
324: body->parent->flags |= MDOC_ENDED;
1.128 schwarze 325: p = node_alloc(mdoc, line, pos, tok, ROFFT_BODY);
1.126 schwarze 326: p->body = body;
1.92 schwarze 327: p->norm = body->norm;
1.59 schwarze 328: p->end = end;
1.119 schwarze 329: node_append(mdoc, p);
1.131 schwarze 330: mdoc->next = ROFF_NEXT_SIBLING;
1.124 schwarze 331: return(p);
1.1 kristaps 332: }
333:
1.129 schwarze 334: struct roff_node *
1.131 schwarze 335: mdoc_block_alloc(struct roff_man *mdoc, int line, int pos,
1.129 schwarze 336: int tok, struct mdoc_arg *args)
1.1 kristaps 337: {
1.129 schwarze 338: struct roff_node *p;
1.1 kristaps 339:
1.128 schwarze 340: p = node_alloc(mdoc, line, pos, tok, ROFFT_BLOCK);
1.4 schwarze 341: p->args = args;
342: if (p->args)
1.1 kristaps 343: (args->refcnt)++;
1.75 schwarze 344:
345: switch (tok) {
1.103 schwarze 346: case MDOC_Bd:
1.75 schwarze 347: /* FALLTHROUGH */
1.103 schwarze 348: case MDOC_Bf:
1.75 schwarze 349: /* FALLTHROUGH */
1.103 schwarze 350: case MDOC_Bl:
1.107 schwarze 351: /* FALLTHROUGH */
352: case MDOC_En:
1.75 schwarze 353: /* FALLTHROUGH */
1.103 schwarze 354: case MDOC_Rs:
1.75 schwarze 355: p->norm = mandoc_calloc(1, sizeof(union mdoc_data));
356: break;
357: default:
358: break;
359: }
1.119 schwarze 360: node_append(mdoc, p);
1.131 schwarze 361: mdoc->next = ROFF_NEXT_CHILD;
1.120 schwarze 362: return(p);
1.1 kristaps 363: }
364:
1.120 schwarze 365: void
1.131 schwarze 366: mdoc_elem_alloc(struct roff_man *mdoc, int line, int pos,
1.129 schwarze 367: int tok, struct mdoc_arg *args)
1.1 kristaps 368: {
1.129 schwarze 369: struct roff_node *p;
1.1 kristaps 370:
1.128 schwarze 371: p = node_alloc(mdoc, line, pos, tok, ROFFT_ELEM);
1.4 schwarze 372: p->args = args;
373: if (p->args)
1.1 kristaps 374: (args->refcnt)++;
1.75 schwarze 375:
376: switch (tok) {
1.103 schwarze 377: case MDOC_An:
1.75 schwarze 378: p->norm = mandoc_calloc(1, sizeof(union mdoc_data));
379: break;
380: default:
381: break;
382: }
1.119 schwarze 383: node_append(mdoc, p);
1.131 schwarze 384: mdoc->next = ROFF_NEXT_CHILD;
1.1 kristaps 385: }
386:
1.120 schwarze 387: void
1.131 schwarze 388: mdoc_word_alloc(struct roff_man *mdoc, int line, int pos, const char *p)
1.1 kristaps 389: {
1.129 schwarze 390: struct roff_node *n;
1.1 kristaps 391:
1.128 schwarze 392: n = node_alloc(mdoc, line, pos, MDOC_MAX, ROFFT_TEXT);
1.93 schwarze 393: n->string = roff_strdup(mdoc->roff, p);
1.119 schwarze 394: node_append(mdoc, n);
1.131 schwarze 395: mdoc->next = ROFF_NEXT_SIBLING;
1.19 schwarze 396: }
397:
1.95 schwarze 398: void
1.131 schwarze 399: mdoc_word_append(struct roff_man *mdoc, const char *p)
1.95 schwarze 400: {
1.129 schwarze 401: struct roff_node *n;
1.95 schwarze 402: char *addstr, *newstr;
403:
404: n = mdoc->last;
405: addstr = roff_strdup(mdoc->roff, p);
1.100 schwarze 406: mandoc_asprintf(&newstr, "%s %s", n->string, addstr);
1.95 schwarze 407: free(addstr);
408: free(n->string);
409: n->string = newstr;
1.131 schwarze 410: mdoc->next = ROFF_NEXT_SIBLING;
1.95 schwarze 411: }
1.19 schwarze 412:
1.61 schwarze 413: static void
1.129 schwarze 414: mdoc_node_free(struct roff_node *p)
1.1 kristaps 415: {
1.61 schwarze 416:
1.128 schwarze 417: if (p->type == ROFFT_BLOCK || p->type == ROFFT_ELEM)
1.75 schwarze 418: free(p->norm);
1.1 kristaps 419: if (p->string)
420: free(p->string);
421: if (p->args)
422: mdoc_argv_free(p->args);
423: free(p);
424: }
425:
1.40 schwarze 426: static void
1.131 schwarze 427: mdoc_node_unlink(struct roff_man *mdoc, struct roff_node *n)
1.40 schwarze 428: {
429:
430: /* Adjust siblings. */
431:
432: if (n->prev)
433: n->prev->next = n->next;
434: if (n->next)
435: n->next->prev = n->prev;
436:
437: /* Adjust parent. */
438:
439: if (n->parent) {
440: n->parent->nchild--;
441: if (n->parent->child == n)
442: n->parent->child = n->prev ? n->prev : n->next;
1.72 schwarze 443: if (n->parent->last == n)
444: n->parent->last = n->prev ? n->prev : NULL;
1.40 schwarze 445: }
446:
447: /* Adjust parse point, if applicable. */
448:
1.93 schwarze 449: if (mdoc && mdoc->last == n) {
1.40 schwarze 450: if (n->prev) {
1.93 schwarze 451: mdoc->last = n->prev;
1.131 schwarze 452: mdoc->next = ROFF_NEXT_SIBLING;
1.40 schwarze 453: } else {
1.93 schwarze 454: mdoc->last = n->parent;
1.131 schwarze 455: mdoc->next = ROFF_NEXT_CHILD;
1.40 schwarze 456: }
457: }
458:
1.93 schwarze 459: if (mdoc && mdoc->first == n)
460: mdoc->first = NULL;
1.40 schwarze 461: }
462:
1.1 kristaps 463: void
1.131 schwarze 464: mdoc_node_delete(struct roff_man *mdoc, struct roff_node *p)
1.1 kristaps 465: {
466:
1.40 schwarze 467: while (p->child) {
468: assert(p->nchild);
1.93 schwarze 469: mdoc_node_delete(mdoc, p->child);
1.40 schwarze 470: }
471: assert(0 == p->nchild);
1.1 kristaps 472:
1.93 schwarze 473: mdoc_node_unlink(mdoc, p);
1.1 kristaps 474: mdoc_node_free(p);
1.91 schwarze 475: }
476:
1.120 schwarze 477: void
1.131 schwarze 478: mdoc_node_relink(struct roff_man *mdoc, struct roff_node *p)
1.91 schwarze 479: {
480:
1.93 schwarze 481: mdoc_node_unlink(mdoc, p);
1.119 schwarze 482: node_append(mdoc, p);
1.1 kristaps 483: }
484:
485: /*
486: * Parse free-form text, that is, a line that does not begin with the
487: * control character.
488: */
489: static int
1.131 schwarze 490: mdoc_ptext(struct roff_man *mdoc, int line, char *buf, int offs)
1.1 kristaps 491: {
1.129 schwarze 492: struct roff_node *n;
1.56 schwarze 493: char *c, *ws, *end;
1.46 schwarze 494:
1.93 schwarze 495: assert(mdoc->last);
496: n = mdoc->last;
1.56 schwarze 497:
498: /*
499: * Divert directly to list processing if we're encountering a
1.128 schwarze 500: * columnar ROFFT_BLOCK with or without a prior ROFFT_BLOCK entry
501: * (a ROFFT_BODY means it's already open, in which case we should
1.56 schwarze 502: * process within its context in the normal way).
503: */
504:
1.128 schwarze 505: if (n->tok == MDOC_Bl && n->type == ROFFT_BODY &&
1.127 schwarze 506: n->end == ENDBODY_NOT && n->norm->Bl.type == LIST_column) {
1.56 schwarze 507: /* `Bl' is open without any children. */
1.93 schwarze 508: mdoc->flags |= MDOC_FREECOL;
1.121 schwarze 509: mdoc_macro(mdoc, MDOC_It, line, offs, &offs, buf);
510: return(1);
1.56 schwarze 511: }
512:
1.128 schwarze 513: if (n->tok == MDOC_It && n->type == ROFFT_BLOCK &&
1.103 schwarze 514: NULL != n->parent &&
515: MDOC_Bl == n->parent->tok &&
516: LIST_column == n->parent->norm->Bl.type) {
1.56 schwarze 517: /* `Bl' has block-level `It' children. */
1.93 schwarze 518: mdoc->flags |= MDOC_FREECOL;
1.121 schwarze 519: mdoc_macro(mdoc, MDOC_It, line, offs, &offs, buf);
520: return(1);
1.56 schwarze 521: }
522:
1.52 schwarze 523: /*
524: * Search for the beginning of unescaped trailing whitespace (ws)
525: * and for the first character not to be output (end).
526: */
1.56 schwarze 527:
528: /* FIXME: replace with strcspn(). */
1.52 schwarze 529: ws = NULL;
1.53 schwarze 530: for (c = end = buf + offs; *c; c++) {
1.52 schwarze 531: switch (*c) {
532: case ' ':
533: if (NULL == ws)
534: ws = c;
535: continue;
536: case '\t':
537: /*
538: * Always warn about trailing tabs,
539: * even outside literal context,
540: * where they should be put on the next line.
541: */
542: if (NULL == ws)
543: ws = c;
544: /*
545: * Strip trailing tabs in literal context only;
546: * outside, they affect the next line.
547: */
1.93 schwarze 548: if (MDOC_LITERAL & mdoc->flags)
1.52 schwarze 549: continue;
550: break;
551: case '\\':
552: /* Skip the escaped character, too, if any. */
553: if (c[1])
554: c++;
555: /* FALLTHROUGH */
556: default:
557: ws = NULL;
558: break;
559: }
560: end = c + 1;
561: }
562: *end = '\0';
1.19 schwarze 563:
1.52 schwarze 564: if (ws)
1.108 schwarze 565: mandoc_msg(MANDOCERR_SPACE_EOL, mdoc->parse,
566: line, (int)(ws-buf), NULL);
1.34 schwarze 567:
1.120 schwarze 568: if (buf[offs] == '\0' && ! (mdoc->flags & MDOC_LITERAL)) {
1.108 schwarze 569: mandoc_msg(MANDOCERR_FI_BLANK, mdoc->parse,
570: line, (int)(c - buf), NULL);
1.46 schwarze 571:
1.40 schwarze 572: /*
1.66 schwarze 573: * Insert a `sp' in the case of a blank line. Technically,
1.46 schwarze 574: * blank lines aren't allowed, but enough manuals assume this
575: * behaviour that we want to work around it.
1.40 schwarze 576: */
1.120 schwarze 577: mdoc_elem_alloc(mdoc, line, offs, MDOC_sp, NULL);
1.131 schwarze 578: mdoc->next = ROFF_NEXT_SIBLING;
1.119 schwarze 579: mdoc_valid_post(mdoc);
580: return(1);
1.38 schwarze 581: }
1.1 kristaps 582:
1.120 schwarze 583: mdoc_word_alloc(mdoc, line, offs, buf+offs);
1.1 kristaps 584:
1.120 schwarze 585: if (mdoc->flags & MDOC_LITERAL)
1.52 schwarze 586: return(1);
587:
1.35 schwarze 588: /*
1.48 schwarze 589: * End-of-sentence check. If the last character is an unescaped
590: * EOS character, then flag the node as being the end of a
591: * sentence. The front-end will know how to interpret this.
1.35 schwarze 592: */
593:
1.52 schwarze 594: assert(buf < end);
1.48 schwarze 595:
1.97 schwarze 596: if (mandoc_eos(buf+offs, (size_t)(end-buf-offs)))
1.93 schwarze 597: mdoc->last->flags |= MDOC_EOS;
1.1 kristaps 598: return(1);
599: }
1.19 schwarze 600:
1.1 kristaps 601: /*
602: * Parse a macro line, that is, a line beginning with the control
603: * character.
604: */
1.61 schwarze 605: static int
1.131 schwarze 606: mdoc_pmacro(struct roff_man *mdoc, int ln, char *buf, int offs)
1.1 kristaps 607: {
1.129 schwarze 608: struct roff_node *n;
1.118 schwarze 609: const char *cp;
1.129 schwarze 610: int tok;
1.83 schwarze 611: int i, sv;
1.56 schwarze 612: char mac[5];
1.1 kristaps 613:
1.83 schwarze 614: sv = offs;
1.47 schwarze 615:
1.103 schwarze 616: /*
1.64 schwarze 617: * Copy the first word into a nil-terminated buffer.
1.118 schwarze 618: * Stop when a space, tab, escape, or eoln is encountered.
1.63 schwarze 619: */
1.1 kristaps 620:
1.83 schwarze 621: i = 0;
1.118 schwarze 622: while (i < 4 && strchr(" \t\\", buf[offs]) == NULL)
1.83 schwarze 623: mac[i++] = buf[offs++];
624:
625: mac[i] = '\0';
626:
1.104 schwarze 627: tok = (i > 1 && i < 4) ? mdoc_hash_find(mac) : MDOC_MAX;
1.1 kristaps 628:
1.118 schwarze 629: if (tok == MDOC_MAX) {
1.112 schwarze 630: mandoc_msg(MANDOCERR_MACRO, mdoc->parse,
631: ln, sv, buf + sv - 1);
1.1 kristaps 632: return(1);
633: }
634:
1.118 schwarze 635: /* Skip a leading escape sequence or tab. */
1.63 schwarze 636:
1.118 schwarze 637: switch (buf[offs]) {
638: case '\\':
639: cp = buf + offs + 1;
640: mandoc_escape(&cp, NULL, NULL);
641: offs = cp - buf;
642: break;
643: case '\t':
1.83 schwarze 644: offs++;
1.118 schwarze 645: break;
646: default:
647: break;
648: }
1.63 schwarze 649:
650: /* Jump to the next non-whitespace word. */
1.1 kristaps 651:
1.83 schwarze 652: while (buf[offs] && ' ' == buf[offs])
653: offs++;
1.34 schwarze 654:
1.103 schwarze 655: /*
1.46 schwarze 656: * Trailing whitespace. Note that tabs are allowed to be passed
657: * into the parser as "text", so we only warn about spaces here.
658: */
1.34 schwarze 659:
1.83 schwarze 660: if ('\0' == buf[offs] && ' ' == buf[offs - 1])
1.108 schwarze 661: mandoc_msg(MANDOCERR_SPACE_EOL, mdoc->parse,
662: ln, offs - 1, NULL);
1.1 kristaps 663:
1.56 schwarze 664: /*
665: * If an initial macro or a list invocation, divert directly
666: * into macro processing.
667: */
668:
1.121 schwarze 669: if (NULL == mdoc->last || MDOC_It == tok || MDOC_El == tok) {
670: mdoc_macro(mdoc, tok, ln, sv, &offs, buf);
671: return(1);
672: }
1.56 schwarze 673:
1.93 schwarze 674: n = mdoc->last;
675: assert(mdoc->last);
1.56 schwarze 676:
677: /*
678: * If the first macro of a `Bl -column', open an `It' block
679: * context around the parsed macro.
680: */
681:
1.128 schwarze 682: if (n->tok == MDOC_Bl && n->type == ROFFT_BODY &&
1.127 schwarze 683: n->end == ENDBODY_NOT && n->norm->Bl.type == LIST_column) {
1.93 schwarze 684: mdoc->flags |= MDOC_FREECOL;
1.121 schwarze 685: mdoc_macro(mdoc, MDOC_It, ln, sv, &sv, buf);
686: return(1);
1.56 schwarze 687: }
688:
689: /*
690: * If we're following a block-level `It' within a `Bl -column'
691: * context (perhaps opened in the above block or in ptext()),
692: * then open an `It' block context around the parsed macro.
1.24 schwarze 693: */
1.56 schwarze 694:
1.128 schwarze 695: if (n->tok == MDOC_It && n->type == ROFFT_BLOCK &&
1.103 schwarze 696: NULL != n->parent &&
697: MDOC_Bl == n->parent->tok &&
698: LIST_column == n->parent->norm->Bl.type) {
1.93 schwarze 699: mdoc->flags |= MDOC_FREECOL;
1.121 schwarze 700: mdoc_macro(mdoc, MDOC_It, ln, sv, &sv, buf);
701: return(1);
1.56 schwarze 702: }
703:
704: /* Normal processing of a macro. */
705:
1.121 schwarze 706: mdoc_macro(mdoc, tok, ln, sv, &offs, buf);
1.98 schwarze 707:
708: /* In quick mode (for mandocdb), abort after the NAME section. */
709:
710: if (mdoc->quick && MDOC_Sh == tok &&
711: SEC_NAME != mdoc->last->sec)
712: return(2);
1.1 kristaps 713:
714: return(1);
715: }
1.24 schwarze 716:
1.83 schwarze 717: enum mdelim
718: mdoc_isdelim(const char *p)
719: {
720:
721: if ('\0' == p[0])
722: return(DELIM_NONE);
723:
724: if ('\0' == p[1])
725: switch (p[0]) {
1.103 schwarze 726: case '(':
1.83 schwarze 727: /* FALLTHROUGH */
1.103 schwarze 728: case '[':
1.83 schwarze 729: return(DELIM_OPEN);
1.103 schwarze 730: case '|':
1.83 schwarze 731: return(DELIM_MIDDLE);
1.103 schwarze 732: case '.':
1.83 schwarze 733: /* FALLTHROUGH */
1.103 schwarze 734: case ',':
1.83 schwarze 735: /* FALLTHROUGH */
1.103 schwarze 736: case ';':
1.83 schwarze 737: /* FALLTHROUGH */
1.103 schwarze 738: case ':':
1.83 schwarze 739: /* FALLTHROUGH */
1.103 schwarze 740: case '?':
1.83 schwarze 741: /* FALLTHROUGH */
1.103 schwarze 742: case '!':
1.83 schwarze 743: /* FALLTHROUGH */
1.103 schwarze 744: case ')':
1.83 schwarze 745: /* FALLTHROUGH */
1.103 schwarze 746: case ']':
1.83 schwarze 747: return(DELIM_CLOSE);
748: default:
749: return(DELIM_NONE);
750: }
751:
752: if ('\\' != p[0])
753: return(DELIM_NONE);
1.24 schwarze 754:
1.83 schwarze 755: if (0 == strcmp(p + 1, "."))
756: return(DELIM_CLOSE);
1.90 schwarze 757: if (0 == strcmp(p + 1, "fR|\\fP"))
1.83 schwarze 758: return(DELIM_MIDDLE);
759:
760: return(DELIM_NONE);
1.101 schwarze 761: }
762:
763: void
1.129 schwarze 764: mdoc_deroff(char **dest, const struct roff_node *n)
1.101 schwarze 765: {
766: char *cp;
767: size_t sz;
768:
1.128 schwarze 769: if (n->type != ROFFT_TEXT) {
1.101 schwarze 770: for (n = n->child; n; n = n->next)
771: mdoc_deroff(dest, n);
772: return;
773: }
774:
775: /* Skip leading whitespace. */
776:
777: for (cp = n->string; '\0' != *cp; cp++)
778: if (0 == isspace((unsigned char)*cp))
779: break;
780:
781: /* Skip trailing whitespace. */
782:
783: for (sz = strlen(cp); sz; sz--)
784: if (0 == isspace((unsigned char)cp[sz-1]))
785: break;
786:
787: /* Skip empty strings. */
788:
789: if (0 == sz)
790: return;
791:
792: if (NULL == *dest) {
793: *dest = mandoc_strndup(cp, sz);
794: return;
795: }
796:
797: mandoc_asprintf(&cp, "%s %*s", *dest, (int)sz, cp);
798: free(*dest);
799: *dest = cp;
1.83 schwarze 800: }