]> git.cameronkatri.com Git - mandoc.git/blob - roff.c
Low-hanging fruit: add OpenBSD's `ad', `hy', `ne', and `nh' handling.
[mandoc.git] / roff.c
1 /* $Id: roff.c,v 1.103 2010/12/01 10:21:25 kristaps Exp $ */
2 /*
3 * Copyright (c) 2010 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2010 Ingo Schwarze <schwarze@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
21
22 #include <assert.h>
23 #include <errno.h>
24 #include <ctype.h>
25 #include <limits.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <stdio.h>
29
30 #include "mandoc.h"
31 #include "roff.h"
32 #include "libmandoc.h"
33
34 #define RSTACK_MAX 128
35
36 #define ROFF_CTL(c) \
37 ('.' == (c) || '\'' == (c))
38
39 #if 1
40 #define ROFF_DEBUG(fmt, args...) \
41 do { /* Nothing. */ } while (/*CONSTCOND*/ 0)
42 #else
43 #define ROFF_DEBUG(fmt, args...) \
44 do { fprintf(stderr, fmt , ##args); } while (/*CONSTCOND*/ 0)
45 #endif
46
47 enum rofft {
48 ROFF_ad,
49 ROFF_am,
50 ROFF_ami,
51 ROFF_am1,
52 ROFF_de,
53 ROFF_dei,
54 ROFF_de1,
55 ROFF_ds,
56 ROFF_el,
57 ROFF_hy,
58 ROFF_ie,
59 ROFF_if,
60 ROFF_ig,
61 ROFF_ne,
62 ROFF_nh,
63 ROFF_rm,
64 ROFF_tr,
65 ROFF_cblock,
66 ROFF_ccond, /* FIXME: remove this. */
67 ROFF_nr,
68 ROFF_MAX
69 };
70
71 enum roffrule {
72 ROFFRULE_ALLOW,
73 ROFFRULE_DENY
74 };
75
76
77 struct roffstr {
78 char *name; /* key of symbol */
79 char *string; /* current value */
80 struct roffstr *next; /* next in list */
81 };
82
83 struct roff {
84 struct roffnode *last; /* leaf of stack */
85 mandocmsg msg; /* err/warn/fatal messages */
86 void *data; /* privdata for messages */
87 enum roffrule rstack[RSTACK_MAX]; /* stack of !`ie' rules */
88 int rstackpos; /* position in rstack */
89 struct regset *regs; /* read/writable registers */
90 struct roffstr *first_string;
91 };
92
93 struct roffnode {
94 enum rofft tok; /* type of node */
95 struct roffnode *parent; /* up one in stack */
96 int line; /* parse line */
97 int col; /* parse col */
98 char *end; /* end-rules: custom token */
99 int endspan; /* end-rules: next-line or infty */
100 enum roffrule rule; /* current evaluation rule */
101 };
102
103 #define ROFF_ARGS struct roff *r, /* parse ctx */ \
104 enum rofft tok, /* tok of macro */ \
105 char **bufp, /* input buffer */ \
106 size_t *szp, /* size of input buffer */ \
107 int ln, /* parse line */ \
108 int ppos, /* original pos in buffer */ \
109 int pos, /* current pos in buffer */ \
110 int *offs /* reset offset of buffer data */
111
112 typedef enum rofferr (*roffproc)(ROFF_ARGS);
113
114 struct roffmac {
115 const char *name; /* macro name */
116 roffproc proc; /* process new macro */
117 roffproc text; /* process as child text of macro */
118 roffproc sub; /* process as child of macro */
119 int flags;
120 #define ROFFMAC_STRUCT (1 << 0) /* always interpret */
121 struct roffmac *next;
122 };
123
124 static enum rofferr roff_block(ROFF_ARGS);
125 static enum rofferr roff_block_text(ROFF_ARGS);
126 static enum rofferr roff_block_sub(ROFF_ARGS);
127 static enum rofferr roff_cblock(ROFF_ARGS);
128 static enum rofferr roff_ccond(ROFF_ARGS);
129 static enum rofferr roff_cond(ROFF_ARGS);
130 static enum rofferr roff_cond_text(ROFF_ARGS);
131 static enum rofferr roff_cond_sub(ROFF_ARGS);
132 static enum rofferr roff_ds(ROFF_ARGS);
133 static enum roffrule roff_evalcond(const char *, int *);
134 static void roff_freestr(struct roff *);
135 static const char *roff_getstrn(const struct roff *,
136 const char *, size_t);
137 static enum rofferr roff_line_ignore(ROFF_ARGS);
138 static enum rofferr roff_nr(ROFF_ARGS);
139 static int roff_res(struct roff *,
140 char **, size_t *, int);
141 static void roff_setstr(struct roff *,
142 const char *, const char *);
143 static char *roff_strdup(const char *);
144
145 /* See roff_hash_find() */
146
147 #define ASCII_HI 126
148 #define ASCII_LO 33
149 #define HASHWIDTH (ASCII_HI - ASCII_LO + 1)
150
151 static struct roffmac *hash[HASHWIDTH];
152
153 static struct roffmac roffs[ROFF_MAX] = {
154 { "ad", roff_line_ignore, NULL, NULL, 0, NULL },
155 { "am", roff_block, roff_block_text, roff_block_sub, 0, NULL },
156 { "ami", roff_block, roff_block_text, roff_block_sub, 0, NULL },
157 { "am1", roff_block, roff_block_text, roff_block_sub, 0, NULL },
158 { "de", roff_block, roff_block_text, roff_block_sub, 0, NULL },
159 { "dei", roff_block, roff_block_text, roff_block_sub, 0, NULL },
160 { "de1", roff_block, roff_block_text, roff_block_sub, 0, NULL },
161 { "ds", roff_ds, NULL, NULL, 0, NULL },
162 { "el", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
163 { "hy", roff_line_ignore, NULL, NULL, 0, NULL },
164 { "ie", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
165 { "if", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
166 { "ig", roff_block, roff_block_text, roff_block_sub, 0, NULL },
167 { "ne", roff_line_ignore, NULL, NULL, 0, NULL },
168 { "nh", roff_line_ignore, NULL, NULL, 0, NULL },
169 { "rm", roff_line_ignore, NULL, NULL, 0, NULL },
170 { "tr", roff_line_ignore, NULL, NULL, 0, NULL },
171 { ".", roff_cblock, NULL, NULL, 0, NULL },
172 { "\\}", roff_ccond, NULL, NULL, 0, NULL },
173 { "nr", roff_nr, NULL, NULL, 0, NULL },
174 };
175
176 static void roff_free1(struct roff *);
177 static enum rofft roff_hash_find(const char *);
178 static void roff_hash_init(void);
179 static void roffnode_cleanscope(struct roff *);
180 static void roffnode_push(struct roff *,
181 enum rofft, int, int);
182 static void roffnode_pop(struct roff *);
183 static enum rofft roff_parse(const char *, int *);
184 static int roff_parse_nat(const char *, unsigned int *);
185
186 /* See roff_hash_find() */
187 #define ROFF_HASH(p) (p[0] - ASCII_LO)
188
189 static void
190 roff_hash_init(void)
191 {
192 struct roffmac *n;
193 int buc, i;
194
195 for (i = 0; i < (int)ROFF_MAX; i++) {
196 assert(roffs[i].name[0] >= ASCII_LO);
197 assert(roffs[i].name[0] <= ASCII_HI);
198
199 buc = ROFF_HASH(roffs[i].name);
200
201 if (NULL != (n = hash[buc])) {
202 for ( ; n->next; n = n->next)
203 /* Do nothing. */ ;
204 n->next = &roffs[i];
205 } else
206 hash[buc] = &roffs[i];
207 }
208 }
209
210
211 /*
212 * Look up a roff token by its name. Returns ROFF_MAX if no macro by
213 * the nil-terminated string name could be found.
214 */
215 static enum rofft
216 roff_hash_find(const char *p)
217 {
218 int buc;
219 struct roffmac *n;
220
221 /*
222 * libroff has an extremely simple hashtable, for the time
223 * being, which simply keys on the first character, which must
224 * be printable, then walks a chain. It works well enough until
225 * optimised.
226 */
227
228 if (p[0] < ASCII_LO || p[0] > ASCII_HI)
229 return(ROFF_MAX);
230
231 buc = ROFF_HASH(p);
232
233 if (NULL == (n = hash[buc]))
234 return(ROFF_MAX);
235 for ( ; n; n = n->next)
236 if (0 == strcmp(n->name, p))
237 return((enum rofft)(n - roffs));
238
239 return(ROFF_MAX);
240 }
241
242
243 /*
244 * Pop the current node off of the stack of roff instructions currently
245 * pending.
246 */
247 static void
248 roffnode_pop(struct roff *r)
249 {
250 struct roffnode *p;
251
252 assert(r->last);
253 p = r->last;
254
255 if (ROFF_el == p->tok)
256 if (r->rstackpos > -1)
257 r->rstackpos--;
258
259 ROFF_DEBUG("roff: popping scope\n");
260 r->last = r->last->parent;
261 if (p->end)
262 free(p->end);
263 free(p);
264 }
265
266
267 /*
268 * Push a roff node onto the instruction stack. This must later be
269 * removed with roffnode_pop().
270 */
271 static void
272 roffnode_push(struct roff *r, enum rofft tok, int line, int col)
273 {
274 struct roffnode *p;
275
276 p = mandoc_calloc(1, sizeof(struct roffnode));
277 p->tok = tok;
278 p->parent = r->last;
279 p->line = line;
280 p->col = col;
281 p->rule = p->parent ? p->parent->rule : ROFFRULE_DENY;
282
283 r->last = p;
284 }
285
286
287 static void
288 roff_free1(struct roff *r)
289 {
290
291 while (r->last)
292 roffnode_pop(r);
293 roff_freestr(r);
294 }
295
296
297 void
298 roff_reset(struct roff *r)
299 {
300
301 roff_free1(r);
302 }
303
304
305 void
306 roff_free(struct roff *r)
307 {
308
309 roff_free1(r);
310 free(r);
311 }
312
313
314 struct roff *
315 roff_alloc(struct regset *regs, void *data, const mandocmsg msg)
316 {
317 struct roff *r;
318
319 r = mandoc_calloc(1, sizeof(struct roff));
320 r->regs = regs;
321 r->msg = msg;
322 r->data = data;
323 r->rstackpos = -1;
324
325 roff_hash_init();
326 return(r);
327 }
328
329
330 /*
331 * Pre-filter each and every line for reserved words (one beginning with
332 * `\*', e.g., `\*(ab'). These must be handled before the actual line
333 * is processed.
334 */
335 static int
336 roff_res(struct roff *r, char **bufp, size_t *szp, int pos)
337 {
338 const char *cp, *cpp, *st, *res;
339 int i, maxl;
340 size_t nsz;
341 char *n;
342
343 /* LINTED */
344 for (cp = &(*bufp)[pos]; (cpp = strstr(cp, "\\*")); cp++) {
345 cp = cpp + 2;
346 switch (*cp) {
347 case ('('):
348 cp++;
349 maxl = 2;
350 break;
351 case ('['):
352 cp++;
353 maxl = 0;
354 break;
355 default:
356 maxl = 1;
357 break;
358 }
359
360 st = cp;
361
362 for (i = 0; 0 == maxl || i < maxl; i++, cp++) {
363 if ('\0' == *cp)
364 return(1); /* Error. */
365 if (0 == maxl && ']' == *cp)
366 break;
367 }
368
369 res = roff_getstrn(r, st, (size_t)i);
370
371 if (NULL == res) {
372 cp -= maxl ? 1 : 0;
373 continue;
374 }
375
376 ROFF_DEBUG("roff: splicing reserved: [%.*s]\n", i, st);
377
378 nsz = *szp + strlen(res) + 1;
379 n = mandoc_malloc(nsz);
380
381 *n = '\0';
382
383 strlcat(n, *bufp, (size_t)(cpp - *bufp + 1));
384 strlcat(n, res, nsz);
385 strlcat(n, cp + (maxl ? 0 : 1), nsz);
386
387 free(*bufp);
388
389 *bufp = n;
390 *szp = nsz;
391 return(0);
392 }
393
394 return(1);
395 }
396
397
398 enum rofferr
399 roff_parseln(struct roff *r, int ln, char **bufp,
400 size_t *szp, int pos, int *offs)
401 {
402 enum rofft t;
403 int ppos;
404
405 /*
406 * Run the reserved-word filter only if we have some reserved
407 * words to fill in.
408 */
409
410 if (r->first_string && ! roff_res(r, bufp, szp, pos))
411 return(ROFF_RERUN);
412
413 /*
414 * First, if a scope is open and we're not a macro, pass the
415 * text through the macro's filter. If a scope isn't open and
416 * we're not a macro, just let it through.
417 */
418
419 if (r->last && ! ROFF_CTL((*bufp)[pos])) {
420 t = r->last->tok;
421 assert(roffs[t].text);
422 ROFF_DEBUG("roff: intercept scoped text: %s, [%s]\n",
423 roffs[t].name, &(*bufp)[pos]);
424 return((*roffs[t].text)
425 (r, t, bufp, szp,
426 ln, pos, pos, offs));
427 } else if ( ! ROFF_CTL((*bufp)[pos]))
428 return(ROFF_CONT);
429
430 /*
431 * If a scope is open, go to the child handler for that macro,
432 * as it may want to preprocess before doing anything with it.
433 */
434
435 if (r->last) {
436 t = r->last->tok;
437 assert(roffs[t].sub);
438 ROFF_DEBUG("roff: intercept scoped context: %s, [%s]\n",
439 roffs[t].name, &(*bufp)[pos]);
440 return((*roffs[t].sub)
441 (r, t, bufp, szp,
442 ln, pos, pos, offs));
443 }
444
445 /*
446 * Lastly, as we've no scope open, try to look up and execute
447 * the new macro. If no macro is found, simply return and let
448 * the compilers handle it.
449 */
450
451 ppos = pos;
452 if (ROFF_MAX == (t = roff_parse(*bufp, &pos)))
453 return(ROFF_CONT);
454
455 ROFF_DEBUG("roff: intercept new-scope: %s, [%s]\n",
456 roffs[t].name, &(*bufp)[pos]);
457 assert(roffs[t].proc);
458 return((*roffs[t].proc)
459 (r, t, bufp, szp,
460 ln, ppos, pos, offs));
461 }
462
463
464 int
465 roff_endparse(struct roff *r)
466 {
467
468 if (NULL == r->last)
469 return(1);
470 return((*r->msg)(MANDOCERR_SCOPEEXIT, r->data, r->last->line,
471 r->last->col, NULL));
472 }
473
474
475 /*
476 * Parse a roff node's type from the input buffer. This must be in the
477 * form of ".foo xxx" in the usual way.
478 */
479 static enum rofft
480 roff_parse(const char *buf, int *pos)
481 {
482 int j;
483 char mac[5];
484 enum rofft t;
485
486 assert(ROFF_CTL(buf[*pos]));
487 (*pos)++;
488
489 while (buf[*pos] && (' ' == buf[*pos] || '\t' == buf[*pos]))
490 (*pos)++;
491
492 if ('\0' == buf[*pos])
493 return(ROFF_MAX);
494
495 for (j = 0; j < 4; j++, (*pos)++)
496 if ('\0' == (mac[j] = buf[*pos]))
497 break;
498 else if (' ' == buf[*pos] || (j && '\\' == buf[*pos]))
499 break;
500
501 if (j == 4 || j < 1)
502 return(ROFF_MAX);
503
504 mac[j] = '\0';
505
506 if (ROFF_MAX == (t = roff_hash_find(mac)))
507 return(t);
508
509 while (buf[*pos] && ' ' == buf[*pos])
510 (*pos)++;
511
512 return(t);
513 }
514
515
516 static int
517 roff_parse_nat(const char *buf, unsigned int *res)
518 {
519 char *ep;
520 long lval;
521
522 errno = 0;
523 lval = strtol(buf, &ep, 10);
524 if (buf[0] == '\0' || *ep != '\0')
525 return(0);
526 if ((errno == ERANGE &&
527 (lval == LONG_MAX || lval == LONG_MIN)) ||
528 (lval > INT_MAX || lval < 0))
529 return(0);
530
531 *res = (unsigned int)lval;
532 return(1);
533 }
534
535
536 /* ARGSUSED */
537 static enum rofferr
538 roff_cblock(ROFF_ARGS)
539 {
540
541 /*
542 * A block-close `..' should only be invoked as a child of an
543 * ignore macro, otherwise raise a warning and just ignore it.
544 */
545
546 if (NULL == r->last) {
547 if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
548 return(ROFF_ERR);
549 return(ROFF_IGN);
550 }
551
552 switch (r->last->tok) {
553 case (ROFF_am):
554 /* FALLTHROUGH */
555 case (ROFF_ami):
556 /* FALLTHROUGH */
557 case (ROFF_am1):
558 /* FALLTHROUGH */
559 case (ROFF_de):
560 /* FALLTHROUGH */
561 case (ROFF_dei):
562 /* FALLTHROUGH */
563 case (ROFF_de1):
564 /* FALLTHROUGH */
565 case (ROFF_ig):
566 break;
567 default:
568 if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
569 return(ROFF_ERR);
570 return(ROFF_IGN);
571 }
572
573 if ((*bufp)[pos])
574 if ( ! (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, pos, NULL))
575 return(ROFF_ERR);
576
577 roffnode_pop(r);
578 roffnode_cleanscope(r);
579 return(ROFF_IGN);
580
581 }
582
583
584 static void
585 roffnode_cleanscope(struct roff *r)
586 {
587
588 while (r->last) {
589 if (--r->last->endspan < 0)
590 break;
591 roffnode_pop(r);
592 }
593 }
594
595
596 /* ARGSUSED */
597 static enum rofferr
598 roff_ccond(ROFF_ARGS)
599 {
600
601 if (NULL == r->last) {
602 if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
603 return(ROFF_ERR);
604 return(ROFF_IGN);
605 }
606
607 switch (r->last->tok) {
608 case (ROFF_el):
609 /* FALLTHROUGH */
610 case (ROFF_ie):
611 /* FALLTHROUGH */
612 case (ROFF_if):
613 break;
614 default:
615 if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
616 return(ROFF_ERR);
617 return(ROFF_IGN);
618 }
619
620 if (r->last->endspan > -1) {
621 if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
622 return(ROFF_ERR);
623 return(ROFF_IGN);
624 }
625
626 if ((*bufp)[pos])
627 if ( ! (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, pos, NULL))
628 return(ROFF_ERR);
629
630 roffnode_pop(r);
631 roffnode_cleanscope(r);
632 return(ROFF_IGN);
633 }
634
635
636 /* ARGSUSED */
637 static enum rofferr
638 roff_block(ROFF_ARGS)
639 {
640 int sv;
641 size_t sz;
642
643 if (ROFF_ig != tok && '\0' == (*bufp)[pos]) {
644 if ( ! (*r->msg)(MANDOCERR_NOARGS, r->data, ln, ppos, NULL))
645 return(ROFF_ERR);
646 return(ROFF_IGN);
647 } else if (ROFF_ig != tok) {
648 while ((*bufp)[pos] && ' ' != (*bufp)[pos])
649 pos++;
650 while (' ' == (*bufp)[pos])
651 pos++;
652 }
653
654 roffnode_push(r, tok, ln, ppos);
655
656 if ('\0' == (*bufp)[pos])
657 return(ROFF_IGN);
658
659 sv = pos;
660 while ((*bufp)[pos] && ' ' != (*bufp)[pos] &&
661 '\t' != (*bufp)[pos])
662 pos++;
663
664 /*
665 * Note: groff does NOT like escape characters in the input.
666 * Instead of detecting this, we're just going to let it fly and
667 * to hell with it.
668 */
669
670 assert(pos > sv);
671 sz = (size_t)(pos - sv);
672
673 if (1 == sz && '.' == (*bufp)[sv])
674 return(ROFF_IGN);
675
676 r->last->end = mandoc_malloc(sz + 1);
677
678 memcpy(r->last->end, *bufp + sv, sz);
679 r->last->end[(int)sz] = '\0';
680
681 if ((*bufp)[pos])
682 if ( ! (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, pos, NULL))
683 return(ROFF_ERR);
684
685 return(ROFF_IGN);
686 }
687
688
689 /* ARGSUSED */
690 static enum rofferr
691 roff_block_sub(ROFF_ARGS)
692 {
693 enum rofft t;
694 int i, j;
695
696 /*
697 * First check whether a custom macro exists at this level. If
698 * it does, then check against it. This is some of groff's
699 * stranger behaviours. If we encountered a custom end-scope
700 * tag and that tag also happens to be a "real" macro, then we
701 * need to try interpreting it again as a real macro. If it's
702 * not, then return ignore. Else continue.
703 */
704
705 if (r->last->end) {
706 i = pos + 1;
707 while (' ' == (*bufp)[i] || '\t' == (*bufp)[i])
708 i++;
709
710 for (j = 0; r->last->end[j]; j++, i++)
711 if ((*bufp)[i] != r->last->end[j])
712 break;
713
714 if ('\0' == r->last->end[j] &&
715 ('\0' == (*bufp)[i] ||
716 ' ' == (*bufp)[i] ||
717 '\t' == (*bufp)[i])) {
718 roffnode_pop(r);
719 roffnode_cleanscope(r);
720
721 if (ROFF_MAX != roff_parse(*bufp, &pos))
722 return(ROFF_RERUN);
723 return(ROFF_IGN);
724 }
725 }
726
727 /*
728 * If we have no custom end-query or lookup failed, then try
729 * pulling it out of the hashtable.
730 */
731
732 ppos = pos;
733 t = roff_parse(*bufp, &pos);
734
735 /* If we're not a comment-end, then throw it away. */
736 if (ROFF_cblock != t)
737 return(ROFF_IGN);
738
739 assert(roffs[t].proc);
740 return((*roffs[t].proc)(r, t, bufp, szp,
741 ln, ppos, pos, offs));
742 }
743
744
745 /* ARGSUSED */
746 static enum rofferr
747 roff_block_text(ROFF_ARGS)
748 {
749
750 return(ROFF_IGN);
751 }
752
753
754 /* ARGSUSED */
755 static enum rofferr
756 roff_cond_sub(ROFF_ARGS)
757 {
758 enum rofft t;
759 enum roffrule rr;
760
761 ppos = pos;
762 rr = r->last->rule;
763
764 /*
765 * Clean out scope. If we've closed ourselves, then don't
766 * continue.
767 */
768
769 roffnode_cleanscope(r);
770
771 if (ROFF_MAX == (t = roff_parse(*bufp, &pos))) {
772 if ('\\' == (*bufp)[pos] && '}' == (*bufp)[pos + 1])
773 return(roff_ccond
774 (r, ROFF_ccond, bufp, szp,
775 ln, pos, pos + 2, offs));
776 return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
777 }
778
779 /*
780 * A denied conditional must evaluate its children if and only
781 * if they're either structurally required (such as loops and
782 * conditionals) or a closing macro.
783 */
784 if (ROFFRULE_DENY == rr)
785 if ( ! (ROFFMAC_STRUCT & roffs[t].flags))
786 if (ROFF_ccond != t)
787 return(ROFF_IGN);
788
789 assert(roffs[t].proc);
790 return((*roffs[t].proc)(r, t, bufp, szp,
791 ln, ppos, pos, offs));
792 }
793
794
795 /* ARGSUSED */
796 static enum rofferr
797 roff_cond_text(ROFF_ARGS)
798 {
799 char *ep, *st;
800 enum roffrule rr;
801
802 rr = r->last->rule;
803
804 /*
805 * We display the value of the text if out current evaluation
806 * scope permits us to do so.
807 */
808
809 /* FIXME: use roff_ccond? */
810
811 st = &(*bufp)[pos];
812 if (NULL == (ep = strstr(st, "\\}"))) {
813 roffnode_cleanscope(r);
814 return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
815 }
816
817 if (ep == st || (ep > st && '\\' != *(ep - 1)))
818 roffnode_pop(r);
819
820 roffnode_cleanscope(r);
821 return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
822 }
823
824
825 static enum roffrule
826 roff_evalcond(const char *v, int *pos)
827 {
828
829 switch (v[*pos]) {
830 case ('n'):
831 (*pos)++;
832 return(ROFFRULE_ALLOW);
833 case ('e'):
834 /* FALLTHROUGH */
835 case ('o'):
836 /* FALLTHROUGH */
837 case ('t'):
838 (*pos)++;
839 return(ROFFRULE_DENY);
840 default:
841 break;
842 }
843
844 while (v[*pos] && ' ' != v[*pos])
845 (*pos)++;
846 return(ROFFRULE_DENY);
847 }
848
849
850 /* ARGSUSED */
851 static enum rofferr
852 roff_line_ignore(ROFF_ARGS)
853 {
854
855 return(ROFF_IGN);
856 }
857
858
859 /* ARGSUSED */
860 static enum rofferr
861 roff_cond(ROFF_ARGS)
862 {
863 int sv;
864 enum roffrule rule;
865
866 /* Stack overflow! */
867
868 if (ROFF_ie == tok && r->rstackpos == RSTACK_MAX - 1) {
869 (*r->msg)(MANDOCERR_MEM, r->data, ln, ppos, NULL);
870 return(ROFF_ERR);
871 }
872
873 /* First, evaluate the conditional. */
874
875 if (ROFF_el == tok) {
876 /*
877 * An `.el' will get the value of the current rstack
878 * entry set in prior `ie' calls or defaults to DENY.
879 */
880 if (r->rstackpos < 0)
881 rule = ROFFRULE_DENY;
882 else
883 rule = r->rstack[r->rstackpos];
884 } else
885 rule = roff_evalcond(*bufp, &pos);
886
887 sv = pos;
888
889 while (' ' == (*bufp)[pos])
890 pos++;
891
892 /*
893 * Roff is weird. If we have just white-space after the
894 * conditional, it's considered the BODY and we exit without
895 * really doing anything. Warn about this. It's probably
896 * wrong.
897 */
898
899 if ('\0' == (*bufp)[pos] && sv != pos) {
900 if ((*r->msg)(MANDOCERR_NOARGS, r->data, ln, ppos, NULL))
901 return(ROFF_IGN);
902 return(ROFF_ERR);
903 }
904
905 roffnode_push(r, tok, ln, ppos);
906
907 r->last->rule = rule;
908
909 ROFF_DEBUG("roff: cond: %s -> %s\n", roffs[tok].name,
910 ROFFRULE_ALLOW == rule ? "allow" : "deny");
911
912 if (ROFF_ie == tok) {
913 /*
914 * An if-else will put the NEGATION of the current
915 * evaluated conditional into the stack.
916 */
917 r->rstackpos++;
918 if (ROFFRULE_DENY == r->last->rule)
919 r->rstack[r->rstackpos] = ROFFRULE_ALLOW;
920 else
921 r->rstack[r->rstackpos] = ROFFRULE_DENY;
922 }
923
924 /* If the parent has false as its rule, then so do we. */
925
926 if (r->last->parent && ROFFRULE_DENY == r->last->parent->rule) {
927 r->last->rule = ROFFRULE_DENY;
928 ROFF_DEBUG("roff: cond override: %s -> deny\n",
929 roffs[tok].name);
930 }
931
932 /*
933 * Determine scope. If we're invoked with "\{" trailing the
934 * conditional, then we're in a multiline scope. Else our scope
935 * expires on the next line.
936 */
937
938 r->last->endspan = 1;
939
940 if ('\\' == (*bufp)[pos] && '{' == (*bufp)[pos + 1]) {
941 r->last->endspan = -1;
942 pos += 2;
943 ROFF_DEBUG("roff: cond-scope: %s, multi-line\n",
944 roffs[tok].name);
945 } else
946 ROFF_DEBUG("roff: cond-scope: %s, one-line\n",
947 roffs[tok].name);
948
949 /*
950 * If there are no arguments on the line, the next-line scope is
951 * assumed.
952 */
953
954 if ('\0' == (*bufp)[pos])
955 return(ROFF_IGN);
956
957 /* Otherwise re-run the roff parser after recalculating. */
958
959 *offs = pos;
960 return(ROFF_RERUN);
961 }
962
963
964 /* ARGSUSED */
965 static enum rofferr
966 roff_ds(ROFF_ARGS)
967 {
968 char *name, *string;
969
970 /*
971 * A symbol is named by the first word following the macro
972 * invocation up to a space. Its value is anything after the
973 * name's trailing whitespace and optional double-quote. Thus,
974 *
975 * [.ds foo "bar " ]
976 *
977 * will have `bar " ' as its value.
978 */
979
980 name = *bufp + pos;
981 if ('\0' == *name)
982 return(ROFF_IGN);
983
984 string = name;
985 /* Read until end of name. */
986 while (*string && ' ' != *string)
987 string++;
988
989 /* Nil-terminate name. */
990 if (*string)
991 *(string++) = '\0';
992
993 /* Read past spaces. */
994 while (*string && ' ' == *string)
995 string++;
996
997 /* Read passed initial double-quote. */
998 if (*string && '"' == *string)
999 string++;
1000
1001 /* The rest is the value. */
1002 roff_setstr(r, name, string);
1003 return(ROFF_IGN);
1004 }
1005
1006
1007 /* ARGSUSED */
1008 static enum rofferr
1009 roff_nr(ROFF_ARGS)
1010 {
1011 const char *key, *val;
1012 struct reg *rg;
1013
1014 key = &(*bufp)[pos];
1015 rg = r->regs->regs;
1016
1017 /* Parse register request. */
1018 while ((*bufp)[pos] && ' ' != (*bufp)[pos])
1019 pos++;
1020
1021 /*
1022 * Set our nil terminator. Because this line is going to be
1023 * ignored anyway, we can munge it as we please.
1024 */
1025 if ((*bufp)[pos])
1026 (*bufp)[pos++] = '\0';
1027
1028 /* Skip whitespace to register token. */
1029 while ((*bufp)[pos] && ' ' == (*bufp)[pos])
1030 pos++;
1031
1032 val = &(*bufp)[pos];
1033
1034 /* Process register token. */
1035
1036 if (0 == strcmp(key, "nS")) {
1037 rg[(int)REG_nS].set = 1;
1038 if ( ! roff_parse_nat(val, &rg[(int)REG_nS].v.u))
1039 rg[(int)REG_nS].v.u = 0;
1040
1041 ROFF_DEBUG("roff: register nS: %u\n",
1042 rg[(int)REG_nS].v.u);
1043 } else
1044 ROFF_DEBUG("roff: ignoring register: %s\n", key);
1045
1046 return(ROFF_IGN);
1047 }
1048
1049
1050 static char *
1051 roff_strdup(const char *name)
1052 {
1053 char *namecopy, *sv;
1054
1055 /*
1056 * This isn't a nice simple mandoc_strdup() because we must
1057 * handle roff's stupid double-escape rule.
1058 */
1059 sv = namecopy = mandoc_malloc(strlen(name) + 1);
1060 while (*name) {
1061 if ('\\' == *name && '\\' == *(name + 1))
1062 name++;
1063 *namecopy++ = *name++;
1064 }
1065
1066 *namecopy = '\0';
1067 return(sv);
1068 }
1069
1070
1071 static void
1072 roff_setstr(struct roff *r, const char *name, const char *string)
1073 {
1074 struct roffstr *n;
1075 char *namecopy;
1076
1077 n = r->first_string;
1078 while (n && strcmp(name, n->name))
1079 n = n->next;
1080
1081 if (NULL == n) {
1082 namecopy = mandoc_strdup(name);
1083 n = mandoc_malloc(sizeof(struct roffstr));
1084 n->name = namecopy;
1085 n->next = r->first_string;
1086 r->first_string = n;
1087 } else
1088 free(n->string);
1089
1090 /* Don't use mandoc_strdup: clean out double-escapes. */
1091 n->string = string ? roff_strdup(string) : NULL;
1092 ROFF_DEBUG("roff: new symbol: [%s] = [%s]\n", name, n->string);
1093 }
1094
1095
1096 static const char *
1097 roff_getstrn(const struct roff *r, const char *name, size_t len)
1098 {
1099 const struct roffstr *n;
1100
1101 n = r->first_string;
1102 while (n && (strncmp(name, n->name, len) || '\0' != n->name[(int)len]))
1103 n = n->next;
1104
1105 return(n ? n->string : NULL);
1106 }
1107
1108
1109 static void
1110 roff_freestr(struct roff *r)
1111 {
1112 struct roffstr *n, *nn;
1113
1114 for (n = r->first_string; n; n = nn) {
1115 free(n->name);
1116 free(n->string);
1117 nn = n->next;
1118 free(n);
1119 }
1120
1121 r->first_string = NULL;
1122 }