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>
6  *
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.
10  *
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.
18  */
19 #include "config.h"
20 
21 #include <sys/types.h>
22 
23 #include <assert.h>
24 #include <ctype.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 
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"
35 
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);
43 
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 */
82 };
83 
84 
85 const struct man_macro *
man_macro(enum roff_tok tok)86 man_macro(enum roff_tok tok)
87 {
88           assert(tok >= MAN_TH && tok <= MAN_MAX);
89           return man_macros + (tok - MAN_TH);
90 }
91 
92 void
man_unscope(struct roff_man * man,const struct roff_node * to)93 man_unscope(struct roff_man *man, const struct roff_node *to)
94 {
95           struct roff_node *n;
96 
97           to = to->parent;
98           n = man->last;
99           while (n != to) {
100 
101                     /* Reached the end of the document? */
102 
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;
116                                         }
117                                         man->last = n;
118                                         n = n->parent;
119                                         roff_node_delete(man, man->last);
120                                         continue;
121                               }
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]);
127                     }
128 
129                     /*
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.
134                      */
135 
136                     man->last = n;
137                     n = n->parent;
138                     man->last->flags |= NODE_VALID;
139           }
140 
141           /*
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.
146            */
147 
148           man->next = (man->last == to) ?
149               ROFF_NEXT_CHILD : ROFF_NEXT_SIBLING;
150 }
151 
152 /*
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.
156  */
157 static void
rew_scope(struct roff_man * man,enum roff_tok tok)158 rew_scope(struct roff_man *man, enum roff_tok tok)
159 {
160           struct roff_node *n;
161 
162           /* Preserve empty paragraphs before RS. */
163 
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;
168 
169           for (;;) {
170                     if (n->type == ROFFT_ROOT)
171                               return;
172                     if (n->flags & NODE_VALID) {
173                               n = n->parent;
174                               continue;
175                     }
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;
183                               }
184                     }
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;
191           }
192 }
193 
194 
195 /*
196  * Close out a generic explicit macro.
197  */
198 void
blk_close(MACRO_PROT_ARGS)199 blk_close(MACRO_PROT_ARGS)
200 {
201           enum roff_tok                  ctok, ntok;
202           const struct roff_node        *nn;
203           char                          *p, *ep;
204           int                            cline, cpos, la, nrew, target;
205 
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;
229                     }
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();
242           }
243 
244           for (nn = man->last->parent; nn; nn = nn->parent)
245                     if (nn->tok == ntok && nn->type == ROFFT_BLOCK && ! --nrew)
246                               break;
247 
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;
257                     }
258                     return;
259           }
260 
261           cline = man->last->line;
262           cpos = man->last->pos;
263           ctok = man->last->tok;
264           man_unscope(man, nn);
265 
266           if (tok == MAN_RE && nn->head->aux > 0)
267                     roff_setreg(man->roff, "an-margin", nn->head->aux, '-');
268 
269           /* Trailing text. */
270 
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;
276           }
277 
278           /* Move a trailing paragraph behind the block. */
279 
280           if (ctok == MAN_LP || ctok == MAN_PP || ctok == MAN_P) {
281                     *pos = strlen(buf);
282                     blk_imp(man, ctok, cline, cpos, pos, buf);
283           }
284 
285           /* Synopsis blocks need an explicit end marker for spacing. */
286 
287           if (tok == MAN_YS && man->last == nn) {
288                     roff_elem_alloc(man, line, ppos, tok);
289                     man_unscope(man, man->last);
290           }
291 }
292 
293 void
blk_exp(MACRO_PROT_ARGS)294 blk_exp(MACRO_PROT_ARGS)
295 {
296           struct roff_node *head;
297           char                *p;
298           int                  la;
299 
300           if (tok == MAN_RS) {
301                     rew_scope(man, tok);
302                     man->flags |= ROFF_NONOFILL;
303           }
304           roff_block_alloc(man, line, ppos, tok);
305           head = roff_head_alloc(man, line, ppos, tok);
306 
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, '+');
317                     }
318                     free(p);
319           }
320 
321           if (buf[*pos] != '\0')
322                     mandoc_msg(MANDOCERR_ARG_EXCESS, line, *pos,
323                         "%s ... %s", roff_name[tok], buf + *pos);
324 
325           man_unscope(man, head);
326           roff_body_alloc(man, line, ppos, tok);
327           man->flags &= ~ROFF_NONOFILL;
328 }
329 
330 /*
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.
335  */
336 void
blk_imp(MACRO_PROT_ARGS)337 blk_imp(MACRO_PROT_ARGS)
338 {
339           int                  la;
340           char                *p;
341           struct roff_node *n;
342 
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);
349 
350           /* Add line arguments. */
351 
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);
358           }
359 
360           /*
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.
364            */
365 
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;
370           }
371 
372           /* Close out the head and open the body. */
373 
374           man_unscope(man, n);
375           roff_body_alloc(man, line, ppos, tok);
376           man->flags &= ~ROFF_NONOFILL;
377 }
378 
379 void
in_line_eoln(MACRO_PROT_ARGS)380 in_line_eoln(MACRO_PROT_ARGS)
381 {
382           int                  la;
383           char                *p;
384           struct roff_node *n;
385 
386           roff_elem_alloc(man, line, ppos, tok);
387           n = man->last;
388 
389           if (tok == MAN_EX)
390                     man->flags |= ROFF_NOFILL;
391           else if (tok == MAN_EE)
392                     man->flags &= ~ROFF_NOFILL;
393 
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;
399                     }
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);
409           }
410 
411           /*
412            * Append NODE_EOS in case the last snipped argument
413            * ends with a dot, e.g. `.IR syslog (3).'
414            */
415 
416           if (n != man->last &&
417               mandoc_eos(man->last->string, strlen(man->last->string)))
418                     man->last->flags |= NODE_EOS;
419 
420           /*
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.
424            */
425 
426           if (n == man->last && man_macro(tok)->flags & MAN_ESCOPED) {
427                     man->flags |= MAN_ELINE;
428                     return;
429           }
430 
431           assert(man->last->type != ROFFT_ROOT);
432           man->next = ROFF_NEXT_SIBLING;
433 
434           /* Rewind our element scope. */
435 
436           for ( ; man->last; man->last = man->last->parent) {
437                     man->last->flags |= NODE_VALID;
438                     if (man->last == n)
439                               break;
440           }
441 
442           /* Rewind next-line scoped ancestors, if any. */
443 
444           if (man_macro(tok)->flags & MAN_ESCOPED)
445                     man_descope(man, line, ppos, NULL);
446 }
447 
448 void
man_endparse(struct roff_man * man)449 man_endparse(struct roff_man *man)
450 {
451           man_unscope(man, man->meta.first);
452 }
453 
454 static int
man_args(struct roff_man * man,int line,int * pos,char * buf,char ** v)455 man_args(struct roff_man *man, int line, int *pos, char *buf, char **v)
456 {
457           char       *start;
458 
459           assert(*pos);
460           *v = start = buf + *pos;
461           assert(' ' != *start);
462 
463           if ('\0' == *start)
464                     return 0;
465 
466           *v = roff_getarg(man->roff, v, line, pos);
467           return 1;
468 }
469