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