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