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