Annotation of src/usr.bin/mandoc/roff.c, Revision 1.41
1.41 ! schwarze 1: /* $Id: roff.c,v 1.40 2011/07/31 14:11:48 schwarze Exp $ */
1.1 schwarze 2: /*
1.27 schwarze 3: * Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
1.25 schwarze 4: * Copyright (c) 2010, 2011 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: *
1.16 schwarze 10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
1.1 schwarze 11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1.16 schwarze 12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
1.1 schwarze 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: #include <assert.h>
1.3 schwarze 19: #include <ctype.h>
1.1 schwarze 20: #include <stdlib.h>
21: #include <string.h>
22:
23: #include "mandoc.h"
1.27 schwarze 24: #include "libroff.h"
1.8 schwarze 25: #include "libmandoc.h"
1.1 schwarze 26:
1.37 schwarze 27: /* Maximum number of nested if-else conditionals. */
1.2 schwarze 28: #define RSTACK_MAX 128
29:
1.1 schwarze 30: enum rofft {
1.20 schwarze 31: ROFF_ad,
1.2 schwarze 32: ROFF_am,
33: ROFF_ami,
34: ROFF_am1,
1.1 schwarze 35: ROFF_de,
36: ROFF_dei,
1.2 schwarze 37: ROFF_de1,
38: ROFF_ds,
39: ROFF_el,
1.20 schwarze 40: ROFF_hy,
1.2 schwarze 41: ROFF_ie,
42: ROFF_if,
1.1 schwarze 43: ROFF_ig,
1.30 schwarze 44: ROFF_it,
1.20 schwarze 45: ROFF_ne,
46: ROFF_nh,
1.14 schwarze 47: ROFF_nr,
1.31 schwarze 48: ROFF_ns,
49: ROFF_ps,
1.2 schwarze 50: ROFF_rm,
1.14 schwarze 51: ROFF_so,
1.31 schwarze 52: ROFF_ta,
1.2 schwarze 53: ROFF_tr,
1.27 schwarze 54: ROFF_TS,
55: ROFF_TE,
56: ROFF_T_,
1.32 schwarze 57: ROFF_EQ,
58: ROFF_EN,
1.2 schwarze 59: ROFF_cblock,
1.37 schwarze 60: ROFF_ccond,
1.16 schwarze 61: ROFF_USERDEF,
1.1 schwarze 62: ROFF_MAX
63: };
64:
1.2 schwarze 65: enum roffrule {
66: ROFFRULE_ALLOW,
67: ROFFRULE_DENY
68: };
69:
1.41 ! schwarze 70: /*
! 71: * A single register entity. If "set" is zero, the value of the
! 72: * register should be the default one, which is per-register.
! 73: * Registers are assumed to be unsigned ints for now.
! 74: */
! 75: struct reg {
! 76: int set; /* whether set or not */
! 77: unsigned int u; /* unsigned integer */
! 78: };
! 79:
1.8 schwarze 80: struct roffstr {
81: char *name; /* key of symbol */
82: char *string; /* current value */
83: struct roffstr *next; /* next in list */
84: };
85:
1.1 schwarze 86: struct roff {
1.35 schwarze 87: struct mparse *parse; /* parse point */
1.1 schwarze 88: struct roffnode *last; /* leaf of stack */
1.2 schwarze 89: enum roffrule rstack[RSTACK_MAX]; /* stack of !`ie' rules */
90: int rstackpos; /* position in rstack */
1.41 ! schwarze 91: struct reg regs[REG__MAX];
1.16 schwarze 92: struct roffstr *first_string; /* user-defined strings & macros */
93: const char *current_string; /* value of last called user macro */
1.27 schwarze 94: struct tbl_node *first_tbl; /* first table parsed */
95: struct tbl_node *last_tbl; /* last table parsed */
96: struct tbl_node *tbl; /* current table being parsed */
1.32 schwarze 97: struct eqn_node *last_eqn; /* last equation parsed */
98: struct eqn_node *first_eqn; /* first equation parsed */
99: struct eqn_node *eqn; /* current equation being parsed */
1.1 schwarze 100: };
101:
102: struct roffnode {
103: enum rofft tok; /* type of node */
104: struct roffnode *parent; /* up one in stack */
105: int line; /* parse line */
106: int col; /* parse col */
1.16 schwarze 107: char *name; /* node name, e.g. macro name */
1.2 schwarze 108: char *end; /* end-rules: custom token */
109: int endspan; /* end-rules: next-line or infty */
110: enum roffrule rule; /* current evaluation rule */
1.1 schwarze 111: };
112:
113: #define ROFF_ARGS struct roff *r, /* parse ctx */ \
114: enum rofft tok, /* tok of macro */ \
115: char **bufp, /* input buffer */ \
116: size_t *szp, /* size of input buffer */ \
117: int ln, /* parse line */ \
1.2 schwarze 118: int ppos, /* original pos in buffer */ \
119: int pos, /* current pos in buffer */ \
120: int *offs /* reset offset of buffer data */
1.1 schwarze 121:
122: typedef enum rofferr (*roffproc)(ROFF_ARGS);
123:
124: struct roffmac {
125: const char *name; /* macro name */
1.2 schwarze 126: roffproc proc; /* process new macro */
127: roffproc text; /* process as child text of macro */
128: roffproc sub; /* process as child of macro */
129: int flags;
130: #define ROFFMAC_STRUCT (1 << 0) /* always interpret */
1.3 schwarze 131: struct roffmac *next;
1.1 schwarze 132: };
133:
1.37 schwarze 134: struct predef {
135: const char *name; /* predefined input name */
136: const char *str; /* replacement symbol */
137: };
138:
139: #define PREDEF(__name, __str) \
140: { (__name), (__str) },
141:
1.2 schwarze 142: static enum rofferr roff_block(ROFF_ARGS);
143: static enum rofferr roff_block_text(ROFF_ARGS);
144: static enum rofferr roff_block_sub(ROFF_ARGS);
145: static enum rofferr roff_cblock(ROFF_ARGS);
146: static enum rofferr roff_ccond(ROFF_ARGS);
147: static enum rofferr roff_cond(ROFF_ARGS);
148: static enum rofferr roff_cond_text(ROFF_ARGS);
149: static enum rofferr roff_cond_sub(ROFF_ARGS);
1.7 schwarze 150: static enum rofferr roff_ds(ROFF_ARGS);
1.8 schwarze 151: static enum roffrule roff_evalcond(const char *, int *);
152: static void roff_freestr(struct roff *);
1.28 schwarze 153: static char *roff_getname(struct roff *, char **, int, int);
1.8 schwarze 154: static const char *roff_getstrn(const struct roff *,
155: const char *, size_t);
1.21 schwarze 156: static enum rofferr roff_line_ignore(ROFF_ARGS);
1.6 schwarze 157: static enum rofferr roff_nr(ROFF_ARGS);
1.41 ! schwarze 158: static void roff_openeqn(struct roff *, const char *,
! 159: int, int, const char *);
1.9 schwarze 160: static int roff_res(struct roff *,
1.37 schwarze 161: char **, size_t *, int, int);
1.29 schwarze 162: static enum rofferr roff_rm(ROFF_ARGS);
1.8 schwarze 163: static void roff_setstr(struct roff *,
1.16 schwarze 164: const char *, const char *, int);
1.14 schwarze 165: static enum rofferr roff_so(ROFF_ARGS);
1.27 schwarze 166: static enum rofferr roff_TE(ROFF_ARGS);
167: static enum rofferr roff_TS(ROFF_ARGS);
1.32 schwarze 168: static enum rofferr roff_EQ(ROFF_ARGS);
169: static enum rofferr roff_EN(ROFF_ARGS);
1.27 schwarze 170: static enum rofferr roff_T_(ROFF_ARGS);
1.16 schwarze 171: static enum rofferr roff_userdef(ROFF_ARGS);
1.1 schwarze 172:
1.3 schwarze 173: /* See roff_hash_find() */
174:
175: #define ASCII_HI 126
176: #define ASCII_LO 33
177: #define HASHWIDTH (ASCII_HI - ASCII_LO + 1)
178:
179: static struct roffmac *hash[HASHWIDTH];
180:
181: static struct roffmac roffs[ROFF_MAX] = {
1.21 schwarze 182: { "ad", roff_line_ignore, NULL, NULL, 0, NULL },
1.3 schwarze 183: { "am", roff_block, roff_block_text, roff_block_sub, 0, NULL },
184: { "ami", roff_block, roff_block_text, roff_block_sub, 0, NULL },
185: { "am1", roff_block, roff_block_text, roff_block_sub, 0, NULL },
186: { "de", roff_block, roff_block_text, roff_block_sub, 0, NULL },
187: { "dei", roff_block, roff_block_text, roff_block_sub, 0, NULL },
188: { "de1", roff_block, roff_block_text, roff_block_sub, 0, NULL },
1.7 schwarze 189: { "ds", roff_ds, NULL, NULL, 0, NULL },
1.3 schwarze 190: { "el", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
1.21 schwarze 191: { "hy", roff_line_ignore, NULL, NULL, 0, NULL },
1.3 schwarze 192: { "ie", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
193: { "if", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
194: { "ig", roff_block, roff_block_text, roff_block_sub, 0, NULL },
1.30 schwarze 195: { "it", roff_line_ignore, NULL, NULL, 0, NULL },
1.21 schwarze 196: { "ne", roff_line_ignore, NULL, NULL, 0, NULL },
197: { "nh", roff_line_ignore, NULL, NULL, 0, NULL },
1.14 schwarze 198: { "nr", roff_nr, NULL, NULL, 0, NULL },
1.31 schwarze 199: { "ns", roff_line_ignore, NULL, NULL, 0, NULL },
200: { "ps", roff_line_ignore, NULL, NULL, 0, NULL },
1.29 schwarze 201: { "rm", roff_rm, NULL, NULL, 0, NULL },
1.14 schwarze 202: { "so", roff_so, NULL, NULL, 0, NULL },
1.31 schwarze 203: { "ta", roff_line_ignore, NULL, NULL, 0, NULL },
1.21 schwarze 204: { "tr", roff_line_ignore, NULL, NULL, 0, NULL },
1.27 schwarze 205: { "TS", roff_TS, NULL, NULL, 0, NULL },
206: { "TE", roff_TE, NULL, NULL, 0, NULL },
207: { "T&", roff_T_, NULL, NULL, 0, NULL },
1.32 schwarze 208: { "EQ", roff_EQ, NULL, NULL, 0, NULL },
209: { "EN", roff_EN, NULL, NULL, 0, NULL },
1.3 schwarze 210: { ".", roff_cblock, NULL, NULL, 0, NULL },
211: { "\\}", roff_ccond, NULL, NULL, 0, NULL },
1.16 schwarze 212: { NULL, roff_userdef, NULL, NULL, 0, NULL },
1.1 schwarze 213: };
214:
1.37 schwarze 215: /* Array of injected predefined strings. */
216: #define PREDEFS_MAX 38
217: static const struct predef predefs[PREDEFS_MAX] = {
218: #include "predefs.in"
219: };
220:
1.1 schwarze 221: static void roff_free1(struct roff *);
1.16 schwarze 222: static enum rofft roff_hash_find(const char *, size_t);
1.3 schwarze 223: static void roff_hash_init(void);
1.2 schwarze 224: static void roffnode_cleanscope(struct roff *);
1.16 schwarze 225: static void roffnode_push(struct roff *, enum rofft,
226: const char *, int, int);
1.1 schwarze 227: static void roffnode_pop(struct roff *);
1.16 schwarze 228: static enum rofft roff_parse(struct roff *, const char *, int *);
1.1 schwarze 229:
1.3 schwarze 230: /* See roff_hash_find() */
231: #define ROFF_HASH(p) (p[0] - ASCII_LO)
232:
233: static void
234: roff_hash_init(void)
235: {
236: struct roffmac *n;
237: int buc, i;
238:
1.16 schwarze 239: for (i = 0; i < (int)ROFF_USERDEF; i++) {
1.3 schwarze 240: assert(roffs[i].name[0] >= ASCII_LO);
241: assert(roffs[i].name[0] <= ASCII_HI);
242:
243: buc = ROFF_HASH(roffs[i].name);
244:
245: if (NULL != (n = hash[buc])) {
246: for ( ; n->next; n = n->next)
247: /* Do nothing. */ ;
248: n->next = &roffs[i];
249: } else
250: hash[buc] = &roffs[i];
251: }
252: }
253:
1.1 schwarze 254: /*
255: * Look up a roff token by its name. Returns ROFF_MAX if no macro by
256: * the nil-terminated string name could be found.
257: */
258: static enum rofft
1.16 schwarze 259: roff_hash_find(const char *p, size_t s)
1.1 schwarze 260: {
1.3 schwarze 261: int buc;
262: struct roffmac *n;
1.1 schwarze 263:
1.3 schwarze 264: /*
265: * libroff has an extremely simple hashtable, for the time
266: * being, which simply keys on the first character, which must
267: * be printable, then walks a chain. It works well enough until
268: * optimised.
269: */
270:
271: if (p[0] < ASCII_LO || p[0] > ASCII_HI)
272: return(ROFF_MAX);
273:
274: buc = ROFF_HASH(p);
275:
276: if (NULL == (n = hash[buc]))
277: return(ROFF_MAX);
278: for ( ; n; n = n->next)
1.16 schwarze 279: if (0 == strncmp(n->name, p, s) && '\0' == n->name[(int)s])
1.3 schwarze 280: return((enum rofft)(n - roffs));
1.1 schwarze 281:
282: return(ROFF_MAX);
283: }
284:
285:
286: /*
287: * Pop the current node off of the stack of roff instructions currently
288: * pending.
289: */
290: static void
291: roffnode_pop(struct roff *r)
292: {
293: struct roffnode *p;
294:
1.2 schwarze 295: assert(r->last);
296: p = r->last;
297:
298: r->last = r->last->parent;
1.16 schwarze 299: free(p->name);
300: free(p->end);
1.1 schwarze 301: free(p);
302: }
303:
304:
305: /*
306: * Push a roff node onto the instruction stack. This must later be
307: * removed with roffnode_pop().
308: */
1.11 schwarze 309: static void
1.16 schwarze 310: roffnode_push(struct roff *r, enum rofft tok, const char *name,
311: int line, int col)
1.1 schwarze 312: {
313: struct roffnode *p;
314:
1.11 schwarze 315: p = mandoc_calloc(1, sizeof(struct roffnode));
1.1 schwarze 316: p->tok = tok;
1.16 schwarze 317: if (name)
318: p->name = mandoc_strdup(name);
1.1 schwarze 319: p->parent = r->last;
320: p->line = line;
321: p->col = col;
1.2 schwarze 322: p->rule = p->parent ? p->parent->rule : ROFFRULE_DENY;
1.1 schwarze 323:
324: r->last = p;
325: }
326:
327:
328: static void
329: roff_free1(struct roff *r)
330: {
1.27 schwarze 331: struct tbl_node *t;
1.32 schwarze 332: struct eqn_node *e;
1.27 schwarze 333:
1.32 schwarze 334: while (NULL != (t = r->first_tbl)) {
1.27 schwarze 335: r->first_tbl = t->next;
336: tbl_free(t);
337: }
338:
339: r->first_tbl = r->last_tbl = r->tbl = NULL;
1.1 schwarze 340:
1.32 schwarze 341: while (NULL != (e = r->first_eqn)) {
342: r->first_eqn = e->next;
343: eqn_free(e);
344: }
345:
346: r->first_eqn = r->last_eqn = r->eqn = NULL;
347:
1.1 schwarze 348: while (r->last)
349: roffnode_pop(r);
1.27 schwarze 350:
1.8 schwarze 351: roff_freestr(r);
1.1 schwarze 352: }
353:
354:
355: void
356: roff_reset(struct roff *r)
357: {
1.38 schwarze 358: int i;
1.1 schwarze 359:
360: roff_free1(r);
1.38 schwarze 361:
1.41 ! schwarze 362: memset(&r->regs, 0, sizeof(struct reg) * REG__MAX);
! 363:
1.38 schwarze 364: for (i = 0; i < PREDEFS_MAX; i++)
365: roff_setstr(r, predefs[i].name, predefs[i].str, 0);
1.1 schwarze 366: }
367:
368:
369: void
370: roff_free(struct roff *r)
371: {
372:
373: roff_free1(r);
374: free(r);
375: }
376:
377:
378: struct roff *
1.41 ! schwarze 379: roff_alloc(struct mparse *parse)
1.1 schwarze 380: {
381: struct roff *r;
1.37 schwarze 382: int i;
1.1 schwarze 383:
1.11 schwarze 384: r = mandoc_calloc(1, sizeof(struct roff));
1.35 schwarze 385: r->parse = parse;
1.2 schwarze 386: r->rstackpos = -1;
1.3 schwarze 387:
388: roff_hash_init();
1.37 schwarze 389:
390: for (i = 0; i < PREDEFS_MAX; i++)
391: roff_setstr(r, predefs[i].name, predefs[i].str, 0);
392:
1.1 schwarze 393: return(r);
394: }
395:
396:
1.8 schwarze 397: /*
398: * Pre-filter each and every line for reserved words (one beginning with
399: * `\*', e.g., `\*(ab'). These must be handled before the actual line
400: * is processed.
401: */
402: static int
1.37 schwarze 403: roff_res(struct roff *r, char **bufp, size_t *szp, int ln, int pos)
1.8 schwarze 404: {
1.23 schwarze 405: const char *stesc; /* start of an escape sequence ('\\') */
406: const char *stnam; /* start of the name, after "[(*" */
407: const char *cp; /* end of the name, e.g. before ']' */
408: const char *res; /* the string to be substituted */
1.8 schwarze 409: int i, maxl;
410: size_t nsz;
411: char *n;
412:
1.24 schwarze 413: /* Search for a leading backslash and save a pointer to it. */
1.23 schwarze 414:
1.24 schwarze 415: cp = *bufp + pos;
416: while (NULL != (cp = strchr(cp, '\\'))) {
417: stesc = cp++;
1.23 schwarze 418:
419: /*
420: * The second character must be an asterisk.
421: * If it isn't, skip it anyway: It is escaped,
422: * so it can't start another escape sequence.
423: */
424:
1.24 schwarze 425: if ('\0' == *cp)
426: return(1);
427: if ('*' != *cp++)
1.23 schwarze 428: continue;
429:
430: /*
431: * The third character decides the length
432: * of the name of the string.
433: * Save a pointer to the name.
434: */
435:
1.24 schwarze 436: switch (*cp) {
437: case ('\0'):
438: return(1);
1.8 schwarze 439: case ('('):
440: cp++;
441: maxl = 2;
442: break;
443: case ('['):
444: cp++;
445: maxl = 0;
446: break;
447: default:
448: maxl = 1;
449: break;
450: }
1.23 schwarze 451: stnam = cp;
1.8 schwarze 452:
1.23 schwarze 453: /* Advance to the end of the name. */
1.8 schwarze 454:
455: for (i = 0; 0 == maxl || i < maxl; i++, cp++) {
456: if ('\0' == *cp)
457: return(1); /* Error. */
458: if (0 == maxl && ']' == *cp)
459: break;
460: }
461:
1.23 schwarze 462: /*
463: * Retrieve the replacement string; if it is
464: * undefined, resume searching for escapes.
465: */
466:
467: res = roff_getstrn(r, stnam, (size_t)i);
1.8 schwarze 468:
469: if (NULL == res) {
1.37 schwarze 470: /* TODO: keep track of the correct position. */
471: mandoc_msg(MANDOCERR_BADESCAPE, r->parse, ln, pos, NULL);
472: res = "";
1.8 schwarze 473: }
474:
1.23 schwarze 475: /* Replace the escape sequence by the string. */
476:
1.8 schwarze 477: nsz = *szp + strlen(res) + 1;
478: n = mandoc_malloc(nsz);
479:
1.23 schwarze 480: strlcpy(n, *bufp, (size_t)(stesc - *bufp + 1));
1.8 schwarze 481: strlcat(n, res, nsz);
482: strlcat(n, cp + (maxl ? 0 : 1), nsz);
483:
484: free(*bufp);
485:
486: *bufp = n;
487: *szp = nsz;
488: return(0);
489: }
490:
491: return(1);
492: }
493:
494:
1.1 schwarze 495: enum rofferr
1.6 schwarze 496: roff_parseln(struct roff *r, int ln, char **bufp,
497: size_t *szp, int pos, int *offs)
1.1 schwarze 498: {
499: enum rofft t;
1.27 schwarze 500: enum rofferr e;
1.35 schwarze 501: int ppos, ctl;
1.1 schwarze 502:
1.2 schwarze 503: /*
1.8 schwarze 504: * Run the reserved-word filter only if we have some reserved
505: * words to fill in.
506: */
507:
1.37 schwarze 508: if (r->first_string && ! roff_res(r, bufp, szp, ln, pos))
1.16 schwarze 509: return(ROFF_REPARSE);
1.8 schwarze 510:
1.35 schwarze 511: ppos = pos;
512: ctl = mandoc_getcontrol(*bufp, &pos);
513:
1.8 schwarze 514: /*
1.2 schwarze 515: * First, if a scope is open and we're not a macro, pass the
516: * text through the macro's filter. If a scope isn't open and
517: * we're not a macro, just let it through.
1.32 schwarze 518: * Finally, if there's an equation scope open, divert it into it
519: * no matter our state.
1.2 schwarze 520: */
521:
1.35 schwarze 522: if (r->last && ! ctl) {
1.2 schwarze 523: t = r->last->tok;
524: assert(roffs[t].text);
1.27 schwarze 525: e = (*roffs[t].text)
526: (r, t, bufp, szp, ln, pos, pos, offs);
527: assert(ROFF_IGN == e || ROFF_CONT == e);
1.32 schwarze 528: if (ROFF_CONT != e)
529: return(e);
530: if (r->eqn)
1.41 ! schwarze 531: return(eqn_read(&r->eqn, ln, *bufp, pos, offs));
1.32 schwarze 532: if (r->tbl)
1.35 schwarze 533: return(tbl_read(r->tbl, ln, *bufp, pos));
1.32 schwarze 534: return(ROFF_CONT);
1.35 schwarze 535: } else if ( ! ctl) {
1.32 schwarze 536: if (r->eqn)
1.41 ! schwarze 537: return(eqn_read(&r->eqn, ln, *bufp, pos, offs));
1.27 schwarze 538: if (r->tbl)
1.35 schwarze 539: return(tbl_read(r->tbl, ln, *bufp, pos));
1.2 schwarze 540: return(ROFF_CONT);
1.32 schwarze 541: } else if (r->eqn)
1.41 ! schwarze 542: return(eqn_read(&r->eqn, ln, *bufp, ppos, offs));
1.2 schwarze 543:
544: /*
545: * If a scope is open, go to the child handler for that macro,
546: * as it may want to preprocess before doing anything with it.
1.32 schwarze 547: * Don't do so if an equation is open.
1.2 schwarze 548: */
549:
550: if (r->last) {
1.1 schwarze 551: t = r->last->tok;
552: assert(roffs[t].sub);
1.2 schwarze 553: return((*roffs[t].sub)
1.8 schwarze 554: (r, t, bufp, szp,
1.35 schwarze 555: ln, ppos, pos, offs));
1.2 schwarze 556: }
557:
558: /*
559: * Lastly, as we've no scope open, try to look up and execute
560: * the new macro. If no macro is found, simply return and let
561: * the compilers handle it.
562: */
563:
1.16 schwarze 564: if (ROFF_MAX == (t = roff_parse(r, *bufp, &pos)))
1.1 schwarze 565: return(ROFF_CONT);
566:
1.2 schwarze 567: assert(roffs[t].proc);
568: return((*roffs[t].proc)
1.8 schwarze 569: (r, t, bufp, szp,
570: ln, ppos, pos, offs));
1.2 schwarze 571: }
572:
1.1 schwarze 573:
1.27 schwarze 574: void
1.2 schwarze 575: roff_endparse(struct roff *r)
576: {
1.1 schwarze 577:
1.27 schwarze 578: if (r->last)
1.35 schwarze 579: mandoc_msg(MANDOCERR_SCOPEEXIT, r->parse,
1.27 schwarze 580: r->last->line, r->last->col, NULL);
581:
1.32 schwarze 582: if (r->eqn) {
1.35 schwarze 583: mandoc_msg(MANDOCERR_SCOPEEXIT, r->parse,
1.41 ! schwarze 584: r->eqn->eqn.ln, r->eqn->eqn.pos, NULL);
! 585: eqn_end(&r->eqn);
1.32 schwarze 586: }
587:
1.27 schwarze 588: if (r->tbl) {
1.35 schwarze 589: mandoc_msg(MANDOCERR_SCOPEEXIT, r->parse,
1.27 schwarze 590: r->tbl->line, r->tbl->pos, NULL);
1.41 ! schwarze 591: tbl_end(&r->tbl);
1.27 schwarze 592: }
1.1 schwarze 593: }
594:
595: /*
596: * Parse a roff node's type from the input buffer. This must be in the
597: * form of ".foo xxx" in the usual way.
598: */
599: static enum rofft
1.16 schwarze 600: roff_parse(struct roff *r, const char *buf, int *pos)
1.1 schwarze 601: {
1.16 schwarze 602: const char *mac;
603: size_t maclen;
1.1 schwarze 604: enum rofft t;
605:
1.39 schwarze 606: if ('\0' == buf[*pos] || '"' == buf[*pos] ||
607: '\t' == buf[*pos] || ' ' == buf[*pos])
1.1 schwarze 608: return(ROFF_MAX);
609:
1.39 schwarze 610: /*
611: * We stop the macro parse at an escape, tab, space, or nil.
612: * However, `\}' is also a valid macro, so make sure we don't
613: * clobber it by seeing the `\' as the end of token.
614: */
615:
1.16 schwarze 616: mac = buf + *pos;
1.39 schwarze 617: maclen = strcspn(mac + 1, " \\\t\0") + 1;
1.1 schwarze 618:
1.16 schwarze 619: t = (r->current_string = roff_getstrn(r, mac, maclen))
620: ? ROFF_USERDEF : roff_hash_find(mac, maclen);
1.1 schwarze 621:
1.34 schwarze 622: *pos += (int)maclen;
1.35 schwarze 623:
1.1 schwarze 624: while (buf[*pos] && ' ' == buf[*pos])
625: (*pos)++;
626:
627: return(t);
628: }
629:
630: /* ARGSUSED */
631: static enum rofferr
1.2 schwarze 632: roff_cblock(ROFF_ARGS)
1.1 schwarze 633: {
634:
1.2 schwarze 635: /*
636: * A block-close `..' should only be invoked as a child of an
637: * ignore macro, otherwise raise a warning and just ignore it.
638: */
639:
640: if (NULL == r->last) {
1.35 schwarze 641: mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
1.2 schwarze 642: return(ROFF_IGN);
643: }
1.1 schwarze 644:
1.2 schwarze 645: switch (r->last->tok) {
646: case (ROFF_am):
647: /* FALLTHROUGH */
648: case (ROFF_ami):
649: /* FALLTHROUGH */
650: case (ROFF_am1):
651: /* FALLTHROUGH */
652: case (ROFF_de):
1.23 schwarze 653: /* ROFF_de1 is remapped to ROFF_de in roff_block(). */
1.2 schwarze 654: /* FALLTHROUGH */
655: case (ROFF_dei):
656: /* FALLTHROUGH */
657: case (ROFF_ig):
658: break;
659: default:
1.35 schwarze 660: mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
1.1 schwarze 661: return(ROFF_IGN);
1.2 schwarze 662: }
663:
664: if ((*bufp)[pos])
1.35 schwarze 665: mandoc_msg(MANDOCERR_ARGSLOST, r->parse, ln, pos, NULL);
1.2 schwarze 666:
667: roffnode_pop(r);
668: roffnode_cleanscope(r);
669: return(ROFF_IGN);
670:
671: }
1.1 schwarze 672:
673:
1.2 schwarze 674: static void
675: roffnode_cleanscope(struct roff *r)
676: {
1.1 schwarze 677:
1.2 schwarze 678: while (r->last) {
679: if (--r->last->endspan < 0)
680: break;
681: roffnode_pop(r);
682: }
683: }
1.1 schwarze 684:
685:
1.2 schwarze 686: /* ARGSUSED */
687: static enum rofferr
688: roff_ccond(ROFF_ARGS)
689: {
1.1 schwarze 690:
1.2 schwarze 691: if (NULL == r->last) {
1.35 schwarze 692: mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
1.1 schwarze 693: return(ROFF_IGN);
1.2 schwarze 694: }
1.1 schwarze 695:
1.2 schwarze 696: switch (r->last->tok) {
697: case (ROFF_el):
698: /* FALLTHROUGH */
699: case (ROFF_ie):
700: /* FALLTHROUGH */
701: case (ROFF_if):
702: break;
703: default:
1.35 schwarze 704: mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
1.2 schwarze 705: return(ROFF_IGN);
706: }
1.1 schwarze 707:
1.2 schwarze 708: if (r->last->endspan > -1) {
1.35 schwarze 709: mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
1.1 schwarze 710: return(ROFF_IGN);
1.2 schwarze 711: }
712:
713: if ((*bufp)[pos])
1.35 schwarze 714: mandoc_msg(MANDOCERR_ARGSLOST, r->parse, ln, pos, NULL);
1.1 schwarze 715:
1.2 schwarze 716: roffnode_pop(r);
717: roffnode_cleanscope(r);
1.1 schwarze 718: return(ROFF_IGN);
719: }
720:
721:
722: /* ARGSUSED */
723: static enum rofferr
1.2 schwarze 724: roff_block(ROFF_ARGS)
1.1 schwarze 725: {
1.2 schwarze 726: int sv;
727: size_t sz;
1.16 schwarze 728: char *name;
729:
730: name = NULL;
1.2 schwarze 731:
1.16 schwarze 732: if (ROFF_ig != tok) {
733: if ('\0' == (*bufp)[pos]) {
1.35 schwarze 734: mandoc_msg(MANDOCERR_NOARGS, r->parse, ln, ppos, NULL);
1.16 schwarze 735: return(ROFF_IGN);
736: }
1.22 schwarze 737:
738: /*
739: * Re-write `de1', since we don't really care about
740: * groff's strange compatibility mode, into `de'.
741: */
742:
1.18 schwarze 743: if (ROFF_de1 == tok)
744: tok = ROFF_de;
1.16 schwarze 745: if (ROFF_de == tok)
746: name = *bufp + pos;
1.21 schwarze 747: else
1.35 schwarze 748: mandoc_msg(MANDOCERR_REQUEST, r->parse, ln, ppos,
1.21 schwarze 749: roffs[tok].name);
1.22 schwarze 750:
1.33 schwarze 751: while ((*bufp)[pos] && ! isspace((unsigned char)(*bufp)[pos]))
1.2 schwarze 752: pos++;
1.22 schwarze 753:
1.33 schwarze 754: while (isspace((unsigned char)(*bufp)[pos]))
1.16 schwarze 755: (*bufp)[pos++] = '\0';
1.2 schwarze 756: }
757:
1.16 schwarze 758: roffnode_push(r, tok, name, ln, ppos);
759:
760: /*
761: * At the beginning of a `de' macro, clear the existing string
762: * with the same name, if there is one. New content will be
763: * added from roff_block_text() in multiline mode.
764: */
1.22 schwarze 765:
1.16 schwarze 766: if (ROFF_de == tok)
1.19 schwarze 767: roff_setstr(r, name, "", 0);
1.2 schwarze 768:
769: if ('\0' == (*bufp)[pos])
770: return(ROFF_IGN);
1.1 schwarze 771:
1.22 schwarze 772: /* If present, process the custom end-of-line marker. */
773:
1.2 schwarze 774: sv = pos;
1.33 schwarze 775: while ((*bufp)[pos] && ! isspace((unsigned char)(*bufp)[pos]))
1.2 schwarze 776: pos++;
777:
778: /*
779: * Note: groff does NOT like escape characters in the input.
780: * Instead of detecting this, we're just going to let it fly and
781: * to hell with it.
782: */
783:
784: assert(pos > sv);
785: sz = (size_t)(pos - sv);
786:
787: if (1 == sz && '.' == (*bufp)[sv])
788: return(ROFF_IGN);
789:
1.11 schwarze 790: r->last->end = mandoc_malloc(sz + 1);
1.2 schwarze 791:
792: memcpy(r->last->end, *bufp + sv, sz);
793: r->last->end[(int)sz] = '\0';
794:
795: if ((*bufp)[pos])
1.35 schwarze 796: mandoc_msg(MANDOCERR_ARGSLOST, r->parse, ln, pos, NULL);
1.1 schwarze 797:
798: return(ROFF_IGN);
799: }
800:
801:
802: /* ARGSUSED */
803: static enum rofferr
1.2 schwarze 804: roff_block_sub(ROFF_ARGS)
1.1 schwarze 805: {
1.2 schwarze 806: enum rofft t;
807: int i, j;
808:
809: /*
810: * First check whether a custom macro exists at this level. If
811: * it does, then check against it. This is some of groff's
812: * stranger behaviours. If we encountered a custom end-scope
813: * tag and that tag also happens to be a "real" macro, then we
814: * need to try interpreting it again as a real macro. If it's
815: * not, then return ignore. Else continue.
816: */
817:
818: if (r->last->end) {
1.35 schwarze 819: for (i = pos, j = 0; r->last->end[j]; j++, i++)
1.2 schwarze 820: if ((*bufp)[i] != r->last->end[j])
821: break;
1.1 schwarze 822:
1.2 schwarze 823: if ('\0' == r->last->end[j] &&
824: ('\0' == (*bufp)[i] ||
825: ' ' == (*bufp)[i] ||
826: '\t' == (*bufp)[i])) {
827: roffnode_pop(r);
828: roffnode_cleanscope(r);
1.1 schwarze 829:
1.35 schwarze 830: while (' ' == (*bufp)[i] || '\t' == (*bufp)[i])
831: i++;
832:
833: pos = i;
1.16 schwarze 834: if (ROFF_MAX != roff_parse(r, *bufp, &pos))
1.2 schwarze 835: return(ROFF_RERUN);
836: return(ROFF_IGN);
837: }
1.1 schwarze 838: }
839:
1.2 schwarze 840: /*
841: * If we have no custom end-query or lookup failed, then try
842: * pulling it out of the hashtable.
843: */
1.1 schwarze 844:
1.36 schwarze 845: t = roff_parse(r, *bufp, &pos);
1.1 schwarze 846:
1.16 schwarze 847: /*
848: * Macros other than block-end are only significant
849: * in `de' blocks; elsewhere, simply throw them away.
850: */
851: if (ROFF_cblock != t) {
852: if (ROFF_de == tok)
853: roff_setstr(r, r->last->name, *bufp + ppos, 1);
1.1 schwarze 854: return(ROFF_IGN);
1.16 schwarze 855: }
1.1 schwarze 856:
1.2 schwarze 857: assert(roffs[t].proc);
1.6 schwarze 858: return((*roffs[t].proc)(r, t, bufp, szp,
859: ln, ppos, pos, offs));
1.2 schwarze 860: }
861:
862:
863: /* ARGSUSED */
864: static enum rofferr
865: roff_block_text(ROFF_ARGS)
866: {
867:
1.16 schwarze 868: if (ROFF_de == tok)
869: roff_setstr(r, r->last->name, *bufp + pos, 1);
870:
1.2 schwarze 871: return(ROFF_IGN);
872: }
873:
874:
875: /* ARGSUSED */
876: static enum rofferr
877: roff_cond_sub(ROFF_ARGS)
878: {
879: enum rofft t;
880: enum roffrule rr;
1.37 schwarze 881: char *ep;
1.2 schwarze 882:
883: rr = r->last->rule;
1.37 schwarze 884: roffnode_cleanscope(r);
1.2 schwarze 885:
1.37 schwarze 886: /*
887: * If the macro is unknown, first check if it contains a closing
888: * delimiter `\}'. If it does, close out our scope and return
889: * the currently-scoped rule (ignore or continue). Else, drop
890: * into the currently-scoped rule.
1.5 schwarze 891: */
892:
1.16 schwarze 893: if (ROFF_MAX == (t = roff_parse(r, *bufp, &pos))) {
1.37 schwarze 894: ep = &(*bufp)[pos];
895: for ( ; NULL != (ep = strchr(ep, '\\')); ep++) {
896: ep++;
897: if ('}' != *ep)
898: continue;
1.39 schwarze 899:
900: /*
901: * Make the \} go away.
902: * This is a little haphazard, as it's not quite
903: * clear how nroff does this.
904: * If we're at the end of line, then just chop
905: * off the \} and resize the buffer.
906: * If we aren't, then conver it to spaces.
907: */
908:
909: if ('\0' == *(ep + 1)) {
910: *--ep = '\0';
911: *szp -= 2;
912: } else
913: *(ep - 1) = *ep = ' ';
914:
1.37 schwarze 915: roff_ccond(r, ROFF_ccond, bufp, szp,
916: ln, pos, pos + 2, offs);
917: break;
918: }
1.2 schwarze 919: return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
1.12 schwarze 920: }
1.2 schwarze 921:
922: /*
923: * A denied conditional must evaluate its children if and only
924: * if they're either structurally required (such as loops and
925: * conditionals) or a closing macro.
926: */
1.37 schwarze 927:
1.2 schwarze 928: if (ROFFRULE_DENY == rr)
929: if ( ! (ROFFMAC_STRUCT & roffs[t].flags))
930: if (ROFF_ccond != t)
931: return(ROFF_IGN);
932:
933: assert(roffs[t].proc);
1.6 schwarze 934: return((*roffs[t].proc)(r, t, bufp, szp,
935: ln, ppos, pos, offs));
1.2 schwarze 936: }
937:
938: /* ARGSUSED */
939: static enum rofferr
940: roff_cond_text(ROFF_ARGS)
941: {
1.37 schwarze 942: char *ep;
1.2 schwarze 943: enum roffrule rr;
944:
945: rr = r->last->rule;
1.37 schwarze 946: roffnode_cleanscope(r);
1.1 schwarze 947:
1.37 schwarze 948: ep = &(*bufp)[pos];
949: for ( ; NULL != (ep = strchr(ep, '\\')); ep++) {
950: ep++;
951: if ('}' != *ep)
952: continue;
953: *ep = '&';
954: roff_ccond(r, ROFF_ccond, bufp, szp,
955: ln, pos, pos + 2, offs);
1.2 schwarze 956: }
957: return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
958: }
959:
1.5 schwarze 960: static enum roffrule
961: roff_evalcond(const char *v, int *pos)
962: {
963:
964: switch (v[*pos]) {
965: case ('n'):
966: (*pos)++;
967: return(ROFFRULE_ALLOW);
968: case ('e'):
969: /* FALLTHROUGH */
970: case ('o'):
971: /* FALLTHROUGH */
972: case ('t'):
973: (*pos)++;
974: return(ROFFRULE_DENY);
975: default:
976: break;
977: }
978:
979: while (v[*pos] && ' ' != v[*pos])
980: (*pos)++;
981: return(ROFFRULE_DENY);
982: }
983:
1.2 schwarze 984: /* ARGSUSED */
985: static enum rofferr
1.21 schwarze 986: roff_line_ignore(ROFF_ARGS)
1.6 schwarze 987: {
1.30 schwarze 988:
989: if (ROFF_it == tok)
1.35 schwarze 990: mandoc_msg(MANDOCERR_REQUEST, r->parse, ln, ppos, "it");
1.6 schwarze 991:
1.21 schwarze 992: return(ROFF_IGN);
993: }
994:
995: /* ARGSUSED */
996: static enum rofferr
1.2 schwarze 997: roff_cond(ROFF_ARGS)
998: {
999: int sv;
1.5 schwarze 1000: enum roffrule rule;
1.2 schwarze 1001:
1.35 schwarze 1002: /*
1003: * An `.el' has no conditional body: it will consume the value
1004: * of the current rstack entry set in prior `ie' calls or
1005: * defaults to DENY.
1006: *
1007: * If we're not an `el', however, then evaluate the conditional.
1008: */
1.1 schwarze 1009:
1.35 schwarze 1010: rule = ROFF_el == tok ?
1011: (r->rstackpos < 0 ?
1012: ROFFRULE_DENY : r->rstack[r->rstackpos--]) :
1013: roff_evalcond(*bufp, &pos);
1.2 schwarze 1014:
1015: sv = pos;
1016: while (' ' == (*bufp)[pos])
1017: pos++;
1018:
1019: /*
1020: * Roff is weird. If we have just white-space after the
1021: * conditional, it's considered the BODY and we exit without
1022: * really doing anything. Warn about this. It's probably
1023: * wrong.
1024: */
1.5 schwarze 1025:
1.2 schwarze 1026: if ('\0' == (*bufp)[pos] && sv != pos) {
1.35 schwarze 1027: mandoc_msg(MANDOCERR_NOARGS, r->parse, ln, ppos, NULL);
1.22 schwarze 1028: return(ROFF_IGN);
1.2 schwarze 1029: }
1030:
1.16 schwarze 1031: roffnode_push(r, tok, NULL, ln, ppos);
1.2 schwarze 1032:
1.5 schwarze 1033: r->last->rule = rule;
1.2 schwarze 1034:
1.35 schwarze 1035: /*
1036: * An if-else will put the NEGATION of the current evaluated
1037: * conditional into the stack of rules.
1038: */
1039:
1.2 schwarze 1040: if (ROFF_ie == tok) {
1.35 schwarze 1041: if (r->rstackpos == RSTACK_MAX - 1) {
1042: mandoc_msg(MANDOCERR_MEM,
1043: r->parse, ln, ppos, NULL);
1044: return(ROFF_ERR);
1045: }
1046: r->rstack[++r->rstackpos] =
1047: ROFFRULE_DENY == r->last->rule ?
1048: ROFFRULE_ALLOW : ROFFRULE_DENY;
1.2 schwarze 1049: }
1.5 schwarze 1050:
1051: /* If the parent has false as its rule, then so do we. */
1052:
1.2 schwarze 1053: if (r->last->parent && ROFFRULE_DENY == r->last->parent->rule)
1054: r->last->rule = ROFFRULE_DENY;
1.5 schwarze 1055:
1056: /*
1057: * Determine scope. If we're invoked with "\{" trailing the
1058: * conditional, then we're in a multiline scope. Else our scope
1059: * expires on the next line.
1060: */
1.2 schwarze 1061:
1062: r->last->endspan = 1;
1063:
1064: if ('\\' == (*bufp)[pos] && '{' == (*bufp)[pos + 1]) {
1065: r->last->endspan = -1;
1066: pos += 2;
1067: }
1068:
1069: /*
1070: * If there are no arguments on the line, the next-line scope is
1071: * assumed.
1072: */
1073:
1074: if ('\0' == (*bufp)[pos])
1075: return(ROFF_IGN);
1076:
1077: /* Otherwise re-run the roff parser after recalculating. */
1.1 schwarze 1078:
1.2 schwarze 1079: *offs = pos;
1080: return(ROFF_RERUN);
1.1 schwarze 1081: }
1082:
1083:
1.2 schwarze 1084: /* ARGSUSED */
1085: static enum rofferr
1.7 schwarze 1086: roff_ds(ROFF_ARGS)
1087: {
1.10 schwarze 1088: char *name, *string;
1089:
1090: /*
1091: * A symbol is named by the first word following the macro
1092: * invocation up to a space. Its value is anything after the
1093: * name's trailing whitespace and optional double-quote. Thus,
1094: *
1095: * [.ds foo "bar " ]
1096: *
1097: * will have `bar " ' as its value.
1098: */
1.7 schwarze 1099:
1.28 schwarze 1100: string = *bufp + pos;
1101: name = roff_getname(r, &string, ln, pos);
1.7 schwarze 1102: if ('\0' == *name)
1103: return(ROFF_IGN);
1104:
1.28 schwarze 1105: /* Read past initial double-quote. */
1106: if ('"' == *string)
1.7 schwarze 1107: string++;
1108:
1.10 schwarze 1109: /* The rest is the value. */
1.16 schwarze 1110: roff_setstr(r, name, string, 0);
1.7 schwarze 1111: return(ROFF_IGN);
1112: }
1113:
1.41 ! schwarze 1114: int
! 1115: roff_regisset(const struct roff *r, enum regs reg)
! 1116: {
! 1117:
! 1118: return(r->regs[(int)reg].set);
! 1119: }
! 1120:
! 1121: unsigned int
! 1122: roff_regget(const struct roff *r, enum regs reg)
! 1123: {
! 1124:
! 1125: return(r->regs[(int)reg].u);
! 1126: }
! 1127:
! 1128: void
! 1129: roff_regunset(struct roff *r, enum regs reg)
! 1130: {
! 1131:
! 1132: r->regs[(int)reg].set = 0;
! 1133: }
1.7 schwarze 1134:
1135: /* ARGSUSED */
1136: static enum rofferr
1.6 schwarze 1137: roff_nr(ROFF_ARGS)
1.1 schwarze 1138: {
1.28 schwarze 1139: const char *key;
1140: char *val;
1.37 schwarze 1141: int iv;
1.6 schwarze 1142:
1.28 schwarze 1143: val = *bufp + pos;
1144: key = roff_getname(r, &val, ln, pos);
1.6 schwarze 1145:
1146: if (0 == strcmp(key, "nS")) {
1.41 ! schwarze 1147: r->regs[(int)REG_nS].set = 1;
! 1148: if ((iv = mandoc_strntoi(val, strlen(val), 10)) >= 0)
! 1149: r->regs[(int)REG_nS].u = (unsigned)iv;
1.37 schwarze 1150: else
1.41 ! schwarze 1151: r->regs[(int)REG_nS].u = 0u;
1.6 schwarze 1152: }
1.1 schwarze 1153:
1.29 schwarze 1154: return(ROFF_IGN);
1155: }
1156:
1157: /* ARGSUSED */
1158: static enum rofferr
1159: roff_rm(ROFF_ARGS)
1160: {
1161: const char *name;
1162: char *cp;
1163:
1164: cp = *bufp + pos;
1165: while ('\0' != *cp) {
1.34 schwarze 1166: name = roff_getname(r, &cp, ln, (int)(cp - *bufp));
1.29 schwarze 1167: if ('\0' != *name)
1168: roff_setstr(r, name, NULL, 0);
1169: }
1.2 schwarze 1170: return(ROFF_IGN);
1.14 schwarze 1171: }
1172:
1173: /* ARGSUSED */
1174: static enum rofferr
1.27 schwarze 1175: roff_TE(ROFF_ARGS)
1176: {
1177:
1178: if (NULL == r->tbl)
1.35 schwarze 1179: mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
1.27 schwarze 1180: else
1.41 ! schwarze 1181: tbl_end(&r->tbl);
1.27 schwarze 1182:
1183: return(ROFF_IGN);
1184: }
1185:
1186: /* ARGSUSED */
1187: static enum rofferr
1188: roff_T_(ROFF_ARGS)
1189: {
1190:
1191: if (NULL == r->tbl)
1.35 schwarze 1192: mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
1.27 schwarze 1193: else
1194: tbl_restart(ppos, ln, r->tbl);
1195:
1196: return(ROFF_IGN);
1197: }
1198:
1.41 ! schwarze 1199: #if 0
! 1200: static int
! 1201: roff_closeeqn(struct roff *r)
! 1202: {
! 1203:
! 1204: return(r->eqn && ROFF_EQN == eqn_end(&r->eqn) ? 1 : 0);
! 1205: }
! 1206: #endif
! 1207:
! 1208: static void
! 1209: roff_openeqn(struct roff *r, const char *name, int line,
! 1210: int offs, const char *buf)
1.32 schwarze 1211: {
1.41 ! schwarze 1212: struct eqn_node *e;
! 1213: int poff;
1.32 schwarze 1214:
1215: assert(NULL == r->eqn);
1.41 ! schwarze 1216: e = eqn_alloc(name, offs, line, r->parse);
1.32 schwarze 1217:
1218: if (r->last_eqn)
1219: r->last_eqn->next = e;
1220: else
1221: r->first_eqn = r->last_eqn = e;
1222:
1223: r->eqn = r->last_eqn = e;
1.41 ! schwarze 1224:
! 1225: if (buf) {
! 1226: poff = 0;
! 1227: eqn_read(&r->eqn, line, buf, offs, &poff);
! 1228: }
! 1229: }
! 1230:
! 1231: /* ARGSUSED */
! 1232: static enum rofferr
! 1233: roff_EQ(ROFF_ARGS)
! 1234: {
! 1235:
! 1236: roff_openeqn(r, *bufp + pos, ln, ppos, NULL);
1.32 schwarze 1237: return(ROFF_IGN);
1238: }
1239:
1240: /* ARGSUSED */
1241: static enum rofferr
1242: roff_EN(ROFF_ARGS)
1243: {
1244:
1.35 schwarze 1245: mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
1.32 schwarze 1246: return(ROFF_IGN);
1247: }
1248:
1249: /* ARGSUSED */
1250: static enum rofferr
1.27 schwarze 1251: roff_TS(ROFF_ARGS)
1252: {
1253: struct tbl_node *t;
1254:
1255: if (r->tbl) {
1.35 schwarze 1256: mandoc_msg(MANDOCERR_SCOPEBROKEN, r->parse, ln, ppos, NULL);
1.41 ! schwarze 1257: tbl_end(&r->tbl);
1.27 schwarze 1258: }
1259:
1.35 schwarze 1260: t = tbl_alloc(ppos, ln, r->parse);
1.27 schwarze 1261:
1262: if (r->last_tbl)
1263: r->last_tbl->next = t;
1264: else
1265: r->first_tbl = r->last_tbl = t;
1266:
1267: r->tbl = r->last_tbl = t;
1268: return(ROFF_IGN);
1269: }
1270:
1271: /* ARGSUSED */
1272: static enum rofferr
1.14 schwarze 1273: roff_so(ROFF_ARGS)
1274: {
1275: char *name;
1.15 schwarze 1276:
1.35 schwarze 1277: mandoc_msg(MANDOCERR_SO, r->parse, ln, ppos, NULL);
1.14 schwarze 1278:
1.22 schwarze 1279: /*
1280: * Handle `so'. Be EXTREMELY careful, as we shouldn't be
1281: * opening anything that's not in our cwd or anything beneath
1282: * it. Thus, explicitly disallow traversing up the file-system
1283: * or using absolute paths.
1284: */
1285:
1.14 schwarze 1286: name = *bufp + pos;
1287: if ('/' == *name || strstr(name, "../") || strstr(name, "/..")) {
1.35 schwarze 1288: mandoc_msg(MANDOCERR_SOPATH, r->parse, ln, pos, NULL);
1.14 schwarze 1289: return(ROFF_ERR);
1290: }
1291:
1292: *offs = pos;
1293: return(ROFF_SO);
1.7 schwarze 1294: }
1295:
1.16 schwarze 1296: /* ARGSUSED */
1297: static enum rofferr
1298: roff_userdef(ROFF_ARGS)
1.12 schwarze 1299: {
1.16 schwarze 1300: const char *arg[9];
1301: char *cp, *n1, *n2;
1.25 schwarze 1302: int i;
1.12 schwarze 1303:
1.16 schwarze 1304: /*
1305: * Collect pointers to macro argument strings
1306: * and null-terminate them.
1307: */
1308: cp = *bufp + pos;
1.25 schwarze 1309: for (i = 0; i < 9; i++)
1.26 schwarze 1310: arg[i] = '\0' == *cp ? "" :
1.35 schwarze 1311: mandoc_getarg(r->parse, &cp, ln, &pos);
1.16 schwarze 1312:
1313: /*
1314: * Expand macro arguments.
1.12 schwarze 1315: */
1.16 schwarze 1316: *szp = 0;
1317: n1 = cp = mandoc_strdup(r->current_string);
1318: while (NULL != (cp = strstr(cp, "\\$"))) {
1319: i = cp[2] - '1';
1320: if (0 > i || 8 < i) {
1321: /* Not an argument invocation. */
1322: cp += 2;
1323: continue;
1324: }
1325:
1326: *szp = strlen(n1) - 3 + strlen(arg[i]) + 1;
1327: n2 = mandoc_malloc(*szp);
1328:
1329: strlcpy(n2, n1, (size_t)(cp - n1 + 1));
1330: strlcat(n2, arg[i], *szp);
1331: strlcat(n2, cp + 3, *szp);
1332:
1333: cp = n2 + (cp - n1);
1334: free(n1);
1335: n1 = n2;
1.12 schwarze 1336: }
1337:
1.16 schwarze 1338: /*
1339: * Replace the macro invocation
1340: * by the expanded macro.
1341: */
1342: free(*bufp);
1343: *bufp = n1;
1344: if (0 == *szp)
1345: *szp = strlen(*bufp) + 1;
1346:
1.19 schwarze 1347: return(*szp > 1 && '\n' == (*bufp)[(int)*szp - 2] ?
1.16 schwarze 1348: ROFF_REPARSE : ROFF_APPEND);
1.12 schwarze 1349: }
1.28 schwarze 1350:
1351: static char *
1352: roff_getname(struct roff *r, char **cpp, int ln, int pos)
1353: {
1354: char *name, *cp;
1355:
1356: name = *cpp;
1357: if ('\0' == *name)
1358: return(name);
1359:
1360: /* Read until end of name. */
1361: for (cp = name; '\0' != *cp && ' ' != *cp; cp++) {
1362: if ('\\' != *cp)
1363: continue;
1364: cp++;
1365: if ('\\' == *cp)
1366: continue;
1.35 schwarze 1367: mandoc_msg(MANDOCERR_NAMESC, r->parse, ln, pos, NULL);
1.28 schwarze 1368: *cp = '\0';
1369: name = cp;
1370: }
1371:
1372: /* Nil-terminate name. */
1373: if ('\0' != *cp)
1374: *(cp++) = '\0';
1375:
1376: /* Read past spaces. */
1377: while (' ' == *cp)
1378: cp++;
1379:
1380: *cpp = cp;
1381: return(name);
1382: }
1383:
1.16 schwarze 1384: /*
1385: * Store *string into the user-defined string called *name.
1386: * In multiline mode, append to an existing entry and append '\n';
1387: * else replace the existing entry, if there is one.
1388: * To clear an existing entry, call with (*r, *name, NULL, 0).
1389: */
1.8 schwarze 1390: static void
1.16 schwarze 1391: roff_setstr(struct roff *r, const char *name, const char *string,
1392: int multiline)
1.7 schwarze 1393: {
1394: struct roffstr *n;
1.16 schwarze 1395: char *c;
1396: size_t oldch, newch;
1.40 schwarze 1397:
1398: /* XXX workaround for the Perl preamble until we get .tr */
1399: if ( ! strcmp(name, "--")) {
1400: string = "--";
1401: multiline = 0;
1402: }
1.7 schwarze 1403:
1.16 schwarze 1404: /* Search for an existing string with the same name. */
1.8 schwarze 1405: n = r->first_string;
1.7 schwarze 1406: while (n && strcmp(name, n->name))
1407: n = n->next;
1.8 schwarze 1408:
1409: if (NULL == n) {
1.16 schwarze 1410: /* Create a new string table entry. */
1.8 schwarze 1411: n = mandoc_malloc(sizeof(struct roffstr));
1.16 schwarze 1412: n->name = mandoc_strdup(name);
1413: n->string = NULL;
1.8 schwarze 1414: n->next = r->first_string;
1415: r->first_string = n;
1.16 schwarze 1416: } else if (0 == multiline) {
1417: /* In multiline mode, append; else replace. */
1.7 schwarze 1418: free(n->string);
1.16 schwarze 1419: n->string = NULL;
1420: }
1421:
1422: if (NULL == string)
1423: return;
1424:
1425: /*
1426: * One additional byte for the '\n' in multiline mode,
1427: * and one for the terminating '\0'.
1428: */
1.34 schwarze 1429: newch = strlen(string) + (multiline ? 2u : 1u);
1.16 schwarze 1430: if (NULL == n->string) {
1431: n->string = mandoc_malloc(newch);
1432: *n->string = '\0';
1433: oldch = 0;
1434: } else {
1435: oldch = strlen(n->string);
1436: n->string = mandoc_realloc(n->string, oldch + newch);
1437: }
1438:
1439: /* Skip existing content in the destination buffer. */
1.34 schwarze 1440: c = n->string + (int)oldch;
1.16 schwarze 1441:
1442: /* Append new content to the destination buffer. */
1443: while (*string) {
1444: /*
1445: * Rudimentary roff copy mode:
1446: * Handle escaped backslashes.
1447: */
1448: if ('\\' == *string && '\\' == *(string + 1))
1449: string++;
1450: *c++ = *string++;
1451: }
1.8 schwarze 1452:
1.16 schwarze 1453: /* Append terminating bytes. */
1454: if (multiline)
1455: *c++ = '\n';
1456: *c = '\0';
1.7 schwarze 1457: }
1458:
1.8 schwarze 1459: static const char *
1460: roff_getstrn(const struct roff *r, const char *name, size_t len)
1.7 schwarze 1461: {
1.8 schwarze 1462: const struct roffstr *n;
1.7 schwarze 1463:
1.8 schwarze 1464: n = r->first_string;
1.10 schwarze 1465: while (n && (strncmp(name, n->name, len) || '\0' != n->name[(int)len]))
1.7 schwarze 1466: n = n->next;
1.8 schwarze 1467:
1468: return(n ? n->string : NULL);
1.7 schwarze 1469: }
1470:
1.8 schwarze 1471: static void
1472: roff_freestr(struct roff *r)
1.7 schwarze 1473: {
1474: struct roffstr *n, *nn;
1475:
1.8 schwarze 1476: for (n = r->first_string; n; n = nn) {
1.7 schwarze 1477: free(n->name);
1478: free(n->string);
1479: nn = n->next;
1480: free(n);
1481: }
1.8 schwarze 1482:
1483: r->first_string = NULL;
1.27 schwarze 1484: }
1485:
1486: const struct tbl_span *
1487: roff_span(const struct roff *r)
1488: {
1489:
1490: return(r->tbl ? tbl_span(r->tbl) : NULL);
1.32 schwarze 1491: }
1492:
1493: const struct eqn *
1494: roff_eqn(const struct roff *r)
1495: {
1496:
1497: return(r->last_eqn ? &r->last_eqn->eqn : NULL);
1.1 schwarze 1498: }