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