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