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