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