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