Annotation of src/usr.bin/mandoc/roff.c, Revision 1.4
1.4 ! schwarze 1: /* $Id: roff.c,v 1.3 2010/06/06 18:08:41 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);
106: static enum rofferr roff_line(ROFF_ARGS);
1.1 schwarze 107:
1.3 schwarze 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.1 schwarze 132: };
133:
134: static void roff_free1(struct roff *);
135: static enum rofft roff_hash_find(const char *);
1.3 schwarze 136: static void roff_hash_init(void);
1.2 schwarze 137: static void roffnode_cleanscope(struct roff *);
1.1 schwarze 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.3 schwarze 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.1 schwarze 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.3 schwarze 175: int buc;
176: struct roffmac *n;
1.1 schwarze 177:
1.3 schwarze 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.1 schwarze 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.2 schwarze 209: assert(r->last);
210: p = r->last;
211:
212: if (ROFF_el == p->tok)
213: if (r->rstackpos > -1)
214: r->rstackpos--;
215:
216: r->last = r->last->parent;
217: if (p->end)
218: free(p->end);
1.1 schwarze 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.2 schwarze 241: p->rule = p->parent ? p->parent->rule : ROFFRULE_DENY;
1.1 schwarze 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.2 schwarze 286: r->rstackpos = -1;
1.3 schwarze 287:
288: roff_hash_init();
1.1 schwarze 289: return(r);
290: }
291:
292:
293: enum rofferr
1.2 schwarze 294: roff_parseln(struct roff *r, int ln,
295: char **bufp, size_t *szp, int pos, int *offs)
1.1 schwarze 296: {
297: enum rofft t;
298: int ppos;
299:
1.2 schwarze 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: */
305:
306: if (r->last && ! ROFF_CTL((*bufp)[pos])) {
307: t = r->last->tok;
308: assert(roffs[t].text);
309: return((*roffs[t].text)
310: (r, t, bufp, szp, ln, pos, pos, offs));
311: } else if ( ! ROFF_CTL((*bufp)[pos]))
312: return(ROFF_CONT);
313:
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: */
318:
319: if (r->last) {
1.1 schwarze 320: t = r->last->tok;
321: assert(roffs[t].sub);
1.2 schwarze 322: return((*roffs[t].sub)
323: (r, t, bufp, szp, ln, pos, pos, offs));
324: }
325:
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: */
331:
332: ppos = pos;
333: if (ROFF_MAX == (t = roff_parse(*bufp, &pos)))
1.1 schwarze 334: return(ROFF_CONT);
335:
1.2 schwarze 336: assert(roffs[t].proc);
337: return((*roffs[t].proc)
338: (r, t, bufp, szp, ln, ppos, pos, offs));
339: }
340:
1.1 schwarze 341:
1.2 schwarze 342: int
343: roff_endparse(struct roff *r)
344: {
1.1 schwarze 345:
1.2 schwarze 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.1 schwarze 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.2 schwarze 364: assert(ROFF_CTL(buf[*pos]));
365: (*pos)++;
1.1 schwarze 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.2 schwarze 376: else if (' ' == buf[*pos] || (j && '\\' == buf[*pos]))
1.1 schwarze 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.2 schwarze 396: roff_cblock(ROFF_ARGS)
1.1 schwarze 397: {
398:
1.2 schwarze 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:
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.1 schwarze 409:
1.2 schwarze 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:
426: if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
427: return(ROFF_ERR);
1.1 schwarze 428: return(ROFF_IGN);
1.2 schwarze 429: }
430:
431: if ((*bufp)[pos])
432: if ( ! (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, pos, NULL))
433: return(ROFF_ERR);
434:
435: roffnode_pop(r);
436: roffnode_cleanscope(r);
437: return(ROFF_IGN);
438:
439: }
1.1 schwarze 440:
441:
1.2 schwarze 442: static void
443: roffnode_cleanscope(struct roff *r)
444: {
1.1 schwarze 445:
1.2 schwarze 446: while (r->last) {
447: if (--r->last->endspan < 0)
448: break;
449: roffnode_pop(r);
450: }
451: }
1.1 schwarze 452:
453:
1.2 schwarze 454: /* ARGSUSED */
455: static enum rofferr
456: roff_ccond(ROFF_ARGS)
457: {
1.1 schwarze 458:
1.2 schwarze 459: if (NULL == r->last) {
460: if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
461: return(ROFF_ERR);
1.1 schwarze 462: return(ROFF_IGN);
1.2 schwarze 463: }
1.1 schwarze 464:
1.2 schwarze 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:
473: if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
474: return(ROFF_ERR);
475: return(ROFF_IGN);
476: }
1.1 schwarze 477:
1.2 schwarze 478: if (r->last->endspan > -1) {
479: if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
480: return(ROFF_ERR);
1.1 schwarze 481: return(ROFF_IGN);
1.2 schwarze 482: }
483:
484: if ((*bufp)[pos])
485: if ( ! (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, pos, NULL))
486: return(ROFF_ERR);
1.1 schwarze 487:
1.2 schwarze 488: roffnode_pop(r);
489: roffnode_cleanscope(r);
1.1 schwarze 490: return(ROFF_IGN);
491: }
492:
493:
494: /* ARGSUSED */
495: static enum rofferr
1.2 schwarze 496: roff_block(ROFF_ARGS)
1.1 schwarze 497: {
1.2 schwarze 498: int sv;
499: size_t sz;
500:
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:
512: if ( ! roffnode_push(r, tok, ln, ppos))
513: return(ROFF_ERR);
514:
515: if ('\0' == (*bufp)[pos])
516: return(ROFF_IGN);
1.1 schwarze 517:
1.2 schwarze 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:
532: if (1 == sz && '.' == (*bufp)[sv])
533: return(ROFF_IGN);
534:
535: r->last->end = malloc(sz + 1);
536:
537: if (NULL == r->last->end) {
538: (*r->msg)(MANDOCERR_MEM, r->data, ln, pos, NULL);
1.1 schwarze 539: return(ROFF_ERR);
1.2 schwarze 540: }
541:
542: memcpy(r->last->end, *bufp + sv, sz);
543: r->last->end[(int)sz] = '\0';
544:
545: if ((*bufp)[pos])
546: if ( ! (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, pos, NULL))
547: return(ROFF_ERR);
1.1 schwarze 548:
549: return(ROFF_IGN);
550: }
551:
552:
553: /* ARGSUSED */
554: static enum rofferr
1.2 schwarze 555: roff_block_sub(ROFF_ARGS)
1.1 schwarze 556: {
1.2 schwarze 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;
1.1 schwarze 577:
1.2 schwarze 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);
1.1 schwarze 584:
1.2 schwarze 585: if (ROFF_MAX != roff_parse(*bufp, &pos))
586: return(ROFF_RERUN);
587: return(ROFF_IGN);
588: }
1.1 schwarze 589: }
590:
1.2 schwarze 591: /*
592: * If we have no custom end-query or lookup failed, then try
593: * pulling it out of the hashtable.
594: */
1.1 schwarze 595:
1.2 schwarze 596: ppos = pos;
597: t = roff_parse(*bufp, &pos);
1.1 schwarze 598:
1.2 schwarze 599: /* If we're not a comment-end, then throw it away. */
600: if (ROFF_cblock != t)
1.1 schwarze 601: return(ROFF_IGN);
602:
1.2 schwarze 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
611: roff_block_text(ROFF_ARGS)
612: {
613:
614: return(ROFF_IGN);
615: }
616:
617:
618: /* ARGSUSED */
619: static enum rofferr
620: roff_cond_sub(ROFF_ARGS)
621: {
622: enum rofft t;
623: enum roffrule rr;
624:
625: ppos = pos;
626: rr = r->last->rule;
627:
628: roff_cond_text(r, tok, bufp, szp, ln, ppos, pos, offs);
629:
630: if (ROFF_MAX == (t = roff_parse(*bufp, &pos)))
631: return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
632:
633: /*
634: * A denied conditional must evaluate its children if and only
635: * if they're either structurally required (such as loops and
636: * conditionals) or a closing macro.
637: */
638: if (ROFFRULE_DENY == rr)
639: if ( ! (ROFFMAC_STRUCT & roffs[t].flags))
640: if (ROFF_ccond != t)
641: return(ROFF_IGN);
642:
643: assert(roffs[t].proc);
644: return((*roffs[t].proc)
645: (r, t, bufp, szp, ln, ppos, pos, offs));
646: }
647:
648:
649: /* ARGSUSED */
650: static enum rofferr
651: roff_cond_text(ROFF_ARGS)
652: {
653: char *ep, *st;
654: enum roffrule rr;
655:
656: rr = r->last->rule;
1.1 schwarze 657:
658: /*
1.2 schwarze 659: * We display the value of the text if out current evaluation
660: * scope permits us to do so.
1.1 schwarze 661: */
662:
1.2 schwarze 663: st = &(*bufp)[pos];
664: if (NULL == (ep = strstr(st, "\\}"))) {
665: roffnode_cleanscope(r);
666: return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
667: }
668:
1.4 ! schwarze 669: if (ep == st || (ep > st && '\\' != *(ep - 1)))
1.2 schwarze 670: roffnode_pop(r);
671:
672: roffnode_cleanscope(r);
673: return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
674: }
675:
676:
677: /* ARGSUSED */
678: static enum rofferr
679: roff_cond(ROFF_ARGS)
680: {
681: int cpos; /* position of the condition */
682: int sv;
683:
684: /* Stack overflow! */
685:
686: if (ROFF_ie == tok && r->rstackpos == RSTACK_MAX - 1) {
1.1 schwarze 687: (*r->msg)(MANDOCERR_MEM, r->data, ln, ppos, NULL);
688: return(ROFF_ERR);
689: }
690:
1.2 schwarze 691: cpos = pos;
692:
693: if (ROFF_if == tok || ROFF_ie == tok) {
694: /*
695: * Read ahead past the conditional. FIXME: this does
696: * not work, as conditionals don't end on whitespace,
697: * but are parsed according to a formal grammar. It's
698: * good enough for now, however.
699: */
700: while ((*bufp)[pos] && ' ' != (*bufp)[pos])
701: pos++;
702: }
703:
704: sv = pos;
705: while (' ' == (*bufp)[pos])
706: pos++;
707:
708: /*
709: * Roff is weird. If we have just white-space after the
710: * conditional, it's considered the BODY and we exit without
711: * really doing anything. Warn about this. It's probably
712: * wrong.
713: */
714: if ('\0' == (*bufp)[pos] && sv != pos) {
715: if ( ! (*r->msg)(MANDOCERR_NOARGS, r->data, ln, ppos, NULL))
716: return(ROFF_ERR);
717: return(ROFF_IGN);
718: }
719:
720: if ( ! roffnode_push(r, tok, ln, ppos))
721: return(ROFF_ERR);
722:
723: /* XXX: Implement more conditionals. */
724:
725: if (ROFF_if == tok || ROFF_ie == tok)
726: r->last->rule = 'n' == (*bufp)[cpos] ?
727: ROFFRULE_ALLOW : ROFFRULE_DENY;
728: else if (ROFF_el == tok) {
729: /*
730: * An `.el' will get the value of the current rstack
731: * entry set in prior `ie' calls or defaults to DENY.
732: */
733: if (r->rstackpos < 0)
734: r->last->rule = ROFFRULE_DENY;
735: else
736: r->last->rule = r->rstack[r->rstackpos];
737: }
738: if (ROFF_ie == tok) {
739: /*
740: * An if-else will put the NEGATION of the current
741: * evaluated conditional into the stack.
742: */
743: r->rstackpos++;
744: if (ROFFRULE_DENY == r->last->rule)
745: r->rstack[r->rstackpos] = ROFFRULE_ALLOW;
746: else
747: r->rstack[r->rstackpos] = ROFFRULE_DENY;
748: }
749: if (r->last->parent && ROFFRULE_DENY == r->last->parent->rule)
750: r->last->rule = ROFFRULE_DENY;
751:
752: r->last->endspan = 1;
753:
754: if ('\\' == (*bufp)[pos] && '{' == (*bufp)[pos + 1]) {
755: r->last->endspan = -1;
756: pos += 2;
757: }
758:
759: /*
760: * If there are no arguments on the line, the next-line scope is
761: * assumed.
762: */
763:
764: if ('\0' == (*bufp)[pos])
765: return(ROFF_IGN);
766:
767: /* Otherwise re-run the roff parser after recalculating. */
1.1 schwarze 768:
1.2 schwarze 769: *offs = pos;
770: return(ROFF_RERUN);
1.1 schwarze 771: }
772:
773:
1.2 schwarze 774: /* ARGSUSED */
775: static enum rofferr
776: roff_line(ROFF_ARGS)
1.1 schwarze 777: {
778:
1.2 schwarze 779: return(ROFF_IGN);
1.1 schwarze 780: }