Import mdocml-1.12.1
[dragonfly.git] / contrib / mdocml / roff.c
1 /*      $Id: roff.c,v 1.172 2011/10/24 21:41:45 schwarze Exp $ */
2 /*
3  * Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4  * Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
21
22 #include <assert.h>
23 #include <ctype.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include "mandoc.h"
28 #include "libroff.h"
29 #include "libmandoc.h"
30
31 /* Maximum number of nested if-else conditionals. */
32 #define RSTACK_MAX      128
33
34 /* Maximum number of string expansions per line, to break infinite loops. */
35 #define EXPAND_LIMIT    1000
36
37 enum    rofft {
38         ROFF_ad,
39         ROFF_am,
40         ROFF_ami,
41         ROFF_am1,
42         ROFF_de,
43         ROFF_dei,
44         ROFF_de1,
45         ROFF_ds,
46         ROFF_el,
47         ROFF_hy,
48         ROFF_ie,
49         ROFF_if,
50         ROFF_ig,
51         ROFF_it,
52         ROFF_ne,
53         ROFF_nh,
54         ROFF_nr,
55         ROFF_ns,
56         ROFF_ps,
57         ROFF_rm,
58         ROFF_so,
59         ROFF_ta,
60         ROFF_tr,
61         ROFF_TS,
62         ROFF_TE,
63         ROFF_T_,
64         ROFF_EQ,
65         ROFF_EN,
66         ROFF_cblock,
67         ROFF_ccond,
68         ROFF_USERDEF,
69         ROFF_MAX
70 };
71
72 enum    roffrule {
73         ROFFRULE_ALLOW,
74         ROFFRULE_DENY
75 };
76
77 /*
78  * A single register entity.  If "set" is zero, the value of the
79  * register should be the default one, which is per-register.
80  * Registers are assumed to be unsigned ints for now.
81  */
82 struct  reg {
83         int              set; /* whether set or not */
84         unsigned int     u; /* unsigned integer */
85 };
86
87 /*
88  * An incredibly-simple string buffer.
89  */
90 struct  roffstr {
91         char            *p; /* nil-terminated buffer */
92         size_t           sz; /* saved strlen(p) */
93 };
94
95 /*
96  * A key-value roffstr pair as part of a singly-linked list.
97  */
98 struct  roffkv {
99         struct roffstr   key;
100         struct roffstr   val;
101         struct roffkv   *next; /* next in list */
102 };
103
104 struct  roff {
105         struct mparse   *parse; /* parse point */
106         struct roffnode *last; /* leaf of stack */
107         enum roffrule    rstack[RSTACK_MAX]; /* stack of !`ie' rules */
108         int              rstackpos; /* position in rstack */
109         struct reg       regs[REG__MAX];
110         struct roffkv   *strtab; /* user-defined strings & macros */
111         struct roffkv   *xmbtab; /* multi-byte trans table (`tr') */
112         struct roffstr  *xtab; /* single-byte trans table (`tr') */
113         const char      *current_string; /* value of last called user macro */
114         struct tbl_node *first_tbl; /* first table parsed */
115         struct tbl_node *last_tbl; /* last table parsed */
116         struct tbl_node *tbl; /* current table being parsed */
117         struct eqn_node *last_eqn; /* last equation parsed */
118         struct eqn_node *first_eqn; /* first equation parsed */
119         struct eqn_node *eqn; /* current equation being parsed */
120 };
121
122 struct  roffnode {
123         enum rofft       tok; /* type of node */
124         struct roffnode *parent; /* up one in stack */
125         int              line; /* parse line */
126         int              col; /* parse col */
127         char            *name; /* node name, e.g. macro name */
128         char            *end; /* end-rules: custom token */
129         int              endspan; /* end-rules: next-line or infty */
130         enum roffrule    rule; /* current evaluation rule */
131 };
132
133 #define ROFF_ARGS        struct roff *r, /* parse ctx */ \
134                          enum rofft tok, /* tok of macro */ \
135                          char **bufp, /* input buffer */ \
136                          size_t *szp, /* size of input buffer */ \
137                          int ln, /* parse line */ \
138                          int ppos, /* original pos in buffer */ \
139                          int pos, /* current pos in buffer */ \
140                          int *offs /* reset offset of buffer data */
141
142 typedef enum rofferr (*roffproc)(ROFF_ARGS);
143
144 struct  roffmac {
145         const char      *name; /* macro name */
146         roffproc         proc; /* process new macro */
147         roffproc         text; /* process as child text of macro */
148         roffproc         sub; /* process as child of macro */
149         int              flags;
150 #define ROFFMAC_STRUCT  (1 << 0) /* always interpret */
151         struct roffmac  *next;
152 };
153
154 struct  predef {
155         const char      *name; /* predefined input name */
156         const char      *str; /* replacement symbol */
157 };
158
159 #define PREDEF(__name, __str) \
160         { (__name), (__str) },
161
162 static  enum rofft       roffhash_find(const char *, size_t);
163 static  void             roffhash_init(void);
164 static  void             roffnode_cleanscope(struct roff *);
165 static  void             roffnode_pop(struct roff *);
166 static  void             roffnode_push(struct roff *, enum rofft,
167                                 const char *, int, int);
168 static  enum rofferr     roff_block(ROFF_ARGS);
169 static  enum rofferr     roff_block_text(ROFF_ARGS);
170 static  enum rofferr     roff_block_sub(ROFF_ARGS);
171 static  enum rofferr     roff_cblock(ROFF_ARGS);
172 static  enum rofferr     roff_ccond(ROFF_ARGS);
173 static  enum rofferr     roff_cond(ROFF_ARGS);
174 static  enum rofferr     roff_cond_text(ROFF_ARGS);
175 static  enum rofferr     roff_cond_sub(ROFF_ARGS);
176 static  enum rofferr     roff_ds(ROFF_ARGS);
177 static  enum roffrule    roff_evalcond(const char *, int *);
178 static  void             roff_free1(struct roff *);
179 static  void             roff_freestr(struct roffkv *);
180 static  char            *roff_getname(struct roff *, char **, int, int);
181 static  const char      *roff_getstrn(const struct roff *, 
182                                 const char *, size_t);
183 static  enum rofferr     roff_line_ignore(ROFF_ARGS);
184 static  enum rofferr     roff_nr(ROFF_ARGS);
185 static  void             roff_openeqn(struct roff *, const char *,
186                                 int, int, const char *);
187 static  enum rofft       roff_parse(struct roff *, const char *, int *);
188 static  enum rofferr     roff_parsetext(char *);
189 static  enum rofferr     roff_res(struct roff *, 
190                                 char **, size_t *, int, int);
191 static  enum rofferr     roff_rm(ROFF_ARGS);
192 static  void             roff_setstr(struct roff *,
193                                 const char *, const char *, int);
194 static  void             roff_setstrn(struct roffkv **, const char *, 
195                                 size_t, const char *, size_t, int);
196 static  enum rofferr     roff_so(ROFF_ARGS);
197 static  enum rofferr     roff_tr(ROFF_ARGS);
198 static  enum rofferr     roff_TE(ROFF_ARGS);
199 static  enum rofferr     roff_TS(ROFF_ARGS);
200 static  enum rofferr     roff_EQ(ROFF_ARGS);
201 static  enum rofferr     roff_EN(ROFF_ARGS);
202 static  enum rofferr     roff_T_(ROFF_ARGS);
203 static  enum rofferr     roff_userdef(ROFF_ARGS);
204
205 /* See roffhash_find() */
206
207 #define ASCII_HI         126
208 #define ASCII_LO         33
209 #define HASHWIDTH       (ASCII_HI - ASCII_LO + 1)
210
211 static  struct roffmac  *hash[HASHWIDTH];
212
213 static  struct roffmac   roffs[ROFF_MAX] = {
214         { "ad", roff_line_ignore, NULL, NULL, 0, NULL },
215         { "am", roff_block, roff_block_text, roff_block_sub, 0, NULL },
216         { "ami", roff_block, roff_block_text, roff_block_sub, 0, NULL },
217         { "am1", roff_block, roff_block_text, roff_block_sub, 0, NULL },
218         { "de", roff_block, roff_block_text, roff_block_sub, 0, NULL },
219         { "dei", roff_block, roff_block_text, roff_block_sub, 0, NULL },
220         { "de1", roff_block, roff_block_text, roff_block_sub, 0, NULL },
221         { "ds", roff_ds, NULL, NULL, 0, NULL },
222         { "el", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
223         { "hy", roff_line_ignore, NULL, NULL, 0, NULL },
224         { "ie", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
225         { "if", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
226         { "ig", roff_block, roff_block_text, roff_block_sub, 0, NULL },
227         { "it", roff_line_ignore, NULL, NULL, 0, NULL },
228         { "ne", roff_line_ignore, NULL, NULL, 0, NULL },
229         { "nh", roff_line_ignore, NULL, NULL, 0, NULL },
230         { "nr", roff_nr, NULL, NULL, 0, NULL },
231         { "ns", roff_line_ignore, NULL, NULL, 0, NULL },
232         { "ps", roff_line_ignore, NULL, NULL, 0, NULL },
233         { "rm", roff_rm, NULL, NULL, 0, NULL },
234         { "so", roff_so, NULL, NULL, 0, NULL },
235         { "ta", roff_line_ignore, NULL, NULL, 0, NULL },
236         { "tr", roff_tr, NULL, NULL, 0, NULL },
237         { "TS", roff_TS, NULL, NULL, 0, NULL },
238         { "TE", roff_TE, NULL, NULL, 0, NULL },
239         { "T&", roff_T_, NULL, NULL, 0, NULL },
240         { "EQ", roff_EQ, NULL, NULL, 0, NULL },
241         { "EN", roff_EN, NULL, NULL, 0, NULL },
242         { ".", roff_cblock, NULL, NULL, 0, NULL },
243         { "\\}", roff_ccond, NULL, NULL, 0, NULL },
244         { NULL, roff_userdef, NULL, NULL, 0, NULL },
245 };
246
247 /* Array of injected predefined strings. */
248 #define PREDEFS_MAX      38
249 static  const struct predef predefs[PREDEFS_MAX] = {
250 #include "predefs.in"
251 };
252
253 /* See roffhash_find() */
254 #define ROFF_HASH(p)    (p[0] - ASCII_LO)
255
256 static void
257 roffhash_init(void)
258 {
259         struct roffmac   *n;
260         int               buc, i;
261
262         for (i = 0; i < (int)ROFF_USERDEF; i++) {
263                 assert(roffs[i].name[0] >= ASCII_LO);
264                 assert(roffs[i].name[0] <= ASCII_HI);
265
266                 buc = ROFF_HASH(roffs[i].name);
267
268                 if (NULL != (n = hash[buc])) {
269                         for ( ; n->next; n = n->next)
270                                 /* Do nothing. */ ;
271                         n->next = &roffs[i];
272                 } else
273                         hash[buc] = &roffs[i];
274         }
275 }
276
277 /*
278  * Look up a roff token by its name.  Returns ROFF_MAX if no macro by
279  * the nil-terminated string name could be found.
280  */
281 static enum rofft
282 roffhash_find(const char *p, size_t s)
283 {
284         int              buc;
285         struct roffmac  *n;
286
287         /*
288          * libroff has an extremely simple hashtable, for the time
289          * being, which simply keys on the first character, which must
290          * be printable, then walks a chain.  It works well enough until
291          * optimised.
292          */
293
294         if (p[0] < ASCII_LO || p[0] > ASCII_HI)
295                 return(ROFF_MAX);
296
297         buc = ROFF_HASH(p);
298
299         if (NULL == (n = hash[buc]))
300                 return(ROFF_MAX);
301         for ( ; n; n = n->next)
302                 if (0 == strncmp(n->name, p, s) && '\0' == n->name[(int)s])
303                         return((enum rofft)(n - roffs));
304
305         return(ROFF_MAX);
306 }
307
308
309 /*
310  * Pop the current node off of the stack of roff instructions currently
311  * pending.
312  */
313 static void
314 roffnode_pop(struct roff *r)
315 {
316         struct roffnode *p;
317
318         assert(r->last);
319         p = r->last; 
320
321         r->last = r->last->parent;
322         free(p->name);
323         free(p->end);
324         free(p);
325 }
326
327
328 /*
329  * Push a roff node onto the instruction stack.  This must later be
330  * removed with roffnode_pop().
331  */
332 static void
333 roffnode_push(struct roff *r, enum rofft tok, const char *name,
334                 int line, int col)
335 {
336         struct roffnode *p;
337
338         p = mandoc_calloc(1, sizeof(struct roffnode));
339         p->tok = tok;
340         if (name)
341                 p->name = mandoc_strdup(name);
342         p->parent = r->last;
343         p->line = line;
344         p->col = col;
345         p->rule = p->parent ? p->parent->rule : ROFFRULE_DENY;
346
347         r->last = p;
348 }
349
350
351 static void
352 roff_free1(struct roff *r)
353 {
354         struct tbl_node *t;
355         struct eqn_node *e;
356         int              i;
357
358         while (NULL != (t = r->first_tbl)) {
359                 r->first_tbl = t->next;
360                 tbl_free(t);
361         }
362
363         r->first_tbl = r->last_tbl = r->tbl = NULL;
364
365         while (NULL != (e = r->first_eqn)) {
366                 r->first_eqn = e->next;
367                 eqn_free(e);
368         }
369
370         r->first_eqn = r->last_eqn = r->eqn = NULL;
371
372         while (r->last)
373                 roffnode_pop(r);
374
375         roff_freestr(r->strtab);
376         roff_freestr(r->xmbtab);
377
378         r->strtab = r->xmbtab = NULL;
379
380         if (r->xtab)
381                 for (i = 0; i < 128; i++)
382                         free(r->xtab[i].p);
383
384         free(r->xtab);
385         r->xtab = NULL;
386 }
387
388 void
389 roff_reset(struct roff *r)
390 {
391         int              i;
392
393         roff_free1(r);
394
395         memset(&r->regs, 0, sizeof(struct reg) * REG__MAX);
396
397         for (i = 0; i < PREDEFS_MAX; i++) 
398                 roff_setstr(r, predefs[i].name, predefs[i].str, 0);
399 }
400
401
402 void
403 roff_free(struct roff *r)
404 {
405
406         roff_free1(r);
407         free(r);
408 }
409
410
411 struct roff *
412 roff_alloc(struct mparse *parse)
413 {
414         struct roff     *r;
415         int              i;
416
417         r = mandoc_calloc(1, sizeof(struct roff));
418         r->parse = parse;
419         r->rstackpos = -1;
420         
421         roffhash_init();
422
423         for (i = 0; i < PREDEFS_MAX; i++) 
424                 roff_setstr(r, predefs[i].name, predefs[i].str, 0);
425
426         return(r);
427 }
428
429 /*
430  * Pre-filter each and every line for reserved words (one beginning with
431  * `\*', e.g., `\*(ab').  These must be handled before the actual line
432  * is processed. 
433  * This also checks the syntax of regular escapes.
434  */
435 static enum rofferr
436 roff_res(struct roff *r, char **bufp, size_t *szp, int ln, int pos)
437 {
438         enum mandoc_esc  esc;
439         const char      *stesc; /* start of an escape sequence ('\\') */
440         const char      *stnam; /* start of the name, after "[(*" */
441         const char      *cp;    /* end of the name, e.g. before ']' */
442         const char      *res;   /* the string to be substituted */
443         int              i, maxl, expand_count;
444         size_t           nsz;
445         char            *n;
446
447         expand_count = 0;
448
449 again:
450         cp = *bufp + pos;
451         while (NULL != (cp = strchr(cp, '\\'))) {
452                 stesc = cp++;
453
454                 /*
455                  * The second character must be an asterisk.
456                  * If it isn't, skip it anyway:  It is escaped,
457                  * so it can't start another escape sequence.
458                  */
459
460                 if ('\0' == *cp)
461                         return(ROFF_CONT);
462
463                 if ('*' != *cp) {
464                         res = cp;
465                         esc = mandoc_escape(&cp, NULL, NULL);
466                         if (ESCAPE_ERROR != esc)
467                                 continue;
468                         cp = res;
469                         mandoc_msg
470                                 (MANDOCERR_BADESCAPE, r->parse, 
471                                  ln, (int)(stesc - *bufp), NULL);
472                         return(ROFF_CONT);
473                 }
474
475                 cp++;
476
477                 /*
478                  * The third character decides the length
479                  * of the name of the string.
480                  * Save a pointer to the name.
481                  */
482
483                 switch (*cp) {
484                 case ('\0'):
485                         return(ROFF_CONT);
486                 case ('('):
487                         cp++;
488                         maxl = 2;
489                         break;
490                 case ('['):
491                         cp++;
492                         maxl = 0;
493                         break;
494                 default:
495                         maxl = 1;
496                         break;
497                 }
498                 stnam = cp;
499
500                 /* Advance to the end of the name. */
501
502                 for (i = 0; 0 == maxl || i < maxl; i++, cp++) {
503                         if ('\0' == *cp) {
504                                 mandoc_msg
505                                         (MANDOCERR_BADESCAPE, 
506                                          r->parse, ln, 
507                                          (int)(stesc - *bufp), NULL);
508                                 return(ROFF_CONT);
509                         }
510                         if (0 == maxl && ']' == *cp)
511                                 break;
512                 }
513
514                 /*
515                  * Retrieve the replacement string; if it is
516                  * undefined, resume searching for escapes.
517                  */
518
519                 res = roff_getstrn(r, stnam, (size_t)i);
520
521                 if (NULL == res) {
522                         mandoc_msg
523                                 (MANDOCERR_BADESCAPE, r->parse, 
524                                  ln, (int)(stesc - *bufp), NULL);
525                         res = "";
526                 }
527
528                 /* Replace the escape sequence by the string. */
529
530                 pos = stesc - *bufp;
531
532                 nsz = *szp + strlen(res) + 1;
533                 n = mandoc_malloc(nsz);
534
535                 strlcpy(n, *bufp, (size_t)(stesc - *bufp + 1));
536                 strlcat(n, res, nsz);
537                 strlcat(n, cp + (maxl ? 0 : 1), nsz);
538
539                 free(*bufp);
540
541                 *bufp = n;
542                 *szp = nsz;
543
544                 if (EXPAND_LIMIT >= ++expand_count)
545                         goto again;
546
547                 /* Just leave the string unexpanded. */
548                 mandoc_msg(MANDOCERR_ROFFLOOP, r->parse, ln, pos, NULL);
549                 return(ROFF_IGN);
550         }
551         return(ROFF_CONT);
552 }
553
554 /*
555  * Process text streams: convert all breakable hyphens into ASCII_HYPH.
556  */
557 static enum rofferr
558 roff_parsetext(char *p)
559 {
560         size_t           sz;
561         const char      *start;
562         enum mandoc_esc  esc;
563
564         start = p;
565
566         while ('\0' != *p) {
567                 sz = strcspn(p, "-\\");
568                 p += sz;
569
570                 if ('\0' == *p)
571                         break;
572
573                 if ('\\' == *p) {
574                         /* Skip over escapes. */
575                         p++;
576                         esc = mandoc_escape
577                                 ((const char **)&p, NULL, NULL);
578                         if (ESCAPE_ERROR == esc)
579                                 break;
580                         continue;
581                 } else if (p == start) {
582                         p++;
583                         continue;
584                 }
585
586                 if (isalpha((unsigned char)p[-1]) &&
587                     isalpha((unsigned char)p[1]))
588                         *p = ASCII_HYPH;
589                 p++;
590         }
591
592         return(ROFF_CONT);
593 }
594
595 enum rofferr
596 roff_parseln(struct roff *r, int ln, char **bufp, 
597                 size_t *szp, int pos, int *offs)
598 {
599         enum rofft       t;
600         enum rofferr     e;
601         int              ppos, ctl;
602
603         /*
604          * Run the reserved-word filter only if we have some reserved
605          * words to fill in.
606          */
607
608         e = roff_res(r, bufp, szp, ln, pos);
609         if (ROFF_IGN == e)
610                 return(e);
611         assert(ROFF_CONT == e);
612
613         ppos = pos;
614         ctl = mandoc_getcontrol(*bufp, &pos);
615
616         /*
617          * First, if a scope is open and we're not a macro, pass the
618          * text through the macro's filter.  If a scope isn't open and
619          * we're not a macro, just let it through.
620          * Finally, if there's an equation scope open, divert it into it
621          * no matter our state.
622          */
623
624         if (r->last && ! ctl) {
625                 t = r->last->tok;
626                 assert(roffs[t].text);
627                 e = (*roffs[t].text)
628                         (r, t, bufp, szp, ln, pos, pos, offs);
629                 assert(ROFF_IGN == e || ROFF_CONT == e);
630                 if (ROFF_CONT != e)
631                         return(e);
632                 if (r->eqn)
633                         return(eqn_read(&r->eqn, ln, *bufp, pos, offs));
634                 if (r->tbl)
635                         return(tbl_read(r->tbl, ln, *bufp, pos));
636                 return(roff_parsetext(*bufp + pos));
637         } else if ( ! ctl) {
638                 if (r->eqn)
639                         return(eqn_read(&r->eqn, ln, *bufp, pos, offs));
640                 if (r->tbl)
641                         return(tbl_read(r->tbl, ln, *bufp, pos));
642                 return(roff_parsetext(*bufp + pos));
643         } else if (r->eqn)
644                 return(eqn_read(&r->eqn, ln, *bufp, ppos, offs));
645
646         /*
647          * If a scope is open, go to the child handler for that macro,
648          * as it may want to preprocess before doing anything with it.
649          * Don't do so if an equation is open.
650          */
651
652         if (r->last) {
653                 t = r->last->tok;
654                 assert(roffs[t].sub);
655                 return((*roffs[t].sub)
656                                 (r, t, bufp, szp, 
657                                  ln, ppos, pos, offs));
658         }
659
660         /*
661          * Lastly, as we've no scope open, try to look up and execute
662          * the new macro.  If no macro is found, simply return and let
663          * the compilers handle it.
664          */
665
666         if (ROFF_MAX == (t = roff_parse(r, *bufp, &pos)))
667                 return(ROFF_CONT);
668
669         assert(roffs[t].proc);
670         return((*roffs[t].proc)
671                         (r, t, bufp, szp, 
672                          ln, ppos, pos, offs));
673 }
674
675
676 void
677 roff_endparse(struct roff *r)
678 {
679
680         if (r->last)
681                 mandoc_msg(MANDOCERR_SCOPEEXIT, r->parse,
682                                 r->last->line, r->last->col, NULL);
683
684         if (r->eqn) {
685                 mandoc_msg(MANDOCERR_SCOPEEXIT, r->parse, 
686                                 r->eqn->eqn.ln, r->eqn->eqn.pos, NULL);
687                 eqn_end(&r->eqn);
688         }
689
690         if (r->tbl) {
691                 mandoc_msg(MANDOCERR_SCOPEEXIT, r->parse, 
692                                 r->tbl->line, r->tbl->pos, NULL);
693                 tbl_end(&r->tbl);
694         }
695 }
696
697 /*
698  * Parse a roff node's type from the input buffer.  This must be in the
699  * form of ".foo xxx" in the usual way.
700  */
701 static enum rofft
702 roff_parse(struct roff *r, const char *buf, int *pos)
703 {
704         const char      *mac;
705         size_t           maclen;
706         enum rofft       t;
707
708         if ('\0' == buf[*pos] || '"' == buf[*pos] || 
709                         '\t' == buf[*pos] || ' ' == buf[*pos])
710                 return(ROFF_MAX);
711
712         /*
713          * We stop the macro parse at an escape, tab, space, or nil.
714          * However, `\}' is also a valid macro, so make sure we don't
715          * clobber it by seeing the `\' as the end of token.
716          */
717
718         mac = buf + *pos;
719         maclen = strcspn(mac + 1, " \\\t\0") + 1;
720
721         t = (r->current_string = roff_getstrn(r, mac, maclen))
722             ? ROFF_USERDEF : roffhash_find(mac, maclen);
723
724         *pos += (int)maclen;
725
726         while (buf[*pos] && ' ' == buf[*pos])
727                 (*pos)++;
728
729         return(t);
730 }
731
732 /* ARGSUSED */
733 static enum rofferr
734 roff_cblock(ROFF_ARGS)
735 {
736
737         /*
738          * A block-close `..' should only be invoked as a child of an
739          * ignore macro, otherwise raise a warning and just ignore it.
740          */
741
742         if (NULL == r->last) {
743                 mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
744                 return(ROFF_IGN);
745         }
746
747         switch (r->last->tok) {
748         case (ROFF_am):
749                 /* FALLTHROUGH */
750         case (ROFF_ami):
751                 /* FALLTHROUGH */
752         case (ROFF_am1):
753                 /* FALLTHROUGH */
754         case (ROFF_de):
755                 /* ROFF_de1 is remapped to ROFF_de in roff_block(). */
756                 /* FALLTHROUGH */
757         case (ROFF_dei):
758                 /* FALLTHROUGH */
759         case (ROFF_ig):
760                 break;
761         default:
762                 mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
763                 return(ROFF_IGN);
764         }
765
766         if ((*bufp)[pos])
767                 mandoc_msg(MANDOCERR_ARGSLOST, r->parse, ln, pos, NULL);
768
769         roffnode_pop(r);
770         roffnode_cleanscope(r);
771         return(ROFF_IGN);
772
773 }
774
775
776 static void
777 roffnode_cleanscope(struct roff *r)
778 {
779
780         while (r->last) {
781                 if (--r->last->endspan < 0)
782                         break;
783                 roffnode_pop(r);
784         }
785 }
786
787
788 /* ARGSUSED */
789 static enum rofferr
790 roff_ccond(ROFF_ARGS)
791 {
792
793         if (NULL == r->last) {
794                 mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
795                 return(ROFF_IGN);
796         }
797
798         switch (r->last->tok) {
799         case (ROFF_el):
800                 /* FALLTHROUGH */
801         case (ROFF_ie):
802                 /* FALLTHROUGH */
803         case (ROFF_if):
804                 break;
805         default:
806                 mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
807                 return(ROFF_IGN);
808         }
809
810         if (r->last->endspan > -1) {
811                 mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
812                 return(ROFF_IGN);
813         }
814
815         if ((*bufp)[pos])
816                 mandoc_msg(MANDOCERR_ARGSLOST, r->parse, ln, pos, NULL);
817
818         roffnode_pop(r);
819         roffnode_cleanscope(r);
820         return(ROFF_IGN);
821 }
822
823
824 /* ARGSUSED */
825 static enum rofferr
826 roff_block(ROFF_ARGS)
827 {
828         int             sv;
829         size_t          sz;
830         char            *name;
831
832         name = NULL;
833
834         if (ROFF_ig != tok) {
835                 if ('\0' == (*bufp)[pos]) {
836                         mandoc_msg(MANDOCERR_NOARGS, r->parse, ln, ppos, NULL);
837                         return(ROFF_IGN);
838                 }
839
840                 /*
841                  * Re-write `de1', since we don't really care about
842                  * groff's strange compatibility mode, into `de'.
843                  */
844
845                 if (ROFF_de1 == tok)
846                         tok = ROFF_de;
847                 if (ROFF_de == tok)
848                         name = *bufp + pos;
849                 else
850                         mandoc_msg(MANDOCERR_REQUEST, r->parse, ln, ppos,
851                             roffs[tok].name);
852
853                 while ((*bufp)[pos] && ! isspace((unsigned char)(*bufp)[pos]))
854                         pos++;
855
856                 while (isspace((unsigned char)(*bufp)[pos]))
857                         (*bufp)[pos++] = '\0';
858         }
859
860         roffnode_push(r, tok, name, ln, ppos);
861
862         /*
863          * At the beginning of a `de' macro, clear the existing string
864          * with the same name, if there is one.  New content will be
865          * added from roff_block_text() in multiline mode.
866          */
867
868         if (ROFF_de == tok)
869                 roff_setstr(r, name, "", 0);
870
871         if ('\0' == (*bufp)[pos])
872                 return(ROFF_IGN);
873
874         /* If present, process the custom end-of-line marker. */
875
876         sv = pos;
877         while ((*bufp)[pos] && ! isspace((unsigned char)(*bufp)[pos]))
878                 pos++;
879
880         /*
881          * Note: groff does NOT like escape characters in the input.
882          * Instead of detecting this, we're just going to let it fly and
883          * to hell with it.
884          */
885
886         assert(pos > sv);
887         sz = (size_t)(pos - sv);
888
889         if (1 == sz && '.' == (*bufp)[sv])
890                 return(ROFF_IGN);
891
892         r->last->end = mandoc_malloc(sz + 1);
893
894         memcpy(r->last->end, *bufp + sv, sz);
895         r->last->end[(int)sz] = '\0';
896
897         if ((*bufp)[pos])
898                 mandoc_msg(MANDOCERR_ARGSLOST, r->parse, ln, pos, NULL);
899
900         return(ROFF_IGN);
901 }
902
903
904 /* ARGSUSED */
905 static enum rofferr
906 roff_block_sub(ROFF_ARGS)
907 {
908         enum rofft      t;
909         int             i, j;
910
911         /*
912          * First check whether a custom macro exists at this level.  If
913          * it does, then check against it.  This is some of groff's
914          * stranger behaviours.  If we encountered a custom end-scope
915          * tag and that tag also happens to be a "real" macro, then we
916          * need to try interpreting it again as a real macro.  If it's
917          * not, then return ignore.  Else continue.
918          */
919
920         if (r->last->end) {
921                 for (i = pos, j = 0; r->last->end[j]; j++, i++)
922                         if ((*bufp)[i] != r->last->end[j])
923                                 break;
924
925                 if ('\0' == r->last->end[j] && 
926                                 ('\0' == (*bufp)[i] ||
927                                  ' ' == (*bufp)[i] ||
928                                  '\t' == (*bufp)[i])) {
929                         roffnode_pop(r);
930                         roffnode_cleanscope(r);
931
932                         while (' ' == (*bufp)[i] || '\t' == (*bufp)[i])
933                                 i++;
934
935                         pos = i;
936                         if (ROFF_MAX != roff_parse(r, *bufp, &pos))
937                                 return(ROFF_RERUN);
938                         return(ROFF_IGN);
939                 }
940         }
941
942         /*
943          * If we have no custom end-query or lookup failed, then try
944          * pulling it out of the hashtable.
945          */
946
947         t = roff_parse(r, *bufp, &pos);
948
949         /*
950          * Macros other than block-end are only significant
951          * in `de' blocks; elsewhere, simply throw them away.
952          */
953         if (ROFF_cblock != t) {
954                 if (ROFF_de == tok)
955                         roff_setstr(r, r->last->name, *bufp + ppos, 1);
956                 return(ROFF_IGN);
957         }
958
959         assert(roffs[t].proc);
960         return((*roffs[t].proc)(r, t, bufp, szp, 
961                                 ln, ppos, pos, offs));
962 }
963
964
965 /* ARGSUSED */
966 static enum rofferr
967 roff_block_text(ROFF_ARGS)
968 {
969
970         if (ROFF_de == tok)
971                 roff_setstr(r, r->last->name, *bufp + pos, 1);
972
973         return(ROFF_IGN);
974 }
975
976
977 /* ARGSUSED */
978 static enum rofferr
979 roff_cond_sub(ROFF_ARGS)
980 {
981         enum rofft       t;
982         enum roffrule    rr;
983         char            *ep;
984
985         rr = r->last->rule;
986         roffnode_cleanscope(r);
987
988         /*
989          * If the macro is unknown, first check if it contains a closing
990          * delimiter `\}'.  If it does, close out our scope and return
991          * the currently-scoped rule (ignore or continue).  Else, drop
992          * into the currently-scoped rule.
993          */
994
995         if (ROFF_MAX == (t = roff_parse(r, *bufp, &pos))) {
996                 ep = &(*bufp)[pos];
997                 for ( ; NULL != (ep = strchr(ep, '\\')); ep++) {
998                         ep++;
999                         if ('}' != *ep)
1000                                 continue;
1001
1002                         /*
1003                          * Make the \} go away.
1004                          * This is a little haphazard, as it's not quite
1005                          * clear how nroff does this.
1006                          * If we're at the end of line, then just chop
1007                          * off the \} and resize the buffer.
1008                          * If we aren't, then conver it to spaces.
1009                          */
1010
1011                         if ('\0' == *(ep + 1)) {
1012                                 *--ep = '\0';
1013                                 *szp -= 2;
1014                         } else
1015                                 *(ep - 1) = *ep = ' ';
1016
1017                         roff_ccond(r, ROFF_ccond, bufp, szp, 
1018                                         ln, pos, pos + 2, offs);
1019                         break;
1020                 }
1021                 return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
1022         }
1023
1024         /*
1025          * A denied conditional must evaluate its children if and only
1026          * if they're either structurally required (such as loops and
1027          * conditionals) or a closing macro.
1028          */
1029
1030         if (ROFFRULE_DENY == rr)
1031                 if ( ! (ROFFMAC_STRUCT & roffs[t].flags))
1032                         if (ROFF_ccond != t)
1033                                 return(ROFF_IGN);
1034
1035         assert(roffs[t].proc);
1036         return((*roffs[t].proc)(r, t, bufp, szp, 
1037                                 ln, ppos, pos, offs));
1038 }
1039
1040 /* ARGSUSED */
1041 static enum rofferr
1042 roff_cond_text(ROFF_ARGS)
1043 {
1044         char            *ep;
1045         enum roffrule    rr;
1046
1047         rr = r->last->rule;
1048         roffnode_cleanscope(r);
1049
1050         ep = &(*bufp)[pos];
1051         for ( ; NULL != (ep = strchr(ep, '\\')); ep++) {
1052                 ep++;
1053                 if ('}' != *ep)
1054                         continue;
1055                 *ep = '&';
1056                 roff_ccond(r, ROFF_ccond, bufp, szp, 
1057                                 ln, pos, pos + 2, offs);
1058         }
1059         return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
1060 }
1061
1062 static enum roffrule
1063 roff_evalcond(const char *v, int *pos)
1064 {
1065
1066         switch (v[*pos]) {
1067         case ('n'):
1068                 (*pos)++;
1069                 return(ROFFRULE_ALLOW);
1070         case ('e'):
1071                 /* FALLTHROUGH */
1072         case ('o'):
1073                 /* FALLTHROUGH */
1074         case ('t'):
1075                 (*pos)++;
1076                 return(ROFFRULE_DENY);
1077         default:
1078                 break;
1079         }
1080
1081         while (v[*pos] && ' ' != v[*pos])
1082                 (*pos)++;
1083         return(ROFFRULE_DENY);
1084 }
1085
1086 /* ARGSUSED */
1087 static enum rofferr
1088 roff_line_ignore(ROFF_ARGS)
1089 {
1090
1091         if (ROFF_it == tok)
1092                 mandoc_msg(MANDOCERR_REQUEST, r->parse, ln, ppos, "it");
1093
1094         return(ROFF_IGN);
1095 }
1096
1097 /* ARGSUSED */
1098 static enum rofferr
1099 roff_cond(ROFF_ARGS)
1100 {
1101         int              sv;
1102         enum roffrule    rule;
1103
1104         /* 
1105          * An `.el' has no conditional body: it will consume the value
1106          * of the current rstack entry set in prior `ie' calls or
1107          * defaults to DENY.  
1108          *
1109          * If we're not an `el', however, then evaluate the conditional.
1110          */
1111
1112         rule = ROFF_el == tok ?
1113                 (r->rstackpos < 0 ? 
1114                  ROFFRULE_DENY : r->rstack[r->rstackpos--]) :
1115                 roff_evalcond(*bufp, &pos);
1116
1117         sv = pos;
1118         while (' ' == (*bufp)[pos])
1119                 pos++;
1120
1121         /*
1122          * Roff is weird.  If we have just white-space after the
1123          * conditional, it's considered the BODY and we exit without
1124          * really doing anything.  Warn about this.  It's probably
1125          * wrong.
1126          */
1127
1128         if ('\0' == (*bufp)[pos] && sv != pos) {
1129                 mandoc_msg(MANDOCERR_NOARGS, r->parse, ln, ppos, NULL);
1130                 return(ROFF_IGN);
1131         }
1132
1133         roffnode_push(r, tok, NULL, ln, ppos);
1134
1135         r->last->rule = rule;
1136
1137         /*
1138          * An if-else will put the NEGATION of the current evaluated
1139          * conditional into the stack of rules.
1140          */
1141
1142         if (ROFF_ie == tok) {
1143                 if (r->rstackpos == RSTACK_MAX - 1) {
1144                         mandoc_msg(MANDOCERR_MEM, 
1145                                 r->parse, ln, ppos, NULL);
1146                         return(ROFF_ERR);
1147                 }
1148                 r->rstack[++r->rstackpos] = 
1149                         ROFFRULE_DENY == r->last->rule ?
1150                         ROFFRULE_ALLOW : ROFFRULE_DENY;
1151         }
1152
1153         /* If the parent has false as its rule, then so do we. */
1154
1155         if (r->last->parent && ROFFRULE_DENY == r->last->parent->rule)
1156                 r->last->rule = ROFFRULE_DENY;
1157
1158         /*
1159          * Determine scope.  If we're invoked with "\{" trailing the
1160          * conditional, then we're in a multiline scope.  Else our scope
1161          * expires on the next line.
1162          */
1163
1164         r->last->endspan = 1;
1165
1166         if ('\\' == (*bufp)[pos] && '{' == (*bufp)[pos + 1]) {
1167                 r->last->endspan = -1;
1168                 pos += 2;
1169         } 
1170
1171         /*
1172          * If there are no arguments on the line, the next-line scope is
1173          * assumed.
1174          */
1175
1176         if ('\0' == (*bufp)[pos])
1177                 return(ROFF_IGN);
1178
1179         /* Otherwise re-run the roff parser after recalculating. */
1180
1181         *offs = pos;
1182         return(ROFF_RERUN);
1183 }
1184
1185
1186 /* ARGSUSED */
1187 static enum rofferr
1188 roff_ds(ROFF_ARGS)
1189 {
1190         char            *name, *string;
1191
1192         /*
1193          * A symbol is named by the first word following the macro
1194          * invocation up to a space.  Its value is anything after the
1195          * name's trailing whitespace and optional double-quote.  Thus,
1196          *
1197          *  [.ds foo "bar  "     ]
1198          *
1199          * will have `bar  "     ' as its value.
1200          */
1201
1202         string = *bufp + pos;
1203         name = roff_getname(r, &string, ln, pos);
1204         if ('\0' == *name)
1205                 return(ROFF_IGN);
1206
1207         /* Read past initial double-quote. */
1208         if ('"' == *string)
1209                 string++;
1210
1211         /* The rest is the value. */
1212         roff_setstr(r, name, string, 0);
1213         return(ROFF_IGN);
1214 }
1215
1216 int
1217 roff_regisset(const struct roff *r, enum regs reg)
1218 {
1219
1220         return(r->regs[(int)reg].set);
1221 }
1222
1223 unsigned int
1224 roff_regget(const struct roff *r, enum regs reg)
1225 {
1226
1227         return(r->regs[(int)reg].u);
1228 }
1229
1230 void
1231 roff_regunset(struct roff *r, enum regs reg)
1232 {
1233
1234         r->regs[(int)reg].set = 0;
1235 }
1236
1237 /* ARGSUSED */
1238 static enum rofferr
1239 roff_nr(ROFF_ARGS)
1240 {
1241         const char      *key;
1242         char            *val;
1243         int              iv;
1244
1245         val = *bufp + pos;
1246         key = roff_getname(r, &val, ln, pos);
1247
1248         if (0 == strcmp(key, "nS")) {
1249                 r->regs[(int)REG_nS].set = 1;
1250                 if ((iv = mandoc_strntoi(val, strlen(val), 10)) >= 0)
1251                         r->regs[(int)REG_nS].u = (unsigned)iv;
1252                 else
1253                         r->regs[(int)REG_nS].u = 0u;
1254         }
1255
1256         return(ROFF_IGN);
1257 }
1258
1259 /* ARGSUSED */
1260 static enum rofferr
1261 roff_rm(ROFF_ARGS)
1262 {
1263         const char       *name;
1264         char             *cp;
1265
1266         cp = *bufp + pos;
1267         while ('\0' != *cp) {
1268                 name = roff_getname(r, &cp, ln, (int)(cp - *bufp));
1269                 if ('\0' != *name)
1270                         roff_setstr(r, name, NULL, 0);
1271         }
1272         return(ROFF_IGN);
1273 }
1274
1275 /* ARGSUSED */
1276 static enum rofferr
1277 roff_TE(ROFF_ARGS)
1278 {
1279
1280         if (NULL == r->tbl)
1281                 mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
1282         else
1283                 tbl_end(&r->tbl);
1284
1285         return(ROFF_IGN);
1286 }
1287
1288 /* ARGSUSED */
1289 static enum rofferr
1290 roff_T_(ROFF_ARGS)
1291 {
1292
1293         if (NULL == r->tbl)
1294                 mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
1295         else
1296                 tbl_restart(ppos, ln, r->tbl);
1297
1298         return(ROFF_IGN);
1299 }
1300
1301 #if 0
1302 static int
1303 roff_closeeqn(struct roff *r)
1304 {
1305
1306         return(r->eqn && ROFF_EQN == eqn_end(&r->eqn) ? 1 : 0);
1307 }
1308 #endif
1309
1310 static void
1311 roff_openeqn(struct roff *r, const char *name, int line, 
1312                 int offs, const char *buf)
1313 {
1314         struct eqn_node *e;
1315         int              poff;
1316
1317         assert(NULL == r->eqn);
1318         e = eqn_alloc(name, offs, line, r->parse);
1319
1320         if (r->last_eqn)
1321                 r->last_eqn->next = e;
1322         else
1323                 r->first_eqn = r->last_eqn = e;
1324
1325         r->eqn = r->last_eqn = e;
1326
1327         if (buf) {
1328                 poff = 0;
1329                 eqn_read(&r->eqn, line, buf, offs, &poff);
1330         }
1331 }
1332
1333 /* ARGSUSED */
1334 static enum rofferr
1335 roff_EQ(ROFF_ARGS)
1336 {
1337
1338         roff_openeqn(r, *bufp + pos, ln, ppos, NULL);
1339         return(ROFF_IGN);
1340 }
1341
1342 /* ARGSUSED */
1343 static enum rofferr
1344 roff_EN(ROFF_ARGS)
1345 {
1346
1347         mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
1348         return(ROFF_IGN);
1349 }
1350
1351 /* ARGSUSED */
1352 static enum rofferr
1353 roff_TS(ROFF_ARGS)
1354 {
1355         struct tbl_node *t;
1356
1357         if (r->tbl) {
1358                 mandoc_msg(MANDOCERR_SCOPEBROKEN, r->parse, ln, ppos, NULL);
1359                 tbl_end(&r->tbl);
1360         }
1361
1362         t = tbl_alloc(ppos, ln, r->parse);
1363
1364         if (r->last_tbl)
1365                 r->last_tbl->next = t;
1366         else
1367                 r->first_tbl = r->last_tbl = t;
1368
1369         r->tbl = r->last_tbl = t;
1370         return(ROFF_IGN);
1371 }
1372
1373 /* ARGSUSED */
1374 static enum rofferr
1375 roff_tr(ROFF_ARGS)
1376 {
1377         const char      *p, *first, *second;
1378         size_t           fsz, ssz;
1379         enum mandoc_esc  esc;
1380
1381         p = *bufp + pos;
1382
1383         if ('\0' == *p) {
1384                 mandoc_msg(MANDOCERR_ARGCOUNT, r->parse, ln, ppos, NULL);
1385                 return(ROFF_IGN);
1386         }
1387
1388         while ('\0' != *p) {
1389                 fsz = ssz = 1;
1390
1391                 first = p++;
1392                 if ('\\' == *first) {
1393                         esc = mandoc_escape(&p, NULL, NULL);
1394                         if (ESCAPE_ERROR == esc) {
1395                                 mandoc_msg
1396                                         (MANDOCERR_BADESCAPE, r->parse, 
1397                                          ln, (int)(p - *bufp), NULL);
1398                                 return(ROFF_IGN);
1399                         }
1400                         fsz = (size_t)(p - first);
1401                 }
1402
1403                 second = p++;
1404                 if ('\\' == *second) {
1405                         esc = mandoc_escape(&p, NULL, NULL);
1406                         if (ESCAPE_ERROR == esc) {
1407                                 mandoc_msg
1408                                         (MANDOCERR_BADESCAPE, r->parse, 
1409                                          ln, (int)(p - *bufp), NULL);
1410                                 return(ROFF_IGN);
1411                         }
1412                         ssz = (size_t)(p - second);
1413                 } else if ('\0' == *second) {
1414                         mandoc_msg(MANDOCERR_ARGCOUNT, r->parse, 
1415                                         ln, (int)(p - *bufp), NULL);
1416                         second = " ";
1417                         p--;
1418                 }
1419
1420                 if (fsz > 1) {
1421                         roff_setstrn(&r->xmbtab, first, 
1422                                         fsz, second, ssz, 0);
1423                         continue;
1424                 }
1425
1426                 if (NULL == r->xtab)
1427                         r->xtab = mandoc_calloc
1428                                 (128, sizeof(struct roffstr));
1429
1430                 free(r->xtab[(int)*first].p);
1431                 r->xtab[(int)*first].p = mandoc_strndup(second, ssz);
1432                 r->xtab[(int)*first].sz = ssz;
1433         }
1434
1435         return(ROFF_IGN);
1436 }
1437
1438 /* ARGSUSED */
1439 static enum rofferr
1440 roff_so(ROFF_ARGS)
1441 {
1442         char *name;
1443
1444         mandoc_msg(MANDOCERR_SO, r->parse, ln, ppos, NULL);
1445
1446         /*
1447          * Handle `so'.  Be EXTREMELY careful, as we shouldn't be
1448          * opening anything that's not in our cwd or anything beneath
1449          * it.  Thus, explicitly disallow traversing up the file-system
1450          * or using absolute paths.
1451          */
1452
1453         name = *bufp + pos;
1454         if ('/' == *name || strstr(name, "../") || strstr(name, "/..")) {
1455                 mandoc_msg(MANDOCERR_SOPATH, r->parse, ln, pos, NULL);
1456                 return(ROFF_ERR);
1457         }
1458
1459         *offs = pos;
1460         return(ROFF_SO);
1461 }
1462
1463 /* ARGSUSED */
1464 static enum rofferr
1465 roff_userdef(ROFF_ARGS)
1466 {
1467         const char       *arg[9];
1468         char             *cp, *n1, *n2;
1469         int               i;
1470
1471         /*
1472          * Collect pointers to macro argument strings
1473          * and null-terminate them.
1474          */
1475         cp = *bufp + pos;
1476         for (i = 0; i < 9; i++)
1477                 arg[i] = '\0' == *cp ? "" :
1478                     mandoc_getarg(r->parse, &cp, ln, &pos);
1479
1480         /*
1481          * Expand macro arguments.
1482          */
1483         *szp = 0;
1484         n1 = cp = mandoc_strdup(r->current_string);
1485         while (NULL != (cp = strstr(cp, "\\$"))) {
1486                 i = cp[2] - '1';
1487                 if (0 > i || 8 < i) {
1488                         /* Not an argument invocation. */
1489                         cp += 2;
1490                         continue;
1491                 }
1492
1493                 *szp = strlen(n1) - 3 + strlen(arg[i]) + 1;
1494                 n2 = mandoc_malloc(*szp);
1495
1496                 strlcpy(n2, n1, (size_t)(cp - n1 + 1));
1497                 strlcat(n2, arg[i], *szp);
1498                 strlcat(n2, cp + 3, *szp);
1499
1500                 cp = n2 + (cp - n1);
1501                 free(n1);
1502                 n1 = n2;
1503         }
1504
1505         /*
1506          * Replace the macro invocation
1507          * by the expanded macro.
1508          */
1509         free(*bufp);
1510         *bufp = n1;
1511         if (0 == *szp)
1512                 *szp = strlen(*bufp) + 1;
1513
1514         return(*szp > 1 && '\n' == (*bufp)[(int)*szp - 2] ?
1515            ROFF_REPARSE : ROFF_APPEND);
1516 }
1517
1518 static char *
1519 roff_getname(struct roff *r, char **cpp, int ln, int pos)
1520 {
1521         char     *name, *cp;
1522
1523         name = *cpp;
1524         if ('\0' == *name)
1525                 return(name);
1526
1527         /* Read until end of name. */
1528         for (cp = name; '\0' != *cp && ' ' != *cp; cp++) {
1529                 if ('\\' != *cp)
1530                         continue;
1531                 cp++;
1532                 if ('\\' == *cp)
1533                         continue;
1534                 mandoc_msg(MANDOCERR_NAMESC, r->parse, ln, pos, NULL);
1535                 *cp = '\0';
1536                 name = cp;
1537         }
1538
1539         /* Nil-terminate name. */
1540         if ('\0' != *cp)
1541                 *(cp++) = '\0';
1542
1543         /* Read past spaces. */
1544         while (' ' == *cp)
1545                 cp++;
1546
1547         *cpp = cp;
1548         return(name);
1549 }
1550
1551 /*
1552  * Store *string into the user-defined string called *name.
1553  * In multiline mode, append to an existing entry and append '\n';
1554  * else replace the existing entry, if there is one.
1555  * To clear an existing entry, call with (*r, *name, NULL, 0).
1556  */
1557 static void
1558 roff_setstr(struct roff *r, const char *name, const char *string,
1559         int multiline)
1560 {
1561
1562         roff_setstrn(&r->strtab, name, strlen(name), string,
1563                         string ? strlen(string) : 0, multiline);
1564 }
1565
1566 static void
1567 roff_setstrn(struct roffkv **r, const char *name, size_t namesz,
1568                 const char *string, size_t stringsz, int multiline)
1569 {
1570         struct roffkv   *n;
1571         char            *c;
1572         int              i;
1573         size_t           oldch, newch;
1574
1575         /* Search for an existing string with the same name. */
1576         n = *r;
1577
1578         while (n && strcmp(name, n->key.p))
1579                 n = n->next;
1580
1581         if (NULL == n) {
1582                 /* Create a new string table entry. */
1583                 n = mandoc_malloc(sizeof(struct roffkv));
1584                 n->key.p = mandoc_strndup(name, namesz);
1585                 n->key.sz = namesz;
1586                 n->val.p = NULL;
1587                 n->val.sz = 0;
1588                 n->next = *r;
1589                 *r = n;
1590         } else if (0 == multiline) {
1591                 /* In multiline mode, append; else replace. */
1592                 free(n->val.p);
1593                 n->val.p = NULL;
1594                 n->val.sz = 0;
1595         }
1596
1597         if (NULL == string)
1598                 return;
1599
1600         /*
1601          * One additional byte for the '\n' in multiline mode,
1602          * and one for the terminating '\0'.
1603          */
1604         newch = stringsz + (multiline ? 2u : 1u);
1605
1606         if (NULL == n->val.p) {
1607                 n->val.p = mandoc_malloc(newch);
1608                 *n->val.p = '\0';
1609                 oldch = 0;
1610         } else {
1611                 oldch = n->val.sz;
1612                 n->val.p = mandoc_realloc(n->val.p, oldch + newch);
1613         }
1614
1615         /* Skip existing content in the destination buffer. */
1616         c = n->val.p + (int)oldch;
1617
1618         /* Append new content to the destination buffer. */
1619         i = 0;
1620         while (i < (int)stringsz) {
1621                 /*
1622                  * Rudimentary roff copy mode:
1623                  * Handle escaped backslashes.
1624                  */
1625                 if ('\\' == string[i] && '\\' == string[i + 1])
1626                         i++;
1627                 *c++ = string[i++];
1628         }
1629
1630         /* Append terminating bytes. */
1631         if (multiline)
1632                 *c++ = '\n';
1633
1634         *c = '\0';
1635         n->val.sz = (int)(c - n->val.p);
1636 }
1637
1638 static const char *
1639 roff_getstrn(const struct roff *r, const char *name, size_t len)
1640 {
1641         const struct roffkv *n;
1642
1643         for (n = r->strtab; n; n = n->next)
1644                 if (0 == strncmp(name, n->key.p, len) && 
1645                                 '\0' == n->key.p[(int)len])
1646                         return(n->val.p);
1647
1648         return(NULL);
1649 }
1650
1651 static void
1652 roff_freestr(struct roffkv *r)
1653 {
1654         struct roffkv    *n, *nn;
1655
1656         for (n = r; n; n = nn) {
1657                 free(n->key.p);
1658                 free(n->val.p);
1659                 nn = n->next;
1660                 free(n);
1661         }
1662 }
1663
1664 const struct tbl_span *
1665 roff_span(const struct roff *r)
1666 {
1667         
1668         return(r->tbl ? tbl_span(r->tbl) : NULL);
1669 }
1670
1671 const struct eqn *
1672 roff_eqn(const struct roff *r)
1673 {
1674         
1675         return(r->last_eqn ? &r->last_eqn->eqn : NULL);
1676 }
1677
1678 /*
1679  * Duplicate an input string, making the appropriate character
1680  * conversations (as stipulated by `tr') along the way.
1681  * Returns a heap-allocated string with all the replacements made.
1682  */
1683 char *
1684 roff_strdup(const struct roff *r, const char *p)
1685 {
1686         const struct roffkv *cp;
1687         char            *res;
1688         const char      *pp;
1689         size_t           ssz, sz;
1690         enum mandoc_esc  esc;
1691
1692         if (NULL == r->xmbtab && NULL == r->xtab)
1693                 return(mandoc_strdup(p));
1694         else if ('\0' == *p)
1695                 return(mandoc_strdup(""));
1696
1697         /*
1698          * Step through each character looking for term matches
1699          * (remember that a `tr' can be invoked with an escape, which is
1700          * a glyph but the escape is multi-character).
1701          * We only do this if the character hash has been initialised
1702          * and the string is >0 length.
1703          */
1704
1705         res = NULL;
1706         ssz = 0;
1707
1708         while ('\0' != *p) {
1709                 if ('\\' != *p && r->xtab && r->xtab[(int)*p].p) {
1710                         sz = r->xtab[(int)*p].sz;
1711                         res = mandoc_realloc(res, ssz + sz + 1);
1712                         memcpy(res + ssz, r->xtab[(int)*p].p, sz);
1713                         ssz += sz;
1714                         p++;
1715                         continue;
1716                 } else if ('\\' != *p) {
1717                         res = mandoc_realloc(res, ssz + 2);
1718                         res[ssz++] = *p++;
1719                         continue;
1720                 }
1721
1722                 /* Search for term matches. */
1723                 for (cp = r->xmbtab; cp; cp = cp->next)
1724                         if (0 == strncmp(p, cp->key.p, cp->key.sz))
1725                                 break;
1726
1727                 if (NULL != cp) {
1728                         /*
1729                          * A match has been found.
1730                          * Append the match to the array and move
1731                          * forward by its keysize.
1732                          */
1733                         res = mandoc_realloc
1734                                 (res, ssz + cp->val.sz + 1);
1735                         memcpy(res + ssz, cp->val.p, cp->val.sz);
1736                         ssz += cp->val.sz;
1737                         p += (int)cp->key.sz;
1738                         continue;
1739                 }
1740
1741                 /*
1742                  * Handle escapes carefully: we need to copy
1743                  * over just the escape itself, or else we might
1744                  * do replacements within the escape itself.
1745                  * Make sure to pass along the bogus string.
1746                  */
1747                 pp = p++;
1748                 esc = mandoc_escape(&p, NULL, NULL);
1749                 if (ESCAPE_ERROR == esc) {
1750                         sz = strlen(pp);
1751                         res = mandoc_realloc(res, ssz + sz + 1);
1752                         memcpy(res + ssz, pp, sz);
1753                         break;
1754                 }
1755                 /* 
1756                  * We bail out on bad escapes. 
1757                  * No need to warn: we already did so when
1758                  * roff_res() was called.
1759                  */
1760                 sz = (int)(p - pp);
1761                 res = mandoc_realloc(res, ssz + sz + 1);
1762                 memcpy(res + ssz, pp, sz);
1763                 ssz += sz;
1764         }
1765
1766         res[(int)ssz] = '\0';
1767         return(res);
1768 }