Annotation of mandoc/man_macro.c, Revision 1.147
1.147 ! schwarze 1: /* $Id: man_macro.c,v 1.146 2022/04/13 14:45:50 schwarze Exp $ */
1.1 kristaps 2: /*
1.146 schwarze 3: * Copyright (c) 2012-2015,2017-2020,2022 Ingo Schwarze <schwarze@openbsd.org>
1.65 schwarze 4: * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
1.79 schwarze 5: * Copyright (c) 2013 Franco Fichtner <franco@lastsummer.de>
1.1 kristaps 6: *
7: * Permission to use, copy, modify, and distribute this software for any
1.14 kristaps 8: * purpose with or without fee is hereby granted, provided that the above
9: * copyright notice and this permission notice appear in all copies.
1.1 kristaps 10: *
1.101 schwarze 11: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
1.14 kristaps 12: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1.101 schwarze 13: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
1.14 kristaps 14: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1.1 kristaps 18: */
1.30 kristaps 19: #include "config.h"
1.88 schwarze 20:
21: #include <sys/types.h>
1.30 kristaps 22:
1.1 kristaps 23: #include <assert.h>
24: #include <ctype.h>
1.134 schwarze 25: #include <stdio.h>
1.1 kristaps 26: #include <stdlib.h>
27: #include <string.h>
28:
1.147 ! schwarze 29: #if DEBUG_MEMORY
! 30: #include "mandoc_dbg.h"
! 31: #endif
1.101 schwarze 32: #include "mandoc.h"
33: #include "roff.h"
1.58 kristaps 34: #include "man.h"
1.57 kristaps 35: #include "libmandoc.h"
1.106 schwarze 36: #include "roff_int.h"
1.1 kristaps 37: #include "libman.h"
38:
1.91 schwarze 39: static void blk_close(MACRO_PROT_ARGS);
40: static void blk_exp(MACRO_PROT_ARGS);
41: static void blk_imp(MACRO_PROT_ARGS);
42: static void in_line_eoln(MACRO_PROT_ARGS);
1.105 schwarze 43: static int man_args(struct roff_man *, int,
1.60 kristaps 44: int *, char *, char **);
1.116 schwarze 45: static void rew_scope(struct roff_man *, enum roff_tok);
1.19 kristaps 46:
1.127 schwarze 47: static const struct man_macro man_macros[MAN_MAX - MAN_TH] = {
1.133 schwarze 48: { in_line_eoln, MAN_XSCOPE }, /* TH */
49: { blk_imp, MAN_XSCOPE | MAN_BSCOPED }, /* SH */
50: { blk_imp, MAN_XSCOPE | MAN_BSCOPED }, /* SS */
51: { blk_imp, MAN_XSCOPE | MAN_BSCOPED }, /* TP */
52: { blk_imp, MAN_XSCOPE | MAN_BSCOPED }, /* TQ */
53: { blk_imp, MAN_XSCOPE }, /* LP */
54: { blk_imp, MAN_XSCOPE }, /* PP */
55: { blk_imp, MAN_XSCOPE }, /* P */
56: { blk_imp, MAN_XSCOPE }, /* IP */
57: { blk_imp, MAN_XSCOPE }, /* HP */
58: { in_line_eoln, MAN_NSCOPED | MAN_ESCOPED | MAN_JOIN }, /* SM */
59: { in_line_eoln, MAN_NSCOPED | MAN_ESCOPED | MAN_JOIN }, /* SB */
1.19 kristaps 60: { in_line_eoln, 0 }, /* BI */
61: { in_line_eoln, 0 }, /* IB */
62: { in_line_eoln, 0 }, /* BR */
63: { in_line_eoln, 0 }, /* RB */
1.133 schwarze 64: { in_line_eoln, MAN_NSCOPED | MAN_ESCOPED | MAN_JOIN }, /* R */
65: { in_line_eoln, MAN_NSCOPED | MAN_ESCOPED | MAN_JOIN }, /* B */
66: { in_line_eoln, MAN_NSCOPED | MAN_ESCOPED | MAN_JOIN }, /* I */
1.19 kristaps 67: { in_line_eoln, 0 }, /* IR */
68: { in_line_eoln, 0 }, /* RI */
1.133 schwarze 69: { blk_close, MAN_XSCOPE }, /* RE */
70: { blk_exp, MAN_XSCOPE }, /* RS */
1.23 kristaps 71: { in_line_eoln, 0 }, /* DT */
1.28 kristaps 72: { in_line_eoln, 0 }, /* UC */
1.100 schwarze 73: { in_line_eoln, MAN_NSCOPED }, /* PD */
1.45 joerg 74: { in_line_eoln, 0 }, /* AT */
1.122 schwarze 75: { in_line_eoln, MAN_NSCOPED }, /* in */
1.133 schwarze 76: { blk_imp, MAN_XSCOPE }, /* SY */
77: { blk_close, MAN_XSCOPE }, /* YS */
1.71 kristaps 78: { in_line_eoln, 0 }, /* OP */
1.133 schwarze 79: { in_line_eoln, MAN_XSCOPE }, /* EX */
80: { in_line_eoln, MAN_XSCOPE }, /* EE */
81: { blk_exp, MAN_XSCOPE }, /* UR */
82: { blk_close, MAN_XSCOPE }, /* UE */
83: { blk_exp, MAN_XSCOPE }, /* MT */
84: { blk_close, MAN_XSCOPE }, /* ME */
1.19 kristaps 85: };
1.1 kristaps 86:
87:
1.127 schwarze 88: const struct man_macro *
89: man_macro(enum roff_tok tok)
90: {
91: assert(tok >= MAN_TH && tok <= MAN_MAX);
92: return man_macros + (tok - MAN_TH);
93: }
94:
1.91 schwarze 95: void
1.105 schwarze 96: man_unscope(struct roff_man *man, const struct roff_node *to)
1.1 kristaps 97: {
1.102 schwarze 98: struct roff_node *n;
1.1 kristaps 99:
1.83 schwarze 100: to = to->parent;
101: n = man->last;
102: while (n != to) {
1.87 schwarze 103:
104: /* Reached the end of the document? */
105:
1.115 schwarze 106: if (to == NULL && ! (n->flags & NODE_VALID)) {
1.87 schwarze 107: if (man->flags & (MAN_BLINE | MAN_ELINE) &&
1.133 schwarze 108: man_macro(n->tok)->flags &
109: (MAN_BSCOPED | MAN_NSCOPED)) {
1.135 schwarze 110: mandoc_msg(MANDOCERR_BLK_LINE,
111: n->line, n->pos,
1.116 schwarze 112: "EOF breaks %s", roff_name[n->tok]);
1.145 schwarze 113: if (man->flags & MAN_ELINE) {
1.146 schwarze 114: if (n->parent->type == ROFFT_ROOT ||
115: (man_macro(n->parent->tok)->flags &
1.145 schwarze 116: MAN_ESCOPED) == 0)
117: man->flags &= ~MAN_ELINE;
118: } else {
1.101 schwarze 119: assert(n->type == ROFFT_HEAD);
1.87 schwarze 120: n = n->parent;
121: man->flags &= ~MAN_BLINE;
122: }
123: man->last = n;
124: n = n->parent;
1.106 schwarze 125: roff_node_delete(man, man->last);
1.87 schwarze 126: continue;
127: }
1.101 schwarze 128: if (n->type == ROFFT_BLOCK &&
1.127 schwarze 129: man_macro(n->tok)->fp == blk_exp)
1.87 schwarze 130: mandoc_msg(MANDOCERR_BLK_NOEND,
1.135 schwarze 131: n->line, n->pos, "%s",
1.116 schwarze 132: roff_name[n->tok]);
1.87 schwarze 133: }
134:
1.55 kristaps 135: /*
1.83 schwarze 136: * We might delete the man->last node
137: * in the post-validation phase.
138: * Save a pointer to the parent such that
139: * we know where to continue the iteration.
1.55 kristaps 140: */
1.89 schwarze 141:
1.83 schwarze 142: man->last = n;
143: n = n->parent;
1.115 schwarze 144: man->last->flags |= NODE_VALID;
1.19 kristaps 145: }
1.89 schwarze 146:
147: /*
148: * If we ended up at the parent of the node we were
149: * supposed to rewind to, that means the target node
150: * got deleted, so add the next node we parse as a child
151: * of the parent instead of as a sibling of the target.
152: */
153:
154: man->next = (man->last == to) ?
1.105 schwarze 155: ROFF_NEXT_CHILD : ROFF_NEXT_SIBLING;
1.19 kristaps 156: }
1.1 kristaps 157:
1.19 kristaps 158: /*
159: * Rewinding entails ascending the parse tree until a coherent point,
160: * for example, the `SH' macro will close out any intervening `SS'
161: * scopes. When a scope is closed, it must be validated and actioned.
162: */
1.91 schwarze 163: static void
1.116 schwarze 164: rew_scope(struct roff_man *man, enum roff_tok tok)
1.19 kristaps 165: {
1.102 schwarze 166: struct roff_node *n;
1.7 kristaps 167:
1.104 schwarze 168: /* Preserve empty paragraphs before RS. */
169:
170: n = man->last;
1.114 schwarze 171: if (tok == MAN_RS && n->child == NULL &&
1.104 schwarze 172: (n->tok == MAN_P || n->tok == MAN_PP || n->tok == MAN_LP))
173: return;
174:
175: for (;;) {
176: if (n->type == ROFFT_ROOT)
177: return;
1.115 schwarze 178: if (n->flags & NODE_VALID) {
1.104 schwarze 179: n = n->parent;
180: continue;
181: }
182: if (n->type != ROFFT_BLOCK) {
183: if (n->parent->type == ROFFT_ROOT) {
184: man_unscope(man, n);
185: return;
186: } else {
187: n = n->parent;
188: continue;
189: }
190: }
191: if (tok != MAN_SH && (n->tok == MAN_SH ||
192: (tok != MAN_SS && (n->tok == MAN_SS ||
1.127 schwarze 193: man_macro(n->tok)->fp == blk_exp))))
1.91 schwarze 194: return;
1.104 schwarze 195: man_unscope(man, n);
196: n = man->last;
1.6 kristaps 197: }
1.19 kristaps 198: }
199:
1.6 kristaps 200:
1.36 kristaps 201: /*
202: * Close out a generic explicit macro.
203: */
1.91 schwarze 204: void
1.21 kristaps 205: blk_close(MACRO_PROT_ARGS)
206: {
1.124 schwarze 207: enum roff_tok ctok, ntok;
1.102 schwarze 208: const struct roff_node *nn;
1.137 schwarze 209: char *p, *ep;
210: int cline, cpos, la, nrew, target;
1.21 kristaps 211:
1.93 schwarze 212: nrew = 1;
1.21 kristaps 213: switch (tok) {
1.82 schwarze 214: case MAN_RE:
1.21 kristaps 215: ntok = MAN_RS;
1.137 schwarze 216: la = *pos;
1.93 schwarze 217: if ( ! man_args(man, line, pos, buf, &p))
218: break;
219: for (nn = man->last->parent; nn; nn = nn->parent)
1.101 schwarze 220: if (nn->tok == ntok && nn->type == ROFFT_BLOCK)
1.93 schwarze 221: nrew++;
1.137 schwarze 222: target = strtol(p, &ep, 10);
223: if (*ep != '\0')
1.135 schwarze 224: mandoc_msg(MANDOCERR_ARG_EXCESS, line,
1.137 schwarze 225: la + (buf[la] == '"') + (int)(ep - p),
226: "RE ... %s", ep);
227: free(p);
1.93 schwarze 228: if (target == 0)
229: target = 1;
230: nrew -= target;
231: if (nrew < 1) {
1.135 schwarze 232: mandoc_msg(MANDOCERR_RE_NOTOPEN,
1.93 schwarze 233: line, ppos, "RE %d", target);
234: return;
235: }
1.128 schwarze 236: break;
237: case MAN_YS:
238: ntok = MAN_SY;
1.76 schwarze 239: break;
1.82 schwarze 240: case MAN_UE:
1.76 schwarze 241: ntok = MAN_UR;
1.123 schwarze 242: break;
243: case MAN_ME:
244: ntok = MAN_MT;
1.21 kristaps 245: break;
246: default:
247: abort();
248: }
249:
1.75 schwarze 250: for (nn = man->last->parent; nn; nn = nn->parent)
1.101 schwarze 251: if (nn->tok == ntok && nn->type == ROFFT_BLOCK && ! --nrew)
1.21 kristaps 252: break;
253:
1.91 schwarze 254: if (nn == NULL) {
1.135 schwarze 255: mandoc_msg(MANDOCERR_BLK_NOTOPEN,
256: line, ppos, "%s", roff_name[tok]);
1.104 schwarze 257: rew_scope(man, MAN_PP);
1.131 schwarze 258: if (tok == MAN_RE) {
259: roff_elem_alloc(man, line, ppos, ROFF_br);
260: man->last->flags |= NODE_LINE |
261: NODE_VALID | NODE_ENDED;
262: man->next = ROFF_NEXT_SIBLING;
263: }
1.124 schwarze 264: return;
265: }
266:
267: cline = man->last->line;
268: cpos = man->last->pos;
269: ctok = man->last->tok;
270: man_unscope(man, nn);
271:
272: if (tok == MAN_RE && nn->head->aux > 0)
273: roff_setreg(man->roff, "an-margin", nn->head->aux, '-');
274:
275: /* Trailing text. */
276:
277: if (buf[*pos] != '\0') {
278: roff_word_alloc(man, line, ppos, buf + *pos);
279: man->last->flags |= NODE_DELIMC;
1.130 schwarze 280: if (mandoc_eos(man->last->string, strlen(man->last->string)))
281: man->last->flags |= NODE_EOS;
1.124 schwarze 282: }
283:
284: /* Move a trailing paragraph behind the block. */
285:
286: if (ctok == MAN_LP || ctok == MAN_PP || ctok == MAN_P) {
287: *pos = strlen(buf);
1.125 schwarze 288: blk_imp(man, ctok, cline, cpos, pos, buf);
1.129 schwarze 289: }
290:
291: /* Synopsis blocks need an explicit end marker for spacing. */
292:
293: if (tok == MAN_YS && man->last == nn) {
294: roff_elem_alloc(man, line, ppos, tok);
295: man_unscope(man, man->last);
1.95 schwarze 296: }
1.21 kristaps 297: }
298:
1.91 schwarze 299: void
1.35 kristaps 300: blk_exp(MACRO_PROT_ARGS)
301: {
1.102 schwarze 302: struct roff_node *head;
1.98 schwarze 303: char *p;
1.59 kristaps 304: int la;
1.35 kristaps 305:
1.144 schwarze 306: if (tok == MAN_RS) {
1.132 schwarze 307: rew_scope(man, tok);
1.144 schwarze 308: man->flags |= ROFF_NONOFILL;
309: }
1.108 schwarze 310: roff_block_alloc(man, line, ppos, tok);
1.106 schwarze 311: head = roff_head_alloc(man, line, ppos, tok);
1.35 kristaps 312:
1.98 schwarze 313: la = *pos;
1.121 schwarze 314: if (man_args(man, line, pos, buf, &p)) {
1.107 schwarze 315: roff_word_alloc(man, line, la, p);
1.121 schwarze 316: if (tok == MAN_RS) {
317: if (roff_getreg(man->roff, "an-margin") == 0)
318: roff_setreg(man->roff, "an-margin",
319: 7 * 24, '=');
320: if ((head->aux = strtod(p, NULL) * 24.0) > 0)
321: roff_setreg(man->roff, "an-margin",
322: head->aux, '+');
323: }
1.137 schwarze 324: free(p);
1.121 schwarze 325: }
1.35 kristaps 326:
1.98 schwarze 327: if (buf[*pos] != '\0')
1.135 schwarze 328: mandoc_msg(MANDOCERR_ARG_EXCESS, line, *pos,
329: "%s ... %s", roff_name[tok], buf + *pos);
1.73 schwarze 330:
1.98 schwarze 331: man_unscope(man, head);
1.106 schwarze 332: roff_body_alloc(man, line, ppos, tok);
1.144 schwarze 333: man->flags &= ~ROFF_NONOFILL;
1.35 kristaps 334: }
335:
1.19 kristaps 336: /*
1.101 schwarze 337: * Parse an implicit-block macro. These contain a ROFFT_HEAD and a
338: * ROFFT_BODY contained within a ROFFT_BLOCK. Rules for closing out other
1.19 kristaps 339: * scopes, such as `SH' closing out an `SS', are defined in the rew
340: * routines.
341: */
1.91 schwarze 342: void
1.19 kristaps 343: blk_imp(MACRO_PROT_ARGS)
344: {
1.59 kristaps 345: int la;
1.19 kristaps 346: char *p;
1.102 schwarze 347: struct roff_node *n;
1.19 kristaps 348:
1.104 schwarze 349: rew_scope(man, tok);
1.143 schwarze 350: man->flags |= ROFF_NONOFILL;
1.140 schwarze 351: if (tok == MAN_SH || tok == MAN_SS)
352: man->flags &= ~ROFF_NOFILL;
353: roff_block_alloc(man, line, ppos, tok);
1.106 schwarze 354: n = roff_head_alloc(man, line, ppos, tok);
1.25 kristaps 355:
1.19 kristaps 356: /* Add line arguments. */
1.3 kristaps 357:
1.19 kristaps 358: for (;;) {
359: la = *pos;
1.75 schwarze 360: if ( ! man_args(man, line, pos, buf, &p))
1.19 kristaps 361: break;
1.107 schwarze 362: roff_word_alloc(man, line, la, p);
1.137 schwarze 363: free(p);
1.6 kristaps 364: }
365:
1.99 schwarze 366: /*
367: * For macros having optional next-line scope,
368: * keep the head open if there were no arguments.
1.133 schwarze 369: * For `TP' and `TQ', always keep the head open.
1.99 schwarze 370: */
1.19 kristaps 371:
1.133 schwarze 372: if (man_macro(tok)->flags & MAN_BSCOPED &&
1.126 schwarze 373: (tok == MAN_TP || tok == MAN_TQ || n == man->last)) {
1.99 schwarze 374: man->flags |= MAN_BLINE;
375: return;
1.27 kristaps 376: }
1.99 schwarze 377:
378: /* Close out the head and open the body. */
379:
1.103 schwarze 380: man_unscope(man, n);
1.106 schwarze 381: roff_body_alloc(man, line, ppos, tok);
1.143 schwarze 382: man->flags &= ~ROFF_NONOFILL;
1.4 kristaps 383: }
384:
1.91 schwarze 385: void
1.19 kristaps 386: in_line_eoln(MACRO_PROT_ARGS)
1.3 kristaps 387: {
1.59 kristaps 388: int la;
1.19 kristaps 389: char *p;
1.102 schwarze 390: struct roff_node *n;
1.3 kristaps 391:
1.108 schwarze 392: roff_elem_alloc(man, line, ppos, tok);
1.75 schwarze 393: n = man->last;
1.141 schwarze 394:
395: if (tok == MAN_EX)
396: man->flags |= ROFF_NOFILL;
397: else if (tok == MAN_EE)
398: man->flags &= ~ROFF_NOFILL;
1.147 ! schwarze 399:
! 400: #if DEBUG_MEMORY
! 401: if (tok == MAN_TH)
! 402: mandoc_dbg_name(buf);
! 403: #endif
1.3 kristaps 404:
1.19 kristaps 405: for (;;) {
1.120 schwarze 406: if (buf[*pos] != '\0' && man->last != n && tok == MAN_PD) {
1.135 schwarze 407: mandoc_msg(MANDOCERR_ARG_EXCESS, line, *pos,
408: "%s ... %s", roff_name[tok], buf + *pos);
1.96 schwarze 409: break;
410: }
1.19 kristaps 411: la = *pos;
1.75 schwarze 412: if ( ! man_args(man, line, pos, buf, &p))
1.19 kristaps 413: break;
1.127 schwarze 414: if (man_macro(tok)->flags & MAN_JOIN &&
1.101 schwarze 415: man->last->type == ROFFT_TEXT)
1.107 schwarze 416: roff_word_append(man, p);
1.91 schwarze 417: else
1.107 schwarze 418: roff_word_alloc(man, line, la, p);
1.137 schwarze 419: free(p);
1.19 kristaps 420: }
1.78 schwarze 421:
422: /*
1.115 schwarze 423: * Append NODE_EOS in case the last snipped argument
1.78 schwarze 424: * ends with a dot, e.g. `.IR syslog (3).'
425: */
426:
427: if (n != man->last &&
1.80 schwarze 428: mandoc_eos(man->last->string, strlen(man->last->string)))
1.115 schwarze 429: man->last->flags |= NODE_EOS;
1.3 kristaps 430:
1.31 kristaps 431: /*
1.133 schwarze 432: * If no arguments are specified and this is MAN_ESCOPED (i.e.,
1.31 kristaps 433: * next-line scoped), then set our mode to indicate that we're
434: * waiting for terms to load into our context.
435: */
436:
1.133 schwarze 437: if (n == man->last && man_macro(tok)->flags & MAN_ESCOPED) {
1.75 schwarze 438: man->flags |= MAN_ELINE;
1.91 schwarze 439: return;
1.31 kristaps 440: }
1.64 kristaps 441:
1.101 schwarze 442: assert(man->last->type != ROFFT_ROOT);
1.105 schwarze 443: man->next = ROFF_NEXT_SIBLING;
1.82 schwarze 444:
1.113 schwarze 445: /* Rewind our element scope. */
1.3 kristaps 446:
1.75 schwarze 447: for ( ; man->last; man->last = man->last->parent) {
1.142 schwarze 448: man->last->flags |= NODE_VALID;
1.75 schwarze 449: if (man->last == n)
1.19 kristaps 450: break;
451: }
1.133 schwarze 452:
453: /* Rewind next-line scoped ancestors, if any. */
454:
455: if (man_macro(tok)->flags & MAN_ESCOPED)
456: man_descope(man, line, ppos, NULL);
1.19 kristaps 457: }
1.3 kristaps 458:
1.91 schwarze 459: void
1.109 schwarze 460: man_endparse(struct roff_man *man)
1.19 kristaps 461: {
1.138 schwarze 462: man_unscope(man, man->meta.first);
1.19 kristaps 463: }
1.3 kristaps 464:
1.60 kristaps 465: static int
1.105 schwarze 466: man_args(struct roff_man *man, int line, int *pos, char *buf, char **v)
1.60 kristaps 467: {
468: char *start;
469:
470: assert(*pos);
471: *v = start = buf + *pos;
472: assert(' ' != *start);
473:
474: if ('\0' == *start)
1.112 schwarze 475: return 0;
1.60 kristaps 476:
1.137 schwarze 477: *v = roff_getarg(man->roff, v, line, pos);
1.112 schwarze 478: return 1;
1.60 kristaps 479: }
CVSweb