netinet{,6}: Assert in{,6}_inithead() are only used for system routing tables.
[dragonfly.git] / contrib / mdocml / roff.c
CommitLineData
070c62a6 1/* $Id: roff.c,v 1.224 2014/08/01 17:27:44 schwarze Exp $ */
80387638 2/*
f88b6c16 3 * Copyright (c) 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
070c62a6 4 * Copyright (c) 2010-2014 Ingo Schwarze <schwarze@openbsd.org>
80387638
SW
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>
80387638 23#include <ctype.h>
f88b6c16 24#include <stdio.h>
80387638
SW
25#include <stdlib.h>
26#include <string.h>
80387638
SW
27
28#include "mandoc.h"
070c62a6 29#include "mandoc_aux.h"
80387638
SW
30#include "libroff.h"
31#include "libmandoc.h"
32
a4c7eb57 33/* Maximum number of nested if-else conditionals. */
80387638
SW
34#define RSTACK_MAX 128
35
36342e81
SW
36/* Maximum number of string expansions per line, to break infinite loops. */
37#define EXPAND_LIMIT 1000
38
80387638
SW
39enum rofft {
40 ROFF_ad,
41 ROFF_am,
42 ROFF_ami,
43 ROFF_am1,
070c62a6 44 ROFF_as,
f88b6c16 45 ROFF_cc,
070c62a6 46 ROFF_ce,
80387638
SW
47 ROFF_de,
48 ROFF_dei,
49 ROFF_de1,
50 ROFF_ds,
51 ROFF_el,
7888c61d
FF
52 ROFF_fam,
53 ROFF_hw,
80387638
SW
54 ROFF_hy,
55 ROFF_ie,
56 ROFF_if,
57 ROFF_ig,
60e1e752 58 ROFF_it,
80387638
SW
59 ROFF_ne,
60 ROFF_nh,
61 ROFF_nr,
60e1e752
SW
62 ROFF_ns,
63 ROFF_ps,
80387638 64 ROFF_rm,
070c62a6 65 ROFF_rr,
80387638 66 ROFF_so,
60e1e752 67 ROFF_ta,
80387638 68 ROFF_tr,
f88b6c16
FF
69 ROFF_Dd,
70 ROFF_TH,
80387638
SW
71 ROFF_TS,
72 ROFF_TE,
73 ROFF_T_,
60e1e752
SW
74 ROFF_EQ,
75 ROFF_EN,
80387638 76 ROFF_cblock,
80387638
SW
77 ROFF_USERDEF,
78 ROFF_MAX
79};
80
36342e81
SW
81/*
82 * An incredibly-simple string buffer.
83 */
80387638 84struct roffstr {
36342e81
SW
85 char *p; /* nil-terminated buffer */
86 size_t sz; /* saved strlen(p) */
87};
88
89/*
90 * A key-value roffstr pair as part of a singly-linked list.
91 */
92struct roffkv {
93 struct roffstr key;
94 struct roffstr val;
95 struct roffkv *next; /* next in list */
80387638
SW
96};
97
7888c61d
FF
98/*
99 * A single number register as part of a singly-linked list.
100 */
101struct roffreg {
102 struct roffstr key;
103 int val;
104 struct roffreg *next;
105};
106
80387638 107struct roff {
60e1e752 108 struct mparse *parse; /* parse point */
80387638 109 struct roffnode *last; /* leaf of stack */
070c62a6 110 int *rstack; /* stack of inverted `ie' values */
7888c61d 111 struct roffreg *regtab; /* number registers */
36342e81
SW
112 struct roffkv *strtab; /* user-defined strings & macros */
113 struct roffkv *xmbtab; /* multi-byte trans table (`tr') */
114 struct roffstr *xtab; /* single-byte trans table (`tr') */
80387638
SW
115 const char *current_string; /* value of last called user macro */
116 struct tbl_node *first_tbl; /* first table parsed */
117 struct tbl_node *last_tbl; /* last table parsed */
118 struct tbl_node *tbl; /* current table being parsed */
60e1e752
SW
119 struct eqn_node *last_eqn; /* last equation parsed */
120 struct eqn_node *first_eqn; /* first equation parsed */
121 struct eqn_node *eqn; /* current equation being parsed */
070c62a6
FF
122 int options; /* parse options */
123 int rstacksz; /* current size limit of rstack */
124 int rstackpos; /* position in rstack */
125 char control; /* control character */
80387638
SW
126};
127
128struct roffnode {
129 enum rofft tok; /* type of node */
130 struct roffnode *parent; /* up one in stack */
131 int line; /* parse line */
132 int col; /* parse col */
133 char *name; /* node name, e.g. macro name */
134 char *end; /* end-rules: custom token */
135 int endspan; /* end-rules: next-line or infty */
070c62a6 136 int rule; /* current evaluation rule */
80387638
SW
137};
138
139#define ROFF_ARGS struct roff *r, /* parse ctx */ \
140 enum rofft tok, /* tok of macro */ \
070c62a6 141 char **bufp, /* input buffer */ \
80387638
SW
142 size_t *szp, /* size of input buffer */ \
143 int ln, /* parse line */ \
144 int ppos, /* original pos in buffer */ \
145 int pos, /* current pos in buffer */ \
146 int *offs /* reset offset of buffer data */
147
148typedef enum rofferr (*roffproc)(ROFF_ARGS);
149
150struct roffmac {
151 const char *name; /* macro name */
152 roffproc proc; /* process new macro */
153 roffproc text; /* process as child text of macro */
154 roffproc sub; /* process as child of macro */
155 int flags;
156#define ROFFMAC_STRUCT (1 << 0) /* always interpret */
157 struct roffmac *next;
158};
159
a4c7eb57
SW
160struct predef {
161 const char *name; /* predefined input name */
162 const char *str; /* replacement symbol */
163};
164
165#define PREDEF(__name, __str) \
166 { (__name), (__str) },
167
36342e81
SW
168static enum rofft roffhash_find(const char *, size_t);
169static void roffhash_init(void);
170static void roffnode_cleanscope(struct roff *);
171static void roffnode_pop(struct roff *);
172static void roffnode_push(struct roff *, enum rofft,
173 const char *, int, int);
80387638
SW
174static enum rofferr roff_block(ROFF_ARGS);
175static enum rofferr roff_block_text(ROFF_ARGS);
176static enum rofferr roff_block_sub(ROFF_ARGS);
177static enum rofferr roff_cblock(ROFF_ARGS);
f88b6c16 178static enum rofferr roff_cc(ROFF_ARGS);
070c62a6 179static void roff_ccond(struct roff *, int, int);
80387638
SW
180static enum rofferr roff_cond(ROFF_ARGS);
181static enum rofferr roff_cond_text(ROFF_ARGS);
182static enum rofferr roff_cond_sub(ROFF_ARGS);
183static enum rofferr roff_ds(ROFF_ARGS);
070c62a6
FF
184static int roff_evalcond(const char *, int *);
185static int roff_evalnum(const char *, int *, int *, int);
186static int roff_evalpar(const char *, int *, int *);
187static int roff_evalstrcond(const char *, int *);
36342e81 188static void roff_free1(struct roff *);
7888c61d 189static void roff_freereg(struct roffreg *);
36342e81 190static void roff_freestr(struct roffkv *);
070c62a6 191static size_t roff_getname(struct roff *, char **, int, int);
7888c61d
FF
192static int roff_getnum(const char *, int *, int *);
193static int roff_getop(const char *, int *, char *);
194static int roff_getregn(const struct roff *,
195 const char *, size_t);
070c62a6
FF
196static int roff_getregro(const char *name);
197static const char *roff_getstrn(const struct roff *,
80387638 198 const char *, size_t);
f88b6c16 199static enum rofferr roff_it(ROFF_ARGS);
80387638 200static enum rofferr roff_line_ignore(ROFF_ARGS);
80387638 201static enum rofferr roff_nr(ROFF_ARGS);
36342e81
SW
202static void roff_openeqn(struct roff *, const char *,
203 int, int, const char *);
070c62a6
FF
204static enum rofft roff_parse(struct roff *, char *, int *,
205 int, int);
f88b6c16 206static enum rofferr roff_parsetext(char **, size_t *, int, int *);
070c62a6 207static enum rofferr roff_res(struct roff *,
a4c7eb57 208 char **, size_t *, int, int);
60e1e752 209static enum rofferr roff_rm(ROFF_ARGS);
070c62a6 210static enum rofferr roff_rr(ROFF_ARGS);
80387638
SW
211static void roff_setstr(struct roff *,
212 const char *, const char *, int);
070c62a6 213static void roff_setstrn(struct roffkv **, const char *,
36342e81 214 size_t, const char *, size_t, int);
80387638 215static enum rofferr roff_so(ROFF_ARGS);
36342e81 216static enum rofferr roff_tr(ROFF_ARGS);
f88b6c16
FF
217static enum rofferr roff_Dd(ROFF_ARGS);
218static enum rofferr roff_TH(ROFF_ARGS);
80387638
SW
219static enum rofferr roff_TE(ROFF_ARGS);
220static enum rofferr roff_TS(ROFF_ARGS);
60e1e752
SW
221static enum rofferr roff_EQ(ROFF_ARGS);
222static enum rofferr roff_EN(ROFF_ARGS);
80387638
SW
223static enum rofferr roff_T_(ROFF_ARGS);
224static enum rofferr roff_userdef(ROFF_ARGS);
225
36342e81 226/* See roffhash_find() */
80387638
SW
227
228#define ASCII_HI 126
229#define ASCII_LO 33
230#define HASHWIDTH (ASCII_HI - ASCII_LO + 1)
231
232static struct roffmac *hash[HASHWIDTH];
233
234static struct roffmac roffs[ROFF_MAX] = {
235 { "ad", roff_line_ignore, NULL, NULL, 0, NULL },
236 { "am", roff_block, roff_block_text, roff_block_sub, 0, NULL },
237 { "ami", roff_block, roff_block_text, roff_block_sub, 0, NULL },
238 { "am1", roff_block, roff_block_text, roff_block_sub, 0, NULL },
070c62a6 239 { "as", roff_ds, NULL, NULL, 0, NULL },
f88b6c16 240 { "cc", roff_cc, NULL, NULL, 0, NULL },
070c62a6 241 { "ce", roff_line_ignore, NULL, NULL, 0, NULL },
80387638
SW
242 { "de", roff_block, roff_block_text, roff_block_sub, 0, NULL },
243 { "dei", roff_block, roff_block_text, roff_block_sub, 0, NULL },
244 { "de1", roff_block, roff_block_text, roff_block_sub, 0, NULL },
245 { "ds", roff_ds, NULL, NULL, 0, NULL },
246 { "el", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
7888c61d
FF
247 { "fam", roff_line_ignore, NULL, NULL, 0, NULL },
248 { "hw", roff_line_ignore, NULL, NULL, 0, NULL },
80387638
SW
249 { "hy", roff_line_ignore, NULL, NULL, 0, NULL },
250 { "ie", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
251 { "if", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
252 { "ig", roff_block, roff_block_text, roff_block_sub, 0, NULL },
f88b6c16 253 { "it", roff_it, NULL, NULL, 0, NULL },
80387638
SW
254 { "ne", roff_line_ignore, NULL, NULL, 0, NULL },
255 { "nh", roff_line_ignore, NULL, NULL, 0, NULL },
256 { "nr", roff_nr, NULL, NULL, 0, NULL },
60e1e752
SW
257 { "ns", roff_line_ignore, NULL, NULL, 0, NULL },
258 { "ps", roff_line_ignore, NULL, NULL, 0, NULL },
259 { "rm", roff_rm, NULL, NULL, 0, NULL },
070c62a6 260 { "rr", roff_rr, NULL, NULL, 0, NULL },
80387638 261 { "so", roff_so, NULL, NULL, 0, NULL },
60e1e752 262 { "ta", roff_line_ignore, NULL, NULL, 0, NULL },
36342e81 263 { "tr", roff_tr, NULL, NULL, 0, NULL },
f88b6c16
FF
264 { "Dd", roff_Dd, NULL, NULL, 0, NULL },
265 { "TH", roff_TH, NULL, NULL, 0, NULL },
80387638
SW
266 { "TS", roff_TS, NULL, NULL, 0, NULL },
267 { "TE", roff_TE, NULL, NULL, 0, NULL },
268 { "T&", roff_T_, NULL, NULL, 0, NULL },
60e1e752
SW
269 { "EQ", roff_EQ, NULL, NULL, 0, NULL },
270 { "EN", roff_EN, NULL, NULL, 0, NULL },
80387638 271 { ".", roff_cblock, NULL, NULL, 0, NULL },
80387638
SW
272 { NULL, roff_userdef, NULL, NULL, 0, NULL },
273};
274
070c62a6 275/* not currently implemented: Ds em Eq LP Me PP pp Or Rd Sf SH */
f88b6c16
FF
276const char *const __mdoc_reserved[] = {
277 "Ac", "Ad", "An", "Ao", "Ap", "Aq", "Ar", "At",
278 "Bc", "Bd", "Bf", "Bk", "Bl", "Bo", "Bq",
279 "Brc", "Bro", "Brq", "Bsx", "Bt", "Bx",
280 "Cd", "Cm", "Db", "Dc", "Dd", "Dl", "Do", "Dq",
070c62a6
FF
281 "Dt", "Dv", "Dx", "D1",
282 "Ec", "Ed", "Ef", "Ek", "El", "Em",
283 "En", "Eo", "Er", "Es", "Ev", "Ex",
f88b6c16 284 "Fa", "Fc", "Fd", "Fl", "Fn", "Fo", "Fr", "Ft", "Fx",
070c62a6
FF
285 "Hf", "Ic", "In", "It", "Lb", "Li", "Lk", "Lp",
286 "Ms", "Mt", "Nd", "Nm", "No", "Ns", "Nx",
f88b6c16 287 "Oc", "Oo", "Op", "Os", "Ot", "Ox",
070c62a6
FF
288 "Pa", "Pc", "Pf", "Po", "Pp", "Pq",
289 "Qc", "Ql", "Qo", "Qq", "Re", "Rs", "Rv",
290 "Sc", "Sh", "Sm", "So", "Sq",
f88b6c16
FF
291 "Ss", "St", "Sx", "Sy",
292 "Ta", "Tn", "Ud", "Ux", "Va", "Vt", "Xc", "Xo", "Xr",
070c62a6 293 "%A", "%B", "%C", "%D", "%I", "%J", "%N", "%O",
f88b6c16
FF
294 "%P", "%Q", "%R", "%T", "%U", "%V",
295 NULL
296};
297
070c62a6 298/* not currently implemented: BT DE DS ME MT PT SY TQ YS */
f88b6c16 299const char *const __man_reserved[] = {
070c62a6
FF
300 "AT", "B", "BI", "BR", "DT",
301 "EE", "EN", "EQ", "EX", "HP", "I", "IB", "IP", "IR",
302 "LP", "OP", "P", "PD", "PP",
303 "R", "RB", "RE", "RI", "RS", "SB", "SH", "SM", "SS",
304 "TE", "TH", "TP", "TS", "T&", "UC", "UE", "UR",
f88b6c16
FF
305 NULL
306};
307
a4c7eb57
SW
308/* Array of injected predefined strings. */
309#define PREDEFS_MAX 38
310static const struct predef predefs[PREDEFS_MAX] = {
311#include "predefs.in"
312};
313
36342e81 314/* See roffhash_find() */
80387638
SW
315#define ROFF_HASH(p) (p[0] - ASCII_LO)
316
f88b6c16
FF
317static int roffit_lines; /* number of lines to delay */
318static char *roffit_macro; /* nil-terminated macro line */
319
070c62a6 320
80387638 321static void
36342e81 322roffhash_init(void)
80387638
SW
323{
324 struct roffmac *n;
325 int buc, i;
326
327 for (i = 0; i < (int)ROFF_USERDEF; i++) {
328 assert(roffs[i].name[0] >= ASCII_LO);
329 assert(roffs[i].name[0] <= ASCII_HI);
330
331 buc = ROFF_HASH(roffs[i].name);
332
333 if (NULL != (n = hash[buc])) {
334 for ( ; n->next; n = n->next)
335 /* Do nothing. */ ;
336 n->next = &roffs[i];
337 } else
338 hash[buc] = &roffs[i];
339 }
340}
341
80387638
SW
342/*
343 * Look up a roff token by its name. Returns ROFF_MAX if no macro by
344 * the nil-terminated string name could be found.
345 */
346static enum rofft
36342e81 347roffhash_find(const char *p, size_t s)
80387638
SW
348{
349 int buc;
350 struct roffmac *n;
351
352 /*
353 * libroff has an extremely simple hashtable, for the time
354 * being, which simply keys on the first character, which must
355 * be printable, then walks a chain. It works well enough until
356 * optimised.
357 */
358
359 if (p[0] < ASCII_LO || p[0] > ASCII_HI)
360 return(ROFF_MAX);
361
362 buc = ROFF_HASH(p);
363
364 if (NULL == (n = hash[buc]))
365 return(ROFF_MAX);
366 for ( ; n; n = n->next)
367 if (0 == strncmp(n->name, p, s) && '\0' == n->name[(int)s])
368 return((enum rofft)(n - roffs));
369
370 return(ROFF_MAX);
371}
372
80387638
SW
373/*
374 * Pop the current node off of the stack of roff instructions currently
375 * pending.
376 */
377static void
378roffnode_pop(struct roff *r)
379{
380 struct roffnode *p;
381
382 assert(r->last);
070c62a6 383 p = r->last;
80387638 384
80387638
SW
385 r->last = r->last->parent;
386 free(p->name);
387 free(p->end);
388 free(p);
389}
390
80387638
SW
391/*
392 * Push a roff node onto the instruction stack. This must later be
393 * removed with roffnode_pop().
394 */
395static void
396roffnode_push(struct roff *r, enum rofft tok, const char *name,
397 int line, int col)
398{
399 struct roffnode *p;
400
401 p = mandoc_calloc(1, sizeof(struct roffnode));
402 p->tok = tok;
403 if (name)
404 p->name = mandoc_strdup(name);
405 p->parent = r->last;
406 p->line = line;
407 p->col = col;
070c62a6 408 p->rule = p->parent ? p->parent->rule : 0;
80387638
SW
409
410 r->last = p;
411}
412
80387638
SW
413static void
414roff_free1(struct roff *r)
415{
f88b6c16 416 struct tbl_node *tbl;
60e1e752 417 struct eqn_node *e;
36342e81 418 int i;
80387638 419
f88b6c16
FF
420 while (NULL != (tbl = r->first_tbl)) {
421 r->first_tbl = tbl->next;
422 tbl_free(tbl);
80387638 423 }
80387638
SW
424 r->first_tbl = r->last_tbl = r->tbl = NULL;
425
60e1e752
SW
426 while (NULL != (e = r->first_eqn)) {
427 r->first_eqn = e->next;
428 eqn_free(e);
429 }
60e1e752
SW
430 r->first_eqn = r->last_eqn = r->eqn = NULL;
431
80387638
SW
432 while (r->last)
433 roffnode_pop(r);
434
070c62a6
FF
435 free (r->rstack);
436 r->rstack = NULL;
437 r->rstacksz = 0;
438 r->rstackpos = -1;
80387638 439
7888c61d 440 roff_freereg(r->regtab);
7888c61d
FF
441 r->regtab = NULL;
442
070c62a6
FF
443 roff_freestr(r->strtab);
444 roff_freestr(r->xmbtab);
445 r->strtab = r->xmbtab = NULL;
446
36342e81
SW
447 if (r->xtab)
448 for (i = 0; i < 128; i++)
449 free(r->xtab[i].p);
36342e81
SW
450 free(r->xtab);
451 r->xtab = NULL;
452}
80387638
SW
453
454void
455roff_reset(struct roff *r)
456{
457
458 roff_free1(r);
f88b6c16 459 r->control = 0;
80387638
SW
460}
461
80387638
SW
462void
463roff_free(struct roff *r)
464{
465
466 roff_free1(r);
467 free(r);
468}
469
80387638 470struct roff *
070c62a6 471roff_alloc(struct mparse *parse, int options)
80387638
SW
472{
473 struct roff *r;
474
475 r = mandoc_calloc(1, sizeof(struct roff));
60e1e752 476 r->parse = parse;
070c62a6 477 r->options = options;
80387638 478 r->rstackpos = -1;
a4c7eb57 479
070c62a6 480 roffhash_init();
a4c7eb57 481
80387638
SW
482 return(r);
483}
484
80387638 485/*
070c62a6
FF
486 * In the current line, expand escape sequences that tend to get
487 * used in numerical expressions and conditional requests.
488 * Also check the syntax of the remaining escape sequences.
80387638 489 */
36342e81 490static enum rofferr
a4c7eb57 491roff_res(struct roff *r, char **bufp, size_t *szp, int ln, int pos)
80387638 492{
070c62a6
FF
493 char ubuf[24]; /* buffer to print the number */
494 const char *start; /* start of the string to process */
495 char *stesc; /* start of an escape sequence ('\\') */
80387638
SW
496 const char *stnam; /* start of the name, after "[(*" */
497 const char *cp; /* end of the name, e.g. before ']' */
498 const char *res; /* the string to be substituted */
7888c61d 499 char *nbuf; /* new buffer to copy bufp to */
7888c61d
FF
500 size_t maxl; /* expected length of the escape name */
501 size_t naml; /* actual length of the escape name */
502 int expand_count; /* to avoid infinite loops */
070c62a6
FF
503 int npos; /* position in numeric expression */
504 int arg_complete; /* argument not interrupted by eol */
505 char term; /* character terminating the escape */
80387638 506
36342e81 507 expand_count = 0;
070c62a6
FF
508 start = *bufp + pos;
509 stesc = strchr(start, '\0') - 1;
510 while (stesc-- > start) {
80387638 511
070c62a6 512 /* Search backwards for the next backslash. */
80387638 513
070c62a6
FF
514 if ('\\' != *stesc)
515 continue;
516
517 /* If it is escaped, skip it. */
80387638 518
070c62a6
FF
519 for (cp = stesc - 1; cp >= start; cp--)
520 if ('\\' != *cp)
521 break;
36342e81 522
070c62a6
FF
523 if (0 == (stesc - cp) % 2) {
524 stesc = (char *)cp;
525 continue;
526 }
527
528 /* Decide whether to expand or to check only. */
529
530 term = '\0';
531 cp = stesc + 1;
7888c61d 532 switch (*cp) {
070c62a6 533 case '*':
7888c61d
FF
534 res = NULL;
535 break;
070c62a6
FF
536 case 'B':
537 /* FALLTHROUGH */
538 case 'w':
539 term = cp[1];
540 /* FALLTHROUGH */
541 case 'n':
7888c61d
FF
542 res = ubuf;
543 break;
544 default:
070c62a6
FF
545 if (ESCAPE_ERROR == mandoc_escape(&cp, NULL, NULL))
546 mandoc_vmsg(MANDOCERR_ESC_BAD,
547 r->parse, ln, (int)(stesc - *bufp),
548 "%.*s", (int)(cp - stesc), stesc);
549 continue;
36342e81
SW
550 }
551
070c62a6
FF
552 if (EXPAND_LIMIT < ++expand_count) {
553 mandoc_msg(MANDOCERR_ROFFLOOP, r->parse,
554 ln, (int)(stesc - *bufp), NULL);
555 return(ROFF_IGN);
556 }
80387638
SW
557
558 /*
559 * The third character decides the length
7888c61d 560 * of the name of the string or register.
80387638
SW
561 * Save a pointer to the name.
562 */
563
070c62a6
FF
564 if ('\0' == term) {
565 switch (*++cp) {
566 case '\0':
567 maxl = 0;
568 break;
569 case '(':
570 cp++;
571 maxl = 2;
572 break;
573 case '[':
574 cp++;
575 term = ']';
576 maxl = 0;
577 break;
578 default:
579 maxl = 1;
580 break;
581 }
582 } else {
583 cp += 2;
80387638 584 maxl = 0;
80387638
SW
585 }
586 stnam = cp;
587
588 /* Advance to the end of the name. */
589
070c62a6 590 arg_complete = 1;
7888c61d 591 for (naml = 0; 0 == maxl || naml < maxl; naml++, cp++) {
36342e81 592 if ('\0' == *cp) {
070c62a6
FF
593 mandoc_msg(MANDOCERR_ESC_BAD, r->parse,
594 ln, (int)(stesc - *bufp), stesc);
595 arg_complete = 0;
596 break;
36342e81 597 }
070c62a6
FF
598 if (0 == maxl && *cp == term) {
599 cp++;
80387638 600 break;
070c62a6 601 }
80387638
SW
602 }
603
604 /*
605 * Retrieve the replacement string; if it is
606 * undefined, resume searching for escapes.
607 */
608
070c62a6
FF
609 switch (stesc[1]) {
610 case '*':
611 if (arg_complete)
612 res = roff_getstrn(r, stnam, naml);
613 break;
614 case 'B':
615 npos = 0;
616 ubuf[0] = arg_complete &&
617 roff_evalnum(stnam, &npos, NULL, 0) &&
618 stnam + npos + 1 == cp ? '1' : '0';
619 ubuf[1] = '\0';
620 break;
621 case 'n':
622 if (arg_complete)
623 (void)snprintf(ubuf, sizeof(ubuf), "%d",
624 roff_getregn(r, stnam, naml));
625 else
626 ubuf[0] = '\0';
627 break;
628 case 'w':
629 /* use even incomplete args */
630 (void)snprintf(ubuf, sizeof(ubuf), "%d",
631 24 * (int)naml);
632 break;
633 }
80387638
SW
634
635 if (NULL == res) {
070c62a6
FF
636 mandoc_vmsg(MANDOCERR_STR_UNDEF,
637 r->parse, ln, (int)(stesc - *bufp),
638 "%.*s", (int)naml, stnam);
a4c7eb57 639 res = "";
80387638
SW
640 }
641
642 /* Replace the escape sequence by the string. */
643
070c62a6
FF
644 *stesc = '\0';
645 *szp = mandoc_asprintf(&nbuf, "%s%s%s",
646 *bufp, res, cp) + 1;
36342e81 647
070c62a6 648 /* Prepare for the next replacement. */
80387638 649
070c62a6
FF
650 start = nbuf + pos;
651 stesc = nbuf + (stesc - *bufp) + strlen(res);
80387638 652 free(*bufp);
7888c61d 653 *bufp = nbuf;
36342e81
SW
654 }
655 return(ROFF_CONT);
80387638
SW
656}
657
36342e81 658/*
f88b6c16
FF
659 * Process text streams:
660 * Convert all breakable hyphens into ASCII_HYPH.
661 * Decrement and spring input line trap.
36342e81
SW
662 */
663static enum rofferr
f88b6c16 664roff_parsetext(char **bufp, size_t *szp, int pos, int *offs)
36342e81
SW
665{
666 size_t sz;
667 const char *start;
f88b6c16
FF
668 char *p;
669 int isz;
36342e81
SW
670 enum mandoc_esc esc;
671
f88b6c16 672 start = p = *bufp + pos;
36342e81
SW
673
674 while ('\0' != *p) {
675 sz = strcspn(p, "-\\");
676 p += sz;
677
678 if ('\0' == *p)
679 break;
680
681 if ('\\' == *p) {
682 /* Skip over escapes. */
683 p++;
7888c61d 684 esc = mandoc_escape((const char **)&p, NULL, NULL);
36342e81
SW
685 if (ESCAPE_ERROR == esc)
686 break;
687 continue;
688 } else if (p == start) {
689 p++;
690 continue;
691 }
692
693 if (isalpha((unsigned char)p[-1]) &&
694 isalpha((unsigned char)p[1]))
695 *p = ASCII_HYPH;
696 p++;
697 }
698
f88b6c16
FF
699 /* Spring the input line trap. */
700 if (1 == roffit_lines) {
070c62a6 701 isz = mandoc_asprintf(&p, "%s\n.%s", *bufp, roffit_macro);
f88b6c16
FF
702 free(*bufp);
703 *bufp = p;
704 *szp = isz + 1;
705 *offs = 0;
706 free(roffit_macro);
707 roffit_lines = 0;
708 return(ROFF_REPARSE);
709 } else if (1 < roffit_lines)
710 --roffit_lines;
36342e81
SW
711 return(ROFF_CONT);
712}
80387638
SW
713
714enum rofferr
070c62a6 715roff_parseln(struct roff *r, int ln, char **bufp,
80387638
SW
716 size_t *szp, int pos, int *offs)
717{
718 enum rofft t;
719 enum rofferr e;
60e1e752 720 int ppos, ctl;
80387638
SW
721
722 /*
723 * Run the reserved-word filter only if we have some reserved
724 * words to fill in.
725 */
726
36342e81
SW
727 e = roff_res(r, bufp, szp, ln, pos);
728 if (ROFF_IGN == e)
729 return(e);
730 assert(ROFF_CONT == e);
80387638 731
60e1e752 732 ppos = pos;
f88b6c16 733 ctl = roff_getcontrol(r, *bufp, &pos);
60e1e752 734
80387638
SW
735 /*
736 * First, if a scope is open and we're not a macro, pass the
737 * text through the macro's filter. If a scope isn't open and
738 * we're not a macro, just let it through.
60e1e752
SW
739 * Finally, if there's an equation scope open, divert it into it
740 * no matter our state.
80387638
SW
741 */
742
60e1e752 743 if (r->last && ! ctl) {
80387638
SW
744 t = r->last->tok;
745 assert(roffs[t].text);
070c62a6 746 e = (*roffs[t].text)(r, t, bufp, szp, ln, pos, pos, offs);
80387638 747 assert(ROFF_IGN == e || ROFF_CONT == e);
60e1e752
SW
748 if (ROFF_CONT != e)
749 return(e);
7888c61d
FF
750 }
751 if (r->eqn)
752 return(eqn_read(&r->eqn, ln, *bufp, ppos, offs));
753 if ( ! ctl) {
60e1e752
SW
754 if (r->tbl)
755 return(tbl_read(r->tbl, ln, *bufp, pos));
f88b6c16 756 return(roff_parsetext(bufp, szp, pos, offs));
7888c61d 757 }
80387638
SW
758
759 /*
760 * If a scope is open, go to the child handler for that macro,
761 * as it may want to preprocess before doing anything with it.
60e1e752 762 * Don't do so if an equation is open.
80387638
SW
763 */
764
765 if (r->last) {
766 t = r->last->tok;
767 assert(roffs[t].sub);
070c62a6
FF
768 return((*roffs[t].sub)(r, t, bufp, szp,
769 ln, ppos, pos, offs));
80387638
SW
770 }
771
772 /*
773 * Lastly, as we've no scope open, try to look up and execute
774 * the new macro. If no macro is found, simply return and let
775 * the compilers handle it.
776 */
777
070c62a6 778 if (ROFF_MAX == (t = roff_parse(r, *bufp, &pos, ln, ppos)))
80387638
SW
779 return(ROFF_CONT);
780
781 assert(roffs[t].proc);
070c62a6 782 return((*roffs[t].proc)(r, t, bufp, szp, ln, ppos, pos, offs));
80387638
SW
783}
784
80387638
SW
785void
786roff_endparse(struct roff *r)
787{
788
789 if (r->last)
070c62a6
FF
790 mandoc_msg(MANDOCERR_BLK_NOEND, r->parse,
791 r->last->line, r->last->col,
792 roffs[r->last->tok].name);
80387638 793
60e1e752 794 if (r->eqn) {
070c62a6
FF
795 mandoc_msg(MANDOCERR_BLK_NOEND, r->parse,
796 r->eqn->eqn.ln, r->eqn->eqn.pos, "EQ");
36342e81 797 eqn_end(&r->eqn);
60e1e752
SW
798 }
799
80387638 800 if (r->tbl) {
070c62a6
FF
801 mandoc_msg(MANDOCERR_BLK_NOEND, r->parse,
802 r->tbl->line, r->tbl->pos, "TS");
36342e81 803 tbl_end(&r->tbl);
80387638
SW
804 }
805}
806
80387638
SW
807/*
808 * Parse a roff node's type from the input buffer. This must be in the
809 * form of ".foo xxx" in the usual way.
810 */
811static enum rofft
070c62a6 812roff_parse(struct roff *r, char *buf, int *pos, int ln, int ppos)
80387638 813{
070c62a6 814 char *cp;
80387638
SW
815 const char *mac;
816 size_t maclen;
817 enum rofft t;
818
070c62a6 819 cp = buf + *pos;
80387638 820
070c62a6
FF
821 if ('\0' == *cp || '"' == *cp || '\t' == *cp || ' ' == *cp)
822 return(ROFF_MAX);
36342e81 823
070c62a6
FF
824 mac = cp;
825 maclen = roff_getname(r, &cp, ln, ppos);
80387638
SW
826
827 t = (r->current_string = roff_getstrn(r, mac, maclen))
36342e81 828 ? ROFF_USERDEF : roffhash_find(mac, maclen);
80387638 829
070c62a6
FF
830 if (ROFF_MAX != t)
831 *pos = cp - buf;
80387638
SW
832
833 return(t);
834}
835
80387638
SW
836static enum rofferr
837roff_cblock(ROFF_ARGS)
838{
839
840 /*
841 * A block-close `..' should only be invoked as a child of an
842 * ignore macro, otherwise raise a warning and just ignore it.
843 */
844
845 if (NULL == r->last) {
070c62a6
FF
846 mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
847 ln, ppos, "..");
80387638
SW
848 return(ROFF_IGN);
849 }
850
851 switch (r->last->tok) {
070c62a6
FF
852 case ROFF_am:
853 /* ROFF_am1 is remapped to ROFF_am in roff_block(). */
80387638 854 /* FALLTHROUGH */
070c62a6 855 case ROFF_ami:
80387638 856 /* FALLTHROUGH */
070c62a6 857 case ROFF_de:
80387638
SW
858 /* ROFF_de1 is remapped to ROFF_de in roff_block(). */
859 /* FALLTHROUGH */
070c62a6 860 case ROFF_dei:
80387638 861 /* FALLTHROUGH */
070c62a6 862 case ROFF_ig:
80387638
SW
863 break;
864 default:
070c62a6
FF
865 mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
866 ln, ppos, "..");
80387638
SW
867 return(ROFF_IGN);
868 }
869
870 if ((*bufp)[pos])
070c62a6
FF
871 mandoc_vmsg(MANDOCERR_ARG_SKIP, r->parse, ln, pos,
872 ".. %s", *bufp + pos);
80387638
SW
873
874 roffnode_pop(r);
875 roffnode_cleanscope(r);
876 return(ROFF_IGN);
877
878}
879
80387638
SW
880static void
881roffnode_cleanscope(struct roff *r)
882{
883
884 while (r->last) {
f88b6c16 885 if (--r->last->endspan != 0)
80387638
SW
886 break;
887 roffnode_pop(r);
888 }
889}
890
070c62a6
FF
891static void
892roff_ccond(struct roff *r, int ln, int ppos)
80387638
SW
893{
894
895 if (NULL == r->last) {
070c62a6
FF
896 mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
897 ln, ppos, "\\}");
898 return;
80387638
SW
899 }
900
901 switch (r->last->tok) {
070c62a6 902 case ROFF_el:
80387638 903 /* FALLTHROUGH */
070c62a6 904 case ROFF_ie:
80387638 905 /* FALLTHROUGH */
070c62a6 906 case ROFF_if:
80387638
SW
907 break;
908 default:
070c62a6
FF
909 mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
910 ln, ppos, "\\}");
911 return;
80387638
SW
912 }
913
914 if (r->last->endspan > -1) {
070c62a6
FF
915 mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
916 ln, ppos, "\\}");
917 return;
80387638
SW
918 }
919
80387638
SW
920 roffnode_pop(r);
921 roffnode_cleanscope(r);
070c62a6 922 return;
80387638
SW
923}
924
80387638
SW
925static enum rofferr
926roff_block(ROFF_ARGS)
927{
070c62a6
FF
928 const char *name;
929 char *iname, *cp;
930 size_t namesz;
80387638 931
070c62a6 932 /* Ignore groff compatibility mode for now. */
80387638 933
070c62a6
FF
934 if (ROFF_de1 == tok)
935 tok = ROFF_de;
936 else if (ROFF_am1 == tok)
937 tok = ROFF_am;
80387638 938
070c62a6
FF
939 /* Parse the macro name argument. */
940
941 cp = *bufp + pos;
942 if (ROFF_ig == tok) {
943 iname = NULL;
944 namesz = 0;
945 } else {
946 iname = cp;
947 namesz = roff_getname(r, &cp, ln, ppos);
948 iname[namesz] = '\0';
949 }
80387638 950
070c62a6 951 /* Resolve the macro name argument if it is indirect. */
80387638 952
070c62a6
FF
953 if (namesz && (ROFF_dei == tok || ROFF_ami == tok)) {
954 if (NULL == (name = roff_getstrn(r, iname, namesz))) {
955 mandoc_vmsg(MANDOCERR_STR_UNDEF,
956 r->parse, ln, (int)(iname - *bufp),
957 "%.*s", (int)namesz, iname);
958 namesz = 0;
959 } else
960 namesz = strlen(name);
961 } else
962 name = iname;
80387638 963
070c62a6
FF
964 if (0 == namesz && ROFF_ig != tok) {
965 mandoc_msg(MANDOCERR_REQ_EMPTY, r->parse,
966 ln, ppos, roffs[tok].name);
967 return(ROFF_IGN);
80387638
SW
968 }
969
970 roffnode_push(r, tok, name, ln, ppos);
971
972 /*
973 * At the beginning of a `de' macro, clear the existing string
974 * with the same name, if there is one. New content will be
070c62a6 975 * appended from roff_block_text() in multiline mode.
80387638
SW
976 */
977
070c62a6
FF
978 if (ROFF_de == tok || ROFF_dei == tok)
979 roff_setstrn(&r->strtab, name, namesz, "", 0, 0);
80387638 980
070c62a6 981 if ('\0' == *cp)
80387638
SW
982 return(ROFF_IGN);
983
070c62a6 984 /* Get the custom end marker. */
80387638 985
070c62a6
FF
986 iname = cp;
987 namesz = roff_getname(r, &cp, ln, ppos);
80387638 988
070c62a6 989 /* Resolve the end marker if it is indirect. */
80387638 990
070c62a6
FF
991 if (namesz && (ROFF_dei == tok || ROFF_ami == tok)) {
992 if (NULL == (name = roff_getstrn(r, iname, namesz))) {
993 mandoc_vmsg(MANDOCERR_STR_UNDEF,
994 r->parse, ln, (int)(iname - *bufp),
995 "%.*s", (int)namesz, iname);
996 namesz = 0;
997 } else
998 namesz = strlen(name);
999 } else
1000 name = iname;
80387638 1001
070c62a6
FF
1002 if (namesz)
1003 r->last->end = mandoc_strndup(name, namesz);
80387638 1004
070c62a6
FF
1005 if ('\0' != *cp)
1006 mandoc_vmsg(MANDOCERR_ARG_EXCESS, r->parse,
1007 ln, pos, ".%s ... %s", roffs[tok].name, cp);
80387638
SW
1008
1009 return(ROFF_IGN);
1010}
1011
80387638
SW
1012static enum rofferr
1013roff_block_sub(ROFF_ARGS)
1014{
1015 enum rofft t;
1016 int i, j;
1017
1018 /*
1019 * First check whether a custom macro exists at this level. If
1020 * it does, then check against it. This is some of groff's
1021 * stranger behaviours. If we encountered a custom end-scope
1022 * tag and that tag also happens to be a "real" macro, then we
1023 * need to try interpreting it again as a real macro. If it's
1024 * not, then return ignore. Else continue.
1025 */
1026
1027 if (r->last->end) {
60e1e752 1028 for (i = pos, j = 0; r->last->end[j]; j++, i++)
80387638
SW
1029 if ((*bufp)[i] != r->last->end[j])
1030 break;
1031
070c62a6
FF
1032 if ('\0' == r->last->end[j] &&
1033 ('\0' == (*bufp)[i] ||
1034 ' ' == (*bufp)[i] ||
1035 '\t' == (*bufp)[i])) {
80387638
SW
1036 roffnode_pop(r);
1037 roffnode_cleanscope(r);
1038
60e1e752
SW
1039 while (' ' == (*bufp)[i] || '\t' == (*bufp)[i])
1040 i++;
1041
1042 pos = i;
070c62a6 1043 if (ROFF_MAX != roff_parse(r, *bufp, &pos, ln, ppos))
80387638
SW
1044 return(ROFF_RERUN);
1045 return(ROFF_IGN);
1046 }
1047 }
1048
1049 /*
1050 * If we have no custom end-query or lookup failed, then try
1051 * pulling it out of the hashtable.
1052 */
1053
070c62a6 1054 t = roff_parse(r, *bufp, &pos, ln, ppos);
80387638 1055
80387638 1056 if (ROFF_cblock != t) {
070c62a6
FF
1057 if (ROFF_ig != tok)
1058 roff_setstr(r, r->last->name, *bufp + ppos, 2);
80387638
SW
1059 return(ROFF_IGN);
1060 }
1061
1062 assert(roffs[t].proc);
070c62a6 1063 return((*roffs[t].proc)(r, t, bufp, szp, ln, ppos, pos, offs));
80387638
SW
1064}
1065
80387638
SW
1066static enum rofferr
1067roff_block_text(ROFF_ARGS)
1068{
1069
070c62a6
FF
1070 if (ROFF_ig != tok)
1071 roff_setstr(r, r->last->name, *bufp + pos, 2);
80387638
SW
1072
1073 return(ROFF_IGN);
1074}
1075
80387638
SW
1076static enum rofferr
1077roff_cond_sub(ROFF_ARGS)
1078{
1079 enum rofft t;
a4c7eb57 1080 char *ep;
070c62a6 1081 int rr;
80387638 1082
80387638 1083 rr = r->last->rule;
a4c7eb57 1084 roffnode_cleanscope(r);
070c62a6 1085 t = roff_parse(r, *bufp, &pos, ln, ppos);
80387638 1086
a4c7eb57 1087 /*
f88b6c16
FF
1088 * Fully handle known macros when they are structurally
1089 * required or when the conditional evaluated to true.
80387638
SW
1090 */
1091
f88b6c16 1092 if ((ROFF_MAX != t) &&
070c62a6 1093 (rr || ROFFMAC_STRUCT & roffs[t].flags)) {
f88b6c16
FF
1094 assert(roffs[t].proc);
1095 return((*roffs[t].proc)(r, t, bufp, szp,
070c62a6 1096 ln, ppos, pos, offs));
f88b6c16 1097 }
36342e81 1098
070c62a6
FF
1099 /*
1100 * If `\}' occurs on a macro line without a preceding macro,
1101 * drop the line completely.
1102 */
80387638 1103
070c62a6
FF
1104 ep = *bufp + pos;
1105 if ('\\' == ep[0] && '}' == ep[1])
1106 rr = 0;
a4c7eb57 1107
070c62a6 1108 /* Always check for the closing delimiter `\}'. */
80387638 1109
070c62a6
FF
1110 while (NULL != (ep = strchr(ep, '\\'))) {
1111 if ('}' == *(++ep)) {
1112 *ep = '&';
1113 roff_ccond(r, ln, ep - *bufp - 1);
1114 }
1115 ++ep;
f88b6c16 1116 }
070c62a6 1117 return(rr ? ROFF_CONT : ROFF_IGN);
80387638
SW
1118}
1119
80387638
SW
1120static enum rofferr
1121roff_cond_text(ROFF_ARGS)
1122{
a4c7eb57 1123 char *ep;
070c62a6 1124 int rr;
80387638
SW
1125
1126 rr = r->last->rule;
a4c7eb57 1127 roffnode_cleanscope(r);
80387638 1128
070c62a6
FF
1129 ep = *bufp + pos;
1130 while (NULL != (ep = strchr(ep, '\\'))) {
1131 if ('}' == *(++ep)) {
1132 *ep = '&';
1133 roff_ccond(r, ln, ep - *bufp - 1);
1134 }
1135 ++ep;
80387638 1136 }
070c62a6 1137 return(rr ? ROFF_CONT : ROFF_IGN);
80387638
SW
1138}
1139
070c62a6
FF
1140/*
1141 * Parse a single signed integer number. Stop at the first non-digit.
1142 * If there is at least one digit, return success and advance the
1143 * parse point, else return failure and let the parse point unchanged.
1144 * Ignore overflows, treat them just like the C language.
1145 */
7888c61d
FF
1146static int
1147roff_getnum(const char *v, int *pos, int *res)
1148{
070c62a6
FF
1149 int myres, n, p;
1150
1151 if (NULL == res)
1152 res = &myres;
7888c61d
FF
1153
1154 p = *pos;
1155 n = v[p] == '-';
1156 if (n)
1157 p++;
1158
1159 for (*res = 0; isdigit((unsigned char)v[p]); p++)
070c62a6 1160 *res = 10 * *res + v[p] - '0';
7888c61d
FF
1161 if (p == *pos + n)
1162 return 0;
1163
1164 if (n)
1165 *res = -*res;
1166
1167 *pos = p;
1168 return 1;
1169}
1170
070c62a6
FF
1171/*
1172 * Evaluate a string comparison condition.
1173 * The first character is the delimiter.
1174 * Succeed if the string up to its second occurrence
1175 * matches the string up to its third occurence.
1176 * Advance the cursor after the third occurrence
1177 * or lacking that, to the end of the line.
1178 */
7888c61d 1179static int
070c62a6 1180roff_evalstrcond(const char *v, int *pos)
7888c61d 1181{
070c62a6
FF
1182 const char *s1, *s2, *s3;
1183 int match;
7888c61d 1184
070c62a6
FF
1185 match = 0;
1186 s1 = v + *pos; /* initial delimiter */
1187 s2 = s1 + 1; /* for scanning the first string */
1188 s3 = strchr(s2, *s1); /* for scanning the second string */
7888c61d 1189
070c62a6
FF
1190 if (NULL == s3) /* found no middle delimiter */
1191 goto out;
7888c61d 1192
070c62a6
FF
1193 while ('\0' != *++s3) {
1194 if (*s2 != *s3) { /* mismatch */
1195 s3 = strchr(s3, *s1);
1196 break;
1197 }
1198 if (*s3 == *s1) { /* found the final delimiter */
1199 match = 1;
1200 break;
1201 }
1202 s2++;
1203 }
7888c61d 1204
070c62a6
FF
1205out:
1206 if (NULL == s3)
1207 s3 = strchr(s2, '\0');
1208 else
1209 s3++;
1210 *pos = s3 - v;
1211 return(match);
7888c61d
FF
1212}
1213
070c62a6
FF
1214/*
1215 * Evaluate an optionally negated single character, numerical,
1216 * or string condition.
1217 */
1218static int
80387638
SW
1219roff_evalcond(const char *v, int *pos)
1220{
070c62a6
FF
1221 int wanttrue, number;
1222
1223 if ('!' == v[*pos]) {
1224 wanttrue = 0;
1225 (*pos)++;
1226 } else
1227 wanttrue = 1;
80387638
SW
1228
1229 switch (v[*pos]) {
070c62a6
FF
1230 case 'n':
1231 /* FALLTHROUGH */
1232 case 'o':
80387638 1233 (*pos)++;
070c62a6
FF
1234 return(wanttrue);
1235 case 'c':
80387638 1236 /* FALLTHROUGH */
070c62a6 1237 case 'd':
80387638 1238 /* FALLTHROUGH */
070c62a6
FF
1239 case 'e':
1240 /* FALLTHROUGH */
1241 case 'r':
1242 /* FALLTHROUGH */
1243 case 't':
7888c61d 1244 (*pos)++;
070c62a6 1245 return(!wanttrue);
80387638
SW
1246 default:
1247 break;
1248 }
1249
070c62a6
FF
1250 if (roff_evalnum(v, pos, &number, 0))
1251 return((number > 0) == wanttrue);
1252 else
1253 return(roff_evalstrcond(v, pos) == wanttrue);
80387638
SW
1254}
1255
80387638
SW
1256static enum rofferr
1257roff_line_ignore(ROFF_ARGS)
1258{
1259
80387638
SW
1260 return(ROFF_IGN);
1261}
1262
80387638
SW
1263static enum rofferr
1264roff_cond(ROFF_ARGS)
1265{
f88b6c16
FF
1266
1267 roffnode_push(r, tok, NULL, ln, ppos);
80387638 1268
070c62a6 1269 /*
a4c7eb57
SW
1270 * An `.el' has no conditional body: it will consume the value
1271 * of the current rstack entry set in prior `ie' calls or
070c62a6 1272 * defaults to DENY.
a4c7eb57
SW
1273 *
1274 * If we're not an `el', however, then evaluate the conditional.
1275 */
80387638 1276
f88b6c16 1277 r->last->rule = ROFF_el == tok ?
070c62a6
FF
1278 (r->rstackpos < 0 ? 0 : r->rstack[r->rstackpos--]) :
1279 roff_evalcond(*bufp, &pos);
80387638 1280
a4c7eb57
SW
1281 /*
1282 * An if-else will put the NEGATION of the current evaluated
1283 * conditional into the stack of rules.
1284 */
1285
80387638 1286 if (ROFF_ie == tok) {
070c62a6
FF
1287 if (r->rstackpos + 1 == r->rstacksz) {
1288 r->rstacksz += 16;
1289 r->rstack = mandoc_reallocarray(r->rstack,
1290 r->rstacksz, sizeof(int));
a4c7eb57 1291 }
070c62a6 1292 r->rstack[++r->rstackpos] = !r->last->rule;
80387638
SW
1293 }
1294
1295 /* If the parent has false as its rule, then so do we. */
1296
070c62a6
FF
1297 if (r->last->parent && !r->last->parent->rule)
1298 r->last->rule = 0;
80387638
SW
1299
1300 /*
f88b6c16
FF
1301 * Determine scope.
1302 * If there is nothing on the line after the conditional,
1303 * not even whitespace, use next-line scope.
80387638
SW
1304 */
1305
f88b6c16
FF
1306 if ('\0' == (*bufp)[pos]) {
1307 r->last->endspan = 2;
1308 goto out;
1309 }
1310
1311 while (' ' == (*bufp)[pos])
1312 pos++;
1313
1314 /* An opening brace requests multiline scope. */
80387638
SW
1315
1316 if ('\\' == (*bufp)[pos] && '{' == (*bufp)[pos + 1]) {
1317 r->last->endspan = -1;
1318 pos += 2;
f88b6c16 1319 goto out;
070c62a6 1320 }
80387638
SW
1321
1322 /*
f88b6c16
FF
1323 * Anything else following the conditional causes
1324 * single-line scope. Warn if the scope contains
1325 * nothing but trailing whitespace.
80387638
SW
1326 */
1327
1328 if ('\0' == (*bufp)[pos])
070c62a6
FF
1329 mandoc_msg(MANDOCERR_COND_EMPTY, r->parse,
1330 ln, ppos, roffs[tok].name);
80387638 1331
f88b6c16 1332 r->last->endspan = 1;
80387638 1333
f88b6c16 1334out:
80387638
SW
1335 *offs = pos;
1336 return(ROFF_RERUN);
1337}
1338
80387638
SW
1339static enum rofferr
1340roff_ds(ROFF_ARGS)
1341{
070c62a6
FF
1342 char *string;
1343 const char *name;
1344 size_t namesz;
80387638
SW
1345
1346 /*
070c62a6
FF
1347 * The first word is the name of the string.
1348 * If it is empty or terminated by an escape sequence,
1349 * abort the `ds' request without defining anything.
80387638
SW
1350 */
1351
070c62a6 1352 name = string = *bufp + pos;
80387638
SW
1353 if ('\0' == *name)
1354 return(ROFF_IGN);
1355
070c62a6
FF
1356 namesz = roff_getname(r, &string, ln, pos);
1357 if ('\\' == name[namesz])
1358 return(ROFF_IGN);
1359
1360 /* Read past the initial double-quote, if any. */
60e1e752 1361 if ('"' == *string)
80387638
SW
1362 string++;
1363
1364 /* The rest is the value. */
070c62a6
FF
1365 roff_setstrn(&r->strtab, name, namesz, string, strlen(string),
1366 ROFF_as == tok);
80387638
SW
1367 return(ROFF_IGN);
1368}
1369
070c62a6
FF
1370/*
1371 * Parse a single operator, one or two characters long.
1372 * If the operator is recognized, return success and advance the
1373 * parse point, else return failure and let the parse point unchanged.
1374 */
1375static int
1376roff_getop(const char *v, int *pos, char *res)
1377{
1378
1379 *res = v[*pos];
1380
1381 switch (*res) {
1382 case '+':
1383 /* FALLTHROUGH */
1384 case '-':
1385 /* FALLTHROUGH */
1386 case '*':
1387 /* FALLTHROUGH */
1388 case '/':
1389 /* FALLTHROUGH */
1390 case '%':
1391 /* FALLTHROUGH */
1392 case '&':
1393 /* FALLTHROUGH */
1394 case ':':
1395 break;
1396 case '<':
1397 switch (v[*pos + 1]) {
1398 case '=':
1399 *res = 'l';
1400 (*pos)++;
1401 break;
1402 case '>':
1403 *res = '!';
1404 (*pos)++;
1405 break;
1406 case '?':
1407 *res = 'i';
1408 (*pos)++;
1409 break;
1410 default:
1411 break;
1412 }
1413 break;
1414 case '>':
1415 switch (v[*pos + 1]) {
1416 case '=':
1417 *res = 'g';
1418 (*pos)++;
1419 break;
1420 case '?':
1421 *res = 'a';
1422 (*pos)++;
1423 break;
1424 default:
1425 break;
1426 }
1427 break;
1428 case '=':
1429 if ('=' == v[*pos + 1])
1430 (*pos)++;
1431 break;
1432 default:
1433 return(0);
1434 }
1435 (*pos)++;
1436
1437 return(*res);
1438}
1439
1440/*
1441 * Evaluate either a parenthesized numeric expression
1442 * or a single signed integer number.
1443 */
1444static int
1445roff_evalpar(const char *v, int *pos, int *res)
1446{
1447
1448 if ('(' != v[*pos])
1449 return(roff_getnum(v, pos, res));
1450
1451 (*pos)++;
1452 if ( ! roff_evalnum(v, pos, res, 1))
1453 return(0);
1454
1455 /*
1456 * Omission of the closing parenthesis
1457 * is an error in validation mode,
1458 * but ignored in evaluation mode.
1459 */
1460
1461 if (')' == v[*pos])
1462 (*pos)++;
1463 else if (NULL == res)
1464 return(0);
1465
1466 return(1);
1467}
1468
1469/*
1470 * Evaluate a complete numeric expression.
1471 * Proceed left to right, there is no concept of precedence.
1472 */
1473static int
1474roff_evalnum(const char *v, int *pos, int *res, int skipwhite)
1475{
1476 int mypos, operand2;
1477 char operator;
1478
1479 if (NULL == pos) {
1480 mypos = 0;
1481 pos = &mypos;
1482 }
1483
1484 if (skipwhite)
1485 while (isspace((unsigned char)v[*pos]))
1486 (*pos)++;
1487
1488 if ( ! roff_evalpar(v, pos, res))
1489 return(0);
1490
1491 while (1) {
1492 if (skipwhite)
1493 while (isspace((unsigned char)v[*pos]))
1494 (*pos)++;
1495
1496 if ( ! roff_getop(v, pos, &operator))
1497 break;
1498
1499 if (skipwhite)
1500 while (isspace((unsigned char)v[*pos]))
1501 (*pos)++;
1502
1503 if ( ! roff_evalpar(v, pos, &operand2))
1504 return(0);
1505
1506 if (skipwhite)
1507 while (isspace((unsigned char)v[*pos]))
1508 (*pos)++;
1509
1510 if (NULL == res)
1511 continue;
1512
1513 switch (operator) {
1514 case '+':
1515 *res += operand2;
1516 break;
1517 case '-':
1518 *res -= operand2;
1519 break;
1520 case '*':
1521 *res *= operand2;
1522 break;
1523 case '/':
1524 *res /= operand2;
1525 break;
1526 case '%':
1527 *res %= operand2;
1528 break;
1529 case '<':
1530 *res = *res < operand2;
1531 break;
1532 case '>':
1533 *res = *res > operand2;
1534 break;
1535 case 'l':
1536 *res = *res <= operand2;
1537 break;
1538 case 'g':
1539 *res = *res >= operand2;
1540 break;
1541 case '=':
1542 *res = *res == operand2;
1543 break;
1544 case '!':
1545 *res = *res != operand2;
1546 break;
1547 case '&':
1548 *res = *res && operand2;
1549 break;
1550 case ':':
1551 *res = *res || operand2;
1552 break;
1553 case 'i':
1554 if (operand2 < *res)
1555 *res = operand2;
1556 break;
1557 case 'a':
1558 if (operand2 > *res)
1559 *res = operand2;
1560 break;
1561 default:
1562 abort();
1563 }
1564 }
1565 return(1);
1566}
1567
7888c61d
FF
1568void
1569roff_setreg(struct roff *r, const char *name, int val, char sign)
1570{
1571 struct roffreg *reg;
1572
1573 /* Search for an existing register with the same name. */
1574 reg = r->regtab;
1575
1576 while (reg && strcmp(name, reg->key.p))
1577 reg = reg->next;
1578
1579 if (NULL == reg) {
1580 /* Create a new register. */
1581 reg = mandoc_malloc(sizeof(struct roffreg));
1582 reg->key.p = mandoc_strdup(name);
1583 reg->key.sz = strlen(name);
1584 reg->val = 0;
1585 reg->next = r->regtab;
1586 r->regtab = reg;
1587 }
1588
1589 if ('+' == sign)
1590 reg->val += val;
1591 else if ('-' == sign)
1592 reg->val -= val;
1593 else
1594 reg->val = val;
1595}
1596
070c62a6
FF
1597/*
1598 * Handle some predefined read-only number registers.
1599 * For now, return -1 if the requested register is not predefined;
1600 * in case a predefined read-only register having the value -1
1601 * were to turn up, another special value would have to be chosen.
1602 */
1603static int
1604roff_getregro(const char *name)
1605{
1606
1607 switch (*name) {
1608 case 'A': /* ASCII approximation mode is always off. */
1609 return(0);
1610 case 'g': /* Groff compatibility mode is always on. */
1611 return(1);
1612 case 'H': /* Fixed horizontal resolution. */
1613 return (24);
1614 case 'j': /* Always adjust left margin only. */
1615 return(0);
1616 case 'T': /* Some output device is always defined. */
1617 return(1);
1618 case 'V': /* Fixed vertical resolution. */
1619 return (40);
1620 default:
1621 return (-1);
1622 }
1623}
1624
36342e81 1625int
7888c61d 1626roff_getreg(const struct roff *r, const char *name)
36342e81 1627{
7888c61d 1628 struct roffreg *reg;
070c62a6
FF
1629 int val;
1630
1631 if ('.' == name[0] && '\0' != name[1] && '\0' == name[2]) {
1632 val = roff_getregro(name + 1);
1633 if (-1 != val)
1634 return (val);
1635 }
36342e81 1636
7888c61d
FF
1637 for (reg = r->regtab; reg; reg = reg->next)
1638 if (0 == strcmp(name, reg->key.p))
1639 return(reg->val);
1640
1641 return(0);
36342e81
SW
1642}
1643
7888c61d
FF
1644static int
1645roff_getregn(const struct roff *r, const char *name, size_t len)
36342e81 1646{
7888c61d 1647 struct roffreg *reg;
070c62a6
FF
1648 int val;
1649
1650 if ('.' == name[0] && 2 == len) {
1651 val = roff_getregro(name + 1);
1652 if (-1 != val)
1653 return (val);
1654 }
36342e81 1655
7888c61d
FF
1656 for (reg = r->regtab; reg; reg = reg->next)
1657 if (len == reg->key.sz &&
1658 0 == strncmp(name, reg->key.p, len))
1659 return(reg->val);
1660
1661 return(0);
36342e81
SW
1662}
1663
7888c61d
FF
1664static void
1665roff_freereg(struct roffreg *reg)
36342e81 1666{
7888c61d 1667 struct roffreg *old_reg;
36342e81 1668
7888c61d
FF
1669 while (NULL != reg) {
1670 free(reg->key.p);
1671 old_reg = reg;
1672 reg = reg->next;
1673 free(old_reg);
1674 }
36342e81 1675}
80387638 1676
80387638
SW
1677static enum rofferr
1678roff_nr(ROFF_ARGS)
1679{
070c62a6
FF
1680 char *key, *val;
1681 size_t keysz;
a4c7eb57 1682 int iv;
7888c61d 1683 char sign;
80387638 1684
070c62a6
FF
1685 key = val = *bufp + pos;
1686 if ('\0' == *key)
1687 return(ROFF_IGN);
1688
1689 keysz = roff_getname(r, &val, ln, pos);
1690 if ('\\' == key[keysz])
1691 return(ROFF_IGN);
1692 key[keysz] = '\0';
80387638 1693
7888c61d
FF
1694 sign = *val;
1695 if ('+' == sign || '-' == sign)
1696 val++;
1697
070c62a6
FF
1698 if (roff_evalnum(val, NULL, &iv, 0))
1699 roff_setreg(r, key, iv, sign);
1700
1701 return(ROFF_IGN);
1702}
7888c61d 1703
070c62a6
FF
1704static enum rofferr
1705roff_rr(ROFF_ARGS)
1706{
1707 struct roffreg *reg, **prev;
1708 char *name, *cp;
1709 size_t namesz;
80387638 1710
070c62a6
FF
1711 name = cp = *bufp + pos;
1712 if ('\0' == *name)
1713 return(ROFF_IGN);
1714 namesz = roff_getname(r, &cp, ln, pos);
1715 name[namesz] = '\0';
1716
1717 prev = &r->regtab;
1718 while (1) {
1719 reg = *prev;
1720 if (NULL == reg || !strcmp(name, reg->key.p))
1721 break;
1722 prev = &reg->next;
1723 }
1724 if (NULL != reg) {
1725 *prev = reg->next;
1726 free(reg->key.p);
1727 free(reg);
1728 }
80387638
SW
1729 return(ROFF_IGN);
1730}
1731
60e1e752
SW
1732static enum rofferr
1733roff_rm(ROFF_ARGS)
1734{
1735 const char *name;
1736 char *cp;
070c62a6 1737 size_t namesz;
60e1e752
SW
1738
1739 cp = *bufp + pos;
1740 while ('\0' != *cp) {
070c62a6
FF
1741 name = cp;
1742 namesz = roff_getname(r, &cp, ln, (int)(cp - *bufp));
1743 roff_setstrn(&r->strtab, name, namesz, NULL, 0, 0);
1744 if ('\\' == name[namesz])
1745 break;
60e1e752
SW
1746 }
1747 return(ROFF_IGN);
1748}
1749
f88b6c16
FF
1750static enum rofferr
1751roff_it(ROFF_ARGS)
1752{
1753 char *cp;
1754 size_t len;
1755 int iv;
1756
1757 /* Parse the number of lines. */
1758 cp = *bufp + pos;
1759 len = strcspn(cp, " \t");
1760 cp[len] = '\0';
1761 if ((iv = mandoc_strntoi(cp, len, 10)) <= 0) {
070c62a6
FF
1762 mandoc_msg(MANDOCERR_IT_NONUM, r->parse,
1763 ln, ppos, *bufp + 1);
f88b6c16
FF
1764 return(ROFF_IGN);
1765 }
1766 cp += len + 1;
1767
1768 /* Arm the input line trap. */
1769 roffit_lines = iv;
1770 roffit_macro = mandoc_strdup(cp);
1771 return(ROFF_IGN);
1772}
1773
f88b6c16
FF
1774static enum rofferr
1775roff_Dd(ROFF_ARGS)
1776{
1777 const char *const *cp;
1778
070c62a6 1779 if (0 == ((MPARSE_MDOC | MPARSE_QUICK) & r->options))
f88b6c16
FF
1780 for (cp = __mdoc_reserved; *cp; cp++)
1781 roff_setstr(r, *cp, NULL, 0);
1782
1783 return(ROFF_CONT);
1784}
1785
f88b6c16
FF
1786static enum rofferr
1787roff_TH(ROFF_ARGS)
1788{
1789 const char *const *cp;
1790
070c62a6 1791 if (0 == (MPARSE_QUICK & r->options))
f88b6c16
FF
1792 for (cp = __man_reserved; *cp; cp++)
1793 roff_setstr(r, *cp, NULL, 0);
1794
1795 return(ROFF_CONT);
1796}
1797
80387638
SW
1798static enum rofferr
1799roff_TE(ROFF_ARGS)
1800{
1801
1802 if (NULL == r->tbl)
070c62a6
FF
1803 mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
1804 ln, ppos, "TE");
80387638 1805 else
36342e81 1806 tbl_end(&r->tbl);
80387638 1807
80387638
SW
1808 return(ROFF_IGN);
1809}
1810
80387638
SW
1811static enum rofferr
1812roff_T_(ROFF_ARGS)
1813{
1814
1815 if (NULL == r->tbl)
070c62a6
FF
1816 mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
1817 ln, ppos, "T&");
80387638
SW
1818 else
1819 tbl_restart(ppos, ln, r->tbl);
1820
1821 return(ROFF_IGN);
1822}
1823
36342e81
SW
1824#if 0
1825static int
1826roff_closeeqn(struct roff *r)
60e1e752 1827{
36342e81
SW
1828
1829 return(r->eqn && ROFF_EQN == eqn_end(&r->eqn) ? 1 : 0);
1830}
1831#endif
1832
1833static void
070c62a6 1834roff_openeqn(struct roff *r, const char *name, int line,
36342e81
SW
1835 int offs, const char *buf)
1836{
1837 struct eqn_node *e;
1838 int poff;
60e1e752
SW
1839
1840 assert(NULL == r->eqn);
36342e81 1841 e = eqn_alloc(name, offs, line, r->parse);
60e1e752
SW
1842
1843 if (r->last_eqn)
1844 r->last_eqn->next = e;
1845 else
1846 r->first_eqn = r->last_eqn = e;
1847
1848 r->eqn = r->last_eqn = e;
36342e81
SW
1849
1850 if (buf) {
1851 poff = 0;
1852 eqn_read(&r->eqn, line, buf, offs, &poff);
1853 }
1854}
1855
36342e81
SW
1856static enum rofferr
1857roff_EQ(ROFF_ARGS)
1858{
1859
1860 roff_openeqn(r, *bufp + pos, ln, ppos, NULL);
60e1e752
SW
1861 return(ROFF_IGN);
1862}
1863
60e1e752
SW
1864static enum rofferr
1865roff_EN(ROFF_ARGS)
1866{
1867
070c62a6 1868 mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, ln, ppos, "EN");
60e1e752
SW
1869 return(ROFF_IGN);
1870}
1871
80387638
SW
1872static enum rofferr
1873roff_TS(ROFF_ARGS)
1874{
f88b6c16 1875 struct tbl_node *tbl;
80387638
SW
1876
1877 if (r->tbl) {
070c62a6
FF
1878 mandoc_msg(MANDOCERR_BLK_BROKEN, r->parse,
1879 ln, ppos, "TS breaks TS");
36342e81 1880 tbl_end(&r->tbl);
80387638
SW
1881 }
1882
f88b6c16 1883 tbl = tbl_alloc(ppos, ln, r->parse);
80387638
SW
1884
1885 if (r->last_tbl)
f88b6c16 1886 r->last_tbl->next = tbl;
80387638 1887 else
f88b6c16
FF
1888 r->first_tbl = r->last_tbl = tbl;
1889
1890 r->tbl = r->last_tbl = tbl;
1891 return(ROFF_IGN);
1892}
1893
f88b6c16
FF
1894static enum rofferr
1895roff_cc(ROFF_ARGS)
1896{
1897 const char *p;
1898
1899 p = *bufp + pos;
1900
1901 if ('\0' == *p || '.' == (r->control = *p++))
1902 r->control = 0;
1903
1904 if ('\0' != *p)
1905 mandoc_msg(MANDOCERR_ARGCOUNT, r->parse, ln, ppos, NULL);
80387638 1906
80387638
SW
1907 return(ROFF_IGN);
1908}
1909
36342e81
SW
1910static enum rofferr
1911roff_tr(ROFF_ARGS)
1912{
1913 const char *p, *first, *second;
1914 size_t fsz, ssz;
1915 enum mandoc_esc esc;
1916
1917 p = *bufp + pos;
1918
1919 if ('\0' == *p) {
1920 mandoc_msg(MANDOCERR_ARGCOUNT, r->parse, ln, ppos, NULL);
1921 return(ROFF_IGN);
1922 }
1923
1924 while ('\0' != *p) {
1925 fsz = ssz = 1;
1926
1927 first = p++;
1928 if ('\\' == *first) {
1929 esc = mandoc_escape(&p, NULL, NULL);
1930 if (ESCAPE_ERROR == esc) {
070c62a6
FF
1931 mandoc_msg(MANDOCERR_ESC_BAD, r->parse,
1932 ln, (int)(p - *bufp), first);
36342e81
SW
1933 return(ROFF_IGN);
1934 }
1935 fsz = (size_t)(p - first);
1936 }
1937
1938 second = p++;
1939 if ('\\' == *second) {
1940 esc = mandoc_escape(&p, NULL, NULL);
1941 if (ESCAPE_ERROR == esc) {
070c62a6
FF
1942 mandoc_msg(MANDOCERR_ESC_BAD, r->parse,
1943 ln, (int)(p - *bufp), second);
36342e81
SW
1944 return(ROFF_IGN);
1945 }
1946 ssz = (size_t)(p - second);
1947 } else if ('\0' == *second) {
070c62a6
FF
1948 mandoc_msg(MANDOCERR_ARGCOUNT, r->parse,
1949 ln, (int)(p - *bufp), NULL);
36342e81
SW
1950 second = " ";
1951 p--;
1952 }
1953
1954 if (fsz > 1) {
070c62a6
FF
1955 roff_setstrn(&r->xmbtab, first, fsz,
1956 second, ssz, 0);
36342e81
SW
1957 continue;
1958 }
1959
1960 if (NULL == r->xtab)
070c62a6
FF
1961 r->xtab = mandoc_calloc(128,
1962 sizeof(struct roffstr));
36342e81
SW
1963
1964 free(r->xtab[(int)*first].p);
1965 r->xtab[(int)*first].p = mandoc_strndup(second, ssz);
1966 r->xtab[(int)*first].sz = ssz;
1967 }
1968
1969 return(ROFF_IGN);
1970}
1971
80387638
SW
1972static enum rofferr
1973roff_so(ROFF_ARGS)
1974{
1975 char *name;
1976
070c62a6
FF
1977 name = *bufp + pos;
1978 mandoc_vmsg(MANDOCERR_SO, r->parse, ln, ppos, "so %s", name);
80387638
SW
1979
1980 /*
1981 * Handle `so'. Be EXTREMELY careful, as we shouldn't be
1982 * opening anything that's not in our cwd or anything beneath
1983 * it. Thus, explicitly disallow traversing up the file-system
1984 * or using absolute paths.
1985 */
1986
80387638 1987 if ('/' == *name || strstr(name, "../") || strstr(name, "/..")) {
070c62a6
FF
1988 mandoc_vmsg(MANDOCERR_SO_PATH, r->parse, ln, ppos,
1989 ".so %s", name);
80387638
SW
1990 return(ROFF_ERR);
1991 }
1992
1993 *offs = pos;
1994 return(ROFF_SO);
1995}
1996
80387638
SW
1997static enum rofferr
1998roff_userdef(ROFF_ARGS)
1999{
2000 const char *arg[9];
2001 char *cp, *n1, *n2;
2002 int i;
2003
2004 /*
2005 * Collect pointers to macro argument strings
7888c61d 2006 * and NUL-terminate them.
80387638
SW
2007 */
2008 cp = *bufp + pos;
2009 for (i = 0; i < 9; i++)
2010 arg[i] = '\0' == *cp ? "" :
60e1e752 2011 mandoc_getarg(r->parse, &cp, ln, &pos);
80387638
SW
2012
2013 /*
2014 * Expand macro arguments.
2015 */
2016 *szp = 0;
2017 n1 = cp = mandoc_strdup(r->current_string);
2018 while (NULL != (cp = strstr(cp, "\\$"))) {
2019 i = cp[2] - '1';
2020 if (0 > i || 8 < i) {
2021 /* Not an argument invocation. */
2022 cp += 2;
2023 continue;
2024 }
070c62a6
FF
2025 *cp = '\0';
2026 *szp = mandoc_asprintf(&n2, "%s%s%s",
2027 n1, arg[i], cp + 3) + 1;
80387638
SW
2028 cp = n2 + (cp - n1);
2029 free(n1);
2030 n1 = n2;
2031 }
2032
2033 /*
2034 * Replace the macro invocation
2035 * by the expanded macro.
2036 */
2037 free(*bufp);
2038 *bufp = n1;
2039 if (0 == *szp)
2040 *szp = strlen(*bufp) + 1;
2041
2042 return(*szp > 1 && '\n' == (*bufp)[(int)*szp - 2] ?
2043 ROFF_REPARSE : ROFF_APPEND);
2044}
2045
070c62a6 2046static size_t
60e1e752
SW
2047roff_getname(struct roff *r, char **cpp, int ln, int pos)
2048{
2049 char *name, *cp;
070c62a6 2050 size_t namesz;
60e1e752
SW
2051
2052 name = *cpp;
2053 if ('\0' == *name)
070c62a6 2054 return(0);
60e1e752 2055
070c62a6
FF
2056 /* Read until end of name and terminate it with NUL. */
2057 for (cp = name; 1; cp++) {
2058 if ('\0' == *cp || ' ' == *cp) {
2059 namesz = cp - name;
2060 break;
2061 }
60e1e752
SW
2062 if ('\\' != *cp)
2063 continue;
070c62a6
FF
2064 namesz = cp - name;
2065 if ('{' == cp[1] || '}' == cp[1])
2066 break;
60e1e752
SW
2067 cp++;
2068 if ('\\' == *cp)
2069 continue;
070c62a6
FF
2070 mandoc_vmsg(MANDOCERR_NAMESC, r->parse, ln, pos,
2071 "%.*s", (int)(cp - name + 1), name);
2072 mandoc_escape((const char **)&cp, NULL, NULL);
2073 break;
60e1e752
SW
2074 }
2075
60e1e752
SW
2076 /* Read past spaces. */
2077 while (' ' == *cp)
2078 cp++;
2079
2080 *cpp = cp;
070c62a6 2081 return(namesz);
60e1e752
SW
2082}
2083
80387638
SW
2084/*
2085 * Store *string into the user-defined string called *name.
80387638 2086 * To clear an existing entry, call with (*r, *name, NULL, 0).
070c62a6
FF
2087 * append == 0: replace mode
2088 * append == 1: single-line append mode
2089 * append == 2: multiline append mode, append '\n' after each call
80387638
SW
2090 */
2091static void
2092roff_setstr(struct roff *r, const char *name, const char *string,
070c62a6 2093 int append)
80387638 2094{
36342e81
SW
2095
2096 roff_setstrn(&r->strtab, name, strlen(name), string,
070c62a6 2097 string ? strlen(string) : 0, append);
36342e81
SW
2098}
2099
2100static void
2101roff_setstrn(struct roffkv **r, const char *name, size_t namesz,
070c62a6 2102 const char *string, size_t stringsz, int append)
36342e81
SW
2103{
2104 struct roffkv *n;
2105 char *c;
2106 int i;
2107 size_t oldch, newch;
80387638
SW
2108
2109 /* Search for an existing string with the same name. */
36342e81
SW
2110 n = *r;
2111
070c62a6
FF
2112 while (n && (namesz != n->key.sz ||
2113 strncmp(n->key.p, name, namesz)))
80387638
SW
2114 n = n->next;
2115
2116 if (NULL == n) {
2117 /* Create a new string table entry. */
36342e81
SW
2118 n = mandoc_malloc(sizeof(struct roffkv));
2119 n->key.p = mandoc_strndup(name, namesz);
2120 n->key.sz = namesz;
2121 n->val.p = NULL;
2122 n->val.sz = 0;
2123 n->next = *r;
2124 *r = n;
070c62a6 2125 } else if (0 == append) {
36342e81
SW
2126 free(n->val.p);
2127 n->val.p = NULL;
2128 n->val.sz = 0;
80387638
SW
2129 }
2130
2131 if (NULL == string)
2132 return;
2133
2134 /*
2135 * One additional byte for the '\n' in multiline mode,
2136 * and one for the terminating '\0'.
2137 */
070c62a6 2138 newch = stringsz + (1 < append ? 2u : 1u);
36342e81
SW
2139
2140 if (NULL == n->val.p) {
2141 n->val.p = mandoc_malloc(newch);
2142 *n->val.p = '\0';
80387638
SW
2143 oldch = 0;
2144 } else {
36342e81
SW
2145 oldch = n->val.sz;
2146 n->val.p = mandoc_realloc(n->val.p, oldch + newch);
80387638
SW
2147 }
2148
2149 /* Skip existing content in the destination buffer. */
36342e81 2150 c = n->val.p + (int)oldch;
80387638
SW
2151
2152 /* Append new content to the destination buffer. */
36342e81
SW
2153 i = 0;
2154 while (i < (int)stringsz) {
80387638
SW
2155 /*
2156 * Rudimentary roff copy mode:
2157 * Handle escaped backslashes.
2158 */
36342e81
SW
2159 if ('\\' == string[i] && '\\' == string[i + 1])
2160 i++;
2161 *c++ = string[i++];
80387638
SW
2162 }
2163
2164 /* Append terminating bytes. */
070c62a6 2165 if (1 < append)
80387638 2166 *c++ = '\n';
36342e81 2167
80387638 2168 *c = '\0';
36342e81 2169 n->val.sz = (int)(c - n->val.p);
80387638
SW
2170}
2171
80387638
SW
2172static const char *
2173roff_getstrn(const struct roff *r, const char *name, size_t len)
2174{
36342e81 2175 const struct roffkv *n;
070c62a6 2176 int i;
80387638 2177
36342e81 2178 for (n = r->strtab; n; n = n->next)
070c62a6
FF
2179 if (0 == strncmp(name, n->key.p, len) &&
2180 '\0' == n->key.p[(int)len])
36342e81 2181 return(n->val.p);
80387638 2182
070c62a6
FF
2183 for (i = 0; i < PREDEFS_MAX; i++)
2184 if (0 == strncmp(name, predefs[i].name, len) &&
2185 '\0' == predefs[i].name[(int)len])
2186 return(predefs[i].str);
2187
36342e81 2188 return(NULL);
80387638
SW
2189}
2190
80387638 2191static void
36342e81 2192roff_freestr(struct roffkv *r)
80387638 2193{
36342e81 2194 struct roffkv *n, *nn;
80387638 2195
36342e81
SW
2196 for (n = r; n; n = nn) {
2197 free(n->key.p);
2198 free(n->val.p);
80387638
SW
2199 nn = n->next;
2200 free(n);
2201 }
80387638
SW
2202}
2203
2204const struct tbl_span *
2205roff_span(const struct roff *r)
2206{
070c62a6 2207
80387638
SW
2208 return(r->tbl ? tbl_span(r->tbl) : NULL);
2209}
60e1e752
SW
2210
2211const struct eqn *
2212roff_eqn(const struct roff *r)
2213{
070c62a6 2214
60e1e752
SW
2215 return(r->last_eqn ? &r->last_eqn->eqn : NULL);
2216}
36342e81
SW
2217
2218/*
2219 * Duplicate an input string, making the appropriate character
2220 * conversations (as stipulated by `tr') along the way.
2221 * Returns a heap-allocated string with all the replacements made.
2222 */
2223char *
2224roff_strdup(const struct roff *r, const char *p)
2225{
2226 const struct roffkv *cp;
2227 char *res;
2228 const char *pp;
2229 size_t ssz, sz;
2230 enum mandoc_esc esc;
2231
2232 if (NULL == r->xmbtab && NULL == r->xtab)
2233 return(mandoc_strdup(p));
2234 else if ('\0' == *p)
2235 return(mandoc_strdup(""));
2236
2237 /*
2238 * Step through each character looking for term matches
2239 * (remember that a `tr' can be invoked with an escape, which is
2240 * a glyph but the escape is multi-character).
2241 * We only do this if the character hash has been initialised
2242 * and the string is >0 length.
2243 */
2244
2245 res = NULL;
2246 ssz = 0;
2247
2248 while ('\0' != *p) {
2249 if ('\\' != *p && r->xtab && r->xtab[(int)*p].p) {
2250 sz = r->xtab[(int)*p].sz;
2251 res = mandoc_realloc(res, ssz + sz + 1);
2252 memcpy(res + ssz, r->xtab[(int)*p].p, sz);
2253 ssz += sz;
2254 p++;
2255 continue;
2256 } else if ('\\' != *p) {
2257 res = mandoc_realloc(res, ssz + 2);
2258 res[ssz++] = *p++;
2259 continue;
2260 }
2261
2262 /* Search for term matches. */
2263 for (cp = r->xmbtab; cp; cp = cp->next)
2264 if (0 == strncmp(p, cp->key.p, cp->key.sz))
2265 break;
2266
2267 if (NULL != cp) {
2268 /*
2269 * A match has been found.
2270 * Append the match to the array and move
2271 * forward by its keysize.
2272 */
070c62a6
FF
2273 res = mandoc_realloc(res,
2274 ssz + cp->val.sz + 1);
36342e81
SW
2275 memcpy(res + ssz, cp->val.p, cp->val.sz);
2276 ssz += cp->val.sz;
2277 p += (int)cp->key.sz;
2278 continue;
2279 }
2280
2281 /*
2282 * Handle escapes carefully: we need to copy
2283 * over just the escape itself, or else we might
2284 * do replacements within the escape itself.
2285 * Make sure to pass along the bogus string.
2286 */
2287 pp = p++;
2288 esc = mandoc_escape(&p, NULL, NULL);
2289 if (ESCAPE_ERROR == esc) {
2290 sz = strlen(pp);
2291 res = mandoc_realloc(res, ssz + sz + 1);
2292 memcpy(res + ssz, pp, sz);
2293 break;
2294 }
070c62a6
FF
2295 /*
2296 * We bail out on bad escapes.
36342e81
SW
2297 * No need to warn: we already did so when
2298 * roff_res() was called.
2299 */
2300 sz = (int)(p - pp);
2301 res = mandoc_realloc(res, ssz + sz + 1);
2302 memcpy(res + ssz, pp, sz);
2303 ssz += sz;
2304 }
2305
2306 res[(int)ssz] = '\0';
2307 return(res);
2308}
f88b6c16
FF
2309
2310/*
070c62a6 2311 * Find out whether a line is a macro line or not.
f88b6c16
FF
2312 * If it is, adjust the current position and return one; if it isn't,
2313 * return zero and don't change the current position.
2314 * If the control character has been set with `.cc', then let that grain
2315 * precedence.
2316 * This is slighly contrary to groff, where using the non-breaking
2317 * control character when `cc' has been invoked will cause the
2318 * non-breaking macro contents to be printed verbatim.
2319 */
2320int
2321roff_getcontrol(const struct roff *r, const char *cp, int *ppos)
2322{
2323 int pos;
2324
2325 pos = *ppos;
2326
2327 if (0 != r->control && cp[pos] == r->control)
2328 pos++;
2329 else if (0 != r->control)
2330 return(0);
2331 else if ('\\' == cp[pos] && '.' == cp[pos + 1])
2332 pos += 2;
2333 else if ('.' == cp[pos] || '\'' == cp[pos])
2334 pos++;
2335 else
2336 return(0);
2337
2338 while (' ' == cp[pos] || '\t' == cp[pos])
2339 pos++;
2340
2341 *ppos = pos;
2342 return(1);
2343}