Annotation of src/usr.bin/mandoc/roff.c, Revision 1.9
1.9 ! schwarze 1: /* $Id: roff.c,v 1.8 2010/07/13 01:09:13 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.1 schwarze 163: static int roffnode_push(struct roff *,
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: */
253: static int
254: roffnode_push(struct roff *r, enum rofft tok, int line, int col)
255: {
256: struct roffnode *p;
257:
258: if (NULL == (p = calloc(1, sizeof(struct roffnode)))) {
259: (*r->msg)(MANDOCERR_MEM, r->data, line, col, NULL);
260: return(0);
261: }
262:
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: return(1);
271: }
272:
273:
274: static void
275: roff_free1(struct roff *r)
276: {
277:
278: while (r->last)
279: roffnode_pop(r);
1.8 schwarze 280: roff_freestr(r);
1.1 schwarze 281: }
282:
283:
284: void
285: roff_reset(struct roff *r)
286: {
287:
288: roff_free1(r);
289: }
290:
291:
292: void
293: roff_free(struct roff *r)
294: {
295:
296: roff_free1(r);
297: free(r);
298: }
299:
300:
301: struct roff *
1.6 schwarze 302: roff_alloc(struct regset *regs, const mandocmsg msg, void *data)
1.1 schwarze 303: {
304: struct roff *r;
305:
306: if (NULL == (r = calloc(1, sizeof(struct roff)))) {
307: (*msg)(MANDOCERR_MEM, data, 0, 0, NULL);
308: return(0);
309: }
310:
1.6 schwarze 311: r->regs = regs;
1.1 schwarze 312: r->msg = msg;
313: r->data = data;
1.2 schwarze 314: r->rstackpos = -1;
1.3 schwarze 315:
316: roff_hash_init();
1.1 schwarze 317: return(r);
318: }
319:
320:
1.8 schwarze 321: /*
322: * Pre-filter each and every line for reserved words (one beginning with
323: * `\*', e.g., `\*(ab'). These must be handled before the actual line
324: * is processed.
325: */
326: static int
1.9 ! schwarze 327: roff_res(struct roff *r, char **bufp, size_t *szp, int pos)
1.8 schwarze 328: {
329: const char *cp, *cpp, *st, *res;
330: int i, maxl;
331: size_t nsz;
332: char *n;
333:
1.9 ! schwarze 334: /* LINTED */
1.8 schwarze 335: for (cp = &(*bufp)[pos]; (cpp = strstr(cp, "\\*")); cp++) {
336: cp = cpp + 2;
337: switch (*cp) {
338: case ('('):
339: cp++;
340: maxl = 2;
341: break;
342: case ('['):
343: cp++;
344: maxl = 0;
345: break;
346: default:
347: maxl = 1;
348: break;
349: }
350:
351: st = cp;
352:
353: for (i = 0; 0 == maxl || i < maxl; i++, cp++) {
354: if ('\0' == *cp)
355: return(1); /* Error. */
356: if (0 == maxl && ']' == *cp)
357: break;
358: }
359:
360: res = roff_getstrn(r, st, (size_t)i);
361:
362: if (NULL == res) {
363: cp -= maxl ? 1 : 0;
364: continue;
365: }
366:
367: nsz = *szp + strlen(res) + 1;
368: n = mandoc_malloc(nsz);
369:
370: *n = '\0';
371:
372: strlcat(n, *bufp, (size_t)(cpp - *bufp + 1));
373: strlcat(n, res, nsz);
374: strlcat(n, cp + (maxl ? 0 : 1), nsz);
375:
376: free(*bufp);
377:
378: *bufp = n;
379: *szp = nsz;
380: return(0);
381: }
382:
383: return(1);
384: }
385:
386:
1.1 schwarze 387: enum rofferr
1.6 schwarze 388: roff_parseln(struct roff *r, int ln, char **bufp,
389: size_t *szp, int pos, int *offs)
1.1 schwarze 390: {
391: enum rofft t;
392: int ppos;
393:
1.2 schwarze 394: /*
1.8 schwarze 395: * Run the reserved-word filter only if we have some reserved
396: * words to fill in.
397: */
398:
1.9 ! schwarze 399: if (r->first_string && ! roff_res(r, bufp, szp, pos))
1.8 schwarze 400: return(ROFF_RERUN);
401:
402: /*
1.2 schwarze 403: * First, if a scope is open and we're not a macro, pass the
404: * text through the macro's filter. If a scope isn't open and
405: * we're not a macro, just let it through.
406: */
407:
408: if (r->last && ! ROFF_CTL((*bufp)[pos])) {
409: t = r->last->tok;
410: assert(roffs[t].text);
411: return((*roffs[t].text)
1.8 schwarze 412: (r, t, bufp, szp,
413: ln, pos, pos, offs));
1.2 schwarze 414: } else if ( ! ROFF_CTL((*bufp)[pos]))
415: return(ROFF_CONT);
416:
417: /*
418: * If a scope is open, go to the child handler for that macro,
419: * as it may want to preprocess before doing anything with it.
420: */
421:
422: if (r->last) {
1.1 schwarze 423: t = r->last->tok;
424: assert(roffs[t].sub);
1.2 schwarze 425: return((*roffs[t].sub)
1.8 schwarze 426: (r, t, bufp, szp,
427: ln, pos, pos, offs));
1.2 schwarze 428: }
429:
430: /*
431: * Lastly, as we've no scope open, try to look up and execute
432: * the new macro. If no macro is found, simply return and let
433: * the compilers handle it.
434: */
435:
436: ppos = pos;
437: if (ROFF_MAX == (t = roff_parse(*bufp, &pos)))
1.1 schwarze 438: return(ROFF_CONT);
439:
1.2 schwarze 440: assert(roffs[t].proc);
441: return((*roffs[t].proc)
1.8 schwarze 442: (r, t, bufp, szp,
443: ln, ppos, pos, offs));
1.2 schwarze 444: }
445:
1.1 schwarze 446:
1.2 schwarze 447: int
448: roff_endparse(struct roff *r)
449: {
1.1 schwarze 450:
1.2 schwarze 451: if (NULL == r->last)
452: return(1);
453: return((*r->msg)(MANDOCERR_SCOPEEXIT, r->data, r->last->line,
454: r->last->col, NULL));
1.1 schwarze 455: }
456:
457:
458: /*
459: * Parse a roff node's type from the input buffer. This must be in the
460: * form of ".foo xxx" in the usual way.
461: */
462: static enum rofft
463: roff_parse(const char *buf, int *pos)
464: {
465: int j;
466: char mac[5];
467: enum rofft t;
468:
1.2 schwarze 469: assert(ROFF_CTL(buf[*pos]));
470: (*pos)++;
1.1 schwarze 471:
472: while (buf[*pos] && (' ' == buf[*pos] || '\t' == buf[*pos]))
473: (*pos)++;
474:
475: if ('\0' == buf[*pos])
476: return(ROFF_MAX);
477:
478: for (j = 0; j < 4; j++, (*pos)++)
479: if ('\0' == (mac[j] = buf[*pos]))
480: break;
1.2 schwarze 481: else if (' ' == buf[*pos] || (j && '\\' == buf[*pos]))
1.1 schwarze 482: break;
483:
484: if (j == 4 || j < 1)
485: return(ROFF_MAX);
486:
487: mac[j] = '\0';
488:
489: if (ROFF_MAX == (t = roff_hash_find(mac)))
490: return(t);
491:
492: while (buf[*pos] && ' ' == buf[*pos])
493: (*pos)++;
494:
495: return(t);
496: }
497:
498:
1.6 schwarze 499: static int
500: roff_parse_nat(const char *buf, unsigned int *res)
501: {
502: char *ep;
503: long lval;
504:
505: errno = 0;
506: lval = strtol(buf, &ep, 10);
507: if (buf[0] == '\0' || *ep != '\0')
508: return(0);
509: if ((errno == ERANGE &&
510: (lval == LONG_MAX || lval == LONG_MIN)) ||
511: (lval > INT_MAX || lval < 0))
512: return(0);
513:
514: *res = (unsigned int)lval;
515: return(1);
516: }
517:
518:
1.1 schwarze 519: /* ARGSUSED */
520: static enum rofferr
1.2 schwarze 521: roff_cblock(ROFF_ARGS)
1.1 schwarze 522: {
523:
1.2 schwarze 524: /*
525: * A block-close `..' should only be invoked as a child of an
526: * ignore macro, otherwise raise a warning and just ignore it.
527: */
528:
529: if (NULL == r->last) {
530: if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
531: return(ROFF_ERR);
532: return(ROFF_IGN);
533: }
1.1 schwarze 534:
1.2 schwarze 535: switch (r->last->tok) {
536: case (ROFF_am):
537: /* FALLTHROUGH */
538: case (ROFF_ami):
539: /* FALLTHROUGH */
540: case (ROFF_am1):
541: /* FALLTHROUGH */
542: case (ROFF_de):
543: /* FALLTHROUGH */
544: case (ROFF_dei):
545: /* FALLTHROUGH */
546: case (ROFF_de1):
547: /* FALLTHROUGH */
548: case (ROFF_ig):
549: break;
550: default:
551: if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
552: return(ROFF_ERR);
1.1 schwarze 553: return(ROFF_IGN);
1.2 schwarze 554: }
555:
556: if ((*bufp)[pos])
557: if ( ! (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, pos, NULL))
558: return(ROFF_ERR);
559:
560: roffnode_pop(r);
561: roffnode_cleanscope(r);
562: return(ROFF_IGN);
563:
564: }
1.1 schwarze 565:
566:
1.2 schwarze 567: static void
568: roffnode_cleanscope(struct roff *r)
569: {
1.1 schwarze 570:
1.2 schwarze 571: while (r->last) {
572: if (--r->last->endspan < 0)
573: break;
574: roffnode_pop(r);
575: }
576: }
1.1 schwarze 577:
578:
1.2 schwarze 579: /* ARGSUSED */
580: static enum rofferr
581: roff_ccond(ROFF_ARGS)
582: {
1.1 schwarze 583:
1.2 schwarze 584: if (NULL == r->last) {
585: if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
586: return(ROFF_ERR);
1.1 schwarze 587: return(ROFF_IGN);
1.2 schwarze 588: }
1.1 schwarze 589:
1.2 schwarze 590: switch (r->last->tok) {
591: case (ROFF_el):
592: /* FALLTHROUGH */
593: case (ROFF_ie):
594: /* FALLTHROUGH */
595: case (ROFF_if):
596: break;
597: default:
598: if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
599: return(ROFF_ERR);
600: return(ROFF_IGN);
601: }
1.1 schwarze 602:
1.2 schwarze 603: if (r->last->endspan > -1) {
604: if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
605: return(ROFF_ERR);
1.1 schwarze 606: return(ROFF_IGN);
1.2 schwarze 607: }
608:
609: if ((*bufp)[pos])
610: if ( ! (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, pos, NULL))
611: return(ROFF_ERR);
1.1 schwarze 612:
1.2 schwarze 613: roffnode_pop(r);
614: roffnode_cleanscope(r);
1.1 schwarze 615: return(ROFF_IGN);
616: }
617:
618:
619: /* ARGSUSED */
620: static enum rofferr
1.2 schwarze 621: roff_block(ROFF_ARGS)
1.1 schwarze 622: {
1.2 schwarze 623: int sv;
624: size_t sz;
625:
626: if (ROFF_ig != tok && '\0' == (*bufp)[pos]) {
627: if ( ! (*r->msg)(MANDOCERR_NOARGS, r->data, ln, ppos, NULL))
628: return(ROFF_ERR);
629: return(ROFF_IGN);
630: } else if (ROFF_ig != tok) {
631: while ((*bufp)[pos] && ' ' != (*bufp)[pos])
632: pos++;
633: while (' ' == (*bufp)[pos])
634: pos++;
635: }
636:
637: if ( ! roffnode_push(r, tok, ln, ppos))
638: return(ROFF_ERR);
639:
640: if ('\0' == (*bufp)[pos])
641: return(ROFF_IGN);
1.1 schwarze 642:
1.2 schwarze 643: sv = pos;
644: while ((*bufp)[pos] && ' ' != (*bufp)[pos] &&
645: '\t' != (*bufp)[pos])
646: pos++;
647:
648: /*
649: * Note: groff does NOT like escape characters in the input.
650: * Instead of detecting this, we're just going to let it fly and
651: * to hell with it.
652: */
653:
654: assert(pos > sv);
655: sz = (size_t)(pos - sv);
656:
657: if (1 == sz && '.' == (*bufp)[sv])
658: return(ROFF_IGN);
659:
660: r->last->end = malloc(sz + 1);
661:
662: if (NULL == r->last->end) {
663: (*r->msg)(MANDOCERR_MEM, r->data, ln, pos, NULL);
1.1 schwarze 664: return(ROFF_ERR);
1.2 schwarze 665: }
666:
667: memcpy(r->last->end, *bufp + sv, sz);
668: r->last->end[(int)sz] = '\0';
669:
670: if ((*bufp)[pos])
671: if ( ! (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, pos, NULL))
672: return(ROFF_ERR);
1.1 schwarze 673:
674: return(ROFF_IGN);
675: }
676:
677:
678: /* ARGSUSED */
679: static enum rofferr
1.2 schwarze 680: roff_block_sub(ROFF_ARGS)
1.1 schwarze 681: {
1.2 schwarze 682: enum rofft t;
683: int i, j;
684:
685: /*
686: * First check whether a custom macro exists at this level. If
687: * it does, then check against it. This is some of groff's
688: * stranger behaviours. If we encountered a custom end-scope
689: * tag and that tag also happens to be a "real" macro, then we
690: * need to try interpreting it again as a real macro. If it's
691: * not, then return ignore. Else continue.
692: */
693:
694: if (r->last->end) {
695: i = pos + 1;
696: while (' ' == (*bufp)[i] || '\t' == (*bufp)[i])
697: i++;
698:
699: for (j = 0; r->last->end[j]; j++, i++)
700: if ((*bufp)[i] != r->last->end[j])
701: break;
1.1 schwarze 702:
1.2 schwarze 703: if ('\0' == r->last->end[j] &&
704: ('\0' == (*bufp)[i] ||
705: ' ' == (*bufp)[i] ||
706: '\t' == (*bufp)[i])) {
707: roffnode_pop(r);
708: roffnode_cleanscope(r);
1.1 schwarze 709:
1.2 schwarze 710: if (ROFF_MAX != roff_parse(*bufp, &pos))
711: return(ROFF_RERUN);
712: return(ROFF_IGN);
713: }
1.1 schwarze 714: }
715:
1.2 schwarze 716: /*
717: * If we have no custom end-query or lookup failed, then try
718: * pulling it out of the hashtable.
719: */
1.1 schwarze 720:
1.2 schwarze 721: ppos = pos;
722: t = roff_parse(*bufp, &pos);
1.1 schwarze 723:
1.2 schwarze 724: /* If we're not a comment-end, then throw it away. */
725: if (ROFF_cblock != t)
1.1 schwarze 726: return(ROFF_IGN);
727:
1.2 schwarze 728: assert(roffs[t].proc);
1.6 schwarze 729: return((*roffs[t].proc)(r, t, bufp, szp,
730: ln, ppos, pos, offs));
1.2 schwarze 731: }
732:
733:
734: /* ARGSUSED */
735: static enum rofferr
736: roff_block_text(ROFF_ARGS)
737: {
738:
739: return(ROFF_IGN);
740: }
741:
742:
743: /* ARGSUSED */
744: static enum rofferr
745: roff_cond_sub(ROFF_ARGS)
746: {
747: enum rofft t;
748: enum roffrule rr;
1.5 schwarze 749: struct roffnode *l;
1.2 schwarze 750:
751: ppos = pos;
752: rr = r->last->rule;
753:
1.5 schwarze 754: /*
755: * Clean out scope. If we've closed ourselves, then don't
756: * continue.
757: */
758:
759: l = r->last;
760: roffnode_cleanscope(r);
761:
762: if (l != r->last)
763: return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
1.2 schwarze 764:
765: if (ROFF_MAX == (t = roff_parse(*bufp, &pos)))
766: return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
767:
768: /*
769: * A denied conditional must evaluate its children if and only
770: * if they're either structurally required (such as loops and
771: * conditionals) or a closing macro.
772: */
773: if (ROFFRULE_DENY == rr)
774: if ( ! (ROFFMAC_STRUCT & roffs[t].flags))
775: if (ROFF_ccond != t)
776: return(ROFF_IGN);
777:
778: assert(roffs[t].proc);
1.6 schwarze 779: return((*roffs[t].proc)(r, t, bufp, szp,
780: ln, ppos, pos, offs));
1.2 schwarze 781: }
782:
783:
784: /* ARGSUSED */
785: static enum rofferr
786: roff_cond_text(ROFF_ARGS)
787: {
788: char *ep, *st;
789: enum roffrule rr;
790:
791: rr = r->last->rule;
1.1 schwarze 792:
793: /*
1.2 schwarze 794: * We display the value of the text if out current evaluation
795: * scope permits us to do so.
1.1 schwarze 796: */
797:
1.2 schwarze 798: st = &(*bufp)[pos];
799: if (NULL == (ep = strstr(st, "\\}"))) {
800: roffnode_cleanscope(r);
801: return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
802: }
803:
1.4 schwarze 804: if (ep == st || (ep > st && '\\' != *(ep - 1)))
1.2 schwarze 805: roffnode_pop(r);
806:
807: roffnode_cleanscope(r);
808: return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
809: }
810:
811:
1.5 schwarze 812: static enum roffrule
813: roff_evalcond(const char *v, int *pos)
814: {
815:
816: switch (v[*pos]) {
817: case ('n'):
818: (*pos)++;
819: return(ROFFRULE_ALLOW);
820: case ('e'):
821: /* FALLTHROUGH */
822: case ('o'):
823: /* FALLTHROUGH */
824: case ('t'):
825: (*pos)++;
826: return(ROFFRULE_DENY);
827: default:
828: break;
829: }
830:
831: while (v[*pos] && ' ' != v[*pos])
832: (*pos)++;
833: return(ROFFRULE_DENY);
834: }
835:
836:
1.2 schwarze 837: /* ARGSUSED */
838: static enum rofferr
1.6 schwarze 839: roff_line(ROFF_ARGS)
840: {
841:
842: return(ROFF_IGN);
843: }
844:
845:
846: /* ARGSUSED */
847: static enum rofferr
1.2 schwarze 848: roff_cond(ROFF_ARGS)
849: {
850: int sv;
1.5 schwarze 851: enum roffrule rule;
1.2 schwarze 852:
853: /* Stack overflow! */
854:
855: if (ROFF_ie == tok && r->rstackpos == RSTACK_MAX - 1) {
1.1 schwarze 856: (*r->msg)(MANDOCERR_MEM, r->data, ln, ppos, NULL);
857: return(ROFF_ERR);
858: }
859:
1.5 schwarze 860: /* First, evaluate the conditional. */
1.2 schwarze 861:
1.5 schwarze 862: if (ROFF_el == tok) {
863: /*
864: * An `.el' will get the value of the current rstack
865: * entry set in prior `ie' calls or defaults to DENY.
866: */
867: if (r->rstackpos < 0)
868: rule = ROFFRULE_DENY;
869: else
870: rule = r->rstack[r->rstackpos];
871: } else
872: rule = roff_evalcond(*bufp, &pos);
1.2 schwarze 873:
874: sv = pos;
1.5 schwarze 875:
1.2 schwarze 876: while (' ' == (*bufp)[pos])
877: pos++;
878:
879: /*
880: * Roff is weird. If we have just white-space after the
881: * conditional, it's considered the BODY and we exit without
882: * really doing anything. Warn about this. It's probably
883: * wrong.
884: */
1.5 schwarze 885:
1.2 schwarze 886: if ('\0' == (*bufp)[pos] && sv != pos) {
1.5 schwarze 887: if ((*r->msg)(MANDOCERR_NOARGS, r->data, ln, ppos, NULL))
888: return(ROFF_IGN);
889: return(ROFF_ERR);
1.2 schwarze 890: }
891:
892: if ( ! roffnode_push(r, tok, ln, ppos))
893: return(ROFF_ERR);
894:
1.5 schwarze 895: r->last->rule = rule;
1.2 schwarze 896:
897: if (ROFF_ie == tok) {
898: /*
899: * An if-else will put the NEGATION of the current
900: * evaluated conditional into the stack.
901: */
902: r->rstackpos++;
903: if (ROFFRULE_DENY == r->last->rule)
904: r->rstack[r->rstackpos] = ROFFRULE_ALLOW;
905: else
906: r->rstack[r->rstackpos] = ROFFRULE_DENY;
907: }
1.5 schwarze 908:
909: /* If the parent has false as its rule, then so do we. */
910:
1.2 schwarze 911: if (r->last->parent && ROFFRULE_DENY == r->last->parent->rule)
912: r->last->rule = ROFFRULE_DENY;
1.5 schwarze 913:
914: /*
915: * Determine scope. If we're invoked with "\{" trailing the
916: * conditional, then we're in a multiline scope. Else our scope
917: * expires on the next line.
918: */
1.2 schwarze 919:
920: r->last->endspan = 1;
921:
922: if ('\\' == (*bufp)[pos] && '{' == (*bufp)[pos + 1]) {
923: r->last->endspan = -1;
924: pos += 2;
925: }
926:
927: /*
928: * If there are no arguments on the line, the next-line scope is
929: * assumed.
930: */
931:
932: if ('\0' == (*bufp)[pos])
933: return(ROFF_IGN);
934:
935: /* Otherwise re-run the roff parser after recalculating. */
1.1 schwarze 936:
1.2 schwarze 937: *offs = pos;
938: return(ROFF_RERUN);
1.1 schwarze 939: }
940:
941:
1.2 schwarze 942: /* ARGSUSED */
943: static enum rofferr
1.7 schwarze 944: roff_ds(ROFF_ARGS)
945: {
946: char *name, *string, *end;
947:
948: name = *bufp + pos;
949: if ('\0' == *name)
950: return(ROFF_IGN);
951:
952: string = name;
953: while (*string && ' ' != *string)
954: string++;
955: if (*string)
956: *(string++) = NULL;
957: if (*string && '"' == *string)
958: string++;
959: while (*string && ' ' == *string)
960: string++;
961: end = string;
962: while (*end)
963: end++;
964: if (string < end) {
965: end--;
966: if (*end == '"')
967: *end = '\0';
968: }
969:
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.7 schwarze 1011: }
1012:
1013:
1.8 schwarze 1014: static void
1015: roff_setstr(struct roff *r, const char *name, const char *string)
1.7 schwarze 1016: {
1017: struct roffstr *n;
1018: char *namecopy;
1019:
1.8 schwarze 1020: n = r->first_string;
1.7 schwarze 1021: while (n && strcmp(name, n->name))
1022: n = n->next;
1.8 schwarze 1023:
1024: if (NULL == n) {
1025: namecopy = mandoc_strdup(name);
1026: n = mandoc_malloc(sizeof(struct roffstr));
1027: n->name = namecopy;
1028: n->next = r->first_string;
1029: r->first_string = n;
1030: } else
1.7 schwarze 1031: free(n->string);
1.8 schwarze 1032:
1033: n->string = string ? strdup(string) : NULL;
1.7 schwarze 1034: }
1035:
1036:
1.8 schwarze 1037: static const char *
1038: roff_getstrn(const struct roff *r, const char *name, size_t len)
1.7 schwarze 1039: {
1.8 schwarze 1040: const struct roffstr *n;
1.7 schwarze 1041:
1.8 schwarze 1042: n = r->first_string;
1.7 schwarze 1043: while (n && (strncmp(name, n->name, len) || '\0' != n->name[len]))
1044: n = n->next;
1.8 schwarze 1045:
1046: return(n ? n->string : NULL);
1.7 schwarze 1047: }
1048:
1.8 schwarze 1049:
1050: static void
1051: roff_freestr(struct roff *r)
1.7 schwarze 1052: {
1053: struct roffstr *n, *nn;
1054:
1.8 schwarze 1055: for (n = r->first_string; n; n = nn) {
1.7 schwarze 1056: free(n->name);
1057: free(n->string);
1058: nn = n->next;
1059: free(n);
1060: }
1.8 schwarze 1061:
1062: r->first_string = NULL;
1.1 schwarze 1063: }