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