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