Unleashed v1.4
[unleashed.git] / bin / mandoc / man_macro.c
blobd195576dee2806528b21830f3df99165cbeca6c3
1 /* $Id: man_macro.c,v 1.144 2019/01/05 18:59:46 schwarze Exp $ */
2 /*
3 * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2012-2015, 2017-2019 Ingo Schwarze <schwarze@openbsd.org>
5 * Copyright (c) 2013 Franco Fichtner <franco@lastsummer.de>
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
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.
19 #include "config.h"
21 #include <sys/types.h>
23 #include <assert.h>
24 #include <ctype.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
29 #include "mandoc.h"
30 #include "roff.h"
31 #include "man.h"
32 #include "libmandoc.h"
33 #include "roff_int.h"
34 #include "libman.h"
36 static void blk_close(MACRO_PROT_ARGS);
37 static void blk_exp(MACRO_PROT_ARGS);
38 static void blk_imp(MACRO_PROT_ARGS);
39 static void in_line_eoln(MACRO_PROT_ARGS);
40 static int man_args(struct roff_man *, int,
41 int *, char *, char **);
42 static void rew_scope(struct roff_man *, enum roff_tok);
44 static const struct man_macro man_macros[MAN_MAX - MAN_TH] = {
45 { in_line_eoln, MAN_XSCOPE }, /* TH */
46 { blk_imp, MAN_XSCOPE | MAN_BSCOPED }, /* SH */
47 { blk_imp, MAN_XSCOPE | MAN_BSCOPED }, /* SS */
48 { blk_imp, MAN_XSCOPE | MAN_BSCOPED }, /* TP */
49 { blk_imp, MAN_XSCOPE | MAN_BSCOPED }, /* TQ */
50 { blk_imp, MAN_XSCOPE }, /* LP */
51 { blk_imp, MAN_XSCOPE }, /* PP */
52 { blk_imp, MAN_XSCOPE }, /* P */
53 { blk_imp, MAN_XSCOPE }, /* IP */
54 { blk_imp, MAN_XSCOPE }, /* HP */
55 { in_line_eoln, MAN_NSCOPED | MAN_ESCOPED | MAN_JOIN }, /* SM */
56 { in_line_eoln, MAN_NSCOPED | MAN_ESCOPED | MAN_JOIN }, /* SB */
57 { in_line_eoln, 0 }, /* BI */
58 { in_line_eoln, 0 }, /* IB */
59 { in_line_eoln, 0 }, /* BR */
60 { in_line_eoln, 0 }, /* RB */
61 { in_line_eoln, MAN_NSCOPED | MAN_ESCOPED | MAN_JOIN }, /* R */
62 { in_line_eoln, MAN_NSCOPED | MAN_ESCOPED | MAN_JOIN }, /* B */
63 { in_line_eoln, MAN_NSCOPED | MAN_ESCOPED | MAN_JOIN }, /* I */
64 { in_line_eoln, 0 }, /* IR */
65 { in_line_eoln, 0 }, /* RI */
66 { blk_close, MAN_XSCOPE }, /* RE */
67 { blk_exp, MAN_XSCOPE }, /* RS */
68 { in_line_eoln, 0 }, /* DT */
69 { in_line_eoln, 0 }, /* UC */
70 { in_line_eoln, MAN_NSCOPED }, /* PD */
71 { in_line_eoln, 0 }, /* AT */
72 { in_line_eoln, MAN_NSCOPED }, /* in */
73 { blk_imp, MAN_XSCOPE }, /* SY */
74 { blk_close, MAN_XSCOPE }, /* YS */
75 { in_line_eoln, 0 }, /* OP */
76 { in_line_eoln, MAN_XSCOPE }, /* EX */
77 { in_line_eoln, MAN_XSCOPE }, /* EE */
78 { blk_exp, MAN_XSCOPE }, /* UR */
79 { blk_close, MAN_XSCOPE }, /* UE */
80 { blk_exp, MAN_XSCOPE }, /* MT */
81 { blk_close, MAN_XSCOPE }, /* ME */
85 const struct man_macro *
86 man_macro(enum roff_tok tok)
88 assert(tok >= MAN_TH && tok <= MAN_MAX);
89 return man_macros + (tok - MAN_TH);
92 void
93 man_unscope(struct roff_man *man, const struct roff_node *to)
95 struct roff_node *n;
97 to = to->parent;
98 n = man->last;
99 while (n != to) {
101 /* Reached the end of the document? */
103 if (to == NULL && ! (n->flags & NODE_VALID)) {
104 if (man->flags & (MAN_BLINE | MAN_ELINE) &&
105 man_macro(n->tok)->flags &
106 (MAN_BSCOPED | MAN_NSCOPED)) {
107 mandoc_msg(MANDOCERR_BLK_LINE,
108 n->line, n->pos,
109 "EOF breaks %s", roff_name[n->tok]);
110 if (man->flags & MAN_ELINE)
111 man->flags &= ~MAN_ELINE;
112 else {
113 assert(n->type == ROFFT_HEAD);
114 n = n->parent;
115 man->flags &= ~MAN_BLINE;
117 man->last = n;
118 n = n->parent;
119 roff_node_delete(man, man->last);
120 continue;
122 if (n->type == ROFFT_BLOCK &&
123 man_macro(n->tok)->fp == blk_exp)
124 mandoc_msg(MANDOCERR_BLK_NOEND,
125 n->line, n->pos, "%s",
126 roff_name[n->tok]);
130 * We might delete the man->last node
131 * in the post-validation phase.
132 * Save a pointer to the parent such that
133 * we know where to continue the iteration.
136 man->last = n;
137 n = n->parent;
138 man->last->flags |= NODE_VALID;
142 * If we ended up at the parent of the node we were
143 * supposed to rewind to, that means the target node
144 * got deleted, so add the next node we parse as a child
145 * of the parent instead of as a sibling of the target.
148 man->next = (man->last == to) ?
149 ROFF_NEXT_CHILD : ROFF_NEXT_SIBLING;
153 * Rewinding entails ascending the parse tree until a coherent point,
154 * for example, the `SH' macro will close out any intervening `SS'
155 * scopes. When a scope is closed, it must be validated and actioned.
157 static void
158 rew_scope(struct roff_man *man, enum roff_tok tok)
160 struct roff_node *n;
162 /* Preserve empty paragraphs before RS. */
164 n = man->last;
165 if (tok == MAN_RS && n->child == NULL &&
166 (n->tok == MAN_P || n->tok == MAN_PP || n->tok == MAN_LP))
167 return;
169 for (;;) {
170 if (n->type == ROFFT_ROOT)
171 return;
172 if (n->flags & NODE_VALID) {
173 n = n->parent;
174 continue;
176 if (n->type != ROFFT_BLOCK) {
177 if (n->parent->type == ROFFT_ROOT) {
178 man_unscope(man, n);
179 return;
180 } else {
181 n = n->parent;
182 continue;
185 if (tok != MAN_SH && (n->tok == MAN_SH ||
186 (tok != MAN_SS && (n->tok == MAN_SS ||
187 man_macro(n->tok)->fp == blk_exp))))
188 return;
189 man_unscope(man, n);
190 n = man->last;
196 * Close out a generic explicit macro.
198 void
199 blk_close(MACRO_PROT_ARGS)
201 enum roff_tok ctok, ntok;
202 const struct roff_node *nn;
203 char *p, *ep;
204 int cline, cpos, la, nrew, target;
206 nrew = 1;
207 switch (tok) {
208 case MAN_RE:
209 ntok = MAN_RS;
210 la = *pos;
211 if ( ! man_args(man, line, pos, buf, &p))
212 break;
213 for (nn = man->last->parent; nn; nn = nn->parent)
214 if (nn->tok == ntok && nn->type == ROFFT_BLOCK)
215 nrew++;
216 target = strtol(p, &ep, 10);
217 if (*ep != '\0')
218 mandoc_msg(MANDOCERR_ARG_EXCESS, line,
219 la + (buf[la] == '"') + (int)(ep - p),
220 "RE ... %s", ep);
221 free(p);
222 if (target == 0)
223 target = 1;
224 nrew -= target;
225 if (nrew < 1) {
226 mandoc_msg(MANDOCERR_RE_NOTOPEN,
227 line, ppos, "RE %d", target);
228 return;
230 break;
231 case MAN_YS:
232 ntok = MAN_SY;
233 break;
234 case MAN_UE:
235 ntok = MAN_UR;
236 break;
237 case MAN_ME:
238 ntok = MAN_MT;
239 break;
240 default:
241 abort();
244 for (nn = man->last->parent; nn; nn = nn->parent)
245 if (nn->tok == ntok && nn->type == ROFFT_BLOCK && ! --nrew)
246 break;
248 if (nn == NULL) {
249 mandoc_msg(MANDOCERR_BLK_NOTOPEN,
250 line, ppos, "%s", roff_name[tok]);
251 rew_scope(man, MAN_PP);
252 if (tok == MAN_RE) {
253 roff_elem_alloc(man, line, ppos, ROFF_br);
254 man->last->flags |= NODE_LINE |
255 NODE_VALID | NODE_ENDED;
256 man->next = ROFF_NEXT_SIBLING;
258 return;
261 cline = man->last->line;
262 cpos = man->last->pos;
263 ctok = man->last->tok;
264 man_unscope(man, nn);
266 if (tok == MAN_RE && nn->head->aux > 0)
267 roff_setreg(man->roff, "an-margin", nn->head->aux, '-');
269 /* Trailing text. */
271 if (buf[*pos] != '\0') {
272 roff_word_alloc(man, line, ppos, buf + *pos);
273 man->last->flags |= NODE_DELIMC;
274 if (mandoc_eos(man->last->string, strlen(man->last->string)))
275 man->last->flags |= NODE_EOS;
278 /* Move a trailing paragraph behind the block. */
280 if (ctok == MAN_LP || ctok == MAN_PP || ctok == MAN_P) {
281 *pos = strlen(buf);
282 blk_imp(man, ctok, cline, cpos, pos, buf);
285 /* Synopsis blocks need an explicit end marker for spacing. */
287 if (tok == MAN_YS && man->last == nn) {
288 roff_elem_alloc(man, line, ppos, tok);
289 man_unscope(man, man->last);
293 void
294 blk_exp(MACRO_PROT_ARGS)
296 struct roff_node *head;
297 char *p;
298 int la;
300 if (tok == MAN_RS) {
301 rew_scope(man, tok);
302 man->flags |= ROFF_NONOFILL;
304 roff_block_alloc(man, line, ppos, tok);
305 head = roff_head_alloc(man, line, ppos, tok);
307 la = *pos;
308 if (man_args(man, line, pos, buf, &p)) {
309 roff_word_alloc(man, line, la, p);
310 if (tok == MAN_RS) {
311 if (roff_getreg(man->roff, "an-margin") == 0)
312 roff_setreg(man->roff, "an-margin",
313 7 * 24, '=');
314 if ((head->aux = strtod(p, NULL) * 24.0) > 0)
315 roff_setreg(man->roff, "an-margin",
316 head->aux, '+');
318 free(p);
321 if (buf[*pos] != '\0')
322 mandoc_msg(MANDOCERR_ARG_EXCESS, line, *pos,
323 "%s ... %s", roff_name[tok], buf + *pos);
325 man_unscope(man, head);
326 roff_body_alloc(man, line, ppos, tok);
327 man->flags &= ~ROFF_NONOFILL;
331 * Parse an implicit-block macro. These contain a ROFFT_HEAD and a
332 * ROFFT_BODY contained within a ROFFT_BLOCK. Rules for closing out other
333 * scopes, such as `SH' closing out an `SS', are defined in the rew
334 * routines.
336 void
337 blk_imp(MACRO_PROT_ARGS)
339 int la;
340 char *p;
341 struct roff_node *n;
343 rew_scope(man, tok);
344 man->flags |= ROFF_NONOFILL;
345 if (tok == MAN_SH || tok == MAN_SS)
346 man->flags &= ~ROFF_NOFILL;
347 roff_block_alloc(man, line, ppos, tok);
348 n = roff_head_alloc(man, line, ppos, tok);
350 /* Add line arguments. */
352 for (;;) {
353 la = *pos;
354 if ( ! man_args(man, line, pos, buf, &p))
355 break;
356 roff_word_alloc(man, line, la, p);
357 free(p);
361 * For macros having optional next-line scope,
362 * keep the head open if there were no arguments.
363 * For `TP' and `TQ', always keep the head open.
366 if (man_macro(tok)->flags & MAN_BSCOPED &&
367 (tok == MAN_TP || tok == MAN_TQ || n == man->last)) {
368 man->flags |= MAN_BLINE;
369 return;
372 /* Close out the head and open the body. */
374 man_unscope(man, n);
375 roff_body_alloc(man, line, ppos, tok);
376 man->flags &= ~ROFF_NONOFILL;
379 void
380 in_line_eoln(MACRO_PROT_ARGS)
382 int la;
383 char *p;
384 struct roff_node *n;
386 roff_elem_alloc(man, line, ppos, tok);
387 n = man->last;
389 if (tok == MAN_EX)
390 man->flags |= ROFF_NOFILL;
391 else if (tok == MAN_EE)
392 man->flags &= ~ROFF_NOFILL;
394 for (;;) {
395 if (buf[*pos] != '\0' && man->last != n && tok == MAN_PD) {
396 mandoc_msg(MANDOCERR_ARG_EXCESS, line, *pos,
397 "%s ... %s", roff_name[tok], buf + *pos);
398 break;
400 la = *pos;
401 if ( ! man_args(man, line, pos, buf, &p))
402 break;
403 if (man_macro(tok)->flags & MAN_JOIN &&
404 man->last->type == ROFFT_TEXT)
405 roff_word_append(man, p);
406 else
407 roff_word_alloc(man, line, la, p);
408 free(p);
412 * Append NODE_EOS in case the last snipped argument
413 * ends with a dot, e.g. `.IR syslog (3).'
416 if (n != man->last &&
417 mandoc_eos(man->last->string, strlen(man->last->string)))
418 man->last->flags |= NODE_EOS;
421 * If no arguments are specified and this is MAN_ESCOPED (i.e.,
422 * next-line scoped), then set our mode to indicate that we're
423 * waiting for terms to load into our context.
426 if (n == man->last && man_macro(tok)->flags & MAN_ESCOPED) {
427 man->flags |= MAN_ELINE;
428 return;
431 assert(man->last->type != ROFFT_ROOT);
432 man->next = ROFF_NEXT_SIBLING;
434 /* Rewind our element scope. */
436 for ( ; man->last; man->last = man->last->parent) {
437 man->last->flags |= NODE_VALID;
438 if (man->last == n)
439 break;
442 /* Rewind next-line scoped ancestors, if any. */
444 if (man_macro(tok)->flags & MAN_ESCOPED)
445 man_descope(man, line, ppos, NULL);
448 void
449 man_endparse(struct roff_man *man)
451 man_unscope(man, man->meta.first);
454 static int
455 man_args(struct roff_man *man, int line, int *pos, char *buf, char **v)
457 char *start;
459 assert(*pos);
460 *v = start = buf + *pos;
461 assert(' ' != *start);
463 if ('\0' == *start)
464 return 0;
466 *v = roff_getarg(man->roff, v, line, pos);
467 return 1;