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