man_macro.c revision 1.3 1 1.3 christos /* Id: man_macro.c,v 1.114 2016/01/08 17:48:09 schwarze Exp */
2 1.1 joerg /*
3 1.2 christos * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps (at) bsd.lv>
4 1.2 christos * Copyright (c) 2012, 2013, 2014, 2015 Ingo Schwarze <schwarze (at) openbsd.org>
5 1.2 christos * Copyright (c) 2013 Franco Fichtner <franco (at) lastsummer.de>
6 1.1 joerg *
7 1.1 joerg * Permission to use, copy, modify, and distribute this software for any
8 1.1 joerg * purpose with or without fee is hereby granted, provided that the above
9 1.1 joerg * copyright notice and this permission notice appear in all copies.
10 1.1 joerg *
11 1.3 christos * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
12 1.1 joerg * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 1.3 christos * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
14 1.1 joerg * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 1.1 joerg * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 1.1 joerg * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 1.1 joerg * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 1.1 joerg */
19 1.2 christos #include "config.h"
20 1.2 christos
21 1.2 christos #include <sys/types.h>
22 1.2 christos
23 1.1 joerg #include <assert.h>
24 1.1 joerg #include <ctype.h>
25 1.1 joerg #include <stdlib.h>
26 1.1 joerg #include <string.h>
27 1.1 joerg
28 1.3 christos #include "mandoc.h"
29 1.3 christos #include "roff.h"
30 1.2 christos #include "man.h"
31 1.2 christos #include "libmandoc.h"
32 1.3 christos #include "roff_int.h"
33 1.1 joerg #include "libman.h"
34 1.1 joerg
35 1.2 christos static void blk_close(MACRO_PROT_ARGS);
36 1.2 christos static void blk_exp(MACRO_PROT_ARGS);
37 1.2 christos static void blk_imp(MACRO_PROT_ARGS);
38 1.2 christos static void in_line_eoln(MACRO_PROT_ARGS);
39 1.3 christos static int man_args(struct roff_man *, int,
40 1.2 christos int *, char *, char **);
41 1.3 christos static void rew_scope(struct roff_man *, int);
42 1.1 joerg
43 1.1 joerg const struct man_macro __man_macros[MAN_MAX] = {
44 1.2 christos { in_line_eoln, MAN_NSCOPED }, /* br */
45 1.2 christos { in_line_eoln, MAN_BSCOPE }, /* TH */
46 1.2 christos { blk_imp, MAN_BSCOPE | MAN_SCOPED }, /* SH */
47 1.2 christos { blk_imp, MAN_BSCOPE | MAN_SCOPED }, /* SS */
48 1.3 christos { blk_imp, MAN_BSCOPE | MAN_SCOPED }, /* TP */
49 1.2 christos { blk_imp, MAN_BSCOPE }, /* LP */
50 1.2 christos { blk_imp, MAN_BSCOPE }, /* PP */
51 1.2 christos { blk_imp, MAN_BSCOPE }, /* P */
52 1.2 christos { blk_imp, MAN_BSCOPE }, /* IP */
53 1.2 christos { blk_imp, MAN_BSCOPE }, /* HP */
54 1.2 christos { in_line_eoln, MAN_SCOPED | MAN_JOIN }, /* SM */
55 1.2 christos { in_line_eoln, MAN_SCOPED | MAN_JOIN }, /* SB */
56 1.1 joerg { in_line_eoln, 0 }, /* BI */
57 1.1 joerg { in_line_eoln, 0 }, /* IB */
58 1.1 joerg { in_line_eoln, 0 }, /* BR */
59 1.1 joerg { in_line_eoln, 0 }, /* RB */
60 1.2 christos { in_line_eoln, MAN_SCOPED | MAN_JOIN }, /* R */
61 1.2 christos { in_line_eoln, MAN_SCOPED | MAN_JOIN }, /* B */
62 1.2 christos { in_line_eoln, MAN_SCOPED | MAN_JOIN }, /* I */
63 1.1 joerg { in_line_eoln, 0 }, /* IR */
64 1.1 joerg { in_line_eoln, 0 }, /* RI */
65 1.2 christos { in_line_eoln, MAN_NSCOPED }, /* sp */
66 1.3 christos { in_line_eoln, MAN_NSCOPED }, /* nf */
67 1.3 christos { in_line_eoln, MAN_NSCOPED }, /* fi */
68 1.2 christos { blk_close, MAN_BSCOPE }, /* RE */
69 1.3 christos { blk_exp, MAN_BSCOPE }, /* RS */
70 1.1 joerg { in_line_eoln, 0 }, /* DT */
71 1.1 joerg { in_line_eoln, 0 }, /* UC */
72 1.3 christos { in_line_eoln, MAN_NSCOPED }, /* PD */
73 1.2 christos { in_line_eoln, 0 }, /* AT */
74 1.2 christos { in_line_eoln, 0 }, /* in */
75 1.2 christos { in_line_eoln, 0 }, /* ft */
76 1.2 christos { in_line_eoln, 0 }, /* OP */
77 1.2 christos { in_line_eoln, MAN_BSCOPE }, /* EX */
78 1.2 christos { in_line_eoln, MAN_BSCOPE }, /* EE */
79 1.3 christos { blk_exp, MAN_BSCOPE }, /* UR */
80 1.2 christos { blk_close, MAN_BSCOPE }, /* UE */
81 1.2 christos { in_line_eoln, 0 }, /* ll */
82 1.1 joerg };
83 1.1 joerg
84 1.1 joerg const struct man_macro * const man_macros = __man_macros;
85 1.1 joerg
86 1.1 joerg
87 1.2 christos void
88 1.3 christos man_unscope(struct roff_man *man, const struct roff_node *to)
89 1.1 joerg {
90 1.3 christos struct roff_node *n;
91 1.1 joerg
92 1.2 christos to = to->parent;
93 1.2 christos n = man->last;
94 1.2 christos while (n != to) {
95 1.2 christos
96 1.2 christos /* Reached the end of the document? */
97 1.2 christos
98 1.2 christos if (to == NULL && ! (n->flags & MAN_VALID)) {
99 1.2 christos if (man->flags & (MAN_BLINE | MAN_ELINE) &&
100 1.2 christos man_macros[n->tok].flags & MAN_SCOPED) {
101 1.2 christos mandoc_vmsg(MANDOCERR_BLK_LINE,
102 1.2 christos man->parse, n->line, n->pos,
103 1.2 christos "EOF breaks %s",
104 1.2 christos man_macronames[n->tok]);
105 1.2 christos if (man->flags & MAN_ELINE)
106 1.2 christos man->flags &= ~MAN_ELINE;
107 1.2 christos else {
108 1.3 christos assert(n->type == ROFFT_HEAD);
109 1.2 christos n = n->parent;
110 1.2 christos man->flags &= ~MAN_BLINE;
111 1.2 christos }
112 1.2 christos man->last = n;
113 1.2 christos n = n->parent;
114 1.3 christos roff_node_delete(man, man->last);
115 1.2 christos continue;
116 1.2 christos }
117 1.3 christos if (n->type == ROFFT_BLOCK &&
118 1.3 christos man_macros[n->tok].fp == blk_exp)
119 1.2 christos mandoc_msg(MANDOCERR_BLK_NOEND,
120 1.2 christos man->parse, n->line, n->pos,
121 1.2 christos man_macronames[n->tok]);
122 1.2 christos }
123 1.1 joerg
124 1.2 christos /*
125 1.2 christos * We might delete the man->last node
126 1.2 christos * in the post-validation phase.
127 1.2 christos * Save a pointer to the parent such that
128 1.2 christos * we know where to continue the iteration.
129 1.2 christos */
130 1.2 christos
131 1.2 christos man->last = n;
132 1.2 christos n = n->parent;
133 1.3 christos man->last->flags |= MAN_VALID;
134 1.1 joerg }
135 1.1 joerg
136 1.2 christos /*
137 1.2 christos * If we ended up at the parent of the node we were
138 1.2 christos * supposed to rewind to, that means the target node
139 1.2 christos * got deleted, so add the next node we parse as a child
140 1.2 christos * of the parent instead of as a sibling of the target.
141 1.2 christos */
142 1.2 christos
143 1.2 christos man->next = (man->last == to) ?
144 1.3 christos ROFF_NEXT_CHILD : ROFF_NEXT_SIBLING;
145 1.1 joerg }
146 1.1 joerg
147 1.1 joerg /*
148 1.1 joerg * Rewinding entails ascending the parse tree until a coherent point,
149 1.1 joerg * for example, the `SH' macro will close out any intervening `SS'
150 1.1 joerg * scopes. When a scope is closed, it must be validated and actioned.
151 1.1 joerg */
152 1.2 christos static void
153 1.3 christos rew_scope(struct roff_man *man, int tok)
154 1.1 joerg {
155 1.3 christos struct roff_node *n;
156 1.3 christos
157 1.3 christos /* Preserve empty paragraphs before RS. */
158 1.3 christos
159 1.3 christos n = man->last;
160 1.3 christos if (tok == MAN_RS && n->child == NULL &&
161 1.3 christos (n->tok == MAN_P || n->tok == MAN_PP || n->tok == MAN_LP))
162 1.3 christos return;
163 1.1 joerg
164 1.3 christos for (;;) {
165 1.3 christos if (n->type == ROFFT_ROOT)
166 1.3 christos return;
167 1.3 christos if (n->flags & MAN_VALID) {
168 1.3 christos n = n->parent;
169 1.3 christos continue;
170 1.3 christos }
171 1.3 christos if (n->type != ROFFT_BLOCK) {
172 1.3 christos if (n->parent->type == ROFFT_ROOT) {
173 1.3 christos man_unscope(man, n);
174 1.3 christos return;
175 1.3 christos } else {
176 1.3 christos n = n->parent;
177 1.3 christos continue;
178 1.3 christos }
179 1.3 christos }
180 1.3 christos if (tok != MAN_SH && (n->tok == MAN_SH ||
181 1.3 christos (tok != MAN_SS && (n->tok == MAN_SS ||
182 1.3 christos man_macros[n->tok].fp == blk_exp))))
183 1.2 christos return;
184 1.3 christos man_unscope(man, n);
185 1.3 christos n = man->last;
186 1.1 joerg }
187 1.1 joerg }
188 1.1 joerg
189 1.1 joerg
190 1.2 christos /*
191 1.2 christos * Close out a generic explicit macro.
192 1.2 christos */
193 1.2 christos void
194 1.1 joerg blk_close(MACRO_PROT_ARGS)
195 1.1 joerg {
196 1.3 christos int ntok;
197 1.3 christos const struct roff_node *nn;
198 1.2 christos char *p;
199 1.2 christos int nrew, target;
200 1.1 joerg
201 1.2 christos nrew = 1;
202 1.1 joerg switch (tok) {
203 1.2 christos case MAN_RE:
204 1.1 joerg ntok = MAN_RS;
205 1.2 christos if ( ! man_args(man, line, pos, buf, &p))
206 1.2 christos break;
207 1.2 christos for (nn = man->last->parent; nn; nn = nn->parent)
208 1.3 christos if (nn->tok == ntok && nn->type == ROFFT_BLOCK)
209 1.2 christos nrew++;
210 1.2 christos target = strtol(p, &p, 10);
211 1.2 christos if (*p != '\0')
212 1.2 christos mandoc_vmsg(MANDOCERR_ARG_EXCESS, man->parse,
213 1.2 christos line, p - buf, "RE ... %s", p);
214 1.2 christos if (target == 0)
215 1.2 christos target = 1;
216 1.2 christos nrew -= target;
217 1.2 christos if (nrew < 1) {
218 1.2 christos mandoc_vmsg(MANDOCERR_RE_NOTOPEN, man->parse,
219 1.2 christos line, ppos, "RE %d", target);
220 1.2 christos return;
221 1.2 christos }
222 1.2 christos break;
223 1.2 christos case MAN_UE:
224 1.2 christos ntok = MAN_UR;
225 1.1 joerg break;
226 1.1 joerg default:
227 1.1 joerg abort();
228 1.1 joerg }
229 1.1 joerg
230 1.2 christos for (nn = man->last->parent; nn; nn = nn->parent)
231 1.3 christos if (nn->tok == ntok && nn->type == ROFFT_BLOCK && ! --nrew)
232 1.1 joerg break;
233 1.1 joerg
234 1.2 christos if (nn == NULL) {
235 1.2 christos mandoc_msg(MANDOCERR_BLK_NOTOPEN, man->parse,
236 1.2 christos line, ppos, man_macronames[tok]);
237 1.3 christos rew_scope(man, MAN_PP);
238 1.2 christos } else {
239 1.2 christos line = man->last->line;
240 1.2 christos ppos = man->last->pos;
241 1.2 christos ntok = man->last->tok;
242 1.2 christos man_unscope(man, nn);
243 1.2 christos
244 1.2 christos /* Move a trailing paragraph behind the block. */
245 1.2 christos
246 1.2 christos if (ntok == MAN_LP || ntok == MAN_PP || ntok == MAN_P) {
247 1.2 christos *pos = strlen(buf);
248 1.2 christos blk_imp(man, ntok, line, ppos, pos, buf);
249 1.2 christos }
250 1.2 christos }
251 1.2 christos }
252 1.2 christos
253 1.2 christos void
254 1.2 christos blk_exp(MACRO_PROT_ARGS)
255 1.2 christos {
256 1.3 christos struct roff_node *head;
257 1.2 christos char *p;
258 1.2 christos int la;
259 1.1 joerg
260 1.3 christos rew_scope(man, tok);
261 1.3 christos roff_block_alloc(man, line, ppos, tok);
262 1.3 christos head = roff_head_alloc(man, line, ppos, tok);
263 1.2 christos
264 1.2 christos la = *pos;
265 1.2 christos if (man_args(man, line, pos, buf, &p))
266 1.3 christos roff_word_alloc(man, line, la, p);
267 1.2 christos
268 1.2 christos if (buf[*pos] != '\0')
269 1.2 christos mandoc_vmsg(MANDOCERR_ARG_EXCESS,
270 1.2 christos man->parse, line, *pos, "%s ... %s",
271 1.2 christos man_macronames[tok], buf + *pos);
272 1.2 christos
273 1.2 christos man_unscope(man, head);
274 1.3 christos roff_body_alloc(man, line, ppos, tok);
275 1.1 joerg }
276 1.1 joerg
277 1.1 joerg /*
278 1.3 christos * Parse an implicit-block macro. These contain a ROFFT_HEAD and a
279 1.3 christos * ROFFT_BODY contained within a ROFFT_BLOCK. Rules for closing out other
280 1.1 joerg * scopes, such as `SH' closing out an `SS', are defined in the rew
281 1.1 joerg * routines.
282 1.1 joerg */
283 1.2 christos void
284 1.1 joerg blk_imp(MACRO_PROT_ARGS)
285 1.1 joerg {
286 1.2 christos int la;
287 1.1 joerg char *p;
288 1.3 christos struct roff_node *n;
289 1.1 joerg
290 1.3 christos rew_scope(man, tok);
291 1.3 christos n = roff_block_alloc(man, line, ppos, tok);
292 1.3 christos if (n->tok == MAN_SH || n->tok == MAN_SS)
293 1.3 christos man->flags &= ~MAN_LITERAL;
294 1.3 christos n = roff_head_alloc(man, line, ppos, tok);
295 1.1 joerg
296 1.1 joerg /* Add line arguments. */
297 1.1 joerg
298 1.1 joerg for (;;) {
299 1.1 joerg la = *pos;
300 1.2 christos if ( ! man_args(man, line, pos, buf, &p))
301 1.1 joerg break;
302 1.3 christos roff_word_alloc(man, line, la, p);
303 1.1 joerg }
304 1.1 joerg
305 1.3 christos /*
306 1.3 christos * For macros having optional next-line scope,
307 1.3 christos * keep the head open if there were no arguments.
308 1.3 christos * For `TP', always keep the head open.
309 1.3 christos */
310 1.1 joerg
311 1.3 christos if (man_macros[tok].flags & MAN_SCOPED &&
312 1.3 christos (tok == MAN_TP || n == man->last)) {
313 1.3 christos man->flags |= MAN_BLINE;
314 1.3 christos return;
315 1.1 joerg }
316 1.3 christos
317 1.3 christos /* Close out the head and open the body. */
318 1.3 christos
319 1.3 christos man_unscope(man, n);
320 1.3 christos roff_body_alloc(man, line, ppos, tok);
321 1.1 joerg }
322 1.1 joerg
323 1.2 christos void
324 1.1 joerg in_line_eoln(MACRO_PROT_ARGS)
325 1.1 joerg {
326 1.2 christos int la;
327 1.1 joerg char *p;
328 1.3 christos struct roff_node *n;
329 1.1 joerg
330 1.3 christos roff_elem_alloc(man, line, ppos, tok);
331 1.2 christos n = man->last;
332 1.1 joerg
333 1.1 joerg for (;;) {
334 1.2 christos if (buf[*pos] != '\0' && (tok == MAN_br ||
335 1.2 christos tok == MAN_fi || tok == MAN_nf)) {
336 1.2 christos mandoc_vmsg(MANDOCERR_ARG_SKIP,
337 1.2 christos man->parse, line, *pos, "%s %s",
338 1.2 christos man_macronames[tok], buf + *pos);
339 1.2 christos break;
340 1.2 christos }
341 1.2 christos if (buf[*pos] != '\0' && man->last != n &&
342 1.2 christos (tok == MAN_PD || tok == MAN_ft || tok == MAN_sp)) {
343 1.2 christos mandoc_vmsg(MANDOCERR_ARG_EXCESS,
344 1.2 christos man->parse, line, *pos, "%s ... %s",
345 1.2 christos man_macronames[tok], buf + *pos);
346 1.2 christos break;
347 1.2 christos }
348 1.1 joerg la = *pos;
349 1.2 christos if ( ! man_args(man, line, pos, buf, &p))
350 1.2 christos break;
351 1.2 christos if (man_macros[tok].flags & MAN_JOIN &&
352 1.3 christos man->last->type == ROFFT_TEXT)
353 1.3 christos roff_word_append(man, p);
354 1.2 christos else
355 1.3 christos roff_word_alloc(man, line, la, p);
356 1.2 christos }
357 1.1 joerg
358 1.2 christos /*
359 1.2 christos * Append MAN_EOS in case the last snipped argument
360 1.2 christos * ends with a dot, e.g. `.IR syslog (3).'
361 1.2 christos */
362 1.2 christos
363 1.2 christos if (n != man->last &&
364 1.2 christos mandoc_eos(man->last->string, strlen(man->last->string)))
365 1.2 christos man->last->flags |= MAN_EOS;
366 1.2 christos
367 1.2 christos /*
368 1.2 christos * If no arguments are specified and this is MAN_SCOPED (i.e.,
369 1.2 christos * next-line scoped), then set our mode to indicate that we're
370 1.2 christos * waiting for terms to load into our context.
371 1.2 christos */
372 1.1 joerg
373 1.2 christos if (n == man->last && man_macros[tok].flags & MAN_SCOPED) {
374 1.2 christos assert( ! (man_macros[tok].flags & MAN_NSCOPED));
375 1.2 christos man->flags |= MAN_ELINE;
376 1.2 christos return;
377 1.1 joerg }
378 1.1 joerg
379 1.3 christos assert(man->last->type != ROFFT_ROOT);
380 1.3 christos man->next = ROFF_NEXT_SIBLING;
381 1.1 joerg
382 1.3 christos /* Rewind our element scope. */
383 1.1 joerg
384 1.2 christos for ( ; man->last; man->last = man->last->parent) {
385 1.3 christos man_state(man, man->last);
386 1.2 christos if (man->last == n)
387 1.1 joerg break;
388 1.1 joerg }
389 1.2 christos }
390 1.2 christos
391 1.2 christos void
392 1.3 christos man_endparse(struct roff_man *man)
393 1.2 christos {
394 1.1 joerg
395 1.2 christos man_unscope(man, man->first);
396 1.3 christos man->flags &= ~MAN_LITERAL;
397 1.1 joerg }
398 1.1 joerg
399 1.2 christos static int
400 1.3 christos man_args(struct roff_man *man, int line, int *pos, char *buf, char **v)
401 1.1 joerg {
402 1.2 christos char *start;
403 1.1 joerg
404 1.2 christos assert(*pos);
405 1.2 christos *v = start = buf + *pos;
406 1.2 christos assert(' ' != *start);
407 1.1 joerg
408 1.2 christos if ('\0' == *start)
409 1.3 christos return 0;
410 1.1 joerg
411 1.2 christos *v = mandoc_getarg(man->parse, v, line, pos);
412 1.3 christos return 1;
413 1.1 joerg }
414