Annotation of mandoc/roff.c, Revision 1.91
1.91 ! kristaps 1: /* $Id: roff.c,v 1.90 2010/06/27 15:52:41 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.90 kristaps 77: struct regset *regs; /* read/writable registers */
1.79 kristaps 78: };
79:
1.67 kristaps 80: struct roffnode {
81: enum rofft tok; /* type of node */
82: struct roffnode *parent; /* up one in stack */
83: int line; /* parse line */
84: int col; /* parse col */
1.79 kristaps 85: char *end; /* end-rules: custom token */
86: int endspan; /* end-rules: next-line or infty */
1.82 kristaps 87: enum roffrule rule; /* current evaluation rule */
1.67 kristaps 88: };
89:
90: #define ROFF_ARGS struct roff *r, /* parse ctx */ \
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.91 ! kristaps 158: static int roff_parse_nat(const char *, unsigned 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 *
1.90 kristaps 292: roff_alloc(struct regset *regs, const mandocmsg msg, void *data)
1.67 kristaps 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:
1.90 kristaps 301: r->regs = regs;
1.67 kristaps 302: r->msg = msg;
303: r->data = data;
1.82 kristaps 304: r->rstackpos = -1;
1.85 kristaps 305:
306: roff_hash_init();
1.67 kristaps 307: return(r);
308: }
309:
310:
311: enum rofferr
1.90 kristaps 312: roff_parseln(struct roff *r, int ln, char **bufp,
313: size_t *szp, int pos, int *offs)
1.67 kristaps 314: {
315: enum rofft t;
1.79 kristaps 316: int ppos;
317:
318: /*
319: * First, if a scope is open and we're not a macro, pass the
320: * text through the macro's filter. If a scope isn't open and
321: * we're not a macro, just let it through.
322: */
1.74 kristaps 323:
1.75 kristaps 324: if (r->last && ! ROFF_CTL((*bufp)[pos])) {
1.78 kristaps 325: t = r->last->tok;
326: assert(roffs[t].text);
1.88 kristaps 327: ROFF_DEBUG("roff: intercept scoped text: %s, [%s]\n",
328: roffs[t].name, &(*bufp)[pos]);
1.78 kristaps 329: return((*roffs[t].text)
1.90 kristaps 330: (r, t, bufp, szp,
331: ln, pos, pos, offs));
1.88 kristaps 332: } else if ( ! ROFF_CTL((*bufp)[pos])) {
333: ROFF_DEBUG("roff: pass non-scoped text: [%s]\n",
334: &(*bufp)[pos]);
1.67 kristaps 335: return(ROFF_CONT);
1.88 kristaps 336: }
1.67 kristaps 337:
1.79 kristaps 338: /*
339: * If a scope is open, go to the child handler for that macro,
340: * as it may want to preprocess before doing anything with it.
341: */
1.78 kristaps 342:
1.79 kristaps 343: if (r->last) {
344: t = r->last->tok;
345: assert(roffs[t].sub);
1.88 kristaps 346: ROFF_DEBUG("roff: intercept scoped context: %s\n",
347: roffs[t].name);
1.79 kristaps 348: return((*roffs[t].sub)
1.90 kristaps 349: (r, t, bufp, szp,
350: ln, pos, pos, offs));
1.79 kristaps 351: }
1.78 kristaps 352:
1.79 kristaps 353: /*
354: * Lastly, as we've no scope open, try to look up and execute
355: * the new macro. If no macro is found, simply return and let
356: * the compilers handle it.
357: */
1.67 kristaps 358:
1.75 kristaps 359: ppos = pos;
1.88 kristaps 360: if (ROFF_MAX == (t = roff_parse(*bufp, &pos))) {
361: ROFF_DEBUG("roff: pass non-scoped non-macro: [%s]\n",
362: &(*bufp)[pos]);
1.79 kristaps 363: return(ROFF_CONT);
1.88 kristaps 364: }
1.67 kristaps 365:
1.88 kristaps 366: ROFF_DEBUG("roff: intercept new-scope: %s, [%s]\n",
367: roffs[t].name, &(*bufp)[pos]);
1.75 kristaps 368: assert(roffs[t].proc);
1.78 kristaps 369: return((*roffs[t].proc)
1.90 kristaps 370: (r, t, bufp, szp,
371: ln, ppos, pos, offs));
1.74 kristaps 372: }
373:
374:
375: int
376: roff_endparse(struct roff *r)
377: {
378:
379: if (NULL == r->last)
380: return(1);
381: return((*r->msg)(MANDOCERR_SCOPEEXIT, r->data, r->last->line,
382: r->last->col, NULL));
1.67 kristaps 383: }
384:
385:
386: /*
387: * Parse a roff node's type from the input buffer. This must be in the
388: * form of ".foo xxx" in the usual way.
389: */
390: static enum rofft
391: roff_parse(const char *buf, int *pos)
392: {
393: int j;
394: char mac[5];
395: enum rofft t;
396:
1.75 kristaps 397: assert(ROFF_CTL(buf[*pos]));
398: (*pos)++;
1.67 kristaps 399:
400: while (buf[*pos] && (' ' == buf[*pos] || '\t' == buf[*pos]))
401: (*pos)++;
402:
403: if ('\0' == buf[*pos])
404: return(ROFF_MAX);
405:
406: for (j = 0; j < 4; j++, (*pos)++)
407: if ('\0' == (mac[j] = buf[*pos]))
408: break;
1.82 kristaps 409: else if (' ' == buf[*pos] || (j && '\\' == buf[*pos]))
1.67 kristaps 410: break;
411:
412: if (j == 4 || j < 1)
413: return(ROFF_MAX);
414:
415: mac[j] = '\0';
416:
417: if (ROFF_MAX == (t = roff_hash_find(mac)))
418: return(t);
419:
420: while (buf[*pos] && ' ' == buf[*pos])
421: (*pos)++;
422:
423: return(t);
424: }
425:
426:
1.89 kristaps 427: static int
1.91 ! kristaps 428: roff_parse_nat(const char *buf, unsigned int *res)
1.89 kristaps 429: {
430: char *ep;
431: long lval;
432:
433: errno = 0;
434: lval = strtol(buf, &ep, 10);
435: if (buf[0] == '\0' || *ep != '\0')
436: return(0);
437: if ((errno == ERANGE &&
438: (lval == LONG_MAX || lval == LONG_MIN)) ||
1.91 ! kristaps 439: (lval > INT_MAX || lval < 0))
1.89 kristaps 440: return(0);
441:
1.91 ! kristaps 442: *res = (unsigned int)lval;
1.89 kristaps 443: return(1);
444: }
445:
446:
1.67 kristaps 447: /* ARGSUSED */
448: static enum rofferr
1.76 kristaps 449: roff_cblock(ROFF_ARGS)
1.67 kristaps 450: {
451:
1.79 kristaps 452: /*
453: * A block-close `..' should only be invoked as a child of an
454: * ignore macro, otherwise raise a warning and just ignore it.
455: */
456:
1.76 kristaps 457: if (NULL == r->last) {
458: if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
459: return(ROFF_ERR);
460: return(ROFF_IGN);
461: }
1.67 kristaps 462:
1.81 kristaps 463: switch (r->last->tok) {
464: case (ROFF_am):
465: /* FALLTHROUGH */
466: case (ROFF_ami):
467: /* FALLTHROUGH */
468: case (ROFF_am1):
469: /* FALLTHROUGH */
470: case (ROFF_de):
471: /* FALLTHROUGH */
472: case (ROFF_dei):
473: /* FALLTHROUGH */
474: case (ROFF_de1):
475: /* FALLTHROUGH */
476: case (ROFF_ig):
477: break;
478: default:
1.76 kristaps 479: if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
480: return(ROFF_ERR);
1.67 kristaps 481: return(ROFF_IGN);
1.76 kristaps 482: }
1.67 kristaps 483:
1.76 kristaps 484: if ((*bufp)[pos])
485: if ( ! (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, pos, NULL))
486: return(ROFF_ERR);
1.71 kristaps 487:
488: roffnode_pop(r);
1.76 kristaps 489: roffnode_cleanscope(r);
490: return(ROFF_IGN);
1.71 kristaps 491:
1.67 kristaps 492: }
493:
494:
1.76 kristaps 495: static void
496: roffnode_cleanscope(struct roff *r)
1.67 kristaps 497: {
498:
1.76 kristaps 499: while (r->last) {
500: if (--r->last->endspan < 0)
501: break;
502: roffnode_pop(r);
503: }
1.67 kristaps 504: }
505:
506:
1.75 kristaps 507: /* ARGSUSED */
1.74 kristaps 508: static enum rofferr
1.75 kristaps 509: roff_ccond(ROFF_ARGS)
1.74 kristaps 510: {
511:
1.76 kristaps 512: if (NULL == r->last) {
513: if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
514: return(ROFF_ERR);
515: return(ROFF_IGN);
516: }
517:
1.82 kristaps 518: switch (r->last->tok) {
519: case (ROFF_el):
520: /* FALLTHROUGH */
521: case (ROFF_ie):
522: /* FALLTHROUGH */
523: case (ROFF_if):
524: break;
525: default:
1.75 kristaps 526: if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
527: return(ROFF_ERR);
528: return(ROFF_IGN);
529: }
530:
1.76 kristaps 531: if (r->last->endspan > -1) {
532: if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
533: return(ROFF_ERR);
534: return(ROFF_IGN);
535: }
536:
537: if ((*bufp)[pos])
538: if ( ! (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, pos, NULL))
539: return(ROFF_ERR);
540:
1.75 kristaps 541: roffnode_pop(r);
1.76 kristaps 542: roffnode_cleanscope(r);
543: return(ROFF_IGN);
544: }
545:
1.75 kristaps 546:
1.76 kristaps 547: /* ARGSUSED */
548: static enum rofferr
1.80 kristaps 549: roff_block(ROFF_ARGS)
1.76 kristaps 550: {
1.78 kristaps 551: int sv;
552: size_t sz;
1.76 kristaps 553:
1.80 kristaps 554: if (ROFF_ig != tok && '\0' == (*bufp)[pos]) {
555: if ( ! (*r->msg)(MANDOCERR_NOARGS, r->data, ln, ppos, NULL))
556: return(ROFF_ERR);
557: return(ROFF_IGN);
558: } else if (ROFF_ig != tok) {
559: while ((*bufp)[pos] && ' ' != (*bufp)[pos])
560: pos++;
561: while (' ' == (*bufp)[pos])
562: pos++;
563: }
564:
1.76 kristaps 565: if ( ! roffnode_push(r, tok, ln, ppos))
566: return(ROFF_ERR);
567:
1.79 kristaps 568: if ('\0' == (*bufp)[pos])
1.78 kristaps 569: return(ROFF_IGN);
570:
571: sv = pos;
572: while ((*bufp)[pos] && ' ' != (*bufp)[pos] &&
573: '\t' != (*bufp)[pos])
574: pos++;
575:
576: /*
577: * Note: groff does NOT like escape characters in the input.
578: * Instead of detecting this, we're just going to let it fly and
579: * to hell with it.
580: */
581:
582: assert(pos > sv);
583: sz = (size_t)(pos - sv);
584:
1.79 kristaps 585: if (1 == sz && '.' == (*bufp)[sv])
586: return(ROFF_IGN);
587:
1.78 kristaps 588: r->last->end = malloc(sz + 1);
589:
590: if (NULL == r->last->end) {
591: (*r->msg)(MANDOCERR_MEM, r->data, ln, pos, NULL);
592: return(ROFF_ERR);
593: }
594:
595: memcpy(r->last->end, *bufp + sv, sz);
596: r->last->end[(int)sz] = '\0';
597:
1.77 kristaps 598: if ((*bufp)[pos])
599: if ( ! (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, pos, NULL))
600: return(ROFF_ERR);
1.74 kristaps 601:
1.78 kristaps 602: return(ROFF_IGN);
603: }
604:
605:
606: /* ARGSUSED */
607: static enum rofferr
1.80 kristaps 608: roff_block_sub(ROFF_ARGS)
1.79 kristaps 609: {
610: enum rofft t;
611: int i, j;
612:
613: /*
614: * First check whether a custom macro exists at this level. If
615: * it does, then check against it. This is some of groff's
616: * stranger behaviours. If we encountered a custom end-scope
617: * tag and that tag also happens to be a "real" macro, then we
618: * need to try interpreting it again as a real macro. If it's
619: * not, then return ignore. Else continue.
620: */
621:
622: if (r->last->end) {
623: i = pos + 1;
624: while (' ' == (*bufp)[i] || '\t' == (*bufp)[i])
625: i++;
626:
627: for (j = 0; r->last->end[j]; j++, i++)
628: if ((*bufp)[i] != r->last->end[j])
629: break;
630:
631: if ('\0' == r->last->end[j] &&
632: ('\0' == (*bufp)[i] ||
633: ' ' == (*bufp)[i] ||
634: '\t' == (*bufp)[i])) {
635: roffnode_pop(r);
636: roffnode_cleanscope(r);
637:
638: if (ROFF_MAX != roff_parse(*bufp, &pos))
639: return(ROFF_RERUN);
640: return(ROFF_IGN);
641: }
642: }
643:
644: /*
645: * If we have no custom end-query or lookup failed, then try
646: * pulling it out of the hashtable.
647: */
648:
649: ppos = pos;
650: t = roff_parse(*bufp, &pos);
651:
652: /* If we're not a comment-end, then throw it away. */
653: if (ROFF_cblock != t)
654: return(ROFF_IGN);
655:
656: assert(roffs[t].proc);
1.90 kristaps 657: return((*roffs[t].proc)(r, t, bufp, szp,
658: ln, ppos, pos, offs));
1.79 kristaps 659: }
660:
661:
662: /* ARGSUSED */
663: static enum rofferr
1.80 kristaps 664: roff_block_text(ROFF_ARGS)
1.78 kristaps 665: {
666:
667: return(ROFF_IGN);
668: }
669:
670:
671: /* ARGSUSED */
672: static enum rofferr
1.82 kristaps 673: roff_cond_sub(ROFF_ARGS)
674: {
675: enum rofft t;
676: enum roffrule rr;
1.87 kristaps 677: struct roffnode *l;
1.82 kristaps 678:
679: ppos = pos;
680: rr = r->last->rule;
681:
1.87 kristaps 682: /*
683: * Clean out scope. If we've closed ourselves, then don't
684: * continue.
685: */
686:
687: l = r->last;
688: roffnode_cleanscope(r);
689:
690: if (l != r->last)
691: return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
1.82 kristaps 692:
693: if (ROFF_MAX == (t = roff_parse(*bufp, &pos)))
694: return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
695:
696: /*
697: * A denied conditional must evaluate its children if and only
698: * if they're either structurally required (such as loops and
699: * conditionals) or a closing macro.
700: */
701: if (ROFFRULE_DENY == rr)
702: if ( ! (ROFFMAC_STRUCT & roffs[t].flags))
703: if (ROFF_ccond != t)
704: return(ROFF_IGN);
705:
706: assert(roffs[t].proc);
1.90 kristaps 707: return((*roffs[t].proc)(r, t, bufp, szp,
708: ln, ppos, pos, offs));
1.82 kristaps 709: }
710:
711:
712: /* ARGSUSED */
713: static enum rofferr
714: roff_cond_text(ROFF_ARGS)
1.78 kristaps 715: {
716: char *ep, *st;
1.82 kristaps 717: enum roffrule rr;
718:
719: rr = r->last->rule;
720:
721: /*
722: * We display the value of the text if out current evaluation
723: * scope permits us to do so.
724: */
1.78 kristaps 725:
726: st = &(*bufp)[pos];
727: if (NULL == (ep = strstr(st, "\\}"))) {
728: roffnode_cleanscope(r);
1.82 kristaps 729: return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
1.78 kristaps 730: }
731:
1.86 kristaps 732: if (ep == st || (ep > st && '\\' != *(ep - 1)))
1.78 kristaps 733: roffnode_pop(r);
734:
735: roffnode_cleanscope(r);
1.82 kristaps 736: return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
1.74 kristaps 737: }
738:
739:
1.88 kristaps 740: static enum roffrule
741: roff_evalcond(const char *v, int *pos)
742: {
743:
744: switch (v[*pos]) {
745: case ('n'):
746: (*pos)++;
747: return(ROFFRULE_ALLOW);
748: case ('e'):
749: /* FALLTHROUGH */
750: case ('o'):
751: /* FALLTHROUGH */
752: case ('t'):
753: (*pos)++;
754: return(ROFFRULE_DENY);
755: default:
756: break;
757: }
758:
759: while (v[*pos] && ' ' != v[*pos])
760: (*pos)++;
761: return(ROFFRULE_DENY);
762: }
763:
764:
1.75 kristaps 765: /* ARGSUSED */
1.74 kristaps 766: static enum rofferr
1.89 kristaps 767: roff_line(ROFF_ARGS)
768: {
769:
770: return(ROFF_IGN);
771: }
772:
773:
774: /* ARGSUSED */
775: static enum rofferr
1.82 kristaps 776: roff_cond(ROFF_ARGS)
1.74 kristaps 777: {
1.77 kristaps 778: int sv;
1.88 kristaps 779: enum roffrule rule;
1.74 kristaps 780:
1.82 kristaps 781: /* Stack overflow! */
782:
783: if (ROFF_ie == tok && r->rstackpos == RSTACK_MAX - 1) {
784: (*r->msg)(MANDOCERR_MEM, r->data, ln, ppos, NULL);
785: return(ROFF_ERR);
786: }
1.74 kristaps 787:
1.88 kristaps 788: /* First, evaluate the conditional. */
1.84 schwarze 789:
1.88 kristaps 790: if (ROFF_el == tok) {
791: /*
792: * An `.el' will get the value of the current rstack
793: * entry set in prior `ie' calls or defaults to DENY.
794: */
795: if (r->rstackpos < 0)
796: rule = ROFFRULE_DENY;
797: else
798: rule = r->rstack[r->rstackpos];
799: } else
800: rule = roff_evalcond(*bufp, &pos);
1.77 kristaps 801:
802: sv = pos;
1.88 kristaps 803:
1.75 kristaps 804: while (' ' == (*bufp)[pos])
805: pos++;
1.74 kristaps 806:
1.77 kristaps 807: /*
808: * Roff is weird. If we have just white-space after the
809: * conditional, it's considered the BODY and we exit without
810: * really doing anything. Warn about this. It's probably
811: * wrong.
812: */
1.88 kristaps 813:
1.77 kristaps 814: if ('\0' == (*bufp)[pos] && sv != pos) {
1.88 kristaps 815: if ((*r->msg)(MANDOCERR_NOARGS, r->data, ln, ppos, NULL))
816: return(ROFF_IGN);
817: return(ROFF_ERR);
1.77 kristaps 818: }
819:
820: if ( ! roffnode_push(r, tok, ln, ppos))
821: return(ROFF_ERR);
822:
1.88 kristaps 823: r->last->rule = rule;
824:
825: ROFF_DEBUG("roff: cond: %s -> %s\n", roffs[tok].name,
826: ROFFRULE_ALLOW == rule ? "allow" : "deny");
1.82 kristaps 827:
1.84 schwarze 828: if (ROFF_ie == tok) {
1.82 kristaps 829: /*
830: * An if-else will put the NEGATION of the current
831: * evaluated conditional into the stack.
832: */
833: r->rstackpos++;
834: if (ROFFRULE_DENY == r->last->rule)
835: r->rstack[r->rstackpos] = ROFFRULE_ALLOW;
836: else
837: r->rstack[r->rstackpos] = ROFFRULE_DENY;
838: }
1.88 kristaps 839:
840: /* If the parent has false as its rule, then so do we. */
841:
842: if (r->last->parent && ROFFRULE_DENY == r->last->parent->rule) {
1.84 schwarze 843: r->last->rule = ROFFRULE_DENY;
1.88 kristaps 844: ROFF_DEBUG("roff: cond override: %s -> deny\n",
845: roffs[tok].name);
846: }
847:
848: /*
849: * Determine scope. If we're invoked with "\{" trailing the
850: * conditional, then we're in a multiline scope. Else our scope
851: * expires on the next line.
852: */
1.74 kristaps 853:
1.75 kristaps 854: r->last->endspan = 1;
855:
856: if ('\\' == (*bufp)[pos] && '{' == (*bufp)[pos + 1]) {
857: r->last->endspan = -1;
858: pos += 2;
1.88 kristaps 859: ROFF_DEBUG("roff: cond-scope: %s, multi-line\n",
860: roffs[tok].name);
861: } else
862: ROFF_DEBUG("roff: cond-scope: %s, one-line\n",
863: roffs[tok].name);
1.74 kristaps 864:
1.77 kristaps 865: /*
866: * If there are no arguments on the line, the next-line scope is
867: * assumed.
868: */
869:
1.75 kristaps 870: if ('\0' == (*bufp)[pos])
871: return(ROFF_IGN);
1.77 kristaps 872:
873: /* Otherwise re-run the roff parser after recalculating. */
1.74 kristaps 874:
1.75 kristaps 875: *offs = pos;
876: return(ROFF_RERUN);
1.83 schwarze 877: }
878:
879:
880: /* ARGSUSED */
881: static enum rofferr
1.89 kristaps 882: roff_nr(ROFF_ARGS)
1.83 schwarze 883: {
1.89 kristaps 884: const char *key, *val;
1.91 ! kristaps 885: struct reg *rg;
1.89 kristaps 886:
887: key = &(*bufp)[pos];
1.91 ! kristaps 888: rg = r->regs->regs;
1.89 kristaps 889:
890: /* Parse register request. */
891: while ((*bufp)[pos] && ' ' != (*bufp)[pos])
892: pos++;
893:
894: /*
895: * Set our nil terminator. Because this line is going to be
896: * ignored anyway, we can munge it as we please.
897: */
898: if ((*bufp)[pos])
899: (*bufp)[pos++] = '\0';
900:
901: /* Skip whitespace to register token. */
902: while ((*bufp)[pos] && ' ' == (*bufp)[pos])
903: pos++;
904:
905: val = &(*bufp)[pos];
906:
907: /* Process register token. */
908:
909: if (0 == strcmp(key, "nS")) {
1.91 ! kristaps 910: rg[(int)REG_nS].set = 1;
! 911: if ( ! roff_parse_nat(val, &rg[(int)REG_nS].v.u))
! 912: rg[(int)REG_nS].v.u = 0;
1.89 kristaps 913:
1.91 ! kristaps 914: ROFF_DEBUG("roff: register nS: %u\n",
! 915: rg[(int)REG_nS].v.u);
1.89 kristaps 916: } else
917: ROFF_DEBUG("roff: ignoring register: %s\n", key);
1.83 schwarze 918:
919: return(ROFF_IGN);
1.74 kristaps 920: }
CVSweb