]> git.cameronkatri.com Git - mandoc.git/blob - roff.c
Downstream maintainers: this removes UGLY! I don't want diverging
[mandoc.git] / roff.c
1 /* $Id: roff.c,v 1.90 2010/06/27 15:52:41 kristaps Exp $ */
2 /*
3 * Copyright (c) 2010 Kristaps Dzonsons <kristaps@bsd.lv>
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 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
20
21 #include <assert.h>
22 #include <errno.h>
23 #include <ctype.h>
24 #include <limits.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <stdio.h>
28
29 #include "mandoc.h"
30 #include "regs.h"
31 #include "roff.h"
32
33 #define RSTACK_MAX 128
34
35 #define ROFF_CTL(c) \
36 ('.' == (c) || '\'' == (c))
37
38 #if 1
39 #define ROFF_DEBUG(fmt, args...) \
40 do { /* Nothing. */ } while (/*CONSTCOND*/ 0)
41 #else
42 #define ROFF_DEBUG(fmt, args...) \
43 do { fprintf(stderr, fmt , ##args); } while (/*CONSTCOND*/ 0)
44 #endif
45
46 enum rofft {
47 ROFF_am,
48 ROFF_ami,
49 ROFF_am1,
50 ROFF_de,
51 ROFF_dei,
52 ROFF_de1,
53 ROFF_ds,
54 ROFF_el,
55 ROFF_ie,
56 ROFF_if,
57 ROFF_ig,
58 ROFF_rm,
59 ROFF_tr,
60 ROFF_cblock,
61 ROFF_ccond,
62 ROFF_nr,
63 ROFF_MAX
64 };
65
66 enum roffrule {
67 ROFFRULE_ALLOW,
68 ROFFRULE_DENY
69 };
70
71 struct roff {
72 struct roffnode *last; /* leaf of stack */
73 mandocmsg msg; /* err/warn/fatal messages */
74 void *data; /* privdata for messages */
75 enum roffrule rstack[RSTACK_MAX]; /* stack of !`ie' rules */
76 int rstackpos; /* position in rstack */
77 struct regset *regs; /* read/writable registers */
78 };
79
80 struct roffnode {
81 enum rofft tok; /* type of node */
82 struct roffnode *parent; /* up one in stack */
83 int line; /* parse line */
84 int col; /* parse col */
85 char *end; /* end-rules: custom token */
86 int endspan; /* end-rules: next-line or infty */
87 enum roffrule rule; /* current evaluation rule */
88 };
89
90 #define ROFF_ARGS struct roff *r, /* parse ctx */ \
91 enum rofft tok, /* tok of macro */ \
92 char **bufp, /* input buffer */ \
93 size_t *szp, /* size of input buffer */ \
94 int ln, /* parse line */ \
95 int ppos, /* original pos in buffer */ \
96 int pos, /* current pos in buffer */ \
97 int *offs /* reset offset of buffer data */
98
99 typedef enum rofferr (*roffproc)(ROFF_ARGS);
100
101 struct roffmac {
102 const char *name; /* macro name */
103 roffproc proc; /* process new macro */
104 roffproc text; /* process as child text of macro */
105 roffproc sub; /* process as child of macro */
106 int flags;
107 #define ROFFMAC_STRUCT (1 << 0) /* always interpret */
108 struct roffmac *next;
109 };
110
111 static enum rofferr roff_block(ROFF_ARGS);
112 static enum rofferr roff_block_text(ROFF_ARGS);
113 static enum rofferr roff_block_sub(ROFF_ARGS);
114 static enum rofferr roff_cblock(ROFF_ARGS);
115 static enum rofferr roff_ccond(ROFF_ARGS);
116 static enum rofferr roff_cond(ROFF_ARGS);
117 static enum rofferr roff_cond_text(ROFF_ARGS);
118 static enum rofferr roff_cond_sub(ROFF_ARGS);
119 static enum rofferr roff_line(ROFF_ARGS);
120 static enum rofferr roff_nr(ROFF_ARGS);
121 static enum roffrule roff_evalcond(const char *, int *);
122
123 /* See roff_hash_find() */
124
125 #define ASCII_HI 126
126 #define ASCII_LO 33
127 #define HASHWIDTH (ASCII_HI - ASCII_LO + 1)
128
129 static struct roffmac *hash[HASHWIDTH];
130
131 static struct roffmac roffs[ROFF_MAX] = {
132 { "am", roff_block, roff_block_text, roff_block_sub, 0, NULL },
133 { "ami", roff_block, roff_block_text, roff_block_sub, 0, NULL },
134 { "am1", roff_block, roff_block_text, roff_block_sub, 0, NULL },
135 { "de", roff_block, roff_block_text, roff_block_sub, 0, NULL },
136 { "dei", roff_block, roff_block_text, roff_block_sub, 0, NULL },
137 { "de1", roff_block, roff_block_text, roff_block_sub, 0, NULL },
138 { "ds", roff_line, NULL, NULL, 0, NULL },
139 { "el", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
140 { "ie", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
141 { "if", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
142 { "ig", roff_block, roff_block_text, roff_block_sub, 0, NULL },
143 { "rm", roff_line, NULL, NULL, 0, NULL },
144 { "tr", roff_line, NULL, NULL, 0, NULL },
145 { ".", roff_cblock, NULL, NULL, 0, NULL },
146 { "\\}", roff_ccond, NULL, NULL, 0, NULL },
147 { "nr", roff_nr, NULL, NULL, 0, NULL },
148 };
149
150 static void roff_free1(struct roff *);
151 static enum rofft roff_hash_find(const char *);
152 static void roff_hash_init(void);
153 static void roffnode_cleanscope(struct roff *);
154 static int roffnode_push(struct roff *,
155 enum rofft, int, int);
156 static void roffnode_pop(struct roff *);
157 static enum rofft roff_parse(const char *, int *);
158 static int roff_parse_nat(const char *, int *);
159
160 /* See roff_hash_find() */
161 #define ROFF_HASH(p) (p[0] - ASCII_LO)
162
163 static void
164 roff_hash_init(void)
165 {
166 struct roffmac *n;
167 int buc, i;
168
169 for (i = 0; i < (int)ROFF_MAX; i++) {
170 assert(roffs[i].name[0] >= ASCII_LO);
171 assert(roffs[i].name[0] <= ASCII_HI);
172
173 buc = ROFF_HASH(roffs[i].name);
174
175 if (NULL != (n = hash[buc])) {
176 for ( ; n->next; n = n->next)
177 /* Do nothing. */ ;
178 n->next = &roffs[i];
179 } else
180 hash[buc] = &roffs[i];
181 }
182 }
183
184
185 /*
186 * Look up a roff token by its name. Returns ROFF_MAX if no macro by
187 * the nil-terminated string name could be found.
188 */
189 static enum rofft
190 roff_hash_find(const char *p)
191 {
192 int buc;
193 struct roffmac *n;
194
195 /*
196 * libroff has an extremely simple hashtable, for the time
197 * being, which simply keys on the first character, which must
198 * be printable, then walks a chain. It works well enough until
199 * optimised.
200 */
201
202 if (p[0] < ASCII_LO || p[0] > ASCII_HI)
203 return(ROFF_MAX);
204
205 buc = ROFF_HASH(p);
206
207 if (NULL == (n = hash[buc]))
208 return(ROFF_MAX);
209 for ( ; n; n = n->next)
210 if (0 == strcmp(n->name, p))
211 return((enum rofft)(n - roffs));
212
213 return(ROFF_MAX);
214 }
215
216
217 /*
218 * Pop the current node off of the stack of roff instructions currently
219 * pending.
220 */
221 static void
222 roffnode_pop(struct roff *r)
223 {
224 struct roffnode *p;
225
226 assert(r->last);
227 p = r->last;
228
229 if (ROFF_el == p->tok)
230 if (r->rstackpos > -1)
231 r->rstackpos--;
232
233 r->last = r->last->parent;
234 if (p->end)
235 free(p->end);
236 free(p);
237 }
238
239
240 /*
241 * Push a roff node onto the instruction stack. This must later be
242 * removed with roffnode_pop().
243 */
244 static int
245 roffnode_push(struct roff *r, enum rofft tok, int line, int col)
246 {
247 struct roffnode *p;
248
249 if (NULL == (p = calloc(1, sizeof(struct roffnode)))) {
250 (*r->msg)(MANDOCERR_MEM, r->data, line, col, NULL);
251 return(0);
252 }
253
254 p->tok = tok;
255 p->parent = r->last;
256 p->line = line;
257 p->col = col;
258 p->rule = p->parent ? p->parent->rule : ROFFRULE_DENY;
259
260 r->last = p;
261 return(1);
262 }
263
264
265 static void
266 roff_free1(struct roff *r)
267 {
268
269 while (r->last)
270 roffnode_pop(r);
271 }
272
273
274 void
275 roff_reset(struct roff *r)
276 {
277
278 roff_free1(r);
279 }
280
281
282 void
283 roff_free(struct roff *r)
284 {
285
286 roff_free1(r);
287 free(r);
288 }
289
290
291 struct roff *
292 roff_alloc(struct regset *regs, const mandocmsg msg, void *data)
293 {
294 struct roff *r;
295
296 if (NULL == (r = calloc(1, sizeof(struct roff)))) {
297 (*msg)(MANDOCERR_MEM, data, 0, 0, NULL);
298 return(0);
299 }
300
301 r->regs = regs;
302 r->msg = msg;
303 r->data = data;
304 r->rstackpos = -1;
305
306 roff_hash_init();
307 return(r);
308 }
309
310
311 enum rofferr
312 roff_parseln(struct roff *r, int ln, char **bufp,
313 size_t *szp, int pos, int *offs)
314 {
315 enum rofft t;
316 int ppos;
317
318 /*
319 * First, if a scope is open and we're not a macro, pass the
320 * text through the macro's filter. If a scope isn't open and
321 * we're not a macro, just let it through.
322 */
323
324 if (r->last && ! ROFF_CTL((*bufp)[pos])) {
325 t = r->last->tok;
326 assert(roffs[t].text);
327 ROFF_DEBUG("roff: intercept scoped text: %s, [%s]\n",
328 roffs[t].name, &(*bufp)[pos]);
329 return((*roffs[t].text)
330 (r, t, bufp, szp,
331 ln, pos, pos, offs));
332 } else if ( ! ROFF_CTL((*bufp)[pos])) {
333 ROFF_DEBUG("roff: pass non-scoped text: [%s]\n",
334 &(*bufp)[pos]);
335 return(ROFF_CONT);
336 }
337
338 /*
339 * If a scope is open, go to the child handler for that macro,
340 * as it may want to preprocess before doing anything with it.
341 */
342
343 if (r->last) {
344 t = r->last->tok;
345 assert(roffs[t].sub);
346 ROFF_DEBUG("roff: intercept scoped context: %s\n",
347 roffs[t].name);
348 return((*roffs[t].sub)
349 (r, t, bufp, szp,
350 ln, pos, pos, offs));
351 }
352
353 /*
354 * Lastly, as we've no scope open, try to look up and execute
355 * the new macro. If no macro is found, simply return and let
356 * the compilers handle it.
357 */
358
359 ppos = pos;
360 if (ROFF_MAX == (t = roff_parse(*bufp, &pos))) {
361 ROFF_DEBUG("roff: pass non-scoped non-macro: [%s]\n",
362 &(*bufp)[pos]);
363 return(ROFF_CONT);
364 }
365
366 ROFF_DEBUG("roff: intercept new-scope: %s, [%s]\n",
367 roffs[t].name, &(*bufp)[pos]);
368 assert(roffs[t].proc);
369 return((*roffs[t].proc)
370 (r, t, bufp, szp,
371 ln, ppos, pos, offs));
372 }
373
374
375 int
376 roff_endparse(struct roff *r)
377 {
378
379 if (NULL == r->last)
380 return(1);
381 return((*r->msg)(MANDOCERR_SCOPEEXIT, r->data, r->last->line,
382 r->last->col, NULL));
383 }
384
385
386 /*
387 * Parse a roff node's type from the input buffer. This must be in the
388 * form of ".foo xxx" in the usual way.
389 */
390 static enum rofft
391 roff_parse(const char *buf, int *pos)
392 {
393 int j;
394 char mac[5];
395 enum rofft t;
396
397 assert(ROFF_CTL(buf[*pos]));
398 (*pos)++;
399
400 while (buf[*pos] && (' ' == buf[*pos] || '\t' == buf[*pos]))
401 (*pos)++;
402
403 if ('\0' == buf[*pos])
404 return(ROFF_MAX);
405
406 for (j = 0; j < 4; j++, (*pos)++)
407 if ('\0' == (mac[j] = buf[*pos]))
408 break;
409 else if (' ' == buf[*pos] || (j && '\\' == buf[*pos]))
410 break;
411
412 if (j == 4 || j < 1)
413 return(ROFF_MAX);
414
415 mac[j] = '\0';
416
417 if (ROFF_MAX == (t = roff_hash_find(mac)))
418 return(t);
419
420 while (buf[*pos] && ' ' == buf[*pos])
421 (*pos)++;
422
423 return(t);
424 }
425
426
427 static int
428 roff_parse_nat(const char *buf, int *res)
429 {
430 char *ep;
431 long lval;
432
433 errno = 0;
434 lval = strtol(buf, &ep, 10);
435 if (buf[0] == '\0' || *ep != '\0')
436 return(0);
437 if ((errno == ERANGE &&
438 (lval == LONG_MAX || lval == LONG_MIN)) ||
439 (lval > INT_MAX || lval <= 0))
440 return(0);
441
442 *res = (int)lval;
443 return(1);
444 }
445
446
447 /* ARGSUSED */
448 static enum rofferr
449 roff_cblock(ROFF_ARGS)
450 {
451
452 /*
453 * A block-close `..' should only be invoked as a child of an
454 * ignore macro, otherwise raise a warning and just ignore it.
455 */
456
457 if (NULL == r->last) {
458 if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
459 return(ROFF_ERR);
460 return(ROFF_IGN);
461 }
462
463 switch (r->last->tok) {
464 case (ROFF_am):
465 /* FALLTHROUGH */
466 case (ROFF_ami):
467 /* FALLTHROUGH */
468 case (ROFF_am1):
469 /* FALLTHROUGH */
470 case (ROFF_de):
471 /* FALLTHROUGH */
472 case (ROFF_dei):
473 /* FALLTHROUGH */
474 case (ROFF_de1):
475 /* FALLTHROUGH */
476 case (ROFF_ig):
477 break;
478 default:
479 if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
480 return(ROFF_ERR);
481 return(ROFF_IGN);
482 }
483
484 if ((*bufp)[pos])
485 if ( ! (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, pos, NULL))
486 return(ROFF_ERR);
487
488 roffnode_pop(r);
489 roffnode_cleanscope(r);
490 return(ROFF_IGN);
491
492 }
493
494
495 static void
496 roffnode_cleanscope(struct roff *r)
497 {
498
499 while (r->last) {
500 if (--r->last->endspan < 0)
501 break;
502 roffnode_pop(r);
503 }
504 }
505
506
507 /* ARGSUSED */
508 static enum rofferr
509 roff_ccond(ROFF_ARGS)
510 {
511
512 if (NULL == r->last) {
513 if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
514 return(ROFF_ERR);
515 return(ROFF_IGN);
516 }
517
518 switch (r->last->tok) {
519 case (ROFF_el):
520 /* FALLTHROUGH */
521 case (ROFF_ie):
522 /* FALLTHROUGH */
523 case (ROFF_if):
524 break;
525 default:
526 if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
527 return(ROFF_ERR);
528 return(ROFF_IGN);
529 }
530
531 if (r->last->endspan > -1) {
532 if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
533 return(ROFF_ERR);
534 return(ROFF_IGN);
535 }
536
537 if ((*bufp)[pos])
538 if ( ! (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, pos, NULL))
539 return(ROFF_ERR);
540
541 roffnode_pop(r);
542 roffnode_cleanscope(r);
543 return(ROFF_IGN);
544 }
545
546
547 /* ARGSUSED */
548 static enum rofferr
549 roff_block(ROFF_ARGS)
550 {
551 int sv;
552 size_t sz;
553
554 if (ROFF_ig != tok && '\0' == (*bufp)[pos]) {
555 if ( ! (*r->msg)(MANDOCERR_NOARGS, r->data, ln, ppos, NULL))
556 return(ROFF_ERR);
557 return(ROFF_IGN);
558 } else if (ROFF_ig != tok) {
559 while ((*bufp)[pos] && ' ' != (*bufp)[pos])
560 pos++;
561 while (' ' == (*bufp)[pos])
562 pos++;
563 }
564
565 if ( ! roffnode_push(r, tok, ln, ppos))
566 return(ROFF_ERR);
567
568 if ('\0' == (*bufp)[pos])
569 return(ROFF_IGN);
570
571 sv = pos;
572 while ((*bufp)[pos] && ' ' != (*bufp)[pos] &&
573 '\t' != (*bufp)[pos])
574 pos++;
575
576 /*
577 * Note: groff does NOT like escape characters in the input.
578 * Instead of detecting this, we're just going to let it fly and
579 * to hell with it.
580 */
581
582 assert(pos > sv);
583 sz = (size_t)(pos - sv);
584
585 if (1 == sz && '.' == (*bufp)[sv])
586 return(ROFF_IGN);
587
588 r->last->end = malloc(sz + 1);
589
590 if (NULL == r->last->end) {
591 (*r->msg)(MANDOCERR_MEM, r->data, ln, pos, NULL);
592 return(ROFF_ERR);
593 }
594
595 memcpy(r->last->end, *bufp + sv, sz);
596 r->last->end[(int)sz] = '\0';
597
598 if ((*bufp)[pos])
599 if ( ! (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, pos, NULL))
600 return(ROFF_ERR);
601
602 return(ROFF_IGN);
603 }
604
605
606 /* ARGSUSED */
607 static enum rofferr
608 roff_block_sub(ROFF_ARGS)
609 {
610 enum rofft t;
611 int i, j;
612
613 /*
614 * First check whether a custom macro exists at this level. If
615 * it does, then check against it. This is some of groff's
616 * stranger behaviours. If we encountered a custom end-scope
617 * tag and that tag also happens to be a "real" macro, then we
618 * need to try interpreting it again as a real macro. If it's
619 * not, then return ignore. Else continue.
620 */
621
622 if (r->last->end) {
623 i = pos + 1;
624 while (' ' == (*bufp)[i] || '\t' == (*bufp)[i])
625 i++;
626
627 for (j = 0; r->last->end[j]; j++, i++)
628 if ((*bufp)[i] != r->last->end[j])
629 break;
630
631 if ('\0' == r->last->end[j] &&
632 ('\0' == (*bufp)[i] ||
633 ' ' == (*bufp)[i] ||
634 '\t' == (*bufp)[i])) {
635 roffnode_pop(r);
636 roffnode_cleanscope(r);
637
638 if (ROFF_MAX != roff_parse(*bufp, &pos))
639 return(ROFF_RERUN);
640 return(ROFF_IGN);
641 }
642 }
643
644 /*
645 * If we have no custom end-query or lookup failed, then try
646 * pulling it out of the hashtable.
647 */
648
649 ppos = pos;
650 t = roff_parse(*bufp, &pos);
651
652 /* If we're not a comment-end, then throw it away. */
653 if (ROFF_cblock != t)
654 return(ROFF_IGN);
655
656 assert(roffs[t].proc);
657 return((*roffs[t].proc)(r, t, bufp, szp,
658 ln, ppos, pos, offs));
659 }
660
661
662 /* ARGSUSED */
663 static enum rofferr
664 roff_block_text(ROFF_ARGS)
665 {
666
667 return(ROFF_IGN);
668 }
669
670
671 /* ARGSUSED */
672 static enum rofferr
673 roff_cond_sub(ROFF_ARGS)
674 {
675 enum rofft t;
676 enum roffrule rr;
677 struct roffnode *l;
678
679 ppos = pos;
680 rr = r->last->rule;
681
682 /*
683 * Clean out scope. If we've closed ourselves, then don't
684 * continue.
685 */
686
687 l = r->last;
688 roffnode_cleanscope(r);
689
690 if (l != r->last)
691 return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
692
693 if (ROFF_MAX == (t = roff_parse(*bufp, &pos)))
694 return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
695
696 /*
697 * A denied conditional must evaluate its children if and only
698 * if they're either structurally required (such as loops and
699 * conditionals) or a closing macro.
700 */
701 if (ROFFRULE_DENY == rr)
702 if ( ! (ROFFMAC_STRUCT & roffs[t].flags))
703 if (ROFF_ccond != t)
704 return(ROFF_IGN);
705
706 assert(roffs[t].proc);
707 return((*roffs[t].proc)(r, t, bufp, szp,
708 ln, ppos, pos, offs));
709 }
710
711
712 /* ARGSUSED */
713 static enum rofferr
714 roff_cond_text(ROFF_ARGS)
715 {
716 char *ep, *st;
717 enum roffrule rr;
718
719 rr = r->last->rule;
720
721 /*
722 * We display the value of the text if out current evaluation
723 * scope permits us to do so.
724 */
725
726 st = &(*bufp)[pos];
727 if (NULL == (ep = strstr(st, "\\}"))) {
728 roffnode_cleanscope(r);
729 return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
730 }
731
732 if (ep == st || (ep > st && '\\' != *(ep - 1)))
733 roffnode_pop(r);
734
735 roffnode_cleanscope(r);
736 return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
737 }
738
739
740 static enum roffrule
741 roff_evalcond(const char *v, int *pos)
742 {
743
744 switch (v[*pos]) {
745 case ('n'):
746 (*pos)++;
747 return(ROFFRULE_ALLOW);
748 case ('e'):
749 /* FALLTHROUGH */
750 case ('o'):
751 /* FALLTHROUGH */
752 case ('t'):
753 (*pos)++;
754 return(ROFFRULE_DENY);
755 default:
756 break;
757 }
758
759 while (v[*pos] && ' ' != v[*pos])
760 (*pos)++;
761 return(ROFFRULE_DENY);
762 }
763
764
765 /* ARGSUSED */
766 static enum rofferr
767 roff_line(ROFF_ARGS)
768 {
769
770 return(ROFF_IGN);
771 }
772
773
774 /* ARGSUSED */
775 static enum rofferr
776 roff_cond(ROFF_ARGS)
777 {
778 int sv;
779 enum roffrule rule;
780
781 /* Stack overflow! */
782
783 if (ROFF_ie == tok && r->rstackpos == RSTACK_MAX - 1) {
784 (*r->msg)(MANDOCERR_MEM, r->data, ln, ppos, NULL);
785 return(ROFF_ERR);
786 }
787
788 /* First, evaluate the conditional. */
789
790 if (ROFF_el == tok) {
791 /*
792 * An `.el' will get the value of the current rstack
793 * entry set in prior `ie' calls or defaults to DENY.
794 */
795 if (r->rstackpos < 0)
796 rule = ROFFRULE_DENY;
797 else
798 rule = r->rstack[r->rstackpos];
799 } else
800 rule = roff_evalcond(*bufp, &pos);
801
802 sv = pos;
803
804 while (' ' == (*bufp)[pos])
805 pos++;
806
807 /*
808 * Roff is weird. If we have just white-space after the
809 * conditional, it's considered the BODY and we exit without
810 * really doing anything. Warn about this. It's probably
811 * wrong.
812 */
813
814 if ('\0' == (*bufp)[pos] && sv != pos) {
815 if ((*r->msg)(MANDOCERR_NOARGS, r->data, ln, ppos, NULL))
816 return(ROFF_IGN);
817 return(ROFF_ERR);
818 }
819
820 if ( ! roffnode_push(r, tok, ln, ppos))
821 return(ROFF_ERR);
822
823 r->last->rule = rule;
824
825 ROFF_DEBUG("roff: cond: %s -> %s\n", roffs[tok].name,
826 ROFFRULE_ALLOW == rule ? "allow" : "deny");
827
828 if (ROFF_ie == tok) {
829 /*
830 * An if-else will put the NEGATION of the current
831 * evaluated conditional into the stack.
832 */
833 r->rstackpos++;
834 if (ROFFRULE_DENY == r->last->rule)
835 r->rstack[r->rstackpos] = ROFFRULE_ALLOW;
836 else
837 r->rstack[r->rstackpos] = ROFFRULE_DENY;
838 }
839
840 /* If the parent has false as its rule, then so do we. */
841
842 if (r->last->parent && ROFFRULE_DENY == r->last->parent->rule) {
843 r->last->rule = ROFFRULE_DENY;
844 ROFF_DEBUG("roff: cond override: %s -> deny\n",
845 roffs[tok].name);
846 }
847
848 /*
849 * Determine scope. If we're invoked with "\{" trailing the
850 * conditional, then we're in a multiline scope. Else our scope
851 * expires on the next line.
852 */
853
854 r->last->endspan = 1;
855
856 if ('\\' == (*bufp)[pos] && '{' == (*bufp)[pos + 1]) {
857 r->last->endspan = -1;
858 pos += 2;
859 ROFF_DEBUG("roff: cond-scope: %s, multi-line\n",
860 roffs[tok].name);
861 } else
862 ROFF_DEBUG("roff: cond-scope: %s, one-line\n",
863 roffs[tok].name);
864
865 /*
866 * If there are no arguments on the line, the next-line scope is
867 * assumed.
868 */
869
870 if ('\0' == (*bufp)[pos])
871 return(ROFF_IGN);
872
873 /* Otherwise re-run the roff parser after recalculating. */
874
875 *offs = pos;
876 return(ROFF_RERUN);
877 }
878
879
880 /* ARGSUSED */
881 static enum rofferr
882 roff_nr(ROFF_ARGS)
883 {
884 const char *key, *val;
885
886 key = &(*bufp)[pos];
887
888 /* Parse register request. */
889 while ((*bufp)[pos] && ' ' != (*bufp)[pos])
890 pos++;
891
892 /*
893 * Set our nil terminator. Because this line is going to be
894 * ignored anyway, we can munge it as we please.
895 */
896 if ((*bufp)[pos])
897 (*bufp)[pos++] = '\0';
898
899 /* Skip whitespace to register token. */
900 while ((*bufp)[pos] && ' ' == (*bufp)[pos])
901 pos++;
902
903 val = &(*bufp)[pos];
904
905 /* Process register token. */
906
907 if (0 == strcmp(key, "nS")) {
908 if ( ! roff_parse_nat(val, &r->regs->regs[(int)REG_nS].i))
909 r->regs->regs[(int)REG_nS].i = 0;
910
911 ROFF_DEBUG("roff: register nS: %d\n",
912 r->regs->regs[(int)REG_nS].i);
913 } else
914 ROFF_DEBUG("roff: ignoring register: %s\n", key);
915
916 return(ROFF_IGN);
917 }