bf9ac2411151cc4ba1cb5b86f70b3f4ed4717135
[dragonfly.git] / usr.bin / mandoc / man_macro.c
1 /*      $Id: man_macro.c,v 1.9 2009/10/27 21:40:07 schwarze Exp $ */
2 /*
3  * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@kth.se>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 #include <assert.h>
18 #include <ctype.h>
19 #include <stdlib.h>
20 #include <string.h>
21
22 #include "libman.h"
23
24 #define REW_REWIND      (0)             /* See rew_scope(). */
25 #define REW_NOHALT      (1)             /* See rew_scope(). */
26 #define REW_HALT        (2)             /* See rew_scope(). */
27
28 static  int              in_line_eoln(MACRO_PROT_ARGS);
29 static  int              blk_imp(MACRO_PROT_ARGS);
30 static  int              blk_close(MACRO_PROT_ARGS);
31
32 static  int              rew_scope(enum man_type, struct man *, int);
33 static  int              rew_dohalt(int, enum man_type,
34                                 const struct man_node *);
35 static  int              rew_block(int, enum man_type,
36                                 const struct man_node *);
37
38 const   struct man_macro __man_macros[MAN_MAX] = {
39         { in_line_eoln, 0 }, /* br */
40         { in_line_eoln, 0 }, /* TH */
41         { blk_imp, MAN_SCOPED }, /* SH */
42         { blk_imp, MAN_SCOPED }, /* SS */
43         { blk_imp, MAN_SCOPED | MAN_FSCOPED }, /* TP */
44         { blk_imp, 0 }, /* LP */
45         { blk_imp, 0 }, /* PP */
46         { blk_imp, 0 }, /* P */
47         { blk_imp, 0 }, /* IP */
48         { blk_imp, 0 }, /* HP */
49         { in_line_eoln, MAN_SCOPED }, /* SM */
50         { in_line_eoln, MAN_SCOPED }, /* SB */
51         { in_line_eoln, 0 }, /* BI */
52         { in_line_eoln, 0 }, /* IB */
53         { in_line_eoln, 0 }, /* BR */
54         { in_line_eoln, 0 }, /* RB */
55         { in_line_eoln, MAN_SCOPED }, /* R */
56         { in_line_eoln, MAN_SCOPED }, /* B */
57         { in_line_eoln, MAN_SCOPED }, /* I */
58         { in_line_eoln, 0 }, /* IR */
59         { in_line_eoln, 0 }, /* RI */
60         { in_line_eoln, 0 }, /* na */
61         { in_line_eoln, 0 }, /* i */
62         { in_line_eoln, 0 }, /* sp */
63         { in_line_eoln, 0 }, /* nf */
64         { in_line_eoln, 0 }, /* fi */
65         { in_line_eoln, 0 }, /* r */
66         { blk_close, 0 }, /* RE */
67         { blk_imp, MAN_EXPLICIT }, /* RS */
68         { in_line_eoln, 0 }, /* DT */
69         { in_line_eoln, 0 }, /* UC */
70         { in_line_eoln, 0 }, /* PD */
71 };
72
73 const   struct man_macro * const man_macros = __man_macros;
74
75
76 int
77 man_unscope(struct man *m, const struct man_node *n)
78 {
79
80         assert(n);
81         m->next = MAN_NEXT_SIBLING;
82
83         /* LINTED */
84         while (m->last != n) {
85                 if ( ! man_valid_post(m))
86                         return(0);
87                 if ( ! man_action_post(m))
88                         return(0);
89                 m->last = m->last->parent;
90                 assert(m->last);
91         }
92
93         if ( ! man_valid_post(m))
94                 return(0);
95         return(man_action_post(m));
96 }
97
98
99 static int
100 rew_block(int ntok, enum man_type type, const struct man_node *n)
101 {
102
103         if (MAN_BLOCK == type && ntok == n->parent->tok &&
104                         MAN_BODY == n->parent->type)
105                 return(REW_REWIND);
106         return(ntok == n->tok ? REW_HALT : REW_NOHALT);
107 }
108
109
110 /*
111  * There are three scope levels: scoped to the root (all), scoped to the
112  * section (all less sections), and scoped to subsections (all less
113  * sections and subsections).
114  */
115 static int
116 rew_dohalt(int tok, enum man_type type, const struct man_node *n)
117 {
118         int              c;
119
120         if (MAN_ROOT == n->type)
121                 return(REW_HALT);
122         assert(n->parent);
123         if (MAN_ROOT == n->parent->type)
124                 return(REW_REWIND);
125         if (MAN_VALID & n->flags)
126                 return(REW_NOHALT);
127
128         /* Rewind to ourselves, first. */
129         if (type == n->type && tok == n->tok)
130                 return(REW_REWIND);
131
132         switch (tok) {
133         case (MAN_SH):
134                 break;
135         case (MAN_SS):
136                 /* Rewind to a section, if a block. */
137                 if (REW_NOHALT != (c = rew_block(MAN_SH, type, n)))
138                         return(c);
139                 break;
140         case (MAN_RS):
141                 /* Rewind to a subsection, if a block. */
142                 if (REW_NOHALT != (c = rew_block(MAN_SS, type, n)))
143                         return(c);
144                 /* Rewind to a section, if a block. */
145                 if (REW_NOHALT != (c = rew_block(MAN_SH, type, n)))
146                         return(c);
147                 break;
148         default:
149                 /* Rewind to an offsetter, if a block. */
150                 if (REW_NOHALT != (c = rew_block(MAN_RS, type, n)))
151                         return(c);
152                 /* Rewind to a subsection, if a block. */
153                 if (REW_NOHALT != (c = rew_block(MAN_SS, type, n)))
154                         return(c);
155                 /* Rewind to a section, if a block. */
156                 if (REW_NOHALT != (c = rew_block(MAN_SH, type, n)))
157                         return(c);
158                 break;
159         }
160
161         return(REW_NOHALT);
162 }
163
164
165 /*
166  * Rewinding entails ascending the parse tree until a coherent point,
167  * for example, the `SH' macro will close out any intervening `SS'
168  * scopes.  When a scope is closed, it must be validated and actioned.
169  */
170 static int
171 rew_scope(enum man_type type, struct man *m, int tok)
172 {
173         struct man_node *n;
174         int              c;
175
176         /* LINTED */
177         for (n = m->last; n; n = n->parent) {
178                 /*
179                  * Whether we should stop immediately (REW_HALT), stop
180                  * and rewind until this point (REW_REWIND), or keep
181                  * rewinding (REW_NOHALT).
182                  */
183                 c = rew_dohalt(tok, type, n);
184                 if (REW_HALT == c)
185                         return(1);
186                 if (REW_REWIND == c)
187                         break;
188         }
189
190         /* Rewind until the current point. */
191
192         assert(n);
193         return(man_unscope(m, n));
194 }
195
196
197 /* ARGSUSED */
198 int
199 blk_close(MACRO_PROT_ARGS)
200 {
201         int                      ntok;
202         const struct man_node   *nn;
203
204         switch (tok) {
205         case (MAN_RE):
206                 ntok = MAN_RS;
207                 break;
208         default:
209                 abort();
210                 /* NOTREACHED */
211         }
212
213         for (nn = m->last->parent; nn; nn = nn->parent)
214                 if (ntok == nn->tok)
215                         break;
216
217         if (NULL == nn)
218                 if ( ! man_pwarn(m, line, ppos, WNOSCOPE))
219                         return(0);
220
221         if ( ! rew_scope(MAN_BODY, m, ntok))
222                 return(0);
223         if ( ! rew_scope(MAN_BLOCK, m, ntok))
224                 return(0);
225         m->next = MAN_NEXT_SIBLING;
226         return(1);
227 }
228
229
230 /*
231  * Parse an implicit-block macro.  These contain a MAN_HEAD and a
232  * MAN_BODY contained within a MAN_BLOCK.  Rules for closing out other
233  * scopes, such as `SH' closing out an `SS', are defined in the rew
234  * routines.
235  */
236 int
237 blk_imp(MACRO_PROT_ARGS)
238 {
239         int              w, la;
240         char            *p;
241         struct man_node *n;
242
243         /* Close out prior scopes. */
244
245         if ( ! rew_scope(MAN_BODY, m, tok))
246                 return(0);
247         if ( ! rew_scope(MAN_BLOCK, m, tok))
248                 return(0);
249
250         /* Allocate new block & head scope. */
251
252         if ( ! man_block_alloc(m, line, ppos, tok))
253                 return(0);
254         if ( ! man_head_alloc(m, line, ppos, tok))
255                 return(0);
256
257         n = m->last;
258
259         /* Add line arguments. */
260
261         for (;;) {
262                 la = *pos;
263                 w = man_args(m, line, pos, buf, &p);
264
265                 if (-1 == w)
266                         return(0);
267                 if (0 == w)
268                         break;
269
270                 if ( ! man_word_alloc(m, line, la, p))
271                         return(0);
272         }
273
274         /* Close out head and open body (unless MAN_SCOPE). */
275
276         if (MAN_SCOPED & man_macros[tok].flags) {
277                 /* If we're forcing scope (`TP'), keep it open. */
278                 if (MAN_FSCOPED & man_macros[tok].flags) {
279                         m->flags |= MAN_BLINE;
280                         return(1);
281                 } else if (n == m->last) {
282                         m->flags |= MAN_BLINE;
283                         return(1);
284                 }
285         }
286
287         if ( ! rew_scope(MAN_HEAD, m, tok))
288                 return(0);
289
290         return(man_body_alloc(m, line, ppos, tok));
291 }
292
293
294 int
295 in_line_eoln(MACRO_PROT_ARGS)
296 {
297         int              w, la;
298         char            *p;
299         struct man_node *n;
300
301         if ( ! man_elem_alloc(m, line, ppos, tok))
302                 return(0);
303
304         n = m->last;
305
306         for (;;) {
307                 la = *pos;
308                 w = man_args(m, line, pos, buf, &p);
309
310                 if (-1 == w)
311                         return(0);
312                 if (0 == w)
313                         break;
314
315                 if ( ! man_word_alloc(m, line, la, p))
316                         return(0);
317         }
318
319         if (n == m->last && MAN_SCOPED & man_macros[tok].flags) {
320                 m->flags |= MAN_ELINE;
321                 return(1);
322         }
323
324         /*
325          * Note that when TH is pruned, we'll be back at the root, so
326          * make sure that we don't clobber as its sibling.
327          */
328
329         for ( ; m->last; m->last = m->last->parent) {
330                 if (m->last == n)
331                         break;
332                 if (m->last->type == MAN_ROOT)
333                         break;
334                 if ( ! man_valid_post(m))
335                         return(0);
336                 if ( ! man_action_post(m))
337                         return(0);
338         }
339
340         assert(m->last);
341
342         /*
343          * Same here regarding whether we're back at the root.
344          */
345
346         if (m->last->type != MAN_ROOT && ! man_valid_post(m))
347                 return(0);
348         if (m->last->type != MAN_ROOT && ! man_action_post(m))
349                 return(0);
350         if (m->last->type != MAN_ROOT)
351                 m->next = MAN_NEXT_SIBLING;
352
353         return(1);
354 }
355
356
357 int
358 man_macroend(struct man *m)
359 {
360         struct man_node *n;
361
362         n = MAN_VALID & m->last->flags ?
363                 m->last->parent : m->last;
364
365         for ( ; n; n = n->parent) {
366                 if (MAN_BLOCK != n->type)
367                         continue;
368                 if ( ! (MAN_EXPLICIT & man_macros[n->tok].flags))
369                         continue;
370                 if ( ! man_nwarn(m, n, WEXITSCOPE))
371                         return(0);
372         }
373
374         return(man_unscope(m, m->first));
375 }