Annotation of mandoc/roff.c, Revision 1.110
1.110 ! kristaps 1: /* $Id: roff.c,v 1.109 2010/12/28 10:51:03 kristaps 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.110 ! kristaps 513: /* FIXME: if r->tbl */
! 514: if (r->last)
1.109 kristaps 515: (*r->msg)(MANDOCERR_SCOPEEXIT, r->data,
516: r->last->line, r->last->col, NULL);
517: return(1);
1.67 kristaps 518: }
519:
520:
521: /*
522: * Parse a roff node's type from the input buffer. This must be in the
523: * form of ".foo xxx" in the usual way.
524: */
525: static enum rofft
1.106 kristaps 526: roff_parse(struct roff *r, const char *buf, int *pos)
1.67 kristaps 527: {
1.106 kristaps 528: const char *mac;
529: size_t maclen;
1.67 kristaps 530: enum rofft t;
531:
1.75 kristaps 532: assert(ROFF_CTL(buf[*pos]));
533: (*pos)++;
1.67 kristaps 534:
1.106 kristaps 535: while (' ' == buf[*pos] || '\t' == buf[*pos])
1.67 kristaps 536: (*pos)++;
537:
538: if ('\0' == buf[*pos])
539: return(ROFF_MAX);
540:
1.106 kristaps 541: mac = buf + *pos;
542: maclen = strcspn(mac, " \\\t\0");
1.67 kristaps 543:
1.106 kristaps 544: t = (r->current_string = roff_getstrn(r, mac, maclen))
545: ? ROFF_USERDEF : roff_hash_find(mac, maclen);
1.67 kristaps 546:
1.106 kristaps 547: *pos += maclen;
1.67 kristaps 548: while (buf[*pos] && ' ' == buf[*pos])
549: (*pos)++;
550:
551: return(t);
552: }
553:
554:
1.89 kristaps 555: static int
1.91 kristaps 556: roff_parse_nat(const char *buf, unsigned int *res)
1.89 kristaps 557: {
558: char *ep;
559: long lval;
560:
561: errno = 0;
562: lval = strtol(buf, &ep, 10);
563: if (buf[0] == '\0' || *ep != '\0')
564: return(0);
565: if ((errno == ERANGE &&
566: (lval == LONG_MAX || lval == LONG_MIN)) ||
1.91 kristaps 567: (lval > INT_MAX || lval < 0))
1.89 kristaps 568: return(0);
569:
1.91 kristaps 570: *res = (unsigned int)lval;
1.89 kristaps 571: return(1);
572: }
573:
574:
1.67 kristaps 575: /* ARGSUSED */
576: static enum rofferr
1.76 kristaps 577: roff_cblock(ROFF_ARGS)
1.67 kristaps 578: {
579:
1.79 kristaps 580: /*
581: * A block-close `..' should only be invoked as a child of an
582: * ignore macro, otherwise raise a warning and just ignore it.
583: */
584:
1.76 kristaps 585: if (NULL == r->last) {
1.109 kristaps 586: (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL);
1.76 kristaps 587: return(ROFF_IGN);
588: }
1.67 kristaps 589:
1.81 kristaps 590: switch (r->last->tok) {
591: case (ROFF_am):
592: /* FALLTHROUGH */
593: case (ROFF_ami):
594: /* FALLTHROUGH */
595: case (ROFF_am1):
596: /* FALLTHROUGH */
597: case (ROFF_de):
1.108 schwarze 598: /* ROFF_de1 is remapped to ROFF_de in roff_block(). */
1.81 kristaps 599: /* FALLTHROUGH */
600: case (ROFF_dei):
601: /* FALLTHROUGH */
602: case (ROFF_ig):
603: break;
604: default:
1.109 kristaps 605: (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL);
1.67 kristaps 606: return(ROFF_IGN);
1.76 kristaps 607: }
1.67 kristaps 608:
1.76 kristaps 609: if ((*bufp)[pos])
1.109 kristaps 610: (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, pos, NULL);
1.71 kristaps 611:
612: roffnode_pop(r);
1.76 kristaps 613: roffnode_cleanscope(r);
614: return(ROFF_IGN);
1.71 kristaps 615:
1.67 kristaps 616: }
617:
618:
1.76 kristaps 619: static void
620: roffnode_cleanscope(struct roff *r)
1.67 kristaps 621: {
622:
1.76 kristaps 623: while (r->last) {
624: if (--r->last->endspan < 0)
625: break;
626: roffnode_pop(r);
627: }
1.67 kristaps 628: }
629:
630:
1.75 kristaps 631: /* ARGSUSED */
1.74 kristaps 632: static enum rofferr
1.75 kristaps 633: roff_ccond(ROFF_ARGS)
1.74 kristaps 634: {
635:
1.76 kristaps 636: if (NULL == r->last) {
1.109 kristaps 637: (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL);
1.76 kristaps 638: return(ROFF_IGN);
639: }
640:
1.82 kristaps 641: switch (r->last->tok) {
642: case (ROFF_el):
643: /* FALLTHROUGH */
644: case (ROFF_ie):
645: /* FALLTHROUGH */
646: case (ROFF_if):
647: break;
648: default:
1.109 kristaps 649: (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL);
1.75 kristaps 650: return(ROFF_IGN);
651: }
652:
1.76 kristaps 653: if (r->last->endspan > -1) {
1.109 kristaps 654: (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL);
1.76 kristaps 655: return(ROFF_IGN);
656: }
657:
658: if ((*bufp)[pos])
1.109 kristaps 659: (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, pos, NULL);
1.76 kristaps 660:
1.75 kristaps 661: roffnode_pop(r);
1.76 kristaps 662: roffnode_cleanscope(r);
663: return(ROFF_IGN);
664: }
665:
1.75 kristaps 666:
1.76 kristaps 667: /* ARGSUSED */
668: static enum rofferr
1.80 kristaps 669: roff_block(ROFF_ARGS)
1.76 kristaps 670: {
1.78 kristaps 671: int sv;
672: size_t sz;
1.106 kristaps 673: char *name;
674:
675: name = NULL;
1.76 kristaps 676:
1.106 kristaps 677: if (ROFF_ig != tok) {
678: if ('\0' == (*bufp)[pos]) {
679: (*r->msg)(MANDOCERR_NOARGS, r->data, ln, ppos, NULL);
680: return(ROFF_IGN);
681: }
1.107 kristaps 682:
683: /*
684: * Re-write `de1', since we don't really care about
685: * groff's strange compatibility mode, into `de'.
686: */
687:
1.106 kristaps 688: if (ROFF_de1 == tok)
689: tok = ROFF_de;
690: if (ROFF_de == tok)
691: name = *bufp + pos;
692: else
693: (*r->msg)(MANDOCERR_REQUEST, r->data, ln, ppos,
694: roffs[tok].name);
1.107 kristaps 695:
1.80 kristaps 696: while ((*bufp)[pos] && ' ' != (*bufp)[pos])
697: pos++;
1.107 kristaps 698:
1.80 kristaps 699: while (' ' == (*bufp)[pos])
1.106 kristaps 700: (*bufp)[pos++] = '\0';
1.80 kristaps 701: }
702:
1.106 kristaps 703: roffnode_push(r, tok, name, ln, ppos);
704:
705: /*
706: * At the beginning of a `de' macro, clear the existing string
707: * with the same name, if there is one. New content will be
708: * added from roff_block_text() in multiline mode.
709: */
1.107 kristaps 710:
1.106 kristaps 711: if (ROFF_de == tok)
1.108 schwarze 712: roff_setstr(r, name, "", 0);
1.76 kristaps 713:
1.79 kristaps 714: if ('\0' == (*bufp)[pos])
1.78 kristaps 715: return(ROFF_IGN);
716:
1.107 kristaps 717: /* If present, process the custom end-of-line marker. */
718:
1.78 kristaps 719: sv = pos;
1.108 schwarze 720: while ((*bufp)[pos] &&
1.107 kristaps 721: ' ' != (*bufp)[pos] &&
1.78 kristaps 722: '\t' != (*bufp)[pos])
723: pos++;
724:
725: /*
726: * Note: groff does NOT like escape characters in the input.
727: * Instead of detecting this, we're just going to let it fly and
728: * to hell with it.
729: */
730:
731: assert(pos > sv);
732: sz = (size_t)(pos - sv);
733:
1.79 kristaps 734: if (1 == sz && '.' == (*bufp)[sv])
735: return(ROFF_IGN);
736:
1.98 schwarze 737: r->last->end = mandoc_malloc(sz + 1);
1.78 kristaps 738:
739: memcpy(r->last->end, *bufp + sv, sz);
740: r->last->end[(int)sz] = '\0';
741:
1.77 kristaps 742: if ((*bufp)[pos])
1.107 kristaps 743: (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, pos, NULL);
1.74 kristaps 744:
1.78 kristaps 745: return(ROFF_IGN);
746: }
747:
748:
749: /* ARGSUSED */
750: static enum rofferr
1.80 kristaps 751: roff_block_sub(ROFF_ARGS)
1.79 kristaps 752: {
753: enum rofft t;
754: int i, j;
755:
756: /*
757: * First check whether a custom macro exists at this level. If
758: * it does, then check against it. This is some of groff's
759: * stranger behaviours. If we encountered a custom end-scope
760: * tag and that tag also happens to be a "real" macro, then we
761: * need to try interpreting it again as a real macro. If it's
762: * not, then return ignore. Else continue.
763: */
764:
765: if (r->last->end) {
766: i = pos + 1;
767: while (' ' == (*bufp)[i] || '\t' == (*bufp)[i])
768: i++;
769:
770: for (j = 0; r->last->end[j]; j++, i++)
771: if ((*bufp)[i] != r->last->end[j])
772: break;
773:
774: if ('\0' == r->last->end[j] &&
775: ('\0' == (*bufp)[i] ||
776: ' ' == (*bufp)[i] ||
777: '\t' == (*bufp)[i])) {
778: roffnode_pop(r);
779: roffnode_cleanscope(r);
780:
1.106 kristaps 781: if (ROFF_MAX != roff_parse(r, *bufp, &pos))
1.79 kristaps 782: return(ROFF_RERUN);
783: return(ROFF_IGN);
784: }
785: }
786:
787: /*
788: * If we have no custom end-query or lookup failed, then try
789: * pulling it out of the hashtable.
790: */
791:
792: ppos = pos;
1.106 kristaps 793: t = roff_parse(r, *bufp, &pos);
1.79 kristaps 794:
1.106 kristaps 795: /*
796: * Macros other than block-end are only significant
797: * in `de' blocks; elsewhere, simply throw them away.
798: */
799: if (ROFF_cblock != t) {
800: if (ROFF_de == tok)
801: roff_setstr(r, r->last->name, *bufp + ppos, 1);
1.79 kristaps 802: return(ROFF_IGN);
1.106 kristaps 803: }
1.79 kristaps 804:
805: assert(roffs[t].proc);
1.90 kristaps 806: return((*roffs[t].proc)(r, t, bufp, szp,
807: ln, ppos, pos, offs));
1.79 kristaps 808: }
809:
810:
811: /* ARGSUSED */
812: static enum rofferr
1.80 kristaps 813: roff_block_text(ROFF_ARGS)
1.78 kristaps 814: {
815:
1.106 kristaps 816: if (ROFF_de == tok)
817: roff_setstr(r, r->last->name, *bufp + pos, 1);
818:
1.78 kristaps 819: return(ROFF_IGN);
820: }
821:
822:
823: /* ARGSUSED */
824: static enum rofferr
1.82 kristaps 825: roff_cond_sub(ROFF_ARGS)
826: {
827: enum rofft t;
828: enum roffrule rr;
829:
830: ppos = pos;
831: rr = r->last->rule;
832:
1.87 kristaps 833: /*
834: * Clean out scope. If we've closed ourselves, then don't
835: * continue.
836: */
837:
838: roffnode_cleanscope(r);
1.82 kristaps 839:
1.106 kristaps 840: if (ROFF_MAX == (t = roff_parse(r, *bufp, &pos))) {
1.100 kristaps 841: if ('\\' == (*bufp)[pos] && '}' == (*bufp)[pos + 1])
842: return(roff_ccond
1.106 kristaps 843: (r, ROFF_ccond, bufp, szp,
1.100 kristaps 844: ln, pos, pos + 2, offs));
1.82 kristaps 845: return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
1.100 kristaps 846: }
1.82 kristaps 847:
848: /*
849: * A denied conditional must evaluate its children if and only
850: * if they're either structurally required (such as loops and
851: * conditionals) or a closing macro.
852: */
853: if (ROFFRULE_DENY == rr)
854: if ( ! (ROFFMAC_STRUCT & roffs[t].flags))
855: if (ROFF_ccond != t)
856: return(ROFF_IGN);
857:
858: assert(roffs[t].proc);
1.90 kristaps 859: return((*roffs[t].proc)(r, t, bufp, szp,
860: ln, ppos, pos, offs));
1.82 kristaps 861: }
862:
863:
864: /* ARGSUSED */
865: static enum rofferr
866: roff_cond_text(ROFF_ARGS)
1.78 kristaps 867: {
868: char *ep, *st;
1.82 kristaps 869: enum roffrule rr;
870:
871: rr = r->last->rule;
872:
873: /*
874: * We display the value of the text if out current evaluation
875: * scope permits us to do so.
876: */
1.100 kristaps 877:
878: /* FIXME: use roff_ccond? */
1.78 kristaps 879:
880: st = &(*bufp)[pos];
881: if (NULL == (ep = strstr(st, "\\}"))) {
882: roffnode_cleanscope(r);
1.82 kristaps 883: return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
1.78 kristaps 884: }
885:
1.86 kristaps 886: if (ep == st || (ep > st && '\\' != *(ep - 1)))
1.78 kristaps 887: roffnode_pop(r);
888:
889: roffnode_cleanscope(r);
1.82 kristaps 890: return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
1.74 kristaps 891: }
892:
893:
1.88 kristaps 894: static enum roffrule
895: roff_evalcond(const char *v, int *pos)
896: {
897:
898: switch (v[*pos]) {
899: case ('n'):
900: (*pos)++;
901: return(ROFFRULE_ALLOW);
902: case ('e'):
903: /* FALLTHROUGH */
904: case ('o'):
905: /* FALLTHROUGH */
906: case ('t'):
907: (*pos)++;
908: return(ROFFRULE_DENY);
909: default:
910: break;
911: }
912:
913: while (v[*pos] && ' ' != v[*pos])
914: (*pos)++;
915: return(ROFFRULE_DENY);
916: }
917:
1.75 kristaps 918: /* ARGSUSED */
1.74 kristaps 919: static enum rofferr
1.103 kristaps 920: roff_line_ignore(ROFF_ARGS)
1.89 kristaps 921: {
922:
923: return(ROFF_IGN);
924: }
925:
1.104 kristaps 926: /* ARGSUSED */
927: static enum rofferr
928: roff_line_error(ROFF_ARGS)
929: {
930:
931: (*r->msg)(MANDOCERR_REQUEST, r->data, ln, ppos, roffs[tok].name);
932: return(ROFF_IGN);
933: }
1.89 kristaps 934:
935: /* ARGSUSED */
936: static enum rofferr
1.82 kristaps 937: roff_cond(ROFF_ARGS)
1.74 kristaps 938: {
1.77 kristaps 939: int sv;
1.88 kristaps 940: enum roffrule rule;
1.74 kristaps 941:
1.82 kristaps 942: /* Stack overflow! */
943:
944: if (ROFF_ie == tok && r->rstackpos == RSTACK_MAX - 1) {
945: (*r->msg)(MANDOCERR_MEM, r->data, ln, ppos, NULL);
946: return(ROFF_ERR);
947: }
1.74 kristaps 948:
1.88 kristaps 949: /* First, evaluate the conditional. */
1.84 schwarze 950:
1.88 kristaps 951: if (ROFF_el == tok) {
952: /*
953: * An `.el' will get the value of the current rstack
954: * entry set in prior `ie' calls or defaults to DENY.
955: */
956: if (r->rstackpos < 0)
957: rule = ROFFRULE_DENY;
958: else
959: rule = r->rstack[r->rstackpos];
960: } else
961: rule = roff_evalcond(*bufp, &pos);
1.77 kristaps 962:
963: sv = pos;
1.88 kristaps 964:
1.75 kristaps 965: while (' ' == (*bufp)[pos])
966: pos++;
1.74 kristaps 967:
1.77 kristaps 968: /*
969: * Roff is weird. If we have just white-space after the
970: * conditional, it's considered the BODY and we exit without
971: * really doing anything. Warn about this. It's probably
972: * wrong.
973: */
1.88 kristaps 974:
1.77 kristaps 975: if ('\0' == (*bufp)[pos] && sv != pos) {
1.107 kristaps 976: (*r->msg)(MANDOCERR_NOARGS, r->data, ln, ppos, NULL);
977: return(ROFF_IGN);
1.77 kristaps 978: }
979:
1.106 kristaps 980: roffnode_push(r, tok, NULL, ln, ppos);
1.77 kristaps 981:
1.88 kristaps 982: r->last->rule = rule;
983:
1.84 schwarze 984: if (ROFF_ie == tok) {
1.82 kristaps 985: /*
986: * An if-else will put the NEGATION of the current
987: * evaluated conditional into the stack.
988: */
989: r->rstackpos++;
990: if (ROFFRULE_DENY == r->last->rule)
991: r->rstack[r->rstackpos] = ROFFRULE_ALLOW;
992: else
993: r->rstack[r->rstackpos] = ROFFRULE_DENY;
994: }
1.88 kristaps 995:
996: /* If the parent has false as its rule, then so do we. */
997:
1.109 kristaps 998: if (r->last->parent && ROFFRULE_DENY == r->last->parent->rule)
1.84 schwarze 999: r->last->rule = ROFFRULE_DENY;
1.88 kristaps 1000:
1001: /*
1002: * Determine scope. If we're invoked with "\{" trailing the
1003: * conditional, then we're in a multiline scope. Else our scope
1004: * expires on the next line.
1005: */
1.74 kristaps 1006:
1.75 kristaps 1007: r->last->endspan = 1;
1008:
1009: if ('\\' == (*bufp)[pos] && '{' == (*bufp)[pos + 1]) {
1010: r->last->endspan = -1;
1011: pos += 2;
1.109 kristaps 1012: }
1.74 kristaps 1013:
1.77 kristaps 1014: /*
1015: * If there are no arguments on the line, the next-line scope is
1016: * assumed.
1017: */
1018:
1.75 kristaps 1019: if ('\0' == (*bufp)[pos])
1020: return(ROFF_IGN);
1.77 kristaps 1021:
1022: /* Otherwise re-run the roff parser after recalculating. */
1.74 kristaps 1023:
1.75 kristaps 1024: *offs = pos;
1025: return(ROFF_RERUN);
1.83 schwarze 1026: }
1027:
1028:
1029: /* ARGSUSED */
1030: static enum rofferr
1.92 schwarze 1031: roff_ds(ROFF_ARGS)
1032: {
1.96 kristaps 1033: char *name, *string;
1034:
1035: /*
1036: * A symbol is named by the first word following the macro
1037: * invocation up to a space. Its value is anything after the
1038: * name's trailing whitespace and optional double-quote. Thus,
1039: *
1040: * [.ds foo "bar " ]
1041: *
1042: * will have `bar " ' as its value.
1043: */
1.92 schwarze 1044:
1045: name = *bufp + pos;
1046: if ('\0' == *name)
1047: return(ROFF_IGN);
1048:
1049: string = name;
1.96 kristaps 1050: /* Read until end of name. */
1.92 schwarze 1051: while (*string && ' ' != *string)
1052: string++;
1.96 kristaps 1053:
1054: /* Nil-terminate name. */
1.92 schwarze 1055: if (*string)
1.96 kristaps 1056: *(string++) = '\0';
1057:
1058: /* Read past spaces. */
1059: while (*string && ' ' == *string)
1060: string++;
1061:
1062: /* Read passed initial double-quote. */
1.92 schwarze 1063: if (*string && '"' == *string)
1064: string++;
1065:
1.96 kristaps 1066: /* The rest is the value. */
1.106 kristaps 1067: roff_setstr(r, name, string, 0);
1.92 schwarze 1068: return(ROFF_IGN);
1069: }
1070:
1071:
1072: /* ARGSUSED */
1073: static enum rofferr
1.89 kristaps 1074: roff_nr(ROFF_ARGS)
1.83 schwarze 1075: {
1.89 kristaps 1076: const char *key, *val;
1.91 kristaps 1077: struct reg *rg;
1.89 kristaps 1078:
1079: key = &(*bufp)[pos];
1.91 kristaps 1080: rg = r->regs->regs;
1.89 kristaps 1081:
1082: /* Parse register request. */
1083: while ((*bufp)[pos] && ' ' != (*bufp)[pos])
1084: pos++;
1085:
1086: /*
1087: * Set our nil terminator. Because this line is going to be
1088: * ignored anyway, we can munge it as we please.
1089: */
1090: if ((*bufp)[pos])
1091: (*bufp)[pos++] = '\0';
1092:
1093: /* Skip whitespace to register token. */
1094: while ((*bufp)[pos] && ' ' == (*bufp)[pos])
1095: pos++;
1096:
1097: val = &(*bufp)[pos];
1098:
1099: /* Process register token. */
1100:
1101: if (0 == strcmp(key, "nS")) {
1.91 kristaps 1102: rg[(int)REG_nS].set = 1;
1103: if ( ! roff_parse_nat(val, &rg[(int)REG_nS].v.u))
1104: rg[(int)REG_nS].v.u = 0;
1.109 kristaps 1105: }
1106:
1107: return(ROFF_IGN);
1108: }
1109:
1110: /* ARGSUSED */
1111: static enum rofferr
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_free(r->tbl);
1119:
1120: r->tbl = NULL;
1121: return(ROFF_IGN);
1122: }
1123:
1124: /* ARGSUSED */
1125: static enum rofferr
1126: roff_TS(ROFF_ARGS)
1127: {
1.89 kristaps 1128:
1.109 kristaps 1129: if (r->tbl) {
1130: (*r->msg)(MANDOCERR_SCOPEBROKEN, r->data, ln, ppos, NULL);
1131: tbl_reset(r->tbl);
1.89 kristaps 1132: } else
1.109 kristaps 1133: r->tbl = tbl_alloc();
1.83 schwarze 1134:
1135: return(ROFF_IGN);
1.92 schwarze 1136: }
1137:
1.105 kristaps 1138: /* ARGSUSED */
1139: static enum rofferr
1140: roff_so(ROFF_ARGS)
1141: {
1142: char *name;
1143:
1144: (*r->msg)(MANDOCERR_SO, r->data, ln, ppos, NULL);
1145:
1146: /*
1147: * Handle `so'. Be EXTREMELY careful, as we shouldn't be
1148: * opening anything that's not in our cwd or anything beneath
1149: * it. Thus, explicitly disallow traversing up the file-system
1150: * or using absolute paths.
1151: */
1152:
1153: name = *bufp + pos;
1154: if ('/' == *name || strstr(name, "../") || strstr(name, "/..")) {
1155: (*r->msg)(MANDOCERR_SOPATH, r->data, ln, pos, NULL);
1156: return(ROFF_ERR);
1157: }
1158:
1159: *offs = pos;
1160: return(ROFF_SO);
1161: }
1.92 schwarze 1162:
1.106 kristaps 1163: /* ARGSUSED */
1164: static enum rofferr
1165: roff_userdef(ROFF_ARGS)
1.99 kristaps 1166: {
1.106 kristaps 1167: const char *arg[9];
1168: char *cp, *n1, *n2;
1169: int i, quoted, pairs;
1170:
1171: /*
1172: * Collect pointers to macro argument strings
1173: * and null-terminate them.
1174: */
1175: cp = *bufp + pos;
1176: for (i = 0; i < 9; i++) {
1177: /* Quoting can only start with a new word. */
1178: if ('"' == *cp) {
1179: quoted = 1;
1180: cp++;
1181: } else
1182: quoted = 0;
1183: arg[i] = cp;
1184: for (pairs = 0; '\0' != *cp; cp++) {
1185: /* Unquoted arguments end at blanks. */
1186: if (0 == quoted) {
1187: if (' ' == *cp)
1188: break;
1189: continue;
1190: }
1191: /* After pairs of quotes, move left. */
1192: if (pairs)
1193: cp[-pairs] = cp[0];
1194: /* Pairs of quotes do not end words, ... */
1195: if ('"' == cp[0] && '"' == cp[1]) {
1196: pairs++;
1197: cp++;
1198: continue;
1199: }
1200: /* ... but solitary quotes do. */
1201: if ('"' != *cp)
1202: continue;
1203: if (pairs)
1204: cp[-pairs] = '\0';
1205: *cp = ' ';
1206: break;
1207: }
1208: /* Last argument; the remaining ones are empty strings. */
1209: if ('\0' == *cp)
1210: continue;
1211: /* Null-terminate argument and move to the next one. */
1212: *cp++ = '\0';
1213: while (' ' == *cp)
1214: cp++;
1215: }
1.99 kristaps 1216:
1.106 kristaps 1217: /*
1218: * Expand macro arguments.
1.99 kristaps 1219: */
1.106 kristaps 1220: *szp = 0;
1221: n1 = cp = mandoc_strdup(r->current_string);
1222: while (NULL != (cp = strstr(cp, "\\$"))) {
1223: i = cp[2] - '1';
1224: if (0 > i || 8 < i) {
1225: /* Not an argument invocation. */
1226: cp += 2;
1227: continue;
1228: }
1229:
1230: *szp = strlen(n1) - 3 + strlen(arg[i]) + 1;
1231: n2 = mandoc_malloc(*szp);
1232:
1233: strlcpy(n2, n1, (size_t)(cp - n1 + 1));
1234: strlcat(n2, arg[i], *szp);
1235: strlcat(n2, cp + 3, *szp);
1236:
1237: cp = n2 + (cp - n1);
1238: free(n1);
1239: n1 = n2;
1.99 kristaps 1240: }
1241:
1.106 kristaps 1242: /*
1243: * Replace the macro invocation
1244: * by the expanded macro.
1245: */
1246: free(*bufp);
1247: *bufp = n1;
1248: if (0 == *szp)
1249: *szp = strlen(*bufp) + 1;
1250:
1251: return(*szp > 1 && '\n' == (*bufp)[(int)*szp - 2] ?
1252: ROFF_REPARSE : ROFF_APPEND);
1.99 kristaps 1253: }
1254:
1.106 kristaps 1255: /*
1256: * Store *string into the user-defined string called *name.
1257: * In multiline mode, append to an existing entry and append '\n';
1258: * else replace the existing entry, if there is one.
1259: * To clear an existing entry, call with (*r, *name, NULL, 0).
1260: */
1.94 kristaps 1261: static void
1.106 kristaps 1262: roff_setstr(struct roff *r, const char *name, const char *string,
1263: int multiline)
1.92 schwarze 1264: {
1265: struct roffstr *n;
1.106 kristaps 1266: char *c;
1267: size_t oldch, newch;
1.92 schwarze 1268:
1.106 kristaps 1269: /* Search for an existing string with the same name. */
1.94 kristaps 1270: n = r->first_string;
1.92 schwarze 1271: while (n && strcmp(name, n->name))
1272: n = n->next;
1.94 kristaps 1273:
1274: if (NULL == n) {
1.106 kristaps 1275: /* Create a new string table entry. */
1.94 kristaps 1276: n = mandoc_malloc(sizeof(struct roffstr));
1.106 kristaps 1277: n->name = mandoc_strdup(name);
1278: n->string = NULL;
1.94 kristaps 1279: n->next = r->first_string;
1280: r->first_string = n;
1.106 kristaps 1281: } else if (0 == multiline) {
1282: /* In multiline mode, append; else replace. */
1.92 schwarze 1283: free(n->string);
1.106 kristaps 1284: n->string = NULL;
1285: }
1286:
1287: if (NULL == string)
1288: return;
1289:
1290: /*
1291: * One additional byte for the '\n' in multiline mode,
1292: * and one for the terminating '\0'.
1293: */
1294: newch = strlen(string) + (multiline ? 2 : 1);
1295: if (NULL == n->string) {
1296: n->string = mandoc_malloc(newch);
1297: *n->string = '\0';
1298: oldch = 0;
1299: } else {
1300: oldch = strlen(n->string);
1301: n->string = mandoc_realloc(n->string, oldch + newch);
1302: }
1303:
1304: /* Skip existing content in the destination buffer. */
1305: c = n->string + oldch;
1306:
1307: /* Append new content to the destination buffer. */
1308: while (*string) {
1309: /*
1310: * Rudimentary roff copy mode:
1311: * Handle escaped backslashes.
1312: */
1313: if ('\\' == *string && '\\' == *(string + 1))
1314: string++;
1315: *c++ = *string++;
1316: }
1.94 kristaps 1317:
1.106 kristaps 1318: /* Append terminating bytes. */
1319: if (multiline)
1320: *c++ = '\n';
1321: *c = '\0';
1.92 schwarze 1322: }
1323:
1324:
1.94 kristaps 1325: static const char *
1326: roff_getstrn(const struct roff *r, const char *name, size_t len)
1.92 schwarze 1327: {
1.94 kristaps 1328: const struct roffstr *n;
1.92 schwarze 1329:
1.94 kristaps 1330: n = r->first_string;
1.97 kristaps 1331: while (n && (strncmp(name, n->name, len) || '\0' != n->name[(int)len]))
1.92 schwarze 1332: n = n->next;
1.94 kristaps 1333:
1334: return(n ? n->string : NULL);
1.92 schwarze 1335: }
1336:
1.94 kristaps 1337:
1338: static void
1339: roff_freestr(struct roff *r)
1.92 schwarze 1340: {
1341: struct roffstr *n, *nn;
1342:
1.94 kristaps 1343: for (n = r->first_string; n; n = nn) {
1.92 schwarze 1344: free(n->name);
1345: free(n->string);
1346: nn = n->next;
1347: free(n);
1348: }
1.94 kristaps 1349:
1350: r->first_string = NULL;
1.74 kristaps 1351: }
CVSweb