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