1*4d131170SRobert Mustacchi /* $Id: roff.c,v 1.378 2021/08/10 12:55:04 schwarze Exp $ */
295c635efSGarrett D'Amore /*
3*4d131170SRobert Mustacchi * Copyright (c) 2010-2015, 2017-2020 Ingo Schwarze <schwarze@openbsd.org>
4371584c2SYuri Pankov * Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
595c635efSGarrett D'Amore *
695c635efSGarrett D'Amore * Permission to use, copy, modify, and distribute this software for any
795c635efSGarrett D'Amore * purpose with or without fee is hereby granted, provided that the above
895c635efSGarrett D'Amore * copyright notice and this permission notice appear in all copies.
995c635efSGarrett D'Amore *
1095c635efSGarrett D'Amore * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
1195c635efSGarrett D'Amore * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1295c635efSGarrett D'Amore * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
1395c635efSGarrett D'Amore * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1495c635efSGarrett D'Amore * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1595c635efSGarrett D'Amore * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1695c635efSGarrett D'Amore * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*4d131170SRobert Mustacchi *
18*4d131170SRobert Mustacchi * Implementation of the roff(7) parser for mandoc(1).
1995c635efSGarrett D'Amore */
2095c635efSGarrett D'Amore #include "config.h"
21260e9a87SYuri Pankov
22260e9a87SYuri Pankov #include <sys/types.h>
2395c635efSGarrett D'Amore
2495c635efSGarrett D'Amore #include <assert.h>
2595c635efSGarrett D'Amore #include <ctype.h>
26260e9a87SYuri Pankov #include <limits.h>
27c66b8046SYuri Pankov #include <stddef.h>
28c66b8046SYuri Pankov #include <stdint.h>
29698f87a4SGarrett D'Amore #include <stdio.h>
3095c635efSGarrett D'Amore #include <stdlib.h>
3195c635efSGarrett D'Amore #include <string.h>
3295c635efSGarrett D'Amore
33260e9a87SYuri Pankov #include "mandoc_aux.h"
34c66b8046SYuri Pankov #include "mandoc_ohash.h"
35cec8643bSMichal Nowak #include "mandoc.h"
36371584c2SYuri Pankov #include "roff.h"
37cec8643bSMichal Nowak #include "mandoc_parse.h"
3895c635efSGarrett D'Amore #include "libmandoc.h"
39371584c2SYuri Pankov #include "roff_int.h"
40cec8643bSMichal Nowak #include "tbl_parse.h"
41cec8643bSMichal Nowak #include "eqn_parse.h"
42cec8643bSMichal Nowak
43cec8643bSMichal Nowak /*
44cec8643bSMichal Nowak * ASCII_ESC is used to signal from roff_getarg() to roff_expand()
45cec8643bSMichal Nowak * that an escape sequence resulted from copy-in processing and
46cec8643bSMichal Nowak * needs to be checked or interpolated. As it is used nowhere
47cec8643bSMichal Nowak * else, it is defined here rather than in a header file.
48cec8643bSMichal Nowak */
49cec8643bSMichal Nowak #define ASCII_ESC 27
5095c635efSGarrett D'Amore
5195c635efSGarrett D'Amore /* Maximum number of string expansions per line, to break infinite loops. */
5295c635efSGarrett D'Amore #define EXPAND_LIMIT 1000
5395c635efSGarrett D'Amore
54c66b8046SYuri Pankov /* Types of definitions of macros and strings. */
55c66b8046SYuri Pankov #define ROFFDEF_USER (1 << 1) /* User-defined. */
56c66b8046SYuri Pankov #define ROFFDEF_PRE (1 << 2) /* Predefined. */
57c66b8046SYuri Pankov #define ROFFDEF_REN (1 << 3) /* Renamed standard macro. */
58c66b8046SYuri Pankov #define ROFFDEF_STD (1 << 4) /* mdoc(7) or man(7) macro. */
59c66b8046SYuri Pankov #define ROFFDEF_ANY (ROFFDEF_USER | ROFFDEF_PRE | \
60c66b8046SYuri Pankov ROFFDEF_REN | ROFFDEF_STD)
616640c13bSYuri Pankov #define ROFFDEF_UNDEF (1 << 5) /* Completely undefined. */
62371584c2SYuri Pankov
63c66b8046SYuri Pankov /* --- data types --------------------------------------------------------- */
6495c635efSGarrett D'Amore
6595c635efSGarrett D'Amore /*
6695c635efSGarrett D'Amore * An incredibly-simple string buffer.
6795c635efSGarrett D'Amore */
6895c635efSGarrett D'Amore struct roffstr {
6995c635efSGarrett D'Amore char *p; /* nil-terminated buffer */
7095c635efSGarrett D'Amore size_t sz; /* saved strlen(p) */
7195c635efSGarrett D'Amore };
7295c635efSGarrett D'Amore
7395c635efSGarrett D'Amore /*
7495c635efSGarrett D'Amore * A key-value roffstr pair as part of a singly-linked list.
7595c635efSGarrett D'Amore */
7695c635efSGarrett D'Amore struct roffkv {
7795c635efSGarrett D'Amore struct roffstr key;
7895c635efSGarrett D'Amore struct roffstr val;
7995c635efSGarrett D'Amore struct roffkv *next; /* next in list */
8095c635efSGarrett D'Amore };
8195c635efSGarrett D'Amore
82698f87a4SGarrett D'Amore /*
83698f87a4SGarrett D'Amore * A single number register as part of a singly-linked list.
84698f87a4SGarrett D'Amore */
85698f87a4SGarrett D'Amore struct roffreg {
86698f87a4SGarrett D'Amore struct roffstr key;
87698f87a4SGarrett D'Amore int val;
886640c13bSYuri Pankov int step;
89698f87a4SGarrett D'Amore struct roffreg *next;
90698f87a4SGarrett D'Amore };
91698f87a4SGarrett D'Amore
92c66b8046SYuri Pankov /*
93c66b8046SYuri Pankov * Association of request and macro names with token IDs.
94c66b8046SYuri Pankov */
95c66b8046SYuri Pankov struct roffreq {
96c66b8046SYuri Pankov enum roff_tok tok;
97c66b8046SYuri Pankov char name[];
98c66b8046SYuri Pankov };
99c66b8046SYuri Pankov
100cec8643bSMichal Nowak /*
101cec8643bSMichal Nowak * A macro processing context.
102cec8643bSMichal Nowak * More than one is needed when macro calls are nested.
103cec8643bSMichal Nowak */
104cec8643bSMichal Nowak struct mctx {
105cec8643bSMichal Nowak char **argv;
106cec8643bSMichal Nowak int argc;
107cec8643bSMichal Nowak int argsz;
108cec8643bSMichal Nowak };
109cec8643bSMichal Nowak
11095c635efSGarrett D'Amore struct roff {
111c66b8046SYuri Pankov struct roff_man *man; /* mdoc or man parser */
11295c635efSGarrett D'Amore struct roffnode *last; /* leaf of stack */
113cec8643bSMichal Nowak struct mctx *mstack; /* stack of macro contexts */
114260e9a87SYuri Pankov int *rstack; /* stack of inverted `ie' values */
115c66b8046SYuri Pankov struct ohash *reqtab; /* request lookup table */
116698f87a4SGarrett D'Amore struct roffreg *regtab; /* number registers */
11795c635efSGarrett D'Amore struct roffkv *strtab; /* user-defined strings & macros */
118c66b8046SYuri Pankov struct roffkv *rentab; /* renamed strings & macros */
11995c635efSGarrett D'Amore struct roffkv *xmbtab; /* multi-byte trans table (`tr') */
12095c635efSGarrett D'Amore struct roffstr *xtab; /* single-byte trans table (`tr') */
12195c635efSGarrett D'Amore const char *current_string; /* value of last called user macro */
12295c635efSGarrett D'Amore struct tbl_node *first_tbl; /* first table parsed */
12395c635efSGarrett D'Amore struct tbl_node *last_tbl; /* last table parsed */
12495c635efSGarrett D'Amore struct tbl_node *tbl; /* current table being parsed */
125c66b8046SYuri Pankov struct eqn_node *last_eqn; /* equation parser */
126c66b8046SYuri Pankov struct eqn_node *eqn; /* active equation parser */
127260e9a87SYuri Pankov int eqn_inline; /* current equation is inline */
128260e9a87SYuri Pankov int options; /* parse options */
129cec8643bSMichal Nowak int mstacksz; /* current size of mstack */
130cec8643bSMichal Nowak int mstackpos; /* position in mstack */
131260e9a87SYuri Pankov int rstacksz; /* current size limit of rstack */
132260e9a87SYuri Pankov int rstackpos; /* position in rstack */
133260e9a87SYuri Pankov int format; /* current file in mdoc or man format */
134260e9a87SYuri Pankov char control; /* control character */
135c66b8046SYuri Pankov char escape; /* escape character */
13695c635efSGarrett D'Amore };
13795c635efSGarrett D'Amore
138*4d131170SRobert Mustacchi /*
139*4d131170SRobert Mustacchi * A macro definition, condition, or ignored block.
140*4d131170SRobert Mustacchi */
14195c635efSGarrett D'Amore struct roffnode {
142c66b8046SYuri Pankov enum roff_tok tok; /* type of node */
14395c635efSGarrett D'Amore struct roffnode *parent; /* up one in stack */
14495c635efSGarrett D'Amore int line; /* parse line */
14595c635efSGarrett D'Amore int col; /* parse col */
14695c635efSGarrett D'Amore char *name; /* node name, e.g. macro name */
147*4d131170SRobert Mustacchi char *end; /* custom end macro of the block */
148*4d131170SRobert Mustacchi int endspan; /* scope to: 1=eol 2=next line -1=\} */
149*4d131170SRobert Mustacchi int rule; /* content is: 1=evaluated 0=skipped */
15095c635efSGarrett D'Amore };
15195c635efSGarrett D'Amore
15295c635efSGarrett D'Amore #define ROFF_ARGS struct roff *r, /* parse ctx */ \
153c66b8046SYuri Pankov enum roff_tok tok, /* tok of macro */ \
154260e9a87SYuri Pankov struct buf *buf, /* input buffer */ \
15595c635efSGarrett D'Amore int ln, /* parse line */ \
15695c635efSGarrett D'Amore int ppos, /* original pos in buffer */ \
15795c635efSGarrett D'Amore int pos, /* current pos in buffer */ \
15895c635efSGarrett D'Amore int *offs /* reset offset of buffer data */
15995c635efSGarrett D'Amore
160cec8643bSMichal Nowak typedef int (*roffproc)(ROFF_ARGS);
16195c635efSGarrett D'Amore
16295c635efSGarrett D'Amore struct roffmac {
16395c635efSGarrett D'Amore roffproc proc; /* process new macro */
16495c635efSGarrett D'Amore roffproc text; /* process as child text of macro */
16595c635efSGarrett D'Amore roffproc sub; /* process as child of macro */
16695c635efSGarrett D'Amore int flags;
16795c635efSGarrett D'Amore #define ROFFMAC_STRUCT (1 << 0) /* always interpret */
16895c635efSGarrett D'Amore };
16995c635efSGarrett D'Amore
17095c635efSGarrett D'Amore struct predef {
17195c635efSGarrett D'Amore const char *name; /* predefined input name */
17295c635efSGarrett D'Amore const char *str; /* replacement symbol */
17395c635efSGarrett D'Amore };
17495c635efSGarrett D'Amore
17595c635efSGarrett D'Amore #define PREDEF(__name, __str) \
17695c635efSGarrett D'Amore { (__name), (__str) },
17795c635efSGarrett D'Amore
178371584c2SYuri Pankov /* --- function prototypes ------------------------------------------------ */
179371584c2SYuri Pankov
180cec8643bSMichal Nowak static int roffnode_cleanscope(struct roff *);
181cec8643bSMichal Nowak static int roffnode_pop(struct roff *);
182c66b8046SYuri Pankov static void roffnode_push(struct roff *, enum roff_tok,
18395c635efSGarrett D'Amore const char *, int, int);
184cec8643bSMichal Nowak static void roff_addtbl(struct roff_man *, int, struct tbl_node *);
185cec8643bSMichal Nowak static int roff_als(ROFF_ARGS);
186cec8643bSMichal Nowak static int roff_block(ROFF_ARGS);
187cec8643bSMichal Nowak static int roff_block_text(ROFF_ARGS);
188cec8643bSMichal Nowak static int roff_block_sub(ROFF_ARGS);
189*4d131170SRobert Mustacchi static int roff_break(ROFF_ARGS);
190cec8643bSMichal Nowak static int roff_cblock(ROFF_ARGS);
191cec8643bSMichal Nowak static int roff_cc(ROFF_ARGS);
192cec8643bSMichal Nowak static int roff_ccond(struct roff *, int, int);
193cec8643bSMichal Nowak static int roff_char(ROFF_ARGS);
194cec8643bSMichal Nowak static int roff_cond(ROFF_ARGS);
195*4d131170SRobert Mustacchi static int roff_cond_checkend(ROFF_ARGS);
196cec8643bSMichal Nowak static int roff_cond_text(ROFF_ARGS);
197cec8643bSMichal Nowak static int roff_cond_sub(ROFF_ARGS);
198cec8643bSMichal Nowak static int roff_ds(ROFF_ARGS);
199cec8643bSMichal Nowak static int roff_ec(ROFF_ARGS);
200cec8643bSMichal Nowak static int roff_eo(ROFF_ARGS);
201cec8643bSMichal Nowak static int roff_eqndelim(struct roff *, struct buf *, int);
202*4d131170SRobert Mustacchi static int roff_evalcond(struct roff *, int, char *, int *);
203260e9a87SYuri Pankov static int roff_evalnum(struct roff *, int,
204260e9a87SYuri Pankov const char *, int *, int *, int);
205260e9a87SYuri Pankov static int roff_evalpar(struct roff *, int,
206260e9a87SYuri Pankov const char *, int *, int *, int);
207260e9a87SYuri Pankov static int roff_evalstrcond(const char *, int *);
208cec8643bSMichal Nowak static int roff_expand(struct roff *, struct buf *,
209cec8643bSMichal Nowak int, int, char);
21095c635efSGarrett D'Amore static void roff_free1(struct roff *);
211698f87a4SGarrett D'Amore static void roff_freereg(struct roffreg *);
21295c635efSGarrett D'Amore static void roff_freestr(struct roffkv *);
213260e9a87SYuri Pankov static size_t roff_getname(struct roff *, char **, int, int);
214260e9a87SYuri Pankov static int roff_getnum(const char *, int *, int *, int);
215698f87a4SGarrett D'Amore static int roff_getop(const char *, int *, char *);
2166640c13bSYuri Pankov static int roff_getregn(struct roff *,
2176640c13bSYuri Pankov const char *, size_t, char);
218371584c2SYuri Pankov static int roff_getregro(const struct roff *,
219371584c2SYuri Pankov const char *name);
2206640c13bSYuri Pankov static const char *roff_getstrn(struct roff *,
221c66b8046SYuri Pankov const char *, size_t, int *);
222371584c2SYuri Pankov static int roff_hasregn(const struct roff *,
223371584c2SYuri Pankov const char *, size_t);
224cec8643bSMichal Nowak static int roff_insec(ROFF_ARGS);
225cec8643bSMichal Nowak static int roff_it(ROFF_ARGS);
226cec8643bSMichal Nowak static int roff_line_ignore(ROFF_ARGS);
227371584c2SYuri Pankov static void roff_man_alloc1(struct roff_man *);
228371584c2SYuri Pankov static void roff_man_free1(struct roff_man *);
229cec8643bSMichal Nowak static int roff_manyarg(ROFF_ARGS);
230cec8643bSMichal Nowak static int roff_noarg(ROFF_ARGS);
231cec8643bSMichal Nowak static int roff_nop(ROFF_ARGS);
232cec8643bSMichal Nowak static int roff_nr(ROFF_ARGS);
233cec8643bSMichal Nowak static int roff_onearg(ROFF_ARGS);
234c66b8046SYuri Pankov static enum roff_tok roff_parse(struct roff *, char *, int *,
235260e9a87SYuri Pankov int, int);
236cec8643bSMichal Nowak static int roff_parsetext(struct roff *, struct buf *,
237c66b8046SYuri Pankov int, int *);
238cec8643bSMichal Nowak static int roff_renamed(ROFF_ARGS);
239cec8643bSMichal Nowak static int roff_return(ROFF_ARGS);
240cec8643bSMichal Nowak static int roff_rm(ROFF_ARGS);
241cec8643bSMichal Nowak static int roff_rn(ROFF_ARGS);
242cec8643bSMichal Nowak static int roff_rr(ROFF_ARGS);
2436640c13bSYuri Pankov static void roff_setregn(struct roff *, const char *,
2446640c13bSYuri Pankov size_t, int, char, int);
24595c635efSGarrett D'Amore static void roff_setstr(struct roff *,
24695c635efSGarrett D'Amore const char *, const char *, int);
24795c635efSGarrett D'Amore static void roff_setstrn(struct roffkv **, const char *,
24895c635efSGarrett D'Amore size_t, const char *, size_t, int);
249cec8643bSMichal Nowak static int roff_shift(ROFF_ARGS);
250cec8643bSMichal Nowak static int roff_so(ROFF_ARGS);
251cec8643bSMichal Nowak static int roff_tr(ROFF_ARGS);
252cec8643bSMichal Nowak static int roff_Dd(ROFF_ARGS);
253cec8643bSMichal Nowak static int roff_TE(ROFF_ARGS);
254cec8643bSMichal Nowak static int roff_TS(ROFF_ARGS);
255cec8643bSMichal Nowak static int roff_EQ(ROFF_ARGS);
256cec8643bSMichal Nowak static int roff_EN(ROFF_ARGS);
257cec8643bSMichal Nowak static int roff_T_(ROFF_ARGS);
258cec8643bSMichal Nowak static int roff_unsupp(ROFF_ARGS);
259cec8643bSMichal Nowak static int roff_userdef(ROFF_ARGS);
26095c635efSGarrett D'Amore
261371584c2SYuri Pankov /* --- constant data ------------------------------------------------------ */
262371584c2SYuri Pankov
263260e9a87SYuri Pankov #define ROFFNUM_SCALE (1 << 0) /* Honour scaling in roff_getnum(). */
264260e9a87SYuri Pankov #define ROFFNUM_WHITE (1 << 1) /* Skip whitespace in roff_evalnum(). */
265260e9a87SYuri Pankov
266c66b8046SYuri Pankov const char *__roff_name[MAN_MAX + 1] = {
267cec8643bSMichal Nowak "br", "ce", "fi", "ft",
268cec8643bSMichal Nowak "ll", "mc", "nf",
269cec8643bSMichal Nowak "po", "rj", "sp",
270c66b8046SYuri Pankov "ta", "ti", NULL,
271c66b8046SYuri Pankov "ab", "ad", "af", "aln",
272c66b8046SYuri Pankov "als", "am", "am1", "ami",
273c66b8046SYuri Pankov "ami1", "as", "as1", "asciify",
274c66b8046SYuri Pankov "backtrace", "bd", "bleedat", "blm",
275c66b8046SYuri Pankov "box", "boxa", "bp", "BP",
276c66b8046SYuri Pankov "break", "breakchar", "brnl", "brp",
277c66b8046SYuri Pankov "brpnl", "c2", "cc",
278c66b8046SYuri Pankov "cf", "cflags", "ch", "char",
279c66b8046SYuri Pankov "chop", "class", "close", "CL",
280c66b8046SYuri Pankov "color", "composite", "continue", "cp",
281c66b8046SYuri Pankov "cropat", "cs", "cu", "da",
282c66b8046SYuri Pankov "dch", "Dd", "de", "de1",
283c66b8046SYuri Pankov "defcolor", "dei", "dei1", "device",
284c66b8046SYuri Pankov "devicem", "di", "do", "ds",
285c66b8046SYuri Pankov "ds1", "dwh", "dt", "ec",
286c66b8046SYuri Pankov "ecr", "ecs", "el", "em",
287c66b8046SYuri Pankov "EN", "eo", "EP", "EQ",
288c66b8046SYuri Pankov "errprint", "ev", "evc", "ex",
289c66b8046SYuri Pankov "fallback", "fam", "fc", "fchar",
290c66b8046SYuri Pankov "fcolor", "fdeferlig", "feature", "fkern",
291c66b8046SYuri Pankov "fl", "flig", "fp", "fps",
292c66b8046SYuri Pankov "fschar", "fspacewidth", "fspecial", "ftr",
293c66b8046SYuri Pankov "fzoom", "gcolor", "hc", "hcode",
294c66b8046SYuri Pankov "hidechar", "hla", "hlm", "hpf",
295c66b8046SYuri Pankov "hpfa", "hpfcode", "hw", "hy",
296c66b8046SYuri Pankov "hylang", "hylen", "hym", "hypp",
297c66b8046SYuri Pankov "hys", "ie", "if", "ig",
298c66b8046SYuri Pankov "index", "it", "itc", "IX",
299c66b8046SYuri Pankov "kern", "kernafter", "kernbefore", "kernpair",
300c66b8046SYuri Pankov "lc", "lc_ctype", "lds", "length",
301c66b8046SYuri Pankov "letadj", "lf", "lg", "lhang",
302c66b8046SYuri Pankov "linetabs", "lnr", "lnrf", "lpfx",
303c66b8046SYuri Pankov "ls", "lsm", "lt",
304c66b8046SYuri Pankov "mediasize", "minss", "mk", "mso",
305c66b8046SYuri Pankov "na", "ne", "nh", "nhychar",
306c66b8046SYuri Pankov "nm", "nn", "nop", "nr",
307c66b8046SYuri Pankov "nrf", "nroff", "ns", "nx",
308c66b8046SYuri Pankov "open", "opena", "os", "output",
309c66b8046SYuri Pankov "padj", "papersize", "pc", "pev",
310c66b8046SYuri Pankov "pi", "PI", "pl", "pm",
311c66b8046SYuri Pankov "pn", "pnr", "ps",
312c66b8046SYuri Pankov "psbb", "pshape", "pso", "ptr",
313c66b8046SYuri Pankov "pvs", "rchar", "rd", "recursionlimit",
314c66b8046SYuri Pankov "return", "rfschar", "rhang",
315c66b8046SYuri Pankov "rm", "rn", "rnn", "rr",
316c66b8046SYuri Pankov "rs", "rt", "schar", "sentchar",
317c66b8046SYuri Pankov "shc", "shift", "sizes", "so",
318c66b8046SYuri Pankov "spacewidth", "special", "spreadwarn", "ss",
319c66b8046SYuri Pankov "sty", "substring", "sv", "sy",
320c66b8046SYuri Pankov "T&", "tc", "TE",
321c66b8046SYuri Pankov "TH", "tkf", "tl",
322c66b8046SYuri Pankov "tm", "tm1", "tmc", "tr",
323c66b8046SYuri Pankov "track", "transchar", "trf", "trimat",
324c66b8046SYuri Pankov "trin", "trnt", "troff", "TS",
325c66b8046SYuri Pankov "uf", "ul", "unformat", "unwatch",
326c66b8046SYuri Pankov "unwatchn", "vpt", "vs", "warn",
327c66b8046SYuri Pankov "warnscale", "watch", "watchlength", "watchn",
328c66b8046SYuri Pankov "wh", "while", "write", "writec",
329c66b8046SYuri Pankov "writem", "xflag", ".", NULL,
330c66b8046SYuri Pankov NULL, "text",
331c66b8046SYuri Pankov "Dd", "Dt", "Os", "Sh",
332c66b8046SYuri Pankov "Ss", "Pp", "D1", "Dl",
333c66b8046SYuri Pankov "Bd", "Ed", "Bl", "El",
334c66b8046SYuri Pankov "It", "Ad", "An", "Ap",
335c66b8046SYuri Pankov "Ar", "Cd", "Cm", "Dv",
336c66b8046SYuri Pankov "Er", "Ev", "Ex", "Fa",
337c66b8046SYuri Pankov "Fd", "Fl", "Fn", "Ft",
338c66b8046SYuri Pankov "Ic", "In", "Li", "Nd",
339c66b8046SYuri Pankov "Nm", "Op", "Ot", "Pa",
340c66b8046SYuri Pankov "Rv", "St", "Va", "Vt",
341c66b8046SYuri Pankov "Xr", "%A", "%B", "%D",
342c66b8046SYuri Pankov "%I", "%J", "%N", "%O",
343c66b8046SYuri Pankov "%P", "%R", "%T", "%V",
344c66b8046SYuri Pankov "Ac", "Ao", "Aq", "At",
345c66b8046SYuri Pankov "Bc", "Bf", "Bo", "Bq",
346c66b8046SYuri Pankov "Bsx", "Bx", "Db", "Dc",
347c66b8046SYuri Pankov "Do", "Dq", "Ec", "Ef",
348c66b8046SYuri Pankov "Em", "Eo", "Fx", "Ms",
349c66b8046SYuri Pankov "No", "Ns", "Nx", "Ox",
350c66b8046SYuri Pankov "Pc", "Pf", "Po", "Pq",
351c66b8046SYuri Pankov "Qc", "Ql", "Qo", "Qq",
352c66b8046SYuri Pankov "Re", "Rs", "Sc", "So",
353c66b8046SYuri Pankov "Sq", "Sm", "Sx", "Sy",
354c66b8046SYuri Pankov "Tn", "Ux", "Xc", "Xo",
355c66b8046SYuri Pankov "Fo", "Fc", "Oo", "Oc",
356c66b8046SYuri Pankov "Bk", "Ek", "Bt", "Hf",
357c66b8046SYuri Pankov "Fr", "Ud", "Lb", "Lp",
358c66b8046SYuri Pankov "Lk", "Mt", "Brq", "Bro",
359c66b8046SYuri Pankov "Brc", "%C", "Es", "En",
360c66b8046SYuri Pankov "Dx", "%Q", "%U", "Ta",
361*4d131170SRobert Mustacchi "Tg", NULL,
362c66b8046SYuri Pankov "TH", "SH", "SS", "TP",
363cec8643bSMichal Nowak "TQ",
364c66b8046SYuri Pankov "LP", "PP", "P", "IP",
365c66b8046SYuri Pankov "HP", "SM", "SB", "BI",
366c66b8046SYuri Pankov "IB", "BR", "RB", "R",
367c66b8046SYuri Pankov "B", "I", "IR", "RI",
368c66b8046SYuri Pankov "RE", "RS", "DT", "UC",
369c66b8046SYuri Pankov "PD", "AT", "in",
370cec8643bSMichal Nowak "SY", "YS", "OP",
371cec8643bSMichal Nowak "EX", "EE", "UR",
372c66b8046SYuri Pankov "UE", "MT", "ME", NULL
37395c635efSGarrett D'Amore };
374c66b8046SYuri Pankov const char *const *roff_name = __roff_name;
37595c635efSGarrett D'Amore
376c66b8046SYuri Pankov static struct roffmac roffs[TOKEN_NONE] = {
377cec8643bSMichal Nowak { roff_noarg, NULL, NULL, 0 }, /* br */
378c66b8046SYuri Pankov { roff_onearg, NULL, NULL, 0 }, /* ce */
379cec8643bSMichal Nowak { roff_noarg, NULL, NULL, 0 }, /* fi */
380c66b8046SYuri Pankov { roff_onearg, NULL, NULL, 0 }, /* ft */
381c66b8046SYuri Pankov { roff_onearg, NULL, NULL, 0 }, /* ll */
382c66b8046SYuri Pankov { roff_onearg, NULL, NULL, 0 }, /* mc */
383cec8643bSMichal Nowak { roff_noarg, NULL, NULL, 0 }, /* nf */
384c66b8046SYuri Pankov { roff_onearg, NULL, NULL, 0 }, /* po */
385c66b8046SYuri Pankov { roff_onearg, NULL, NULL, 0 }, /* rj */
386c66b8046SYuri Pankov { roff_onearg, NULL, NULL, 0 }, /* sp */
387c66b8046SYuri Pankov { roff_manyarg, NULL, NULL, 0 }, /* ta */
388c66b8046SYuri Pankov { roff_onearg, NULL, NULL, 0 }, /* ti */
389c66b8046SYuri Pankov { NULL, NULL, NULL, 0 }, /* ROFF_MAX */
390c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* ab */
391c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* ad */
392c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* af */
393c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* aln */
394c66b8046SYuri Pankov { roff_als, NULL, NULL, 0 }, /* als */
395c66b8046SYuri Pankov { roff_block, roff_block_text, roff_block_sub, 0 }, /* am */
396c66b8046SYuri Pankov { roff_block, roff_block_text, roff_block_sub, 0 }, /* am1 */
397c66b8046SYuri Pankov { roff_block, roff_block_text, roff_block_sub, 0 }, /* ami */
398c66b8046SYuri Pankov { roff_block, roff_block_text, roff_block_sub, 0 }, /* ami1 */
399c66b8046SYuri Pankov { roff_ds, NULL, NULL, 0 }, /* as */
400c66b8046SYuri Pankov { roff_ds, NULL, NULL, 0 }, /* as1 */
401c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* asciify */
402c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* backtrace */
403c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* bd */
404c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* bleedat */
405c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* blm */
406c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* box */
407c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* boxa */
408c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* bp */
409c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* BP */
410*4d131170SRobert Mustacchi { roff_break, NULL, NULL, 0 }, /* break */
411c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* breakchar */
412c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* brnl */
413cec8643bSMichal Nowak { roff_noarg, NULL, NULL, 0 }, /* brp */
414c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* brpnl */
415c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* c2 */
416c66b8046SYuri Pankov { roff_cc, NULL, NULL, 0 }, /* cc */
417c66b8046SYuri Pankov { roff_insec, NULL, NULL, 0 }, /* cf */
418c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* cflags */
419c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* ch */
420cec8643bSMichal Nowak { roff_char, NULL, NULL, 0 }, /* char */
421c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* chop */
422c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* class */
423c66b8046SYuri Pankov { roff_insec, NULL, NULL, 0 }, /* close */
424c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* CL */
425c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* color */
426c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* composite */
427c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* continue */
428c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* cp */
429c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* cropat */
430c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* cs */
431c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* cu */
432c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* da */
433c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* dch */
434c66b8046SYuri Pankov { roff_Dd, NULL, NULL, 0 }, /* Dd */
435c66b8046SYuri Pankov { roff_block, roff_block_text, roff_block_sub, 0 }, /* de */
436c66b8046SYuri Pankov { roff_block, roff_block_text, roff_block_sub, 0 }, /* de1 */
437c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* defcolor */
438c66b8046SYuri Pankov { roff_block, roff_block_text, roff_block_sub, 0 }, /* dei */
439c66b8046SYuri Pankov { roff_block, roff_block_text, roff_block_sub, 0 }, /* dei1 */
440c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* device */
441c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* devicem */
442c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* di */
443c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* do */
444c66b8046SYuri Pankov { roff_ds, NULL, NULL, 0 }, /* ds */
445c66b8046SYuri Pankov { roff_ds, NULL, NULL, 0 }, /* ds1 */
446c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* dwh */
447c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* dt */
448c66b8046SYuri Pankov { roff_ec, NULL, NULL, 0 }, /* ec */
449c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* ecr */
450c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* ecs */
451c66b8046SYuri Pankov { roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT }, /* el */
452c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* em */
453c66b8046SYuri Pankov { roff_EN, NULL, NULL, 0 }, /* EN */
454c66b8046SYuri Pankov { roff_eo, NULL, NULL, 0 }, /* eo */
455c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* EP */
456c66b8046SYuri Pankov { roff_EQ, NULL, NULL, 0 }, /* EQ */
457c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* errprint */
458c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* ev */
459c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* evc */
460c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* ex */
461c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* fallback */
462c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* fam */
463c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* fc */
464c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* fchar */
465c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* fcolor */
466c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* fdeferlig */
467c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* feature */
468c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* fkern */
469c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* fl */
470c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* flig */
471c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* fp */
472c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* fps */
473c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* fschar */
474c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* fspacewidth */
475c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* fspecial */
476c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* ftr */
477c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* fzoom */
478c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* gcolor */
479c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* hc */
480c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* hcode */
481c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* hidechar */
482c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* hla */
483c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* hlm */
484c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* hpf */
485c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* hpfa */
486c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* hpfcode */
487c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* hw */
488c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* hy */
489c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* hylang */
490c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* hylen */
491c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* hym */
492c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* hypp */
493c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* hys */
494c66b8046SYuri Pankov { roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT }, /* ie */
495c66b8046SYuri Pankov { roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT }, /* if */
496c66b8046SYuri Pankov { roff_block, roff_block_text, roff_block_sub, 0 }, /* ig */
497c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* index */
498c66b8046SYuri Pankov { roff_it, NULL, NULL, 0 }, /* it */
499c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* itc */
500c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* IX */
501c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* kern */
502c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* kernafter */
503c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* kernbefore */
504c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* kernpair */
505c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* lc */
506c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* lc_ctype */
507c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* lds */
508c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* length */
509c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* letadj */
510c66b8046SYuri Pankov { roff_insec, NULL, NULL, 0 }, /* lf */
511c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* lg */
512c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* lhang */
513c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* linetabs */
514c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* lnr */
515c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* lnrf */
516c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* lpfx */
517c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* ls */
518c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* lsm */
519c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* lt */
520c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* mediasize */
521c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* minss */
522c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* mk */
523c66b8046SYuri Pankov { roff_insec, NULL, NULL, 0 }, /* mso */
524c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* na */
525c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* ne */
526c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* nh */
527c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* nhychar */
528c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* nm */
529c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* nn */
530cec8643bSMichal Nowak { roff_nop, NULL, NULL, 0 }, /* nop */
531c66b8046SYuri Pankov { roff_nr, NULL, NULL, 0 }, /* nr */
532c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* nrf */
533c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* nroff */
534c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* ns */
535c66b8046SYuri Pankov { roff_insec, NULL, NULL, 0 }, /* nx */
536c66b8046SYuri Pankov { roff_insec, NULL, NULL, 0 }, /* open */
537c66b8046SYuri Pankov { roff_insec, NULL, NULL, 0 }, /* opena */
538c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* os */
539c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* output */
540c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* padj */
541c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* papersize */
542c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* pc */
543c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* pev */
544c66b8046SYuri Pankov { roff_insec, NULL, NULL, 0 }, /* pi */
545c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* PI */
546c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* pl */
547c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* pm */
548c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* pn */
549c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* pnr */
550c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* ps */
551c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* psbb */
552c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* pshape */
553c66b8046SYuri Pankov { roff_insec, NULL, NULL, 0 }, /* pso */
554c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* ptr */
555c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* pvs */
556c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* rchar */
557c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* rd */
558c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* recursionlimit */
559cec8643bSMichal Nowak { roff_return, NULL, NULL, 0 }, /* return */
560c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* rfschar */
561c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* rhang */
562c66b8046SYuri Pankov { roff_rm, NULL, NULL, 0 }, /* rm */
563c66b8046SYuri Pankov { roff_rn, NULL, NULL, 0 }, /* rn */
564c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* rnn */
565c66b8046SYuri Pankov { roff_rr, NULL, NULL, 0 }, /* rr */
566c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* rs */
567c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* rt */
568c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* schar */
569c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* sentchar */
570c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* shc */
571cec8643bSMichal Nowak { roff_shift, NULL, NULL, 0 }, /* shift */
572c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* sizes */
573c66b8046SYuri Pankov { roff_so, NULL, NULL, 0 }, /* so */
574c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* spacewidth */
575c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* special */
576c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* spreadwarn */
577c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* ss */
578c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* sty */
579c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* substring */
580c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* sv */
581c66b8046SYuri Pankov { roff_insec, NULL, NULL, 0 }, /* sy */
582c66b8046SYuri Pankov { roff_T_, NULL, NULL, 0 }, /* T& */
583c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* tc */
584c66b8046SYuri Pankov { roff_TE, NULL, NULL, 0 }, /* TE */
585c66b8046SYuri Pankov { roff_Dd, NULL, NULL, 0 }, /* TH */
586c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* tkf */
587c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* tl */
588c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* tm */
589c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* tm1 */
590c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* tmc */
591c66b8046SYuri Pankov { roff_tr, NULL, NULL, 0 }, /* tr */
592c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* track */
593c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* transchar */
594c66b8046SYuri Pankov { roff_insec, NULL, NULL, 0 }, /* trf */
595c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* trimat */
596c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* trin */
597c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* trnt */
598c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* troff */
599c66b8046SYuri Pankov { roff_TS, NULL, NULL, 0 }, /* TS */
600c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* uf */
601c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* ul */
602c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* unformat */
603c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* unwatch */
604c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* unwatchn */
605c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* vpt */
606c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* vs */
607c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* warn */
608c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* warnscale */
609c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* watch */
610c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* watchlength */
611c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* watchn */
612c66b8046SYuri Pankov { roff_unsupp, NULL, NULL, 0 }, /* wh */
613cec8643bSMichal Nowak { roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT }, /*while*/
614c66b8046SYuri Pankov { roff_insec, NULL, NULL, 0 }, /* write */
615c66b8046SYuri Pankov { roff_insec, NULL, NULL, 0 }, /* writec */
616c66b8046SYuri Pankov { roff_insec, NULL, NULL, 0 }, /* writem */
617c66b8046SYuri Pankov { roff_line_ignore, NULL, NULL, 0 }, /* xflag */
618c66b8046SYuri Pankov { roff_cblock, NULL, NULL, 0 }, /* . */
619c66b8046SYuri Pankov { roff_renamed, NULL, NULL, 0 },
620c66b8046SYuri Pankov { roff_userdef, NULL, NULL, 0 }
621698f87a4SGarrett D'Amore };
622698f87a4SGarrett D'Amore
62395c635efSGarrett D'Amore /* Array of injected predefined strings. */
62495c635efSGarrett D'Amore #define PREDEFS_MAX 38
62595c635efSGarrett D'Amore static const struct predef predefs[PREDEFS_MAX] = {
62695c635efSGarrett D'Amore #include "predefs.in"
62795c635efSGarrett D'Amore };
62895c635efSGarrett D'Amore
629c66b8046SYuri Pankov static int roffce_lines; /* number of input lines to center */
630c66b8046SYuri Pankov static struct roff_node *roffce_node; /* active request */
631698f87a4SGarrett D'Amore static int roffit_lines; /* number of lines to delay */
632698f87a4SGarrett D'Amore static char *roffit_macro; /* nil-terminated macro line */
633698f87a4SGarrett D'Amore
634260e9a87SYuri Pankov
635371584c2SYuri Pankov /* --- request table ------------------------------------------------------ */
636371584c2SYuri Pankov
637c66b8046SYuri Pankov struct ohash *
roffhash_alloc(enum roff_tok mintok,enum roff_tok maxtok)638c66b8046SYuri Pankov roffhash_alloc(enum roff_tok mintok, enum roff_tok maxtok)
63995c635efSGarrett D'Amore {
640c66b8046SYuri Pankov struct ohash *htab;
641c66b8046SYuri Pankov struct roffreq *req;
642c66b8046SYuri Pankov enum roff_tok tok;
643c66b8046SYuri Pankov size_t sz;
644c66b8046SYuri Pankov unsigned int slot;
64595c635efSGarrett D'Amore
646c66b8046SYuri Pankov htab = mandoc_malloc(sizeof(*htab));
647c66b8046SYuri Pankov mandoc_ohash_init(htab, 8, offsetof(struct roffreq, name));
64895c635efSGarrett D'Amore
649c66b8046SYuri Pankov for (tok = mintok; tok < maxtok; tok++) {
650c66b8046SYuri Pankov if (roff_name[tok] == NULL)
651c66b8046SYuri Pankov continue;
652c66b8046SYuri Pankov sz = strlen(roff_name[tok]);
653c66b8046SYuri Pankov req = mandoc_malloc(sizeof(*req) + sz + 1);
654c66b8046SYuri Pankov req->tok = tok;
655c66b8046SYuri Pankov memcpy(req->name, roff_name[tok], sz + 1);
656c66b8046SYuri Pankov slot = ohash_qlookup(htab, req->name);
657c66b8046SYuri Pankov ohash_insert(htab, slot, req);
658c66b8046SYuri Pankov }
659c66b8046SYuri Pankov return htab;
660c66b8046SYuri Pankov }
66195c635efSGarrett D'Amore
662c66b8046SYuri Pankov void
roffhash_free(struct ohash * htab)663c66b8046SYuri Pankov roffhash_free(struct ohash *htab)
664c66b8046SYuri Pankov {
665c66b8046SYuri Pankov struct roffreq *req;
666c66b8046SYuri Pankov unsigned int slot;
667c66b8046SYuri Pankov
668c66b8046SYuri Pankov if (htab == NULL)
669c66b8046SYuri Pankov return;
670c66b8046SYuri Pankov for (req = ohash_first(htab, &slot); req != NULL;
671c66b8046SYuri Pankov req = ohash_next(htab, &slot))
672c66b8046SYuri Pankov free(req);
673c66b8046SYuri Pankov ohash_delete(htab);
674c66b8046SYuri Pankov free(htab);
675c66b8046SYuri Pankov }
676c66b8046SYuri Pankov
677c66b8046SYuri Pankov enum roff_tok
roffhash_find(struct ohash * htab,const char * name,size_t sz)678c66b8046SYuri Pankov roffhash_find(struct ohash *htab, const char *name, size_t sz)
679c66b8046SYuri Pankov {
680c66b8046SYuri Pankov struct roffreq *req;
681c66b8046SYuri Pankov const char *end;
682c66b8046SYuri Pankov
683c66b8046SYuri Pankov if (sz) {
684c66b8046SYuri Pankov end = name + sz;
685c66b8046SYuri Pankov req = ohash_find(htab, ohash_qlookupi(htab, name, &end));
68695c635efSGarrett D'Amore } else
687c66b8046SYuri Pankov req = ohash_find(htab, ohash_qlookup(htab, name));
688c66b8046SYuri Pankov return req == NULL ? TOKEN_NONE : req->tok;
68995c635efSGarrett D'Amore }
69095c635efSGarrett D'Amore
691371584c2SYuri Pankov /* --- stack of request blocks -------------------------------------------- */
692371584c2SYuri Pankov
69395c635efSGarrett D'Amore /*
69495c635efSGarrett D'Amore * Pop the current node off of the stack of roff instructions currently
695*4d131170SRobert Mustacchi * pending. Return 1 if it is a loop or 0 otherwise.
69695c635efSGarrett D'Amore */
697cec8643bSMichal Nowak static int
roffnode_pop(struct roff * r)69895c635efSGarrett D'Amore roffnode_pop(struct roff *r)
69995c635efSGarrett D'Amore {
70095c635efSGarrett D'Amore struct roffnode *p;
701cec8643bSMichal Nowak int inloop;
70295c635efSGarrett D'Amore
70395c635efSGarrett D'Amore p = r->last;
704cec8643bSMichal Nowak inloop = p->tok == ROFF_while;
705cec8643bSMichal Nowak r->last = p->parent;
70695c635efSGarrett D'Amore free(p->name);
70795c635efSGarrett D'Amore free(p->end);
70895c635efSGarrett D'Amore free(p);
709cec8643bSMichal Nowak return inloop;
71095c635efSGarrett D'Amore }
71195c635efSGarrett D'Amore
71295c635efSGarrett D'Amore /*
71395c635efSGarrett D'Amore * Push a roff node onto the instruction stack. This must later be
71495c635efSGarrett D'Amore * removed with roffnode_pop().
71595c635efSGarrett D'Amore */
71695c635efSGarrett D'Amore static void
roffnode_push(struct roff * r,enum roff_tok tok,const char * name,int line,int col)717c66b8046SYuri Pankov roffnode_push(struct roff *r, enum roff_tok tok, const char *name,
71895c635efSGarrett D'Amore int line, int col)
71995c635efSGarrett D'Amore {
72095c635efSGarrett D'Amore struct roffnode *p;
72195c635efSGarrett D'Amore
72295c635efSGarrett D'Amore p = mandoc_calloc(1, sizeof(struct roffnode));
72395c635efSGarrett D'Amore p->tok = tok;
72495c635efSGarrett D'Amore if (name)
72595c635efSGarrett D'Amore p->name = mandoc_strdup(name);
72695c635efSGarrett D'Amore p->parent = r->last;
72795c635efSGarrett D'Amore p->line = line;
72895c635efSGarrett D'Amore p->col = col;
729260e9a87SYuri Pankov p->rule = p->parent ? p->parent->rule : 0;
73095c635efSGarrett D'Amore
73195c635efSGarrett D'Amore r->last = p;
73295c635efSGarrett D'Amore }
73395c635efSGarrett D'Amore
734371584c2SYuri Pankov /* --- roff parser state data management ---------------------------------- */
735371584c2SYuri Pankov
73695c635efSGarrett D'Amore static void
roff_free1(struct roff * r)73795c635efSGarrett D'Amore roff_free1(struct roff *r)
73895c635efSGarrett D'Amore {
73995c635efSGarrett D'Amore int i;
74095c635efSGarrett D'Amore
741cec8643bSMichal Nowak tbl_free(r->first_tbl);
74295c635efSGarrett D'Amore r->first_tbl = r->last_tbl = r->tbl = NULL;
74395c635efSGarrett D'Amore
744c66b8046SYuri Pankov eqn_free(r->last_eqn);
745c66b8046SYuri Pankov r->last_eqn = r->eqn = NULL;
74695c635efSGarrett D'Amore
747cec8643bSMichal Nowak while (r->mstackpos >= 0)
748cec8643bSMichal Nowak roff_userret(r);
749cec8643bSMichal Nowak
75095c635efSGarrett D'Amore while (r->last)
75195c635efSGarrett D'Amore roffnode_pop(r);
75295c635efSGarrett D'Amore
753260e9a87SYuri Pankov free (r->rstack);
754260e9a87SYuri Pankov r->rstack = NULL;
755260e9a87SYuri Pankov r->rstacksz = 0;
756260e9a87SYuri Pankov r->rstackpos = -1;
75795c635efSGarrett D'Amore
758698f87a4SGarrett D'Amore roff_freereg(r->regtab);
759698f87a4SGarrett D'Amore r->regtab = NULL;
760698f87a4SGarrett D'Amore
761260e9a87SYuri Pankov roff_freestr(r->strtab);
762c66b8046SYuri Pankov roff_freestr(r->rentab);
763260e9a87SYuri Pankov roff_freestr(r->xmbtab);
764c66b8046SYuri Pankov r->strtab = r->rentab = r->xmbtab = NULL;
765260e9a87SYuri Pankov
76695c635efSGarrett D'Amore if (r->xtab)
76795c635efSGarrett D'Amore for (i = 0; i < 128; i++)
76895c635efSGarrett D'Amore free(r->xtab[i].p);
76995c635efSGarrett D'Amore free(r->xtab);
77095c635efSGarrett D'Amore r->xtab = NULL;
77195c635efSGarrett D'Amore }
77295c635efSGarrett D'Amore
77395c635efSGarrett D'Amore void
roff_reset(struct roff * r)77495c635efSGarrett D'Amore roff_reset(struct roff *r)
77595c635efSGarrett D'Amore {
77695c635efSGarrett D'Amore roff_free1(r);
777*4d131170SRobert Mustacchi r->options |= MPARSE_COMMENT;
778260e9a87SYuri Pankov r->format = r->options & (MPARSE_MDOC | MPARSE_MAN);
779c66b8046SYuri Pankov r->control = '\0';
780c66b8046SYuri Pankov r->escape = '\\';
781c66b8046SYuri Pankov roffce_lines = 0;
782c66b8046SYuri Pankov roffce_node = NULL;
783c66b8046SYuri Pankov roffit_lines = 0;
784c66b8046SYuri Pankov roffit_macro = NULL;
78595c635efSGarrett D'Amore }
78695c635efSGarrett D'Amore
78795c635efSGarrett D'Amore void
roff_free(struct roff * r)78895c635efSGarrett D'Amore roff_free(struct roff *r)
78995c635efSGarrett D'Amore {
790cec8643bSMichal Nowak int i;
791cec8643bSMichal Nowak
79295c635efSGarrett D'Amore roff_free1(r);
793cec8643bSMichal Nowak for (i = 0; i < r->mstacksz; i++)
794cec8643bSMichal Nowak free(r->mstack[i].argv);
795cec8643bSMichal Nowak free(r->mstack);
796c66b8046SYuri Pankov roffhash_free(r->reqtab);
79795c635efSGarrett D'Amore free(r);
79895c635efSGarrett D'Amore }
79995c635efSGarrett D'Amore
80095c635efSGarrett D'Amore struct roff *
roff_alloc(int options)801cec8643bSMichal Nowak roff_alloc(int options)
80295c635efSGarrett D'Amore {
80395c635efSGarrett D'Amore struct roff *r;
80495c635efSGarrett D'Amore
80595c635efSGarrett D'Amore r = mandoc_calloc(1, sizeof(struct roff));
8066640c13bSYuri Pankov r->reqtab = roffhash_alloc(0, ROFF_RENAMED);
807*4d131170SRobert Mustacchi r->options = options | MPARSE_COMMENT;
808260e9a87SYuri Pankov r->format = options & (MPARSE_MDOC | MPARSE_MAN);
809cec8643bSMichal Nowak r->mstackpos = -1;
81095c635efSGarrett D'Amore r->rstackpos = -1;
811c66b8046SYuri Pankov r->escape = '\\';
812371584c2SYuri Pankov return r;
81395c635efSGarrett D'Amore }
81495c635efSGarrett D'Amore
815371584c2SYuri Pankov /* --- syntax tree state data management ---------------------------------- */
816371584c2SYuri Pankov
817371584c2SYuri Pankov static void
roff_man_free1(struct roff_man * man)818371584c2SYuri Pankov roff_man_free1(struct roff_man *man)
819371584c2SYuri Pankov {
820cec8643bSMichal Nowak if (man->meta.first != NULL)
821cec8643bSMichal Nowak roff_node_delete(man, man->meta.first);
822371584c2SYuri Pankov free(man->meta.msec);
823371584c2SYuri Pankov free(man->meta.vol);
824371584c2SYuri Pankov free(man->meta.os);
825371584c2SYuri Pankov free(man->meta.arch);
826371584c2SYuri Pankov free(man->meta.title);
827371584c2SYuri Pankov free(man->meta.name);
828371584c2SYuri Pankov free(man->meta.date);
829cec8643bSMichal Nowak free(man->meta.sodest);
830cec8643bSMichal Nowak }
831cec8643bSMichal Nowak
832cec8643bSMichal Nowak void
roff_state_reset(struct roff_man * man)833cec8643bSMichal Nowak roff_state_reset(struct roff_man *man)
834cec8643bSMichal Nowak {
835cec8643bSMichal Nowak man->last = man->meta.first;
836cec8643bSMichal Nowak man->last_es = NULL;
837cec8643bSMichal Nowak man->flags = 0;
838cec8643bSMichal Nowak man->lastsec = man->lastnamed = SEC_NONE;
839cec8643bSMichal Nowak man->next = ROFF_NEXT_CHILD;
840cec8643bSMichal Nowak roff_setreg(man->roff, "nS", 0, '=');
841371584c2SYuri Pankov }
842371584c2SYuri Pankov
843371584c2SYuri Pankov static void
roff_man_alloc1(struct roff_man * man)844371584c2SYuri Pankov roff_man_alloc1(struct roff_man *man)
845371584c2SYuri Pankov {
846371584c2SYuri Pankov memset(&man->meta, 0, sizeof(man->meta));
847cec8643bSMichal Nowak man->meta.first = mandoc_calloc(1, sizeof(*man->meta.first));
848cec8643bSMichal Nowak man->meta.first->type = ROFFT_ROOT;
849cec8643bSMichal Nowak man->meta.macroset = MACROSET_NONE;
850cec8643bSMichal Nowak roff_state_reset(man);
851371584c2SYuri Pankov }
852371584c2SYuri Pankov
853371584c2SYuri Pankov void
roff_man_reset(struct roff_man * man)854371584c2SYuri Pankov roff_man_reset(struct roff_man *man)
855371584c2SYuri Pankov {
856371584c2SYuri Pankov roff_man_free1(man);
857371584c2SYuri Pankov roff_man_alloc1(man);
858371584c2SYuri Pankov }
859371584c2SYuri Pankov
860371584c2SYuri Pankov void
roff_man_free(struct roff_man * man)861371584c2SYuri Pankov roff_man_free(struct roff_man *man)
862371584c2SYuri Pankov {
863371584c2SYuri Pankov roff_man_free1(man);
864371584c2SYuri Pankov free(man);
865371584c2SYuri Pankov }
866371584c2SYuri Pankov
867371584c2SYuri Pankov struct roff_man *
roff_man_alloc(struct roff * roff,const char * os_s,int quick)868cec8643bSMichal Nowak roff_man_alloc(struct roff *roff, const char *os_s, int quick)
869371584c2SYuri Pankov {
870371584c2SYuri Pankov struct roff_man *man;
871371584c2SYuri Pankov
872371584c2SYuri Pankov man = mandoc_calloc(1, sizeof(*man));
873371584c2SYuri Pankov man->roff = roff;
874c66b8046SYuri Pankov man->os_s = os_s;
875371584c2SYuri Pankov man->quick = quick;
876371584c2SYuri Pankov roff_man_alloc1(man);
877c66b8046SYuri Pankov roff->man = man;
878371584c2SYuri Pankov return man;
879371584c2SYuri Pankov }
880371584c2SYuri Pankov
881371584c2SYuri Pankov /* --- syntax tree handling ----------------------------------------------- */
882371584c2SYuri Pankov
883371584c2SYuri Pankov struct roff_node *
roff_node_alloc(struct roff_man * man,int line,int pos,enum roff_type type,int tok)884371584c2SYuri Pankov roff_node_alloc(struct roff_man *man, int line, int pos,
885371584c2SYuri Pankov enum roff_type type, int tok)
886371584c2SYuri Pankov {
887371584c2SYuri Pankov struct roff_node *n;
888371584c2SYuri Pankov
889371584c2SYuri Pankov n = mandoc_calloc(1, sizeof(*n));
890371584c2SYuri Pankov n->line = line;
891371584c2SYuri Pankov n->pos = pos;
892371584c2SYuri Pankov n->tok = tok;
893371584c2SYuri Pankov n->type = type;
894371584c2SYuri Pankov n->sec = man->lastsec;
895371584c2SYuri Pankov
896371584c2SYuri Pankov if (man->flags & MDOC_SYNOPSIS)
897a40ea1a7SYuri Pankov n->flags |= NODE_SYNPRETTY;
898371584c2SYuri Pankov else
899a40ea1a7SYuri Pankov n->flags &= ~NODE_SYNPRETTY;
900cec8643bSMichal Nowak if ((man->flags & (ROFF_NOFILL | ROFF_NONOFILL)) == ROFF_NOFILL)
901cec8643bSMichal Nowak n->flags |= NODE_NOFILL;
902cec8643bSMichal Nowak else
903cec8643bSMichal Nowak n->flags &= ~NODE_NOFILL;
904371584c2SYuri Pankov if (man->flags & MDOC_NEWLINE)
905a40ea1a7SYuri Pankov n->flags |= NODE_LINE;
906371584c2SYuri Pankov man->flags &= ~MDOC_NEWLINE;
907371584c2SYuri Pankov
908371584c2SYuri Pankov return n;
909371584c2SYuri Pankov }
910371584c2SYuri Pankov
911371584c2SYuri Pankov void
roff_node_append(struct roff_man * man,struct roff_node * n)912371584c2SYuri Pankov roff_node_append(struct roff_man *man, struct roff_node *n)
913371584c2SYuri Pankov {
914371584c2SYuri Pankov
915371584c2SYuri Pankov switch (man->next) {
916371584c2SYuri Pankov case ROFF_NEXT_SIBLING:
917371584c2SYuri Pankov if (man->last->next != NULL) {
918371584c2SYuri Pankov n->next = man->last->next;
919371584c2SYuri Pankov man->last->next->prev = n;
920371584c2SYuri Pankov } else
921371584c2SYuri Pankov man->last->parent->last = n;
922371584c2SYuri Pankov man->last->next = n;
923371584c2SYuri Pankov n->prev = man->last;
924371584c2SYuri Pankov n->parent = man->last->parent;
925371584c2SYuri Pankov break;
926371584c2SYuri Pankov case ROFF_NEXT_CHILD:
927a40ea1a7SYuri Pankov if (man->last->child != NULL) {
928a40ea1a7SYuri Pankov n->next = man->last->child;
929a40ea1a7SYuri Pankov man->last->child->prev = n;
930a40ea1a7SYuri Pankov } else
931a40ea1a7SYuri Pankov man->last->last = n;
932371584c2SYuri Pankov man->last->child = n;
933371584c2SYuri Pankov n->parent = man->last;
934371584c2SYuri Pankov break;
935371584c2SYuri Pankov default:
936371584c2SYuri Pankov abort();
937371584c2SYuri Pankov }
938371584c2SYuri Pankov man->last = n;
939371584c2SYuri Pankov
940371584c2SYuri Pankov switch (n->type) {
941371584c2SYuri Pankov case ROFFT_HEAD:
942371584c2SYuri Pankov n->parent->head = n;
943371584c2SYuri Pankov break;
944371584c2SYuri Pankov case ROFFT_BODY:
945371584c2SYuri Pankov if (n->end != ENDBODY_NOT)
946371584c2SYuri Pankov return;
947371584c2SYuri Pankov n->parent->body = n;
948371584c2SYuri Pankov break;
949371584c2SYuri Pankov case ROFFT_TAIL:
950371584c2SYuri Pankov n->parent->tail = n;
951371584c2SYuri Pankov break;
952371584c2SYuri Pankov default:
953371584c2SYuri Pankov return;
954371584c2SYuri Pankov }
955371584c2SYuri Pankov
956371584c2SYuri Pankov /*
957371584c2SYuri Pankov * Copy over the normalised-data pointer of our parent. Not
958371584c2SYuri Pankov * everybody has one, but copying a null pointer is fine.
959371584c2SYuri Pankov */
960371584c2SYuri Pankov
961371584c2SYuri Pankov n->norm = n->parent->norm;
962371584c2SYuri Pankov assert(n->parent->type == ROFFT_BLOCK);
963371584c2SYuri Pankov }
964371584c2SYuri Pankov
965371584c2SYuri Pankov void
roff_word_alloc(struct roff_man * man,int line,int pos,const char * word)966371584c2SYuri Pankov roff_word_alloc(struct roff_man *man, int line, int pos, const char *word)
967371584c2SYuri Pankov {
968371584c2SYuri Pankov struct roff_node *n;
969371584c2SYuri Pankov
970371584c2SYuri Pankov n = roff_node_alloc(man, line, pos, ROFFT_TEXT, TOKEN_NONE);
971371584c2SYuri Pankov n->string = roff_strdup(man->roff, word);
972371584c2SYuri Pankov roff_node_append(man, n);
973a40ea1a7SYuri Pankov n->flags |= NODE_VALID | NODE_ENDED;
974371584c2SYuri Pankov man->next = ROFF_NEXT_SIBLING;
975371584c2SYuri Pankov }
976371584c2SYuri Pankov
977371584c2SYuri Pankov void
roff_word_append(struct roff_man * man,const char * word)978371584c2SYuri Pankov roff_word_append(struct roff_man *man, const char *word)
979371584c2SYuri Pankov {
980371584c2SYuri Pankov struct roff_node *n;
981371584c2SYuri Pankov char *addstr, *newstr;
982371584c2SYuri Pankov
983371584c2SYuri Pankov n = man->last;
984371584c2SYuri Pankov addstr = roff_strdup(man->roff, word);
985371584c2SYuri Pankov mandoc_asprintf(&newstr, "%s %s", n->string, addstr);
986371584c2SYuri Pankov free(addstr);
987371584c2SYuri Pankov free(n->string);
988371584c2SYuri Pankov n->string = newstr;
989371584c2SYuri Pankov man->next = ROFF_NEXT_SIBLING;
990371584c2SYuri Pankov }
991371584c2SYuri Pankov
992371584c2SYuri Pankov void
roff_elem_alloc(struct roff_man * man,int line,int pos,int tok)993371584c2SYuri Pankov roff_elem_alloc(struct roff_man *man, int line, int pos, int tok)
994371584c2SYuri Pankov {
995371584c2SYuri Pankov struct roff_node *n;
996371584c2SYuri Pankov
997371584c2SYuri Pankov n = roff_node_alloc(man, line, pos, ROFFT_ELEM, tok);
998371584c2SYuri Pankov roff_node_append(man, n);
999371584c2SYuri Pankov man->next = ROFF_NEXT_CHILD;
1000371584c2SYuri Pankov }
1001371584c2SYuri Pankov
1002371584c2SYuri Pankov struct roff_node *
roff_block_alloc(struct roff_man * man,int line,int pos,int tok)1003371584c2SYuri Pankov roff_block_alloc(struct roff_man *man, int line, int pos, int tok)
1004371584c2SYuri Pankov {
1005371584c2SYuri Pankov struct roff_node *n;
1006371584c2SYuri Pankov
1007371584c2SYuri Pankov n = roff_node_alloc(man, line, pos, ROFFT_BLOCK, tok);
1008371584c2SYuri Pankov roff_node_append(man, n);
1009371584c2SYuri Pankov man->next = ROFF_NEXT_CHILD;
1010371584c2SYuri Pankov return n;
1011371584c2SYuri Pankov }
1012371584c2SYuri Pankov
1013371584c2SYuri Pankov struct roff_node *
roff_head_alloc(struct roff_man * man,int line,int pos,int tok)1014371584c2SYuri Pankov roff_head_alloc(struct roff_man *man, int line, int pos, int tok)
1015371584c2SYuri Pankov {
1016371584c2SYuri Pankov struct roff_node *n;
1017371584c2SYuri Pankov
1018371584c2SYuri Pankov n = roff_node_alloc(man, line, pos, ROFFT_HEAD, tok);
1019371584c2SYuri Pankov roff_node_append(man, n);
1020371584c2SYuri Pankov man->next = ROFF_NEXT_CHILD;
1021371584c2SYuri Pankov return n;
1022371584c2SYuri Pankov }
1023371584c2SYuri Pankov
1024371584c2SYuri Pankov struct roff_node *
roff_body_alloc(struct roff_man * man,int line,int pos,int tok)1025371584c2SYuri Pankov roff_body_alloc(struct roff_man *man, int line, int pos, int tok)
1026371584c2SYuri Pankov {
1027371584c2SYuri Pankov struct roff_node *n;
1028371584c2SYuri Pankov
1029371584c2SYuri Pankov n = roff_node_alloc(man, line, pos, ROFFT_BODY, tok);
1030371584c2SYuri Pankov roff_node_append(man, n);
1031371584c2SYuri Pankov man->next = ROFF_NEXT_CHILD;
1032371584c2SYuri Pankov return n;
1033371584c2SYuri Pankov }
1034371584c2SYuri Pankov
1035c66b8046SYuri Pankov static void
roff_addtbl(struct roff_man * man,int line,struct tbl_node * tbl)1036cec8643bSMichal Nowak roff_addtbl(struct roff_man *man, int line, struct tbl_node *tbl)
1037371584c2SYuri Pankov {
1038371584c2SYuri Pankov struct roff_node *n;
1039cec8643bSMichal Nowak struct tbl_span *span;
1040371584c2SYuri Pankov
1041cec8643bSMichal Nowak if (man->meta.macroset == MACROSET_MAN)
1042c66b8046SYuri Pankov man_breakscope(man, ROFF_TS);
1043c66b8046SYuri Pankov while ((span = tbl_span(tbl)) != NULL) {
1044cec8643bSMichal Nowak n = roff_node_alloc(man, line, 0, ROFFT_TBL, TOKEN_NONE);
1045c66b8046SYuri Pankov n->span = span;
1046371584c2SYuri Pankov roff_node_append(man, n);
1047a40ea1a7SYuri Pankov n->flags |= NODE_VALID | NODE_ENDED;
1048371584c2SYuri Pankov man->next = ROFF_NEXT_SIBLING;
1049371584c2SYuri Pankov }
1050c66b8046SYuri Pankov }
1051371584c2SYuri Pankov
1052371584c2SYuri Pankov void
roff_node_unlink(struct roff_man * man,struct roff_node * n)1053371584c2SYuri Pankov roff_node_unlink(struct roff_man *man, struct roff_node *n)
1054371584c2SYuri Pankov {
1055371584c2SYuri Pankov
1056371584c2SYuri Pankov /* Adjust siblings. */
1057371584c2SYuri Pankov
1058371584c2SYuri Pankov if (n->prev)
1059371584c2SYuri Pankov n->prev->next = n->next;
1060371584c2SYuri Pankov if (n->next)
1061371584c2SYuri Pankov n->next->prev = n->prev;
1062371584c2SYuri Pankov
1063371584c2SYuri Pankov /* Adjust parent. */
1064371584c2SYuri Pankov
1065371584c2SYuri Pankov if (n->parent != NULL) {
1066371584c2SYuri Pankov if (n->parent->child == n)
1067371584c2SYuri Pankov n->parent->child = n->next;
1068371584c2SYuri Pankov if (n->parent->last == n)
1069371584c2SYuri Pankov n->parent->last = n->prev;
1070371584c2SYuri Pankov }
1071371584c2SYuri Pankov
1072371584c2SYuri Pankov /* Adjust parse point. */
1073371584c2SYuri Pankov
1074371584c2SYuri Pankov if (man == NULL)
1075371584c2SYuri Pankov return;
1076371584c2SYuri Pankov if (man->last == n) {
1077371584c2SYuri Pankov if (n->prev == NULL) {
1078371584c2SYuri Pankov man->last = n->parent;
1079371584c2SYuri Pankov man->next = ROFF_NEXT_CHILD;
1080371584c2SYuri Pankov } else {
1081371584c2SYuri Pankov man->last = n->prev;
1082371584c2SYuri Pankov man->next = ROFF_NEXT_SIBLING;
1083371584c2SYuri Pankov }
1084371584c2SYuri Pankov }
1085cec8643bSMichal Nowak if (man->meta.first == n)
1086cec8643bSMichal Nowak man->meta.first = NULL;
1087cec8643bSMichal Nowak }
1088cec8643bSMichal Nowak
1089cec8643bSMichal Nowak void
roff_node_relink(struct roff_man * man,struct roff_node * n)1090cec8643bSMichal Nowak roff_node_relink(struct roff_man *man, struct roff_node *n)
1091cec8643bSMichal Nowak {
1092cec8643bSMichal Nowak roff_node_unlink(man, n);
1093cec8643bSMichal Nowak n->prev = n->next = NULL;
1094cec8643bSMichal Nowak roff_node_append(man, n);
1095371584c2SYuri Pankov }
1096371584c2SYuri Pankov
1097371584c2SYuri Pankov void
roff_node_free(struct roff_node * n)1098371584c2SYuri Pankov roff_node_free(struct roff_node *n)
1099371584c2SYuri Pankov {
1100371584c2SYuri Pankov
1101371584c2SYuri Pankov if (n->args != NULL)
1102371584c2SYuri Pankov mdoc_argv_free(n->args);
1103371584c2SYuri Pankov if (n->type == ROFFT_BLOCK || n->type == ROFFT_ELEM)
1104371584c2SYuri Pankov free(n->norm);
1105c66b8046SYuri Pankov eqn_box_free(n->eqn);
1106371584c2SYuri Pankov free(n->string);
1107*4d131170SRobert Mustacchi free(n->tag);
1108371584c2SYuri Pankov free(n);
1109371584c2SYuri Pankov }
1110371584c2SYuri Pankov
1111371584c2SYuri Pankov void
roff_node_delete(struct roff_man * man,struct roff_node * n)1112371584c2SYuri Pankov roff_node_delete(struct roff_man *man, struct roff_node *n)
1113371584c2SYuri Pankov {
1114371584c2SYuri Pankov
1115371584c2SYuri Pankov while (n->child != NULL)
1116371584c2SYuri Pankov roff_node_delete(man, n->child);
1117371584c2SYuri Pankov roff_node_unlink(man, n);
1118371584c2SYuri Pankov roff_node_free(n);
1119371584c2SYuri Pankov }
1120371584c2SYuri Pankov
1121*4d131170SRobert Mustacchi int
roff_node_transparent(struct roff_node * n)1122*4d131170SRobert Mustacchi roff_node_transparent(struct roff_node *n)
1123*4d131170SRobert Mustacchi {
1124*4d131170SRobert Mustacchi if (n == NULL)
1125*4d131170SRobert Mustacchi return 0;
1126*4d131170SRobert Mustacchi if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT)
1127*4d131170SRobert Mustacchi return 1;
1128*4d131170SRobert Mustacchi return roff_tok_transparent(n->tok);
1129*4d131170SRobert Mustacchi }
1130*4d131170SRobert Mustacchi
1131*4d131170SRobert Mustacchi int
roff_tok_transparent(enum roff_tok tok)1132*4d131170SRobert Mustacchi roff_tok_transparent(enum roff_tok tok)
1133*4d131170SRobert Mustacchi {
1134*4d131170SRobert Mustacchi switch (tok) {
1135*4d131170SRobert Mustacchi case ROFF_ft:
1136*4d131170SRobert Mustacchi case ROFF_ll:
1137*4d131170SRobert Mustacchi case ROFF_mc:
1138*4d131170SRobert Mustacchi case ROFF_po:
1139*4d131170SRobert Mustacchi case ROFF_ta:
1140*4d131170SRobert Mustacchi case MDOC_Db:
1141*4d131170SRobert Mustacchi case MDOC_Es:
1142*4d131170SRobert Mustacchi case MDOC_Sm:
1143*4d131170SRobert Mustacchi case MDOC_Tg:
1144*4d131170SRobert Mustacchi case MAN_DT:
1145*4d131170SRobert Mustacchi case MAN_UC:
1146*4d131170SRobert Mustacchi case MAN_PD:
1147*4d131170SRobert Mustacchi case MAN_AT:
1148*4d131170SRobert Mustacchi return 1;
1149*4d131170SRobert Mustacchi default:
1150*4d131170SRobert Mustacchi return 0;
1151*4d131170SRobert Mustacchi }
1152*4d131170SRobert Mustacchi }
1153*4d131170SRobert Mustacchi
1154*4d131170SRobert Mustacchi struct roff_node *
roff_node_child(struct roff_node * n)1155*4d131170SRobert Mustacchi roff_node_child(struct roff_node *n)
1156*4d131170SRobert Mustacchi {
1157*4d131170SRobert Mustacchi for (n = n->child; roff_node_transparent(n); n = n->next)
1158*4d131170SRobert Mustacchi continue;
1159*4d131170SRobert Mustacchi return n;
1160*4d131170SRobert Mustacchi }
1161*4d131170SRobert Mustacchi
1162*4d131170SRobert Mustacchi struct roff_node *
roff_node_prev(struct roff_node * n)1163*4d131170SRobert Mustacchi roff_node_prev(struct roff_node *n)
1164*4d131170SRobert Mustacchi {
1165*4d131170SRobert Mustacchi do {
1166*4d131170SRobert Mustacchi n = n->prev;
1167*4d131170SRobert Mustacchi } while (roff_node_transparent(n));
1168*4d131170SRobert Mustacchi return n;
1169*4d131170SRobert Mustacchi }
1170*4d131170SRobert Mustacchi
1171*4d131170SRobert Mustacchi struct roff_node *
roff_node_next(struct roff_node * n)1172*4d131170SRobert Mustacchi roff_node_next(struct roff_node *n)
1173*4d131170SRobert Mustacchi {
1174*4d131170SRobert Mustacchi do {
1175*4d131170SRobert Mustacchi n = n->next;
1176*4d131170SRobert Mustacchi } while (roff_node_transparent(n));
1177*4d131170SRobert Mustacchi return n;
1178*4d131170SRobert Mustacchi }
1179*4d131170SRobert Mustacchi
1180371584c2SYuri Pankov void
deroff(char ** dest,const struct roff_node * n)1181371584c2SYuri Pankov deroff(char **dest, const struct roff_node *n)
1182371584c2SYuri Pankov {
1183371584c2SYuri Pankov char *cp;
1184371584c2SYuri Pankov size_t sz;
1185371584c2SYuri Pankov
1186*4d131170SRobert Mustacchi if (n->string == NULL) {
1187371584c2SYuri Pankov for (n = n->child; n != NULL; n = n->next)
1188371584c2SYuri Pankov deroff(dest, n);
1189371584c2SYuri Pankov return;
1190371584c2SYuri Pankov }
1191371584c2SYuri Pankov
1192a40ea1a7SYuri Pankov /* Skip leading whitespace. */
1193371584c2SYuri Pankov
1194a40ea1a7SYuri Pankov for (cp = n->string; *cp != '\0'; cp++) {
1195a40ea1a7SYuri Pankov if (cp[0] == '\\' && cp[1] != '\0' &&
1196a40ea1a7SYuri Pankov strchr(" %&0^|~", cp[1]) != NULL)
1197371584c2SYuri Pankov cp++;
1198a40ea1a7SYuri Pankov else if ( ! isspace((unsigned char)*cp))
1199371584c2SYuri Pankov break;
1200371584c2SYuri Pankov }
1201371584c2SYuri Pankov
1202a40ea1a7SYuri Pankov /* Skip trailing backslash. */
1203a40ea1a7SYuri Pankov
1204a40ea1a7SYuri Pankov sz = strlen(cp);
1205c66b8046SYuri Pankov if (sz > 0 && cp[sz - 1] == '\\')
1206a40ea1a7SYuri Pankov sz--;
1207a40ea1a7SYuri Pankov
1208371584c2SYuri Pankov /* Skip trailing whitespace. */
1209371584c2SYuri Pankov
1210a40ea1a7SYuri Pankov for (; sz; sz--)
1211371584c2SYuri Pankov if ( ! isspace((unsigned char)cp[sz-1]))
1212371584c2SYuri Pankov break;
1213371584c2SYuri Pankov
1214371584c2SYuri Pankov /* Skip empty strings. */
1215371584c2SYuri Pankov
1216371584c2SYuri Pankov if (sz == 0)
1217371584c2SYuri Pankov return;
1218371584c2SYuri Pankov
1219371584c2SYuri Pankov if (*dest == NULL) {
1220371584c2SYuri Pankov *dest = mandoc_strndup(cp, sz);
1221371584c2SYuri Pankov return;
1222371584c2SYuri Pankov }
1223371584c2SYuri Pankov
1224371584c2SYuri Pankov mandoc_asprintf(&cp, "%s %*s", *dest, (int)sz, cp);
1225371584c2SYuri Pankov free(*dest);
1226371584c2SYuri Pankov *dest = cp;
1227371584c2SYuri Pankov }
1228371584c2SYuri Pankov
1229371584c2SYuri Pankov /* --- main functions of the roff parser ---------------------------------- */
1230371584c2SYuri Pankov
123195c635efSGarrett D'Amore /*
1232cec8643bSMichal Nowak * In the current line, expand escape sequences that produce parsable
1233cec8643bSMichal Nowak * input text. Also check the syntax of the remaining escape sequences,
1234cec8643bSMichal Nowak * which typically produce output glyphs or change formatter state.
123595c635efSGarrett D'Amore */
1236cec8643bSMichal Nowak static int
roff_expand(struct roff * r,struct buf * buf,int ln,int pos,char newesc)1237cec8643bSMichal Nowak roff_expand(struct roff *r, struct buf *buf, int ln, int pos, char newesc)
123895c635efSGarrett D'Amore {
1239cec8643bSMichal Nowak struct mctx *ctx; /* current macro call context */
1240260e9a87SYuri Pankov char ubuf[24]; /* buffer to print the number */
12416640c13bSYuri Pankov struct roff_node *n; /* used for header comments */
1242260e9a87SYuri Pankov const char *start; /* start of the string to process */
1243260e9a87SYuri Pankov char *stesc; /* start of an escape sequence ('\\') */
1244cec8643bSMichal Nowak const char *esct; /* type of esccape sequence */
12456640c13bSYuri Pankov char *ep; /* end of comment string */
124695c635efSGarrett D'Amore const char *stnam; /* start of the name, after "[(*" */
124795c635efSGarrett D'Amore const char *cp; /* end of the name, e.g. before ']' */
124895c635efSGarrett D'Amore const char *res; /* the string to be substituted */
1249260e9a87SYuri Pankov char *nbuf; /* new buffer to copy buf->buf to */
1250698f87a4SGarrett D'Amore size_t maxl; /* expected length of the escape name */
1251698f87a4SGarrett D'Amore size_t naml; /* actual length of the escape name */
1252cec8643bSMichal Nowak size_t asz; /* length of the replacement */
1253cec8643bSMichal Nowak size_t rsz; /* length of the rest of the string */
1254260e9a87SYuri Pankov int inaml; /* length returned from mandoc_escape() */
1255698f87a4SGarrett D'Amore int expand_count; /* to avoid infinite loops */
1256260e9a87SYuri Pankov int npos; /* position in numeric expression */
1257260e9a87SYuri Pankov int arg_complete; /* argument not interrupted by eol */
1258cec8643bSMichal Nowak int quote_args; /* true for \\$@, false for \\$* */
1259c66b8046SYuri Pankov int done; /* no more input available */
1260c66b8046SYuri Pankov int deftype; /* type of definition to paste */
1261c66b8046SYuri Pankov int rcsid; /* kind of RCS id seen */
1262cec8643bSMichal Nowak enum mandocerr err; /* for escape sequence problems */
12636640c13bSYuri Pankov char sign; /* increment number register */
1264260e9a87SYuri Pankov char term; /* character terminating the escape */
126595c635efSGarrett D'Amore
1266c66b8046SYuri Pankov /* Search forward for comments. */
1267c66b8046SYuri Pankov
1268c66b8046SYuri Pankov done = 0;
1269260e9a87SYuri Pankov start = buf->buf + pos;
1270c66b8046SYuri Pankov for (stesc = buf->buf + pos; *stesc != '\0'; stesc++) {
1271cec8643bSMichal Nowak if (stesc[0] != newesc || stesc[1] == '\0')
1272c66b8046SYuri Pankov continue;
1273c66b8046SYuri Pankov stesc++;
1274c66b8046SYuri Pankov if (*stesc != '"' && *stesc != '#')
1275c66b8046SYuri Pankov continue;
1276c66b8046SYuri Pankov
1277c66b8046SYuri Pankov /* Comment found, look for RCS id. */
1278c66b8046SYuri Pankov
1279c66b8046SYuri Pankov rcsid = 0;
1280c66b8046SYuri Pankov if ((cp = strstr(stesc, "$" "OpenBSD")) != NULL) {
1281c66b8046SYuri Pankov rcsid = 1 << MANDOC_OS_OPENBSD;
1282c66b8046SYuri Pankov cp += 8;
1283c66b8046SYuri Pankov } else if ((cp = strstr(stesc, "$" "NetBSD")) != NULL) {
1284c66b8046SYuri Pankov rcsid = 1 << MANDOC_OS_NETBSD;
1285c66b8046SYuri Pankov cp += 7;
1286c66b8046SYuri Pankov }
1287c66b8046SYuri Pankov if (cp != NULL &&
1288c66b8046SYuri Pankov isalnum((unsigned char)*cp) == 0 &&
1289c66b8046SYuri Pankov strchr(cp, '$') != NULL) {
1290c66b8046SYuri Pankov if (r->man->meta.rcsids & rcsid)
1291cec8643bSMichal Nowak mandoc_msg(MANDOCERR_RCS_REP, ln,
1292cec8643bSMichal Nowak (int)(stesc - buf->buf) + 1,
1293cec8643bSMichal Nowak "%s", stesc + 1);
1294c66b8046SYuri Pankov r->man->meta.rcsids |= rcsid;
1295c66b8046SYuri Pankov }
1296c66b8046SYuri Pankov
1297c66b8046SYuri Pankov /* Handle trailing whitespace. */
1298c66b8046SYuri Pankov
12996640c13bSYuri Pankov ep = strchr(stesc--, '\0') - 1;
13006640c13bSYuri Pankov if (*ep == '\n') {
1301c66b8046SYuri Pankov done = 1;
13026640c13bSYuri Pankov ep--;
1303c66b8046SYuri Pankov }
13046640c13bSYuri Pankov if (*ep == ' ' || *ep == '\t')
1305cec8643bSMichal Nowak mandoc_msg(MANDOCERR_SPACE_EOL,
1306cec8643bSMichal Nowak ln, (int)(ep - buf->buf), NULL);
13076640c13bSYuri Pankov
13086640c13bSYuri Pankov /*
13096640c13bSYuri Pankov * Save comments preceding the title macro
13106640c13bSYuri Pankov * in the syntax tree.
13116640c13bSYuri Pankov */
13126640c13bSYuri Pankov
1313*4d131170SRobert Mustacchi if (newesc != ASCII_ESC && r->options & MPARSE_COMMENT) {
13146640c13bSYuri Pankov while (*ep == ' ' || *ep == '\t')
13156640c13bSYuri Pankov ep--;
13166640c13bSYuri Pankov ep[1] = '\0';
13176640c13bSYuri Pankov n = roff_node_alloc(r->man,
13186640c13bSYuri Pankov ln, stesc + 1 - buf->buf,
13196640c13bSYuri Pankov ROFFT_COMMENT, TOKEN_NONE);
13206640c13bSYuri Pankov n->string = mandoc_strdup(stesc + 2);
13216640c13bSYuri Pankov roff_node_append(r->man, n);
13226640c13bSYuri Pankov n->flags |= NODE_VALID | NODE_ENDED;
13236640c13bSYuri Pankov r->man->next = ROFF_NEXT_SIBLING;
13246640c13bSYuri Pankov }
13256640c13bSYuri Pankov
1326cec8643bSMichal Nowak /* Line continuation with comment. */
13276640c13bSYuri Pankov
1328cec8643bSMichal Nowak if (stesc[1] == '#') {
1329cec8643bSMichal Nowak *stesc = '\0';
1330cec8643bSMichal Nowak return ROFF_IGN | ROFF_APPEND;
1331cec8643bSMichal Nowak }
1332cec8643bSMichal Nowak
1333cec8643bSMichal Nowak /* Discard normal comments. */
1334cec8643bSMichal Nowak
1335cec8643bSMichal Nowak while (stesc > start && stesc[-1] == ' ' &&
1336cec8643bSMichal Nowak (stesc == start + 1 || stesc[-2] != '\\'))
1337c66b8046SYuri Pankov stesc--;
1338c66b8046SYuri Pankov *stesc = '\0';
1339c66b8046SYuri Pankov break;
1340c66b8046SYuri Pankov }
1341c66b8046SYuri Pankov if (stesc == start)
1342c66b8046SYuri Pankov return ROFF_CONT;
1343c66b8046SYuri Pankov stesc--;
1344c66b8046SYuri Pankov
1345c66b8046SYuri Pankov /* Notice the end of the input. */
1346c66b8046SYuri Pankov
1347c66b8046SYuri Pankov if (*stesc == '\n') {
1348c66b8046SYuri Pankov *stesc-- = '\0';
1349c66b8046SYuri Pankov done = 1;
1350c66b8046SYuri Pankov }
1351c66b8046SYuri Pankov
1352c66b8046SYuri Pankov expand_count = 0;
1353c66b8046SYuri Pankov while (stesc >= start) {
1354cec8643bSMichal Nowak if (*stesc != newesc) {
135595c635efSGarrett D'Amore
1356cec8643bSMichal Nowak /*
1357cec8643bSMichal Nowak * If we have a non-standard escape character,
1358cec8643bSMichal Nowak * escape literal backslashes because all
1359cec8643bSMichal Nowak * processing in subsequent functions uses
1360cec8643bSMichal Nowak * the standard escaping rules.
1361cec8643bSMichal Nowak */
136295c635efSGarrett D'Amore
1363cec8643bSMichal Nowak if (newesc != ASCII_ESC && *stesc == '\\') {
1364c66b8046SYuri Pankov *stesc = '\0';
1365c66b8046SYuri Pankov buf->sz = mandoc_asprintf(&nbuf, "%s\\e%s",
1366c66b8046SYuri Pankov buf->buf, stesc + 1) + 1;
1367c66b8046SYuri Pankov start = nbuf + pos;
1368c66b8046SYuri Pankov stesc = nbuf + (stesc - buf->buf);
1369c66b8046SYuri Pankov free(buf->buf);
1370c66b8046SYuri Pankov buf->buf = nbuf;
1371c66b8046SYuri Pankov }
1372cec8643bSMichal Nowak
1373cec8643bSMichal Nowak /* Search backwards for the next escape. */
1374cec8643bSMichal Nowak
1375c66b8046SYuri Pankov stesc--;
1376260e9a87SYuri Pankov continue;
1377c66b8046SYuri Pankov }
137895c635efSGarrett D'Amore
1379260e9a87SYuri Pankov /* If it is escaped, skip it. */
138095c635efSGarrett D'Amore
1381260e9a87SYuri Pankov for (cp = stesc - 1; cp >= start; cp--)
1382c66b8046SYuri Pankov if (*cp != r->escape)
1383260e9a87SYuri Pankov break;
1384260e9a87SYuri Pankov
1385260e9a87SYuri Pankov if ((stesc - cp) % 2 == 0) {
1386c66b8046SYuri Pankov while (stesc > cp)
1387c66b8046SYuri Pankov *stesc-- = '\\';
1388260e9a87SYuri Pankov continue;
1389c66b8046SYuri Pankov } else if (stesc[1] != '\0') {
1390c66b8046SYuri Pankov *stesc = '\\';
1391c66b8046SYuri Pankov } else {
1392c66b8046SYuri Pankov *stesc-- = '\0';
1393c66b8046SYuri Pankov if (done)
1394c66b8046SYuri Pankov continue;
1395c66b8046SYuri Pankov else
1396cec8643bSMichal Nowak return ROFF_IGN | ROFF_APPEND;
1397260e9a87SYuri Pankov }
1398260e9a87SYuri Pankov
1399260e9a87SYuri Pankov /* Decide whether to expand or to check only. */
1400260e9a87SYuri Pankov
1401260e9a87SYuri Pankov term = '\0';
1402260e9a87SYuri Pankov cp = stesc + 1;
1403cec8643bSMichal Nowak if (*cp == 'E')
1404cec8643bSMichal Nowak cp++;
1405cec8643bSMichal Nowak esct = cp;
1406cec8643bSMichal Nowak switch (*esct) {
1407260e9a87SYuri Pankov case '*':
1408cec8643bSMichal Nowak case '$':
1409698f87a4SGarrett D'Amore res = NULL;
1410698f87a4SGarrett D'Amore break;
1411260e9a87SYuri Pankov case 'B':
1412260e9a87SYuri Pankov case 'w':
1413260e9a87SYuri Pankov term = cp[1];
1414260e9a87SYuri Pankov /* FALLTHROUGH */
1415260e9a87SYuri Pankov case 'n':
14166640c13bSYuri Pankov sign = cp[1];
14176640c13bSYuri Pankov if (sign == '+' || sign == '-')
14186640c13bSYuri Pankov cp++;
1419698f87a4SGarrett D'Amore res = ubuf;
1420698f87a4SGarrett D'Amore break;
1421698f87a4SGarrett D'Amore default:
1422cec8643bSMichal Nowak err = MANDOCERR_OK;
1423cec8643bSMichal Nowak switch(mandoc_escape(&cp, &stnam, &inaml)) {
1424cec8643bSMichal Nowak case ESCAPE_SPECIAL:
1425cec8643bSMichal Nowak if (mchars_spec2cp(stnam, inaml) >= 0)
1426cec8643bSMichal Nowak break;
1427cec8643bSMichal Nowak /* FALLTHROUGH */
1428cec8643bSMichal Nowak case ESCAPE_ERROR:
1429cec8643bSMichal Nowak err = MANDOCERR_ESC_BAD;
1430cec8643bSMichal Nowak break;
1431cec8643bSMichal Nowak case ESCAPE_UNDEF:
1432cec8643bSMichal Nowak err = MANDOCERR_ESC_UNDEF;
1433cec8643bSMichal Nowak break;
1434cec8643bSMichal Nowak case ESCAPE_UNSUPP:
1435cec8643bSMichal Nowak err = MANDOCERR_ESC_UNSUPP;
1436cec8643bSMichal Nowak break;
1437cec8643bSMichal Nowak default:
1438cec8643bSMichal Nowak break;
1439cec8643bSMichal Nowak }
1440cec8643bSMichal Nowak if (err != MANDOCERR_OK)
1441cec8643bSMichal Nowak mandoc_msg(err, ln, (int)(stesc - buf->buf),
1442260e9a87SYuri Pankov "%.*s", (int)(cp - stesc), stesc);
1443c66b8046SYuri Pankov stesc--;
144495c635efSGarrett D'Amore continue;
144595c635efSGarrett D'Amore }
144695c635efSGarrett D'Amore
1447260e9a87SYuri Pankov if (EXPAND_LIMIT < ++expand_count) {
1448cec8643bSMichal Nowak mandoc_msg(MANDOCERR_ROFFLOOP,
1449260e9a87SYuri Pankov ln, (int)(stesc - buf->buf), NULL);
1450371584c2SYuri Pankov return ROFF_IGN;
1451260e9a87SYuri Pankov }
145295c635efSGarrett D'Amore
145395c635efSGarrett D'Amore /*
145495c635efSGarrett D'Amore * The third character decides the length
1455698f87a4SGarrett D'Amore * of the name of the string or register.
145695c635efSGarrett D'Amore * Save a pointer to the name.
145795c635efSGarrett D'Amore */
145895c635efSGarrett D'Amore
1459260e9a87SYuri Pankov if (term == '\0') {
1460260e9a87SYuri Pankov switch (*++cp) {
1461260e9a87SYuri Pankov case '\0':
1462260e9a87SYuri Pankov maxl = 0;
1463260e9a87SYuri Pankov break;
1464260e9a87SYuri Pankov case '(':
146595c635efSGarrett D'Amore cp++;
146695c635efSGarrett D'Amore maxl = 2;
146795c635efSGarrett D'Amore break;
1468260e9a87SYuri Pankov case '[':
146995c635efSGarrett D'Amore cp++;
1470260e9a87SYuri Pankov term = ']';
147195c635efSGarrett D'Amore maxl = 0;
147295c635efSGarrett D'Amore break;
147395c635efSGarrett D'Amore default:
147495c635efSGarrett D'Amore maxl = 1;
147595c635efSGarrett D'Amore break;
147695c635efSGarrett D'Amore }
1477260e9a87SYuri Pankov } else {
1478260e9a87SYuri Pankov cp += 2;
1479260e9a87SYuri Pankov maxl = 0;
1480260e9a87SYuri Pankov }
148195c635efSGarrett D'Amore stnam = cp;
148295c635efSGarrett D'Amore
148395c635efSGarrett D'Amore /* Advance to the end of the name. */
148495c635efSGarrett D'Amore
1485260e9a87SYuri Pankov naml = 0;
1486260e9a87SYuri Pankov arg_complete = 1;
1487260e9a87SYuri Pankov while (maxl == 0 || naml < maxl) {
1488260e9a87SYuri Pankov if (*cp == '\0') {
1489cec8643bSMichal Nowak mandoc_msg(MANDOCERR_ESC_BAD, ln,
1490cec8643bSMichal Nowak (int)(stesc - buf->buf), "%s", stesc);
1491260e9a87SYuri Pankov arg_complete = 0;
149295c635efSGarrett D'Amore break;
149395c635efSGarrett D'Amore }
1494260e9a87SYuri Pankov if (maxl == 0 && *cp == term) {
1495260e9a87SYuri Pankov cp++;
1496260e9a87SYuri Pankov break;
1497260e9a87SYuri Pankov }
1498cec8643bSMichal Nowak if (*cp++ != '\\' || *esct != 'w') {
1499260e9a87SYuri Pankov naml++;
1500260e9a87SYuri Pankov continue;
1501260e9a87SYuri Pankov }
1502260e9a87SYuri Pankov switch (mandoc_escape(&cp, NULL, NULL)) {
1503260e9a87SYuri Pankov case ESCAPE_SPECIAL:
1504260e9a87SYuri Pankov case ESCAPE_UNICODE:
1505260e9a87SYuri Pankov case ESCAPE_NUMBERED:
1506cec8643bSMichal Nowak case ESCAPE_UNDEF:
1507260e9a87SYuri Pankov case ESCAPE_OVERSTRIKE:
1508260e9a87SYuri Pankov naml++;
1509260e9a87SYuri Pankov break;
1510260e9a87SYuri Pankov default:
1511260e9a87SYuri Pankov break;
1512260e9a87SYuri Pankov }
1513260e9a87SYuri Pankov }
151495c635efSGarrett D'Amore
151595c635efSGarrett D'Amore /*
151695c635efSGarrett D'Amore * Retrieve the replacement string; if it is
151795c635efSGarrett D'Amore * undefined, resume searching for escapes.
151895c635efSGarrett D'Amore */
151995c635efSGarrett D'Amore
1520cec8643bSMichal Nowak switch (*esct) {
1521260e9a87SYuri Pankov case '*':
1522c66b8046SYuri Pankov if (arg_complete) {
1523c66b8046SYuri Pankov deftype = ROFFDEF_USER | ROFFDEF_PRE;
1524c66b8046SYuri Pankov res = roff_getstrn(r, stnam, naml, &deftype);
1525cec8643bSMichal Nowak
1526cec8643bSMichal Nowak /*
1527cec8643bSMichal Nowak * If not overriden, let \*(.T
1528cec8643bSMichal Nowak * through to the formatters.
1529cec8643bSMichal Nowak */
1530cec8643bSMichal Nowak
1531cec8643bSMichal Nowak if (res == NULL && naml == 2 &&
1532cec8643bSMichal Nowak stnam[0] == '.' && stnam[1] == 'T') {
1533cec8643bSMichal Nowak roff_setstrn(&r->strtab,
1534cec8643bSMichal Nowak ".T", 2, NULL, 0, 0);
1535cec8643bSMichal Nowak stesc--;
1536cec8643bSMichal Nowak continue;
1537cec8643bSMichal Nowak }
1538c66b8046SYuri Pankov }
1539260e9a87SYuri Pankov break;
1540cec8643bSMichal Nowak case '$':
1541cec8643bSMichal Nowak if (r->mstackpos < 0) {
1542cec8643bSMichal Nowak mandoc_msg(MANDOCERR_ARG_UNDEF, ln,
1543cec8643bSMichal Nowak (int)(stesc - buf->buf), "%.3s", stesc);
1544cec8643bSMichal Nowak break;
1545cec8643bSMichal Nowak }
1546cec8643bSMichal Nowak ctx = r->mstack + r->mstackpos;
1547cec8643bSMichal Nowak npos = esct[1] - '1';
1548cec8643bSMichal Nowak if (npos >= 0 && npos <= 8) {
1549cec8643bSMichal Nowak res = npos < ctx->argc ?
1550cec8643bSMichal Nowak ctx->argv[npos] : "";
1551cec8643bSMichal Nowak break;
1552cec8643bSMichal Nowak }
1553cec8643bSMichal Nowak if (esct[1] == '*')
1554cec8643bSMichal Nowak quote_args = 0;
1555cec8643bSMichal Nowak else if (esct[1] == '@')
1556cec8643bSMichal Nowak quote_args = 1;
1557cec8643bSMichal Nowak else {
1558cec8643bSMichal Nowak mandoc_msg(MANDOCERR_ARG_NONUM, ln,
1559cec8643bSMichal Nowak (int)(stesc - buf->buf), "%.3s", stesc);
1560cec8643bSMichal Nowak break;
1561cec8643bSMichal Nowak }
1562cec8643bSMichal Nowak asz = 0;
1563cec8643bSMichal Nowak for (npos = 0; npos < ctx->argc; npos++) {
1564cec8643bSMichal Nowak if (npos)
1565cec8643bSMichal Nowak asz++; /* blank */
1566cec8643bSMichal Nowak if (quote_args)
1567cec8643bSMichal Nowak asz += 2; /* quotes */
1568cec8643bSMichal Nowak asz += strlen(ctx->argv[npos]);
1569cec8643bSMichal Nowak }
1570cec8643bSMichal Nowak if (asz != 3) {
1571cec8643bSMichal Nowak rsz = buf->sz - (stesc - buf->buf) - 3;
1572cec8643bSMichal Nowak if (asz < 3)
1573cec8643bSMichal Nowak memmove(stesc + asz, stesc + 3, rsz);
1574cec8643bSMichal Nowak buf->sz += asz - 3;
1575cec8643bSMichal Nowak nbuf = mandoc_realloc(buf->buf, buf->sz);
1576cec8643bSMichal Nowak start = nbuf + pos;
1577cec8643bSMichal Nowak stesc = nbuf + (stesc - buf->buf);
1578cec8643bSMichal Nowak buf->buf = nbuf;
1579cec8643bSMichal Nowak if (asz > 3)
1580cec8643bSMichal Nowak memmove(stesc + asz, stesc + 3, rsz);
1581cec8643bSMichal Nowak }
1582cec8643bSMichal Nowak for (npos = 0; npos < ctx->argc; npos++) {
1583cec8643bSMichal Nowak if (npos)
1584cec8643bSMichal Nowak *stesc++ = ' ';
1585cec8643bSMichal Nowak if (quote_args)
1586cec8643bSMichal Nowak *stesc++ = '"';
1587cec8643bSMichal Nowak cp = ctx->argv[npos];
1588cec8643bSMichal Nowak while (*cp != '\0')
1589cec8643bSMichal Nowak *stesc++ = *cp++;
1590cec8643bSMichal Nowak if (quote_args)
1591cec8643bSMichal Nowak *stesc++ = '"';
1592cec8643bSMichal Nowak }
1593cec8643bSMichal Nowak continue;
1594260e9a87SYuri Pankov case 'B':
1595260e9a87SYuri Pankov npos = 0;
1596260e9a87SYuri Pankov ubuf[0] = arg_complete &&
1597260e9a87SYuri Pankov roff_evalnum(r, ln, stnam, &npos,
1598260e9a87SYuri Pankov NULL, ROFFNUM_SCALE) &&
1599260e9a87SYuri Pankov stnam + npos + 1 == cp ? '1' : '0';
1600260e9a87SYuri Pankov ubuf[1] = '\0';
1601260e9a87SYuri Pankov break;
1602260e9a87SYuri Pankov case 'n':
1603260e9a87SYuri Pankov if (arg_complete)
1604260e9a87SYuri Pankov (void)snprintf(ubuf, sizeof(ubuf), "%d",
16056640c13bSYuri Pankov roff_getregn(r, stnam, naml, sign));
1606260e9a87SYuri Pankov else
1607260e9a87SYuri Pankov ubuf[0] = '\0';
1608260e9a87SYuri Pankov break;
1609260e9a87SYuri Pankov case 'w':
1610260e9a87SYuri Pankov /* use even incomplete args */
1611260e9a87SYuri Pankov (void)snprintf(ubuf, sizeof(ubuf), "%d",
1612260e9a87SYuri Pankov 24 * (int)naml);
1613260e9a87SYuri Pankov break;
1614260e9a87SYuri Pankov }
161595c635efSGarrett D'Amore
1616260e9a87SYuri Pankov if (res == NULL) {
1617cec8643bSMichal Nowak if (*esct == '*')
1618cec8643bSMichal Nowak mandoc_msg(MANDOCERR_STR_UNDEF,
1619cec8643bSMichal Nowak ln, (int)(stesc - buf->buf),
1620260e9a87SYuri Pankov "%.*s", (int)naml, stnam);
162195c635efSGarrett D'Amore res = "";
1622260e9a87SYuri Pankov } else if (buf->sz + strlen(res) > SHRT_MAX) {
1623cec8643bSMichal Nowak mandoc_msg(MANDOCERR_ROFFLOOP,
1624260e9a87SYuri Pankov ln, (int)(stesc - buf->buf), NULL);
1625371584c2SYuri Pankov return ROFF_IGN;
162695c635efSGarrett D'Amore }
162795c635efSGarrett D'Amore
162895c635efSGarrett D'Amore /* Replace the escape sequence by the string. */
162995c635efSGarrett D'Amore
1630260e9a87SYuri Pankov *stesc = '\0';
1631260e9a87SYuri Pankov buf->sz = mandoc_asprintf(&nbuf, "%s%s%s",
1632260e9a87SYuri Pankov buf->buf, res, cp) + 1;
163395c635efSGarrett D'Amore
1634260e9a87SYuri Pankov /* Prepare for the next replacement. */
163595c635efSGarrett D'Amore
1636260e9a87SYuri Pankov start = nbuf + pos;
1637260e9a87SYuri Pankov stesc = nbuf + (stesc - buf->buf) + strlen(res);
1638260e9a87SYuri Pankov free(buf->buf);
1639260e9a87SYuri Pankov buf->buf = nbuf;
164095c635efSGarrett D'Amore }
1641371584c2SYuri Pankov return ROFF_CONT;
164295c635efSGarrett D'Amore }
164395c635efSGarrett D'Amore
164495c635efSGarrett D'Amore /*
1645cec8643bSMichal Nowak * Parse a quoted or unquoted roff-style request or macro argument.
1646cec8643bSMichal Nowak * Return a pointer to the parsed argument, which is either the original
1647cec8643bSMichal Nowak * pointer or advanced by one byte in case the argument is quoted.
1648cec8643bSMichal Nowak * NUL-terminate the argument in place.
1649cec8643bSMichal Nowak * Collapse pairs of quotes inside quoted arguments.
1650cec8643bSMichal Nowak * Advance the argument pointer to the next argument,
1651cec8643bSMichal Nowak * or to the NUL byte terminating the argument line.
1652cec8643bSMichal Nowak */
1653cec8643bSMichal Nowak char *
roff_getarg(struct roff * r,char ** cpp,int ln,int * pos)1654cec8643bSMichal Nowak roff_getarg(struct roff *r, char **cpp, int ln, int *pos)
1655cec8643bSMichal Nowak {
1656cec8643bSMichal Nowak struct buf buf;
1657cec8643bSMichal Nowak char *cp, *start;
1658cec8643bSMichal Nowak int newesc, pairs, quoted, white;
1659cec8643bSMichal Nowak
1660cec8643bSMichal Nowak /* Quoting can only start with a new word. */
1661cec8643bSMichal Nowak start = *cpp;
1662cec8643bSMichal Nowak quoted = 0;
1663cec8643bSMichal Nowak if ('"' == *start) {
1664cec8643bSMichal Nowak quoted = 1;
1665cec8643bSMichal Nowak start++;
1666cec8643bSMichal Nowak }
1667cec8643bSMichal Nowak
1668cec8643bSMichal Nowak newesc = pairs = white = 0;
1669cec8643bSMichal Nowak for (cp = start; '\0' != *cp; cp++) {
1670cec8643bSMichal Nowak
1671cec8643bSMichal Nowak /*
1672cec8643bSMichal Nowak * Move the following text left
1673cec8643bSMichal Nowak * after quoted quotes and after "\\" and "\t".
1674cec8643bSMichal Nowak */
1675cec8643bSMichal Nowak if (pairs)
1676cec8643bSMichal Nowak cp[-pairs] = cp[0];
1677cec8643bSMichal Nowak
1678cec8643bSMichal Nowak if ('\\' == cp[0]) {
1679cec8643bSMichal Nowak /*
1680cec8643bSMichal Nowak * In copy mode, translate double to single
1681cec8643bSMichal Nowak * backslashes and backslash-t to literal tabs.
1682cec8643bSMichal Nowak */
1683cec8643bSMichal Nowak switch (cp[1]) {
1684cec8643bSMichal Nowak case 'a':
1685cec8643bSMichal Nowak case 't':
1686cec8643bSMichal Nowak cp[-pairs] = '\t';
1687cec8643bSMichal Nowak pairs++;
1688cec8643bSMichal Nowak cp++;
1689cec8643bSMichal Nowak break;
1690cec8643bSMichal Nowak case '\\':
1691cec8643bSMichal Nowak newesc = 1;
1692cec8643bSMichal Nowak cp[-pairs] = ASCII_ESC;
1693cec8643bSMichal Nowak pairs++;
1694cec8643bSMichal Nowak cp++;
1695cec8643bSMichal Nowak break;
1696cec8643bSMichal Nowak case ' ':
1697cec8643bSMichal Nowak /* Skip escaped blanks. */
1698cec8643bSMichal Nowak if (0 == quoted)
1699cec8643bSMichal Nowak cp++;
1700cec8643bSMichal Nowak break;
1701cec8643bSMichal Nowak default:
1702cec8643bSMichal Nowak break;
1703cec8643bSMichal Nowak }
1704cec8643bSMichal Nowak } else if (0 == quoted) {
1705cec8643bSMichal Nowak if (' ' == cp[0]) {
1706cec8643bSMichal Nowak /* Unescaped blanks end unquoted args. */
1707cec8643bSMichal Nowak white = 1;
1708cec8643bSMichal Nowak break;
1709cec8643bSMichal Nowak }
1710cec8643bSMichal Nowak } else if ('"' == cp[0]) {
1711cec8643bSMichal Nowak if ('"' == cp[1]) {
1712cec8643bSMichal Nowak /* Quoted quotes collapse. */
1713cec8643bSMichal Nowak pairs++;
1714cec8643bSMichal Nowak cp++;
1715cec8643bSMichal Nowak } else {
1716cec8643bSMichal Nowak /* Unquoted quotes end quoted args. */
1717cec8643bSMichal Nowak quoted = 2;
1718cec8643bSMichal Nowak break;
1719cec8643bSMichal Nowak }
1720cec8643bSMichal Nowak }
1721cec8643bSMichal Nowak }
1722cec8643bSMichal Nowak
1723cec8643bSMichal Nowak /* Quoted argument without a closing quote. */
1724cec8643bSMichal Nowak if (1 == quoted)
1725cec8643bSMichal Nowak mandoc_msg(MANDOCERR_ARG_QUOTE, ln, *pos, NULL);
1726cec8643bSMichal Nowak
1727cec8643bSMichal Nowak /* NUL-terminate this argument and move to the next one. */
1728cec8643bSMichal Nowak if (pairs)
1729cec8643bSMichal Nowak cp[-pairs] = '\0';
1730cec8643bSMichal Nowak if ('\0' != *cp) {
1731cec8643bSMichal Nowak *cp++ = '\0';
1732cec8643bSMichal Nowak while (' ' == *cp)
1733cec8643bSMichal Nowak cp++;
1734cec8643bSMichal Nowak }
1735cec8643bSMichal Nowak *pos += (int)(cp - start) + (quoted ? 1 : 0);
1736cec8643bSMichal Nowak *cpp = cp;
1737cec8643bSMichal Nowak
1738cec8643bSMichal Nowak if ('\0' == *cp && (white || ' ' == cp[-1]))
1739cec8643bSMichal Nowak mandoc_msg(MANDOCERR_SPACE_EOL, ln, *pos, NULL);
1740cec8643bSMichal Nowak
1741cec8643bSMichal Nowak start = mandoc_strdup(start);
1742cec8643bSMichal Nowak if (newesc == 0)
1743cec8643bSMichal Nowak return start;
1744cec8643bSMichal Nowak
1745cec8643bSMichal Nowak buf.buf = start;
1746cec8643bSMichal Nowak buf.sz = strlen(start) + 1;
1747cec8643bSMichal Nowak buf.next = NULL;
1748cec8643bSMichal Nowak if (roff_expand(r, &buf, ln, 0, ASCII_ESC) & ROFF_IGN) {
1749cec8643bSMichal Nowak free(buf.buf);
1750cec8643bSMichal Nowak buf.buf = mandoc_strdup("");
1751cec8643bSMichal Nowak }
1752cec8643bSMichal Nowak return buf.buf;
1753cec8643bSMichal Nowak }
1754cec8643bSMichal Nowak
1755cec8643bSMichal Nowak
1756cec8643bSMichal Nowak /*
1757371584c2SYuri Pankov * Process text streams.
175895c635efSGarrett D'Amore */
1759cec8643bSMichal Nowak static int
roff_parsetext(struct roff * r,struct buf * buf,int pos,int * offs)1760c66b8046SYuri Pankov roff_parsetext(struct roff *r, struct buf *buf, int pos, int *offs)
176195c635efSGarrett D'Amore {
176295c635efSGarrett D'Amore size_t sz;
176395c635efSGarrett D'Amore const char *start;
1764698f87a4SGarrett D'Amore char *p;
1765698f87a4SGarrett D'Amore int isz;
176695c635efSGarrett D'Amore enum mandoc_esc esc;
176795c635efSGarrett D'Amore
1768371584c2SYuri Pankov /* Spring the input line trap. */
1769371584c2SYuri Pankov
1770371584c2SYuri Pankov if (roffit_lines == 1) {
1771371584c2SYuri Pankov isz = mandoc_asprintf(&p, "%s\n.%s", buf->buf, roffit_macro);
1772371584c2SYuri Pankov free(buf->buf);
1773371584c2SYuri Pankov buf->buf = p;
1774371584c2SYuri Pankov buf->sz = isz + 1;
1775371584c2SYuri Pankov *offs = 0;
1776371584c2SYuri Pankov free(roffit_macro);
1777371584c2SYuri Pankov roffit_lines = 0;
1778371584c2SYuri Pankov return ROFF_REPARSE;
1779371584c2SYuri Pankov } else if (roffit_lines > 1)
1780371584c2SYuri Pankov --roffit_lines;
1781371584c2SYuri Pankov
1782c66b8046SYuri Pankov if (roffce_node != NULL && buf->buf[pos] != '\0') {
1783c66b8046SYuri Pankov if (roffce_lines < 1) {
1784c66b8046SYuri Pankov r->man->last = roffce_node;
1785c66b8046SYuri Pankov r->man->next = ROFF_NEXT_SIBLING;
1786c66b8046SYuri Pankov roffce_lines = 0;
1787c66b8046SYuri Pankov roffce_node = NULL;
1788c66b8046SYuri Pankov } else
1789c66b8046SYuri Pankov roffce_lines--;
1790c66b8046SYuri Pankov }
1791c66b8046SYuri Pankov
1792371584c2SYuri Pankov /* Convert all breakable hyphens into ASCII_HYPH. */
1793371584c2SYuri Pankov
1794260e9a87SYuri Pankov start = p = buf->buf + pos;
179595c635efSGarrett D'Amore
1796260e9a87SYuri Pankov while (*p != '\0') {
179795c635efSGarrett D'Amore sz = strcspn(p, "-\\");
179895c635efSGarrett D'Amore p += sz;
179995c635efSGarrett D'Amore
1800260e9a87SYuri Pankov if (*p == '\0')
180195c635efSGarrett D'Amore break;
180295c635efSGarrett D'Amore
1803260e9a87SYuri Pankov if (*p == '\\') {
180495c635efSGarrett D'Amore /* Skip over escapes. */
180595c635efSGarrett D'Amore p++;
1806698f87a4SGarrett D'Amore esc = mandoc_escape((const char **)&p, NULL, NULL);
1807260e9a87SYuri Pankov if (esc == ESCAPE_ERROR)
180895c635efSGarrett D'Amore break;
1809371584c2SYuri Pankov while (*p == '-')
1810371584c2SYuri Pankov p++;
181195c635efSGarrett D'Amore continue;
181295c635efSGarrett D'Amore } else if (p == start) {
181395c635efSGarrett D'Amore p++;
181495c635efSGarrett D'Amore continue;
181595c635efSGarrett D'Amore }
181695c635efSGarrett D'Amore
181795c635efSGarrett D'Amore if (isalpha((unsigned char)p[-1]) &&
181895c635efSGarrett D'Amore isalpha((unsigned char)p[1]))
181995c635efSGarrett D'Amore *p = ASCII_HYPH;
182095c635efSGarrett D'Amore p++;
182195c635efSGarrett D'Amore }
1822371584c2SYuri Pankov return ROFF_CONT;
182395c635efSGarrett D'Amore }
182495c635efSGarrett D'Amore
1825cec8643bSMichal Nowak int
roff_parseln(struct roff * r,int ln,struct buf * buf,int * offs,size_t len)1826*4d131170SRobert Mustacchi roff_parseln(struct roff *r, int ln, struct buf *buf, int *offs, size_t len)
182795c635efSGarrett D'Amore {
1828c66b8046SYuri Pankov enum roff_tok t;
1829cec8643bSMichal Nowak int e;
1830260e9a87SYuri Pankov int pos; /* parse point */
1831260e9a87SYuri Pankov int spos; /* saved parse point for messages */
1832260e9a87SYuri Pankov int ppos; /* original offset in buf->buf */
1833260e9a87SYuri Pankov int ctl; /* macro line (boolean) */
183495c635efSGarrett D'Amore
1835260e9a87SYuri Pankov ppos = pos = *offs;
183695c635efSGarrett D'Amore
1837*4d131170SRobert Mustacchi if (len > 80 && r->tbl == NULL && r->eqn == NULL &&
1838*4d131170SRobert Mustacchi (r->man->flags & ROFF_NOFILL) == 0 &&
1839*4d131170SRobert Mustacchi strchr(" .\\", buf->buf[pos]) == NULL &&
1840*4d131170SRobert Mustacchi buf->buf[pos] != r->control &&
1841*4d131170SRobert Mustacchi strcspn(buf->buf, " ") < 80)
1842*4d131170SRobert Mustacchi mandoc_msg(MANDOCERR_TEXT_LONG, ln, (int)len - 1,
1843*4d131170SRobert Mustacchi "%.20s...", buf->buf + pos);
1844*4d131170SRobert Mustacchi
1845260e9a87SYuri Pankov /* Handle in-line equation delimiters. */
1846260e9a87SYuri Pankov
1847260e9a87SYuri Pankov if (r->tbl == NULL &&
1848260e9a87SYuri Pankov r->last_eqn != NULL && r->last_eqn->delim &&
1849260e9a87SYuri Pankov (r->eqn == NULL || r->eqn_inline)) {
1850260e9a87SYuri Pankov e = roff_eqndelim(r, buf, pos);
1851260e9a87SYuri Pankov if (e == ROFF_REPARSE)
1852371584c2SYuri Pankov return e;
1853260e9a87SYuri Pankov assert(e == ROFF_CONT);
1854260e9a87SYuri Pankov }
185595c635efSGarrett D'Amore
1856260e9a87SYuri Pankov /* Expand some escape sequences. */
1857260e9a87SYuri Pankov
1858cec8643bSMichal Nowak e = roff_expand(r, buf, ln, pos, r->escape);
1859cec8643bSMichal Nowak if ((e & ROFF_MASK) == ROFF_IGN)
1860371584c2SYuri Pankov return e;
1861260e9a87SYuri Pankov assert(e == ROFF_CONT);
1862260e9a87SYuri Pankov
1863260e9a87SYuri Pankov ctl = roff_getcontrol(r, buf->buf, &pos);
186495c635efSGarrett D'Amore
186595c635efSGarrett D'Amore /*
186695c635efSGarrett D'Amore * First, if a scope is open and we're not a macro, pass the
1867260e9a87SYuri Pankov * text through the macro's filter.
1868260e9a87SYuri Pankov * Equations process all content themselves.
1869260e9a87SYuri Pankov * Tables process almost all content themselves, but we want
1870260e9a87SYuri Pankov * to warn about macros before passing it there.
187195c635efSGarrett D'Amore */
187295c635efSGarrett D'Amore
1873260e9a87SYuri Pankov if (r->last != NULL && ! ctl) {
187495c635efSGarrett D'Amore t = r->last->tok;
1875260e9a87SYuri Pankov e = (*roffs[t].text)(r, t, buf, ln, pos, pos, offs);
1876cec8643bSMichal Nowak if ((e & ROFF_MASK) == ROFF_IGN)
1877371584c2SYuri Pankov return e;
1878cec8643bSMichal Nowak e &= ~ROFF_MASK;
1879cec8643bSMichal Nowak } else
1880cec8643bSMichal Nowak e = ROFF_IGN;
1881c66b8046SYuri Pankov if (r->eqn != NULL && strncmp(buf->buf + ppos, ".EN", 3)) {
1882c66b8046SYuri Pankov eqn_read(r->eqn, buf->buf + ppos);
1883cec8643bSMichal Nowak return e;
1884c66b8046SYuri Pankov }
1885c66b8046SYuri Pankov if (r->tbl != NULL && (ctl == 0 || buf->buf[pos] == '\0')) {
1886c66b8046SYuri Pankov tbl_read(r->tbl, ln, buf->buf, ppos);
1887cec8643bSMichal Nowak roff_addtbl(r->man, ln, r->tbl);
1888cec8643bSMichal Nowak return e;
1889c66b8046SYuri Pankov }
1890*4d131170SRobert Mustacchi if ( ! ctl) {
1891*4d131170SRobert Mustacchi r->options &= ~MPARSE_COMMENT;
1892cec8643bSMichal Nowak return roff_parsetext(r, buf, pos, offs) | e;
1893*4d131170SRobert Mustacchi }
1894260e9a87SYuri Pankov
1895260e9a87SYuri Pankov /* Skip empty request lines. */
1896260e9a87SYuri Pankov
1897260e9a87SYuri Pankov if (buf->buf[pos] == '"') {
1898cec8643bSMichal Nowak mandoc_msg(MANDOCERR_COMMENT_BAD, ln, pos, NULL);
1899371584c2SYuri Pankov return ROFF_IGN;
1900260e9a87SYuri Pankov } else if (buf->buf[pos] == '\0')
1901371584c2SYuri Pankov return ROFF_IGN;
190295c635efSGarrett D'Amore
190395c635efSGarrett D'Amore /*
190495c635efSGarrett D'Amore * If a scope is open, go to the child handler for that macro,
190595c635efSGarrett D'Amore * as it may want to preprocess before doing anything with it.
190695c635efSGarrett D'Amore * Don't do so if an equation is open.
190795c635efSGarrett D'Amore */
190895c635efSGarrett D'Amore
190995c635efSGarrett D'Amore if (r->last) {
191095c635efSGarrett D'Amore t = r->last->tok;
1911371584c2SYuri Pankov return (*roffs[t].sub)(r, t, buf, ln, ppos, pos, offs);
1912260e9a87SYuri Pankov }
1913260e9a87SYuri Pankov
1914260e9a87SYuri Pankov /* No scope is open. This is a new request or macro. */
1915260e9a87SYuri Pankov
1916*4d131170SRobert Mustacchi r->options &= ~MPARSE_COMMENT;
1917260e9a87SYuri Pankov spos = pos;
1918260e9a87SYuri Pankov t = roff_parse(r, buf->buf, &pos, ln, ppos);
1919260e9a87SYuri Pankov
1920260e9a87SYuri Pankov /* Tables ignore most macros. */
1921260e9a87SYuri Pankov
1922c66b8046SYuri Pankov if (r->tbl != NULL && (t == TOKEN_NONE || t == ROFF_TS ||
1923c66b8046SYuri Pankov t == ROFF_br || t == ROFF_ce || t == ROFF_rj || t == ROFF_sp)) {
1924cec8643bSMichal Nowak mandoc_msg(MANDOCERR_TBLMACRO,
1925cec8643bSMichal Nowak ln, pos, "%s", buf->buf + spos);
1926c66b8046SYuri Pankov if (t != TOKEN_NONE)
1927371584c2SYuri Pankov return ROFF_IGN;
1928260e9a87SYuri Pankov while (buf->buf[pos] != '\0' && buf->buf[pos] != ' ')
1929260e9a87SYuri Pankov pos++;
1930c66b8046SYuri Pankov while (buf->buf[pos] == ' ')
1931260e9a87SYuri Pankov pos++;
1932c66b8046SYuri Pankov tbl_read(r->tbl, ln, buf->buf, pos);
1933cec8643bSMichal Nowak roff_addtbl(r->man, ln, r->tbl);
1934c66b8046SYuri Pankov return ROFF_IGN;
1935c66b8046SYuri Pankov }
1936c66b8046SYuri Pankov
1937c66b8046SYuri Pankov /* For now, let high level macros abort .ce mode. */
1938c66b8046SYuri Pankov
1939c66b8046SYuri Pankov if (ctl && roffce_node != NULL &&
1940c66b8046SYuri Pankov (t == TOKEN_NONE || t == ROFF_Dd || t == ROFF_EQ ||
1941c66b8046SYuri Pankov t == ROFF_TH || t == ROFF_TS)) {
1942c66b8046SYuri Pankov r->man->last = roffce_node;
1943c66b8046SYuri Pankov r->man->next = ROFF_NEXT_SIBLING;
1944c66b8046SYuri Pankov roffce_lines = 0;
1945c66b8046SYuri Pankov roffce_node = NULL;
194695c635efSGarrett D'Amore }
194795c635efSGarrett D'Amore
194895c635efSGarrett D'Amore /*
1949260e9a87SYuri Pankov * This is neither a roff request nor a user-defined macro.
1950260e9a87SYuri Pankov * Let the standard macro set parsers handle it.
195195c635efSGarrett D'Amore */
195295c635efSGarrett D'Amore
1953c66b8046SYuri Pankov if (t == TOKEN_NONE)
1954371584c2SYuri Pankov return ROFF_CONT;
195595c635efSGarrett D'Amore
1956260e9a87SYuri Pankov /* Execute a roff request or a user defined macro. */
195795c635efSGarrett D'Amore
1958c66b8046SYuri Pankov return (*roffs[t].proc)(r, t, buf, ln, spos, pos, offs);
1959260e9a87SYuri Pankov }
196095c635efSGarrett D'Amore
1961cec8643bSMichal Nowak /*
1962cec8643bSMichal Nowak * Internal interface function to tell the roff parser that execution
1963cec8643bSMichal Nowak * of the current macro ended. This is required because macro
1964cec8643bSMichal Nowak * definitions usually do not end with a .return request.
1965cec8643bSMichal Nowak */
1966cec8643bSMichal Nowak void
roff_userret(struct roff * r)1967cec8643bSMichal Nowak roff_userret(struct roff *r)
1968cec8643bSMichal Nowak {
1969cec8643bSMichal Nowak struct mctx *ctx;
1970cec8643bSMichal Nowak int i;
1971cec8643bSMichal Nowak
1972cec8643bSMichal Nowak assert(r->mstackpos >= 0);
1973cec8643bSMichal Nowak ctx = r->mstack + r->mstackpos;
1974cec8643bSMichal Nowak for (i = 0; i < ctx->argc; i++)
1975cec8643bSMichal Nowak free(ctx->argv[i]);
1976cec8643bSMichal Nowak ctx->argc = 0;
1977cec8643bSMichal Nowak r->mstackpos--;
1978cec8643bSMichal Nowak }
1979cec8643bSMichal Nowak
198095c635efSGarrett D'Amore void
roff_endparse(struct roff * r)198195c635efSGarrett D'Amore roff_endparse(struct roff *r)
198295c635efSGarrett D'Amore {
1983c66b8046SYuri Pankov if (r->last != NULL)
1984cec8643bSMichal Nowak mandoc_msg(MANDOCERR_BLK_NOEND, r->last->line,
1985cec8643bSMichal Nowak r->last->col, "%s", roff_name[r->last->tok]);
198695c635efSGarrett D'Amore
1987c66b8046SYuri Pankov if (r->eqn != NULL) {
1988cec8643bSMichal Nowak mandoc_msg(MANDOCERR_BLK_NOEND,
1989c66b8046SYuri Pankov r->eqn->node->line, r->eqn->node->pos, "EQ");
1990c66b8046SYuri Pankov eqn_parse(r->eqn);
1991c66b8046SYuri Pankov r->eqn = NULL;
199295c635efSGarrett D'Amore }
199395c635efSGarrett D'Amore
1994c66b8046SYuri Pankov if (r->tbl != NULL) {
1995cec8643bSMichal Nowak tbl_end(r->tbl, 1);
1996c66b8046SYuri Pankov r->tbl = NULL;
199795c635efSGarrett D'Amore }
199895c635efSGarrett D'Amore }
199995c635efSGarrett D'Amore
200095c635efSGarrett D'Amore /*
200195c635efSGarrett D'Amore * Parse a roff node's type from the input buffer. This must be in the
200295c635efSGarrett D'Amore * form of ".foo xxx" in the usual way.
200395c635efSGarrett D'Amore */
2004c66b8046SYuri Pankov static enum roff_tok
roff_parse(struct roff * r,char * buf,int * pos,int ln,int ppos)2005260e9a87SYuri Pankov roff_parse(struct roff *r, char *buf, int *pos, int ln, int ppos)
200695c635efSGarrett D'Amore {
2007260e9a87SYuri Pankov char *cp;
200895c635efSGarrett D'Amore const char *mac;
200995c635efSGarrett D'Amore size_t maclen;
2010c66b8046SYuri Pankov int deftype;
2011c66b8046SYuri Pankov enum roff_tok t;
201295c635efSGarrett D'Amore
2013260e9a87SYuri Pankov cp = buf + *pos;
2014260e9a87SYuri Pankov
2015260e9a87SYuri Pankov if ('\0' == *cp || '"' == *cp || '\t' == *cp || ' ' == *cp)
2016c66b8046SYuri Pankov return TOKEN_NONE;
201795c635efSGarrett D'Amore
2018260e9a87SYuri Pankov mac = cp;
2019260e9a87SYuri Pankov maclen = roff_getname(r, &cp, ln, ppos);
202095c635efSGarrett D'Amore
2021c66b8046SYuri Pankov deftype = ROFFDEF_USER | ROFFDEF_REN;
2022c66b8046SYuri Pankov r->current_string = roff_getstrn(r, mac, maclen, &deftype);
2023c66b8046SYuri Pankov switch (deftype) {
2024c66b8046SYuri Pankov case ROFFDEF_USER:
2025c66b8046SYuri Pankov t = ROFF_USERDEF;
2026c66b8046SYuri Pankov break;
2027c66b8046SYuri Pankov case ROFFDEF_REN:
2028c66b8046SYuri Pankov t = ROFF_RENAMED;
2029c66b8046SYuri Pankov break;
2030c66b8046SYuri Pankov default:
2031c66b8046SYuri Pankov t = roffhash_find(r->reqtab, mac, maclen);
2032c66b8046SYuri Pankov break;
2033c66b8046SYuri Pankov }
2034c66b8046SYuri Pankov if (t != TOKEN_NONE)
2035260e9a87SYuri Pankov *pos = cp - buf;
20366640c13bSYuri Pankov else if (deftype == ROFFDEF_UNDEF) {
20376640c13bSYuri Pankov /* Using an undefined macro defines it to be empty. */
20386640c13bSYuri Pankov roff_setstrn(&r->strtab, mac, maclen, "", 0, 0);
20396640c13bSYuri Pankov roff_setstrn(&r->rentab, mac, maclen, NULL, 0, 0);
20406640c13bSYuri Pankov }
2041371584c2SYuri Pankov return t;
204295c635efSGarrett D'Amore }
204395c635efSGarrett D'Amore
2044371584c2SYuri Pankov /* --- handling of request blocks ----------------------------------------- */
2045371584c2SYuri Pankov
2046*4d131170SRobert Mustacchi /*
2047*4d131170SRobert Mustacchi * Close a macro definition block or an "ignore" block.
2048*4d131170SRobert Mustacchi */
2049cec8643bSMichal Nowak static int
roff_cblock(ROFF_ARGS)205095c635efSGarrett D'Amore roff_cblock(ROFF_ARGS)
205195c635efSGarrett D'Amore {
2052*4d131170SRobert Mustacchi int rr;
205395c635efSGarrett D'Amore
2054260e9a87SYuri Pankov if (r->last == NULL) {
2055cec8643bSMichal Nowak mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "..");
2056371584c2SYuri Pankov return ROFF_IGN;
205795c635efSGarrett D'Amore }
205895c635efSGarrett D'Amore
205995c635efSGarrett D'Amore switch (r->last->tok) {
2060260e9a87SYuri Pankov case ROFF_am:
2061260e9a87SYuri Pankov case ROFF_ami:
2062260e9a87SYuri Pankov case ROFF_de:
2063260e9a87SYuri Pankov case ROFF_dei:
2064260e9a87SYuri Pankov case ROFF_ig:
206595c635efSGarrett D'Amore break;
2066*4d131170SRobert Mustacchi case ROFF_am1:
2067*4d131170SRobert Mustacchi case ROFF_de1:
2068*4d131170SRobert Mustacchi /* Remapped in roff_block(). */
2069*4d131170SRobert Mustacchi abort();
207095c635efSGarrett D'Amore default:
2071cec8643bSMichal Nowak mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "..");
2072371584c2SYuri Pankov return ROFF_IGN;
207395c635efSGarrett D'Amore }
207495c635efSGarrett D'Amore
2075*4d131170SRobert Mustacchi roffnode_pop(r);
2076*4d131170SRobert Mustacchi roffnode_cleanscope(r);
2077*4d131170SRobert Mustacchi
2078*4d131170SRobert Mustacchi /*
2079*4d131170SRobert Mustacchi * If a conditional block with braces is still open,
2080*4d131170SRobert Mustacchi * check for "\}" block end markers.
2081*4d131170SRobert Mustacchi */
2082*4d131170SRobert Mustacchi
2083*4d131170SRobert Mustacchi if (r->last != NULL && r->last->endspan < 0) {
2084*4d131170SRobert Mustacchi rr = 1; /* If arguments follow "\}", warn about them. */
2085*4d131170SRobert Mustacchi roff_cond_checkend(r, tok, buf, ln, ppos, pos, &rr);
2086*4d131170SRobert Mustacchi }
2087*4d131170SRobert Mustacchi
2088260e9a87SYuri Pankov if (buf->buf[pos] != '\0')
2089cec8643bSMichal Nowak mandoc_msg(MANDOCERR_ARG_SKIP, ln, pos,
2090260e9a87SYuri Pankov ".. %s", buf->buf + pos);
209195c635efSGarrett D'Amore
2092371584c2SYuri Pankov return ROFF_IGN;
209395c635efSGarrett D'Amore }
209495c635efSGarrett D'Amore
2095*4d131170SRobert Mustacchi /*
2096*4d131170SRobert Mustacchi * Pop all nodes ending at the end of the current input line.
2097*4d131170SRobert Mustacchi * Return the number of loops ended.
2098*4d131170SRobert Mustacchi */
2099cec8643bSMichal Nowak static int
roffnode_cleanscope(struct roff * r)210095c635efSGarrett D'Amore roffnode_cleanscope(struct roff *r)
210195c635efSGarrett D'Amore {
2102cec8643bSMichal Nowak int inloop;
210395c635efSGarrett D'Amore
2104cec8643bSMichal Nowak inloop = 0;
2105*4d131170SRobert Mustacchi while (r->last != NULL && r->last->endspan > 0) {
2106698f87a4SGarrett D'Amore if (--r->last->endspan != 0)
210795c635efSGarrett D'Amore break;
2108cec8643bSMichal Nowak inloop += roffnode_pop(r);
210995c635efSGarrett D'Amore }
2110cec8643bSMichal Nowak return inloop;
211195c635efSGarrett D'Amore }
211295c635efSGarrett D'Amore
2113*4d131170SRobert Mustacchi /*
2114*4d131170SRobert Mustacchi * Handle the closing "\}" of a conditional block.
2115*4d131170SRobert Mustacchi * Apart from generating warnings, this only pops nodes.
2116*4d131170SRobert Mustacchi * Return the number of loops ended.
2117*4d131170SRobert Mustacchi */
2118cec8643bSMichal Nowak static int
roff_ccond(struct roff * r,int ln,int ppos)2119260e9a87SYuri Pankov roff_ccond(struct roff *r, int ln, int ppos)
212095c635efSGarrett D'Amore {
212195c635efSGarrett D'Amore if (NULL == r->last) {
2122cec8643bSMichal Nowak mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "\\}");
2123cec8643bSMichal Nowak return 0;
212495c635efSGarrett D'Amore }
212595c635efSGarrett D'Amore
212695c635efSGarrett D'Amore switch (r->last->tok) {
2127260e9a87SYuri Pankov case ROFF_el:
2128260e9a87SYuri Pankov case ROFF_ie:
2129260e9a87SYuri Pankov case ROFF_if:
2130cec8643bSMichal Nowak case ROFF_while:
213195c635efSGarrett D'Amore break;
213295c635efSGarrett D'Amore default:
2133cec8643bSMichal Nowak mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "\\}");
2134cec8643bSMichal Nowak return 0;
213595c635efSGarrett D'Amore }
213695c635efSGarrett D'Amore
213795c635efSGarrett D'Amore if (r->last->endspan > -1) {
2138cec8643bSMichal Nowak mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "\\}");
2139cec8643bSMichal Nowak return 0;
214095c635efSGarrett D'Amore }
214195c635efSGarrett D'Amore
2142cec8643bSMichal Nowak return roffnode_pop(r) + roffnode_cleanscope(r);
214395c635efSGarrett D'Amore }
214495c635efSGarrett D'Amore
2145cec8643bSMichal Nowak static int
roff_block(ROFF_ARGS)214695c635efSGarrett D'Amore roff_block(ROFF_ARGS)
214795c635efSGarrett D'Amore {
2148c66b8046SYuri Pankov const char *name, *value;
2149c66b8046SYuri Pankov char *call, *cp, *iname, *rname;
2150c66b8046SYuri Pankov size_t csz, namesz, rsz;
2151c66b8046SYuri Pankov int deftype;
215295c635efSGarrett D'Amore
2153260e9a87SYuri Pankov /* Ignore groff compatibility mode for now. */
215495c635efSGarrett D'Amore
2155260e9a87SYuri Pankov if (tok == ROFF_de1)
2156260e9a87SYuri Pankov tok = ROFF_de;
2157260e9a87SYuri Pankov else if (tok == ROFF_dei1)
2158260e9a87SYuri Pankov tok = ROFF_dei;
2159260e9a87SYuri Pankov else if (tok == ROFF_am1)
2160260e9a87SYuri Pankov tok = ROFF_am;
2161260e9a87SYuri Pankov else if (tok == ROFF_ami1)
2162260e9a87SYuri Pankov tok = ROFF_ami;
2163260e9a87SYuri Pankov
2164260e9a87SYuri Pankov /* Parse the macro name argument. */
2165260e9a87SYuri Pankov
2166260e9a87SYuri Pankov cp = buf->buf + pos;
2167260e9a87SYuri Pankov if (tok == ROFF_ig) {
2168260e9a87SYuri Pankov iname = NULL;
2169260e9a87SYuri Pankov namesz = 0;
2170260e9a87SYuri Pankov } else {
2171260e9a87SYuri Pankov iname = cp;
2172260e9a87SYuri Pankov namesz = roff_getname(r, &cp, ln, ppos);
2173260e9a87SYuri Pankov iname[namesz] = '\0';
217495c635efSGarrett D'Amore }
217595c635efSGarrett D'Amore
2176260e9a87SYuri Pankov /* Resolve the macro name argument if it is indirect. */
217795c635efSGarrett D'Amore
2178260e9a87SYuri Pankov if (namesz && (tok == ROFF_dei || tok == ROFF_ami)) {
2179c66b8046SYuri Pankov deftype = ROFFDEF_USER;
2180c66b8046SYuri Pankov name = roff_getstrn(r, iname, namesz, &deftype);
2181c66b8046SYuri Pankov if (name == NULL) {
2182cec8643bSMichal Nowak mandoc_msg(MANDOCERR_STR_UNDEF,
2183cec8643bSMichal Nowak ln, (int)(iname - buf->buf),
2184260e9a87SYuri Pankov "%.*s", (int)namesz, iname);
2185260e9a87SYuri Pankov namesz = 0;
2186260e9a87SYuri Pankov } else
2187260e9a87SYuri Pankov namesz = strlen(name);
2188260e9a87SYuri Pankov } else
2189260e9a87SYuri Pankov name = iname;
219095c635efSGarrett D'Amore
2191260e9a87SYuri Pankov if (namesz == 0 && tok != ROFF_ig) {
2192cec8643bSMichal Nowak mandoc_msg(MANDOCERR_REQ_EMPTY,
2193cec8643bSMichal Nowak ln, ppos, "%s", roff_name[tok]);
2194371584c2SYuri Pankov return ROFF_IGN;
219595c635efSGarrett D'Amore }
219695c635efSGarrett D'Amore
219795c635efSGarrett D'Amore roffnode_push(r, tok, name, ln, ppos);
219895c635efSGarrett D'Amore
219995c635efSGarrett D'Amore /*
220095c635efSGarrett D'Amore * At the beginning of a `de' macro, clear the existing string
220195c635efSGarrett D'Amore * with the same name, if there is one. New content will be
2202260e9a87SYuri Pankov * appended from roff_block_text() in multiline mode.
220395c635efSGarrett D'Amore */
220495c635efSGarrett D'Amore
2205c66b8046SYuri Pankov if (tok == ROFF_de || tok == ROFF_dei) {
2206260e9a87SYuri Pankov roff_setstrn(&r->strtab, name, namesz, "", 0, 0);
2207c66b8046SYuri Pankov roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0);
2208c66b8046SYuri Pankov } else if (tok == ROFF_am || tok == ROFF_ami) {
2209c66b8046SYuri Pankov deftype = ROFFDEF_ANY;
2210c66b8046SYuri Pankov value = roff_getstrn(r, iname, namesz, &deftype);
2211c66b8046SYuri Pankov switch (deftype) { /* Before appending, ... */
2212c66b8046SYuri Pankov case ROFFDEF_PRE: /* copy predefined to user-defined. */
2213c66b8046SYuri Pankov roff_setstrn(&r->strtab, name, namesz,
2214c66b8046SYuri Pankov value, strlen(value), 0);
2215c66b8046SYuri Pankov break;
2216c66b8046SYuri Pankov case ROFFDEF_REN: /* call original standard macro. */
2217c66b8046SYuri Pankov csz = mandoc_asprintf(&call, ".%.*s \\$* \\\"\n",
2218c66b8046SYuri Pankov (int)strlen(value), value);
2219c66b8046SYuri Pankov roff_setstrn(&r->strtab, name, namesz, call, csz, 0);
2220c66b8046SYuri Pankov roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0);
2221c66b8046SYuri Pankov free(call);
2222c66b8046SYuri Pankov break;
2223c66b8046SYuri Pankov case ROFFDEF_STD: /* rename and call standard macro. */
2224c66b8046SYuri Pankov rsz = mandoc_asprintf(&rname, "__%s_renamed", name);
2225c66b8046SYuri Pankov roff_setstrn(&r->rentab, rname, rsz, name, namesz, 0);
2226c66b8046SYuri Pankov csz = mandoc_asprintf(&call, ".%.*s \\$* \\\"\n",
2227c66b8046SYuri Pankov (int)rsz, rname);
2228c66b8046SYuri Pankov roff_setstrn(&r->strtab, name, namesz, call, csz, 0);
2229c66b8046SYuri Pankov free(call);
2230c66b8046SYuri Pankov free(rname);
2231c66b8046SYuri Pankov break;
2232c66b8046SYuri Pankov default:
2233c66b8046SYuri Pankov break;
2234c66b8046SYuri Pankov }
2235c66b8046SYuri Pankov }
223695c635efSGarrett D'Amore
2237260e9a87SYuri Pankov if (*cp == '\0')
2238371584c2SYuri Pankov return ROFF_IGN;
223995c635efSGarrett D'Amore
2240260e9a87SYuri Pankov /* Get the custom end marker. */
224195c635efSGarrett D'Amore
2242260e9a87SYuri Pankov iname = cp;
2243260e9a87SYuri Pankov namesz = roff_getname(r, &cp, ln, ppos);
224495c635efSGarrett D'Amore
2245260e9a87SYuri Pankov /* Resolve the end marker if it is indirect. */
224695c635efSGarrett D'Amore
2247260e9a87SYuri Pankov if (namesz && (tok == ROFF_dei || tok == ROFF_ami)) {
2248c66b8046SYuri Pankov deftype = ROFFDEF_USER;
2249c66b8046SYuri Pankov name = roff_getstrn(r, iname, namesz, &deftype);
2250c66b8046SYuri Pankov if (name == NULL) {
2251cec8643bSMichal Nowak mandoc_msg(MANDOCERR_STR_UNDEF,
2252cec8643bSMichal Nowak ln, (int)(iname - buf->buf),
2253260e9a87SYuri Pankov "%.*s", (int)namesz, iname);
2254260e9a87SYuri Pankov namesz = 0;
2255260e9a87SYuri Pankov } else
2256260e9a87SYuri Pankov namesz = strlen(name);
2257260e9a87SYuri Pankov } else
2258260e9a87SYuri Pankov name = iname;
225995c635efSGarrett D'Amore
2260260e9a87SYuri Pankov if (namesz)
2261260e9a87SYuri Pankov r->last->end = mandoc_strndup(name, namesz);
226295c635efSGarrett D'Amore
2263260e9a87SYuri Pankov if (*cp != '\0')
2264cec8643bSMichal Nowak mandoc_msg(MANDOCERR_ARG_EXCESS,
2265c66b8046SYuri Pankov ln, pos, ".%s ... %s", roff_name[tok], cp);
226695c635efSGarrett D'Amore
2267371584c2SYuri Pankov return ROFF_IGN;
226895c635efSGarrett D'Amore }
226995c635efSGarrett D'Amore
2270cec8643bSMichal Nowak static int
roff_block_sub(ROFF_ARGS)227195c635efSGarrett D'Amore roff_block_sub(ROFF_ARGS)
227295c635efSGarrett D'Amore {
2273c66b8046SYuri Pankov enum roff_tok t;
227495c635efSGarrett D'Amore int i, j;
227595c635efSGarrett D'Amore
227695c635efSGarrett D'Amore /*
227795c635efSGarrett D'Amore * First check whether a custom macro exists at this level. If
227895c635efSGarrett D'Amore * it does, then check against it. This is some of groff's
227995c635efSGarrett D'Amore * stranger behaviours. If we encountered a custom end-scope
228095c635efSGarrett D'Amore * tag and that tag also happens to be a "real" macro, then we
228195c635efSGarrett D'Amore * need to try interpreting it again as a real macro. If it's
228295c635efSGarrett D'Amore * not, then return ignore. Else continue.
228395c635efSGarrett D'Amore */
228495c635efSGarrett D'Amore
228595c635efSGarrett D'Amore if (r->last->end) {
228695c635efSGarrett D'Amore for (i = pos, j = 0; r->last->end[j]; j++, i++)
2287260e9a87SYuri Pankov if (buf->buf[i] != r->last->end[j])
228895c635efSGarrett D'Amore break;
228995c635efSGarrett D'Amore
2290260e9a87SYuri Pankov if (r->last->end[j] == '\0' &&
2291260e9a87SYuri Pankov (buf->buf[i] == '\0' ||
2292260e9a87SYuri Pankov buf->buf[i] == ' ' ||
2293260e9a87SYuri Pankov buf->buf[i] == '\t')) {
229495c635efSGarrett D'Amore roffnode_pop(r);
229595c635efSGarrett D'Amore roffnode_cleanscope(r);
229695c635efSGarrett D'Amore
2297260e9a87SYuri Pankov while (buf->buf[i] == ' ' || buf->buf[i] == '\t')
229895c635efSGarrett D'Amore i++;
229995c635efSGarrett D'Amore
230095c635efSGarrett D'Amore pos = i;
2301260e9a87SYuri Pankov if (roff_parse(r, buf->buf, &pos, ln, ppos) !=
2302c66b8046SYuri Pankov TOKEN_NONE)
2303371584c2SYuri Pankov return ROFF_RERUN;
2304371584c2SYuri Pankov return ROFF_IGN;
230595c635efSGarrett D'Amore }
230695c635efSGarrett D'Amore }
230795c635efSGarrett D'Amore
230895c635efSGarrett D'Amore /*
230995c635efSGarrett D'Amore * If we have no custom end-query or lookup failed, then try
231095c635efSGarrett D'Amore * pulling it out of the hashtable.
231195c635efSGarrett D'Amore */
231295c635efSGarrett D'Amore
2313260e9a87SYuri Pankov t = roff_parse(r, buf->buf, &pos, ln, ppos);
231495c635efSGarrett D'Amore
2315260e9a87SYuri Pankov if (t != ROFF_cblock) {
2316260e9a87SYuri Pankov if (tok != ROFF_ig)
2317260e9a87SYuri Pankov roff_setstr(r, r->last->name, buf->buf + ppos, 2);
2318371584c2SYuri Pankov return ROFF_IGN;
231995c635efSGarrett D'Amore }
232095c635efSGarrett D'Amore
2321371584c2SYuri Pankov return (*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs);
232295c635efSGarrett D'Amore }
232395c635efSGarrett D'Amore
2324cec8643bSMichal Nowak static int
roff_block_text(ROFF_ARGS)232595c635efSGarrett D'Amore roff_block_text(ROFF_ARGS)
232695c635efSGarrett D'Amore {
232795c635efSGarrett D'Amore
2328260e9a87SYuri Pankov if (tok != ROFF_ig)
2329260e9a87SYuri Pankov roff_setstr(r, r->last->name, buf->buf + pos, 2);
233095c635efSGarrett D'Amore
2331371584c2SYuri Pankov return ROFF_IGN;
233295c635efSGarrett D'Amore }
233395c635efSGarrett D'Amore
2334*4d131170SRobert Mustacchi /*
2335*4d131170SRobert Mustacchi * Check for a closing "\}" and handle it.
2336*4d131170SRobert Mustacchi * In this function, the final "int *offs" argument is used for
2337*4d131170SRobert Mustacchi * different purposes than elsewhere:
2338*4d131170SRobert Mustacchi * Input: *offs == 0: caller wants to discard arguments following \}
2339*4d131170SRobert Mustacchi * *offs == 1: caller wants to preserve text following \}
2340*4d131170SRobert Mustacchi * Output: *offs = 0: tell caller to discard input line
2341*4d131170SRobert Mustacchi * *offs = 1: tell caller to use input line
2342*4d131170SRobert Mustacchi */
2343cec8643bSMichal Nowak static int
roff_cond_checkend(ROFF_ARGS)2344*4d131170SRobert Mustacchi roff_cond_checkend(ROFF_ARGS)
234595c635efSGarrett D'Amore {
234695c635efSGarrett D'Amore char *ep;
2347cec8643bSMichal Nowak int endloop, irc, rr;
234895c635efSGarrett D'Amore
2349cec8643bSMichal Nowak irc = ROFF_IGN;
235095c635efSGarrett D'Amore rr = r->last->rule;
2351cec8643bSMichal Nowak endloop = tok != ROFF_while ? ROFF_IGN :
2352cec8643bSMichal Nowak rr ? ROFF_LOOPCONT : ROFF_LOOPEXIT;
2353cec8643bSMichal Nowak if (roffnode_cleanscope(r))
2354cec8643bSMichal Nowak irc |= endloop;
2355698f87a4SGarrett D'Amore
2356260e9a87SYuri Pankov /*
2357*4d131170SRobert Mustacchi * If "\}" occurs on a macro line without a preceding macro or
2358*4d131170SRobert Mustacchi * a text line contains nothing else, drop the line completely.
2359260e9a87SYuri Pankov */
2360260e9a87SYuri Pankov
2361260e9a87SYuri Pankov ep = buf->buf + pos;
2362*4d131170SRobert Mustacchi if (ep[0] == '\\' && ep[1] == '}' && (ep[2] == '\0' || *offs == 0))
2363260e9a87SYuri Pankov rr = 0;
2364260e9a87SYuri Pankov
2365cec8643bSMichal Nowak /*
2366*4d131170SRobert Mustacchi * The closing delimiter "\}" rewinds the conditional scope
2367cec8643bSMichal Nowak * but is otherwise ignored when interpreting the line.
2368cec8643bSMichal Nowak */
2369698f87a4SGarrett D'Amore
2370260e9a87SYuri Pankov while ((ep = strchr(ep, '\\')) != NULL) {
2371c66b8046SYuri Pankov switch (ep[1]) {
2372c66b8046SYuri Pankov case '}':
2373*4d131170SRobert Mustacchi if (ep[2] == '\0')
2374*4d131170SRobert Mustacchi ep[0] = '\0';
2375*4d131170SRobert Mustacchi else if (rr)
2376*4d131170SRobert Mustacchi ep[1] = '&';
2377*4d131170SRobert Mustacchi else
2378c66b8046SYuri Pankov memmove(ep, ep + 2, strlen(ep + 2) + 1);
2379cec8643bSMichal Nowak if (roff_ccond(r, ln, ep - buf->buf))
2380cec8643bSMichal Nowak irc |= endloop;
2381c66b8046SYuri Pankov break;
2382c66b8046SYuri Pankov case '\0':
2383260e9a87SYuri Pankov ++ep;
2384c66b8046SYuri Pankov break;
2385c66b8046SYuri Pankov default:
2386c66b8046SYuri Pankov ep += 2;
2387c66b8046SYuri Pankov break;
2388260e9a87SYuri Pankov }
2389c66b8046SYuri Pankov }
2390*4d131170SRobert Mustacchi *offs = rr;
2391*4d131170SRobert Mustacchi return irc;
2392*4d131170SRobert Mustacchi }
2393*4d131170SRobert Mustacchi
2394*4d131170SRobert Mustacchi /*
2395*4d131170SRobert Mustacchi * Parse and process a request or macro line in conditional scope.
2396*4d131170SRobert Mustacchi */
2397*4d131170SRobert Mustacchi static int
roff_cond_sub(ROFF_ARGS)2398*4d131170SRobert Mustacchi roff_cond_sub(ROFF_ARGS)
2399*4d131170SRobert Mustacchi {
2400*4d131170SRobert Mustacchi struct roffnode *bl;
2401*4d131170SRobert Mustacchi int irc, rr;
2402*4d131170SRobert Mustacchi enum roff_tok t;
2403*4d131170SRobert Mustacchi
2404*4d131170SRobert Mustacchi rr = 0; /* If arguments follow "\}", skip them. */
2405*4d131170SRobert Mustacchi irc = roff_cond_checkend(r, tok, buf, ln, ppos, pos, &rr);
2406*4d131170SRobert Mustacchi t = roff_parse(r, buf->buf, &pos, ln, ppos);
2407*4d131170SRobert Mustacchi
2408*4d131170SRobert Mustacchi /* For now, let high level macros abort .ce mode. */
2409*4d131170SRobert Mustacchi
2410*4d131170SRobert Mustacchi if (roffce_node != NULL &&
2411*4d131170SRobert Mustacchi (t == TOKEN_NONE || t == ROFF_Dd || t == ROFF_EQ ||
2412*4d131170SRobert Mustacchi t == ROFF_TH || t == ROFF_TS)) {
2413*4d131170SRobert Mustacchi r->man->last = roffce_node;
2414*4d131170SRobert Mustacchi r->man->next = ROFF_NEXT_SIBLING;
2415*4d131170SRobert Mustacchi roffce_lines = 0;
2416*4d131170SRobert Mustacchi roffce_node = NULL;
2417*4d131170SRobert Mustacchi }
2418c66b8046SYuri Pankov
2419c66b8046SYuri Pankov /*
2420c66b8046SYuri Pankov * Fully handle known macros when they are structurally
2421c66b8046SYuri Pankov * required or when the conditional evaluated to true.
2422c66b8046SYuri Pankov */
2423c66b8046SYuri Pankov
2424*4d131170SRobert Mustacchi if (t == ROFF_break) {
2425*4d131170SRobert Mustacchi if (irc & ROFF_LOOPMASK)
2426*4d131170SRobert Mustacchi irc = ROFF_IGN | ROFF_LOOPEXIT;
2427*4d131170SRobert Mustacchi else if (rr) {
2428*4d131170SRobert Mustacchi for (bl = r->last; bl != NULL; bl = bl->parent) {
2429*4d131170SRobert Mustacchi bl->rule = 0;
2430*4d131170SRobert Mustacchi if (bl->tok == ROFF_while)
2431*4d131170SRobert Mustacchi break;
2432*4d131170SRobert Mustacchi }
2433*4d131170SRobert Mustacchi }
2434*4d131170SRobert Mustacchi } else if (t != TOKEN_NONE &&
2435*4d131170SRobert Mustacchi (rr || roffs[t].flags & ROFFMAC_STRUCT))
2436*4d131170SRobert Mustacchi irc |= (*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs);
2437*4d131170SRobert Mustacchi else
2438*4d131170SRobert Mustacchi irc |= rr ? ROFF_CONT : ROFF_IGN;
2439cec8643bSMichal Nowak return irc;
244095c635efSGarrett D'Amore }
244195c635efSGarrett D'Amore
2442*4d131170SRobert Mustacchi /*
2443*4d131170SRobert Mustacchi * Parse and process a text line in conditional scope.
2444*4d131170SRobert Mustacchi */
2445cec8643bSMichal Nowak static int
roff_cond_text(ROFF_ARGS)244695c635efSGarrett D'Amore roff_cond_text(ROFF_ARGS)
244795c635efSGarrett D'Amore {
2448*4d131170SRobert Mustacchi int irc, rr;
244995c635efSGarrett D'Amore
2450*4d131170SRobert Mustacchi rr = 1; /* If arguments follow "\}", preserve them. */
2451*4d131170SRobert Mustacchi irc = roff_cond_checkend(r, tok, buf, ln, ppos, pos, &rr);
2452cec8643bSMichal Nowak if (rr)
2453cec8643bSMichal Nowak irc |= ROFF_CONT;
2454cec8643bSMichal Nowak return irc;
245595c635efSGarrett D'Amore }
245695c635efSGarrett D'Amore
2457371584c2SYuri Pankov /* --- handling of numeric and conditional expressions -------------------- */
2458371584c2SYuri Pankov
2459260e9a87SYuri Pankov /*
2460260e9a87SYuri Pankov * Parse a single signed integer number. Stop at the first non-digit.
2461260e9a87SYuri Pankov * If there is at least one digit, return success and advance the
2462260e9a87SYuri Pankov * parse point, else return failure and let the parse point unchanged.
2463260e9a87SYuri Pankov * Ignore overflows, treat them just like the C language.
2464260e9a87SYuri Pankov */
2465698f87a4SGarrett D'Amore static int
roff_getnum(const char * v,int * pos,int * res,int flags)2466260e9a87SYuri Pankov roff_getnum(const char *v, int *pos, int *res, int flags)
2467698f87a4SGarrett D'Amore {
2468260e9a87SYuri Pankov int myres, scaled, n, p;
2469260e9a87SYuri Pankov
2470260e9a87SYuri Pankov if (NULL == res)
2471260e9a87SYuri Pankov res = &myres;
2472698f87a4SGarrett D'Amore
2473698f87a4SGarrett D'Amore p = *pos;
2474698f87a4SGarrett D'Amore n = v[p] == '-';
2475260e9a87SYuri Pankov if (n || v[p] == '+')
2476260e9a87SYuri Pankov p++;
2477260e9a87SYuri Pankov
2478260e9a87SYuri Pankov if (flags & ROFFNUM_WHITE)
2479260e9a87SYuri Pankov while (isspace((unsigned char)v[p]))
2480698f87a4SGarrett D'Amore p++;
2481698f87a4SGarrett D'Amore
2482698f87a4SGarrett D'Amore for (*res = 0; isdigit((unsigned char)v[p]); p++)
2483260e9a87SYuri Pankov *res = 10 * *res + v[p] - '0';
2484698f87a4SGarrett D'Amore if (p == *pos + n)
2485698f87a4SGarrett D'Amore return 0;
2486698f87a4SGarrett D'Amore
2487698f87a4SGarrett D'Amore if (n)
2488698f87a4SGarrett D'Amore *res = -*res;
2489698f87a4SGarrett D'Amore
2490260e9a87SYuri Pankov /* Each number may be followed by one optional scaling unit. */
2491698f87a4SGarrett D'Amore
2492260e9a87SYuri Pankov switch (v[p]) {
2493260e9a87SYuri Pankov case 'f':
2494260e9a87SYuri Pankov scaled = *res * 65536;
2495698f87a4SGarrett D'Amore break;
2496260e9a87SYuri Pankov case 'i':
2497260e9a87SYuri Pankov scaled = *res * 240;
2498698f87a4SGarrett D'Amore break;
2499260e9a87SYuri Pankov case 'c':
2500260e9a87SYuri Pankov scaled = *res * 240 / 2.54;
2501260e9a87SYuri Pankov break;
2502260e9a87SYuri Pankov case 'v':
2503260e9a87SYuri Pankov case 'P':
2504260e9a87SYuri Pankov scaled = *res * 40;
2505260e9a87SYuri Pankov break;
2506260e9a87SYuri Pankov case 'm':
2507260e9a87SYuri Pankov case 'n':
2508260e9a87SYuri Pankov scaled = *res * 24;
2509260e9a87SYuri Pankov break;
2510260e9a87SYuri Pankov case 'p':
2511260e9a87SYuri Pankov scaled = *res * 10 / 3;
2512260e9a87SYuri Pankov break;
2513260e9a87SYuri Pankov case 'u':
2514260e9a87SYuri Pankov scaled = *res;
2515260e9a87SYuri Pankov break;
2516260e9a87SYuri Pankov case 'M':
2517260e9a87SYuri Pankov scaled = *res * 6 / 25;
2518698f87a4SGarrett D'Amore break;
2519698f87a4SGarrett D'Amore default:
2520260e9a87SYuri Pankov scaled = *res;
2521260e9a87SYuri Pankov p--;
2522260e9a87SYuri Pankov break;
2523260e9a87SYuri Pankov }
2524260e9a87SYuri Pankov if (flags & ROFFNUM_SCALE)
2525260e9a87SYuri Pankov *res = scaled;
2526260e9a87SYuri Pankov
2527260e9a87SYuri Pankov *pos = p + 1;
2528371584c2SYuri Pankov return 1;
2529260e9a87SYuri Pankov }
2530260e9a87SYuri Pankov
2531260e9a87SYuri Pankov /*
2532260e9a87SYuri Pankov * Evaluate a string comparison condition.
2533260e9a87SYuri Pankov * The first character is the delimiter.
2534260e9a87SYuri Pankov * Succeed if the string up to its second occurrence
2535260e9a87SYuri Pankov * matches the string up to its third occurence.
2536260e9a87SYuri Pankov * Advance the cursor after the third occurrence
2537260e9a87SYuri Pankov * or lacking that, to the end of the line.
2538260e9a87SYuri Pankov */
2539260e9a87SYuri Pankov static int
roff_evalstrcond(const char * v,int * pos)2540260e9a87SYuri Pankov roff_evalstrcond(const char *v, int *pos)
2541260e9a87SYuri Pankov {
2542260e9a87SYuri Pankov const char *s1, *s2, *s3;
2543260e9a87SYuri Pankov int match;
2544260e9a87SYuri Pankov
2545260e9a87SYuri Pankov match = 0;
2546260e9a87SYuri Pankov s1 = v + *pos; /* initial delimiter */
2547260e9a87SYuri Pankov s2 = s1 + 1; /* for scanning the first string */
2548260e9a87SYuri Pankov s3 = strchr(s2, *s1); /* for scanning the second string */
2549260e9a87SYuri Pankov
2550260e9a87SYuri Pankov if (NULL == s3) /* found no middle delimiter */
2551260e9a87SYuri Pankov goto out;
2552260e9a87SYuri Pankov
2553260e9a87SYuri Pankov while ('\0' != *++s3) {
2554260e9a87SYuri Pankov if (*s2 != *s3) { /* mismatch */
2555260e9a87SYuri Pankov s3 = strchr(s3, *s1);
2556260e9a87SYuri Pankov break;
2557260e9a87SYuri Pankov }
2558260e9a87SYuri Pankov if (*s3 == *s1) { /* found the final delimiter */
2559260e9a87SYuri Pankov match = 1;
2560260e9a87SYuri Pankov break;
2561260e9a87SYuri Pankov }
2562260e9a87SYuri Pankov s2++;
2563260e9a87SYuri Pankov }
2564260e9a87SYuri Pankov
2565260e9a87SYuri Pankov out:
2566260e9a87SYuri Pankov if (NULL == s3)
2567260e9a87SYuri Pankov s3 = strchr(s2, '\0');
2568260e9a87SYuri Pankov else if (*s3 != '\0')
2569260e9a87SYuri Pankov s3++;
2570260e9a87SYuri Pankov *pos = s3 - v;
2571371584c2SYuri Pankov return match;
2572260e9a87SYuri Pankov }
2573260e9a87SYuri Pankov
2574260e9a87SYuri Pankov /*
2575260e9a87SYuri Pankov * Evaluate an optionally negated single character, numerical,
2576260e9a87SYuri Pankov * or string condition.
2577260e9a87SYuri Pankov */
2578260e9a87SYuri Pankov static int
roff_evalcond(struct roff * r,int ln,char * v,int * pos)2579371584c2SYuri Pankov roff_evalcond(struct roff *r, int ln, char *v, int *pos)
2580260e9a87SYuri Pankov {
2581cec8643bSMichal Nowak const char *start, *end;
2582371584c2SYuri Pankov char *cp, *name;
2583371584c2SYuri Pankov size_t sz;
2584cec8643bSMichal Nowak int deftype, len, number, savepos, istrue, wanttrue;
2585260e9a87SYuri Pankov
2586260e9a87SYuri Pankov if ('!' == v[*pos]) {
2587260e9a87SYuri Pankov wanttrue = 0;
2588260e9a87SYuri Pankov (*pos)++;
2589260e9a87SYuri Pankov } else
2590260e9a87SYuri Pankov wanttrue = 1;
2591260e9a87SYuri Pankov
2592260e9a87SYuri Pankov switch (v[*pos]) {
2593260e9a87SYuri Pankov case '\0':
2594371584c2SYuri Pankov return 0;
2595260e9a87SYuri Pankov case 'n':
2596260e9a87SYuri Pankov case 'o':
2597260e9a87SYuri Pankov (*pos)++;
2598371584c2SYuri Pankov return wanttrue;
2599260e9a87SYuri Pankov case 'e':
2600260e9a87SYuri Pankov case 't':
2601260e9a87SYuri Pankov case 'v':
2602260e9a87SYuri Pankov (*pos)++;
2603371584c2SYuri Pankov return !wanttrue;
2604cec8643bSMichal Nowak case 'c':
2605cec8643bSMichal Nowak do {
2606cec8643bSMichal Nowak (*pos)++;
2607cec8643bSMichal Nowak } while (v[*pos] == ' ');
2608cec8643bSMichal Nowak
2609cec8643bSMichal Nowak /*
2610cec8643bSMichal Nowak * Quirk for groff compatibility:
2611cec8643bSMichal Nowak * The horizontal tab is neither available nor unavailable.
2612cec8643bSMichal Nowak */
2613cec8643bSMichal Nowak
2614cec8643bSMichal Nowak if (v[*pos] == '\t') {
2615cec8643bSMichal Nowak (*pos)++;
2616cec8643bSMichal Nowak return 0;
2617cec8643bSMichal Nowak }
2618cec8643bSMichal Nowak
2619cec8643bSMichal Nowak /* Printable ASCII characters are available. */
2620cec8643bSMichal Nowak
2621cec8643bSMichal Nowak if (v[*pos] != '\\') {
2622cec8643bSMichal Nowak (*pos)++;
2623cec8643bSMichal Nowak return wanttrue;
2624cec8643bSMichal Nowak }
2625cec8643bSMichal Nowak
2626cec8643bSMichal Nowak end = v + ++*pos;
2627cec8643bSMichal Nowak switch (mandoc_escape(&end, &start, &len)) {
2628cec8643bSMichal Nowak case ESCAPE_SPECIAL:
2629cec8643bSMichal Nowak istrue = mchars_spec2cp(start, len) != -1;
2630cec8643bSMichal Nowak break;
2631cec8643bSMichal Nowak case ESCAPE_UNICODE:
2632cec8643bSMichal Nowak istrue = 1;
2633cec8643bSMichal Nowak break;
2634cec8643bSMichal Nowak case ESCAPE_NUMBERED:
2635cec8643bSMichal Nowak istrue = mchars_num2char(start, len) != -1;
2636cec8643bSMichal Nowak break;
2637cec8643bSMichal Nowak default:
2638cec8643bSMichal Nowak istrue = !wanttrue;
2639cec8643bSMichal Nowak break;
2640cec8643bSMichal Nowak }
2641cec8643bSMichal Nowak *pos = end - v;
2642cec8643bSMichal Nowak return istrue == wanttrue;
2643c66b8046SYuri Pankov case 'd':
2644371584c2SYuri Pankov case 'r':
2645c66b8046SYuri Pankov cp = v + *pos + 1;
2646c66b8046SYuri Pankov while (*cp == ' ')
2647c66b8046SYuri Pankov cp++;
2648c66b8046SYuri Pankov name = cp;
2649c66b8046SYuri Pankov sz = roff_getname(r, &cp, ln, cp - v);
2650c66b8046SYuri Pankov if (sz == 0)
2651c66b8046SYuri Pankov istrue = 0;
2652c66b8046SYuri Pankov else if (v[*pos] == 'r')
2653c66b8046SYuri Pankov istrue = roff_hasregn(r, name, sz);
2654c66b8046SYuri Pankov else {
2655c66b8046SYuri Pankov deftype = ROFFDEF_ANY;
2656c66b8046SYuri Pankov roff_getstrn(r, name, sz, &deftype);
2657c66b8046SYuri Pankov istrue = !!deftype;
2658c66b8046SYuri Pankov }
2659cec8643bSMichal Nowak *pos = (name + sz) - v;
2660c66b8046SYuri Pankov return istrue == wanttrue;
2661260e9a87SYuri Pankov default:
2662260e9a87SYuri Pankov break;
2663260e9a87SYuri Pankov }
2664260e9a87SYuri Pankov
2665260e9a87SYuri Pankov savepos = *pos;
2666260e9a87SYuri Pankov if (roff_evalnum(r, ln, v, pos, &number, ROFFNUM_SCALE))
2667371584c2SYuri Pankov return (number > 0) == wanttrue;
2668260e9a87SYuri Pankov else if (*pos == savepos)
2669371584c2SYuri Pankov return roff_evalstrcond(v, pos) == wanttrue;
2670260e9a87SYuri Pankov else
2671371584c2SYuri Pankov return 0;
2672698f87a4SGarrett D'Amore }
2673698f87a4SGarrett D'Amore
2674cec8643bSMichal Nowak static int
roff_line_ignore(ROFF_ARGS)267595c635efSGarrett D'Amore roff_line_ignore(ROFF_ARGS)
267695c635efSGarrett D'Amore {
267795c635efSGarrett D'Amore
2678371584c2SYuri Pankov return ROFF_IGN;
267995c635efSGarrett D'Amore }
268095c635efSGarrett D'Amore
2681cec8643bSMichal Nowak static int
roff_insec(ROFF_ARGS)2682260e9a87SYuri Pankov roff_insec(ROFF_ARGS)
2683260e9a87SYuri Pankov {
2684260e9a87SYuri Pankov
2685cec8643bSMichal Nowak mandoc_msg(MANDOCERR_REQ_INSEC, ln, ppos, "%s", roff_name[tok]);
2686371584c2SYuri Pankov return ROFF_IGN;
2687260e9a87SYuri Pankov }
2688260e9a87SYuri Pankov
2689cec8643bSMichal Nowak static int
roff_unsupp(ROFF_ARGS)2690260e9a87SYuri Pankov roff_unsupp(ROFF_ARGS)
2691260e9a87SYuri Pankov {
2692260e9a87SYuri Pankov
2693cec8643bSMichal Nowak mandoc_msg(MANDOCERR_REQ_UNSUPP, ln, ppos, "%s", roff_name[tok]);
2694371584c2SYuri Pankov return ROFF_IGN;
2695260e9a87SYuri Pankov }
2696260e9a87SYuri Pankov
2697cec8643bSMichal Nowak static int
roff_cond(ROFF_ARGS)269895c635efSGarrett D'Amore roff_cond(ROFF_ARGS)
269995c635efSGarrett D'Amore {
2700cec8643bSMichal Nowak int irc;
2701698f87a4SGarrett D'Amore
2702698f87a4SGarrett D'Amore roffnode_push(r, tok, NULL, ln, ppos);
270395c635efSGarrett D'Amore
270495c635efSGarrett D'Amore /*
270595c635efSGarrett D'Amore * An `.el' has no conditional body: it will consume the value
270695c635efSGarrett D'Amore * of the current rstack entry set in prior `ie' calls or
270795c635efSGarrett D'Amore * defaults to DENY.
270895c635efSGarrett D'Amore *
270995c635efSGarrett D'Amore * If we're not an `el', however, then evaluate the conditional.
271095c635efSGarrett D'Amore */
271195c635efSGarrett D'Amore
2712260e9a87SYuri Pankov r->last->rule = tok == ROFF_el ?
2713260e9a87SYuri Pankov (r->rstackpos < 0 ? 0 : r->rstack[r->rstackpos--]) :
2714260e9a87SYuri Pankov roff_evalcond(r, ln, buf->buf, &pos);
271595c635efSGarrett D'Amore
271695c635efSGarrett D'Amore /*
271795c635efSGarrett D'Amore * An if-else will put the NEGATION of the current evaluated
271895c635efSGarrett D'Amore * conditional into the stack of rules.
271995c635efSGarrett D'Amore */
272095c635efSGarrett D'Amore
2721260e9a87SYuri Pankov if (tok == ROFF_ie) {
2722260e9a87SYuri Pankov if (r->rstackpos + 1 == r->rstacksz) {
2723260e9a87SYuri Pankov r->rstacksz += 16;
2724260e9a87SYuri Pankov r->rstack = mandoc_reallocarray(r->rstack,
2725260e9a87SYuri Pankov r->rstacksz, sizeof(int));
272695c635efSGarrett D'Amore }
2727260e9a87SYuri Pankov r->rstack[++r->rstackpos] = !r->last->rule;
272895c635efSGarrett D'Amore }
272995c635efSGarrett D'Amore
273095c635efSGarrett D'Amore /* If the parent has false as its rule, then so do we. */
273195c635efSGarrett D'Amore
2732260e9a87SYuri Pankov if (r->last->parent && !r->last->parent->rule)
2733260e9a87SYuri Pankov r->last->rule = 0;
273495c635efSGarrett D'Amore
273595c635efSGarrett D'Amore /*
2736698f87a4SGarrett D'Amore * Determine scope.
2737698f87a4SGarrett D'Amore * If there is nothing on the line after the conditional,
2738698f87a4SGarrett D'Amore * not even whitespace, use next-line scope.
2739cec8643bSMichal Nowak * Except that .while does not support next-line scope.
274095c635efSGarrett D'Amore */
274195c635efSGarrett D'Amore
2742cec8643bSMichal Nowak if (buf->buf[pos] == '\0' && tok != ROFF_while) {
2743698f87a4SGarrett D'Amore r->last->endspan = 2;
2744698f87a4SGarrett D'Amore goto out;
2745698f87a4SGarrett D'Amore }
2746698f87a4SGarrett D'Amore
2747260e9a87SYuri Pankov while (buf->buf[pos] == ' ')
2748698f87a4SGarrett D'Amore pos++;
2749698f87a4SGarrett D'Amore
2750698f87a4SGarrett D'Amore /* An opening brace requests multiline scope. */
275195c635efSGarrett D'Amore
2752260e9a87SYuri Pankov if (buf->buf[pos] == '\\' && buf->buf[pos + 1] == '{') {
275395c635efSGarrett D'Amore r->last->endspan = -1;
275495c635efSGarrett D'Amore pos += 2;
2755371584c2SYuri Pankov while (buf->buf[pos] == ' ')
2756371584c2SYuri Pankov pos++;
2757698f87a4SGarrett D'Amore goto out;
275895c635efSGarrett D'Amore }
275995c635efSGarrett D'Amore
276095c635efSGarrett D'Amore /*
2761698f87a4SGarrett D'Amore * Anything else following the conditional causes
2762698f87a4SGarrett D'Amore * single-line scope. Warn if the scope contains
2763698f87a4SGarrett D'Amore * nothing but trailing whitespace.
276495c635efSGarrett D'Amore */
276595c635efSGarrett D'Amore
2766260e9a87SYuri Pankov if (buf->buf[pos] == '\0')
2767cec8643bSMichal Nowak mandoc_msg(MANDOCERR_COND_EMPTY,
2768cec8643bSMichal Nowak ln, ppos, "%s", roff_name[tok]);
276995c635efSGarrett D'Amore
2770698f87a4SGarrett D'Amore r->last->endspan = 1;
277195c635efSGarrett D'Amore
2772698f87a4SGarrett D'Amore out:
277395c635efSGarrett D'Amore *offs = pos;
2774cec8643bSMichal Nowak irc = ROFF_RERUN;
2775cec8643bSMichal Nowak if (tok == ROFF_while)
2776cec8643bSMichal Nowak irc |= ROFF_WHILE;
2777cec8643bSMichal Nowak return irc;
277895c635efSGarrett D'Amore }
277995c635efSGarrett D'Amore
2780cec8643bSMichal Nowak static int
roff_ds(ROFF_ARGS)278195c635efSGarrett D'Amore roff_ds(ROFF_ARGS)
278295c635efSGarrett D'Amore {
2783260e9a87SYuri Pankov char *string;
2784260e9a87SYuri Pankov const char *name;
2785260e9a87SYuri Pankov size_t namesz;
2786260e9a87SYuri Pankov
2787260e9a87SYuri Pankov /* Ignore groff compatibility mode for now. */
2788260e9a87SYuri Pankov
2789260e9a87SYuri Pankov if (tok == ROFF_ds1)
2790260e9a87SYuri Pankov tok = ROFF_ds;
2791260e9a87SYuri Pankov else if (tok == ROFF_as1)
2792260e9a87SYuri Pankov tok = ROFF_as;
279395c635efSGarrett D'Amore
279495c635efSGarrett D'Amore /*
2795260e9a87SYuri Pankov * The first word is the name of the string.
2796260e9a87SYuri Pankov * If it is empty or terminated by an escape sequence,
2797260e9a87SYuri Pankov * abort the `ds' request without defining anything.
279895c635efSGarrett D'Amore */
279995c635efSGarrett D'Amore
2800260e9a87SYuri Pankov name = string = buf->buf + pos;
2801260e9a87SYuri Pankov if (*name == '\0')
2802371584c2SYuri Pankov return ROFF_IGN;
280395c635efSGarrett D'Amore
2804260e9a87SYuri Pankov namesz = roff_getname(r, &string, ln, pos);
2805cec8643bSMichal Nowak switch (name[namesz]) {
2806cec8643bSMichal Nowak case '\\':
2807371584c2SYuri Pankov return ROFF_IGN;
2808cec8643bSMichal Nowak case '\t':
2809cec8643bSMichal Nowak string = buf->buf + pos + namesz;
2810cec8643bSMichal Nowak break;
2811cec8643bSMichal Nowak default:
2812cec8643bSMichal Nowak break;
2813cec8643bSMichal Nowak }
2814260e9a87SYuri Pankov
2815260e9a87SYuri Pankov /* Read past the initial double-quote, if any. */
2816260e9a87SYuri Pankov if (*string == '"')
281795c635efSGarrett D'Amore string++;
281895c635efSGarrett D'Amore
281995c635efSGarrett D'Amore /* The rest is the value. */
2820260e9a87SYuri Pankov roff_setstrn(&r->strtab, name, namesz, string, strlen(string),
2821260e9a87SYuri Pankov ROFF_as == tok);
2822c66b8046SYuri Pankov roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0);
2823371584c2SYuri Pankov return ROFF_IGN;
282495c635efSGarrett D'Amore }
282595c635efSGarrett D'Amore
2826260e9a87SYuri Pankov /*
2827260e9a87SYuri Pankov * Parse a single operator, one or two characters long.
2828260e9a87SYuri Pankov * If the operator is recognized, return success and advance the
2829260e9a87SYuri Pankov * parse point, else return failure and let the parse point unchanged.
2830260e9a87SYuri Pankov */
2831260e9a87SYuri Pankov static int
roff_getop(const char * v,int * pos,char * res)2832260e9a87SYuri Pankov roff_getop(const char *v, int *pos, char *res)
2833260e9a87SYuri Pankov {
2834260e9a87SYuri Pankov
2835260e9a87SYuri Pankov *res = v[*pos];
2836260e9a87SYuri Pankov
2837260e9a87SYuri Pankov switch (*res) {
2838260e9a87SYuri Pankov case '+':
2839260e9a87SYuri Pankov case '-':
2840260e9a87SYuri Pankov case '*':
2841260e9a87SYuri Pankov case '/':
2842260e9a87SYuri Pankov case '%':
2843260e9a87SYuri Pankov case '&':
2844260e9a87SYuri Pankov case ':':
2845260e9a87SYuri Pankov break;
2846260e9a87SYuri Pankov case '<':
2847260e9a87SYuri Pankov switch (v[*pos + 1]) {
2848260e9a87SYuri Pankov case '=':
2849260e9a87SYuri Pankov *res = 'l';
2850260e9a87SYuri Pankov (*pos)++;
2851260e9a87SYuri Pankov break;
2852260e9a87SYuri Pankov case '>':
2853260e9a87SYuri Pankov *res = '!';
2854260e9a87SYuri Pankov (*pos)++;
2855260e9a87SYuri Pankov break;
2856260e9a87SYuri Pankov case '?':
2857260e9a87SYuri Pankov *res = 'i';
2858260e9a87SYuri Pankov (*pos)++;
2859260e9a87SYuri Pankov break;
2860260e9a87SYuri Pankov default:
2861260e9a87SYuri Pankov break;
2862260e9a87SYuri Pankov }
2863260e9a87SYuri Pankov break;
2864260e9a87SYuri Pankov case '>':
2865260e9a87SYuri Pankov switch (v[*pos + 1]) {
2866260e9a87SYuri Pankov case '=':
2867260e9a87SYuri Pankov *res = 'g';
2868260e9a87SYuri Pankov (*pos)++;
2869260e9a87SYuri Pankov break;
2870260e9a87SYuri Pankov case '?':
2871260e9a87SYuri Pankov *res = 'a';
2872260e9a87SYuri Pankov (*pos)++;
2873260e9a87SYuri Pankov break;
2874260e9a87SYuri Pankov default:
2875260e9a87SYuri Pankov break;
2876260e9a87SYuri Pankov }
2877260e9a87SYuri Pankov break;
2878260e9a87SYuri Pankov case '=':
2879260e9a87SYuri Pankov if ('=' == v[*pos + 1])
2880260e9a87SYuri Pankov (*pos)++;
2881260e9a87SYuri Pankov break;
2882260e9a87SYuri Pankov default:
2883371584c2SYuri Pankov return 0;
2884260e9a87SYuri Pankov }
2885260e9a87SYuri Pankov (*pos)++;
2886260e9a87SYuri Pankov
2887371584c2SYuri Pankov return *res;
2888260e9a87SYuri Pankov }
2889260e9a87SYuri Pankov
2890260e9a87SYuri Pankov /*
2891260e9a87SYuri Pankov * Evaluate either a parenthesized numeric expression
2892260e9a87SYuri Pankov * or a single signed integer number.
2893260e9a87SYuri Pankov */
2894260e9a87SYuri Pankov static int
roff_evalpar(struct roff * r,int ln,const char * v,int * pos,int * res,int flags)2895260e9a87SYuri Pankov roff_evalpar(struct roff *r, int ln,
2896260e9a87SYuri Pankov const char *v, int *pos, int *res, int flags)
2897260e9a87SYuri Pankov {
2898260e9a87SYuri Pankov
2899260e9a87SYuri Pankov if ('(' != v[*pos])
2900371584c2SYuri Pankov return roff_getnum(v, pos, res, flags);
2901260e9a87SYuri Pankov
2902260e9a87SYuri Pankov (*pos)++;
2903260e9a87SYuri Pankov if ( ! roff_evalnum(r, ln, v, pos, res, flags | ROFFNUM_WHITE))
2904371584c2SYuri Pankov return 0;
2905260e9a87SYuri Pankov
2906260e9a87SYuri Pankov /*
2907260e9a87SYuri Pankov * Omission of the closing parenthesis
2908260e9a87SYuri Pankov * is an error in validation mode,
2909260e9a87SYuri Pankov * but ignored in evaluation mode.
2910260e9a87SYuri Pankov */
2911260e9a87SYuri Pankov
2912260e9a87SYuri Pankov if (')' == v[*pos])
2913260e9a87SYuri Pankov (*pos)++;
2914260e9a87SYuri Pankov else if (NULL == res)
2915371584c2SYuri Pankov return 0;
2916260e9a87SYuri Pankov
2917371584c2SYuri Pankov return 1;
2918260e9a87SYuri Pankov }
2919260e9a87SYuri Pankov
2920260e9a87SYuri Pankov /*
2921260e9a87SYuri Pankov * Evaluate a complete numeric expression.
2922260e9a87SYuri Pankov * Proceed left to right, there is no concept of precedence.
2923260e9a87SYuri Pankov */
2924260e9a87SYuri Pankov static int
roff_evalnum(struct roff * r,int ln,const char * v,int * pos,int * res,int flags)2925260e9a87SYuri Pankov roff_evalnum(struct roff *r, int ln, const char *v,
2926260e9a87SYuri Pankov int *pos, int *res, int flags)
2927260e9a87SYuri Pankov {
2928260e9a87SYuri Pankov int mypos, operand2;
2929260e9a87SYuri Pankov char operator;
2930260e9a87SYuri Pankov
2931260e9a87SYuri Pankov if (NULL == pos) {
2932260e9a87SYuri Pankov mypos = 0;
2933260e9a87SYuri Pankov pos = &mypos;
2934260e9a87SYuri Pankov }
2935260e9a87SYuri Pankov
2936260e9a87SYuri Pankov if (flags & ROFFNUM_WHITE)
2937260e9a87SYuri Pankov while (isspace((unsigned char)v[*pos]))
2938260e9a87SYuri Pankov (*pos)++;
2939260e9a87SYuri Pankov
2940260e9a87SYuri Pankov if ( ! roff_evalpar(r, ln, v, pos, res, flags))
2941371584c2SYuri Pankov return 0;
2942260e9a87SYuri Pankov
2943260e9a87SYuri Pankov while (1) {
2944260e9a87SYuri Pankov if (flags & ROFFNUM_WHITE)
2945260e9a87SYuri Pankov while (isspace((unsigned char)v[*pos]))
2946260e9a87SYuri Pankov (*pos)++;
2947260e9a87SYuri Pankov
2948260e9a87SYuri Pankov if ( ! roff_getop(v, pos, &operator))
2949260e9a87SYuri Pankov break;
2950260e9a87SYuri Pankov
2951260e9a87SYuri Pankov if (flags & ROFFNUM_WHITE)
2952260e9a87SYuri Pankov while (isspace((unsigned char)v[*pos]))
2953260e9a87SYuri Pankov (*pos)++;
2954260e9a87SYuri Pankov
2955260e9a87SYuri Pankov if ( ! roff_evalpar(r, ln, v, pos, &operand2, flags))
2956371584c2SYuri Pankov return 0;
2957260e9a87SYuri Pankov
2958260e9a87SYuri Pankov if (flags & ROFFNUM_WHITE)
2959260e9a87SYuri Pankov while (isspace((unsigned char)v[*pos]))
2960260e9a87SYuri Pankov (*pos)++;
2961260e9a87SYuri Pankov
2962260e9a87SYuri Pankov if (NULL == res)
2963260e9a87SYuri Pankov continue;
2964260e9a87SYuri Pankov
2965260e9a87SYuri Pankov switch (operator) {
2966260e9a87SYuri Pankov case '+':
2967260e9a87SYuri Pankov *res += operand2;
2968260e9a87SYuri Pankov break;
2969260e9a87SYuri Pankov case '-':
2970260e9a87SYuri Pankov *res -= operand2;
2971260e9a87SYuri Pankov break;
2972260e9a87SYuri Pankov case '*':
2973260e9a87SYuri Pankov *res *= operand2;
2974260e9a87SYuri Pankov break;
2975260e9a87SYuri Pankov case '/':
2976260e9a87SYuri Pankov if (operand2 == 0) {
2977260e9a87SYuri Pankov mandoc_msg(MANDOCERR_DIVZERO,
2978cec8643bSMichal Nowak ln, *pos, "%s", v);
2979260e9a87SYuri Pankov *res = 0;
2980260e9a87SYuri Pankov break;
2981260e9a87SYuri Pankov }
2982260e9a87SYuri Pankov *res /= operand2;
2983260e9a87SYuri Pankov break;
2984260e9a87SYuri Pankov case '%':
2985260e9a87SYuri Pankov if (operand2 == 0) {
2986260e9a87SYuri Pankov mandoc_msg(MANDOCERR_DIVZERO,
2987cec8643bSMichal Nowak ln, *pos, "%s", v);
2988260e9a87SYuri Pankov *res = 0;
2989260e9a87SYuri Pankov break;
2990260e9a87SYuri Pankov }
2991260e9a87SYuri Pankov *res %= operand2;
2992260e9a87SYuri Pankov break;
2993260e9a87SYuri Pankov case '<':
2994260e9a87SYuri Pankov *res = *res < operand2;
2995260e9a87SYuri Pankov break;
2996260e9a87SYuri Pankov case '>':
2997260e9a87SYuri Pankov *res = *res > operand2;
2998260e9a87SYuri Pankov break;
2999260e9a87SYuri Pankov case 'l':
3000260e9a87SYuri Pankov *res = *res <= operand2;
3001260e9a87SYuri Pankov break;
3002260e9a87SYuri Pankov case 'g':
3003260e9a87SYuri Pankov *res = *res >= operand2;
3004260e9a87SYuri Pankov break;
3005260e9a87SYuri Pankov case '=':
3006260e9a87SYuri Pankov *res = *res == operand2;
3007260e9a87SYuri Pankov break;
3008260e9a87SYuri Pankov case '!':
3009260e9a87SYuri Pankov *res = *res != operand2;
3010260e9a87SYuri Pankov break;
3011260e9a87SYuri Pankov case '&':
3012260e9a87SYuri Pankov *res = *res && operand2;
3013260e9a87SYuri Pankov break;
3014260e9a87SYuri Pankov case ':':
3015260e9a87SYuri Pankov *res = *res || operand2;
3016260e9a87SYuri Pankov break;
3017260e9a87SYuri Pankov case 'i':
3018260e9a87SYuri Pankov if (operand2 < *res)
3019260e9a87SYuri Pankov *res = operand2;
3020260e9a87SYuri Pankov break;
3021260e9a87SYuri Pankov case 'a':
3022260e9a87SYuri Pankov if (operand2 > *res)
3023260e9a87SYuri Pankov *res = operand2;
3024260e9a87SYuri Pankov break;
3025260e9a87SYuri Pankov default:
3026260e9a87SYuri Pankov abort();
3027260e9a87SYuri Pankov }
3028260e9a87SYuri Pankov }
3029371584c2SYuri Pankov return 1;
3030260e9a87SYuri Pankov }
3031260e9a87SYuri Pankov
3032371584c2SYuri Pankov /* --- register management ------------------------------------------------ */
3033371584c2SYuri Pankov
303495c635efSGarrett D'Amore void
roff_setreg(struct roff * r,const char * name,int val,char sign)3035698f87a4SGarrett D'Amore roff_setreg(struct roff *r, const char *name, int val, char sign)
303695c635efSGarrett D'Amore {
30376640c13bSYuri Pankov roff_setregn(r, name, strlen(name), val, sign, INT_MIN);
30386640c13bSYuri Pankov }
30396640c13bSYuri Pankov
30406640c13bSYuri Pankov static void
roff_setregn(struct roff * r,const char * name,size_t len,int val,char sign,int step)30416640c13bSYuri Pankov roff_setregn(struct roff *r, const char *name, size_t len,
30426640c13bSYuri Pankov int val, char sign, int step)
30436640c13bSYuri Pankov {
3044698f87a4SGarrett D'Amore struct roffreg *reg;
304595c635efSGarrett D'Amore
3046698f87a4SGarrett D'Amore /* Search for an existing register with the same name. */
3047698f87a4SGarrett D'Amore reg = r->regtab;
3048698f87a4SGarrett D'Amore
30496640c13bSYuri Pankov while (reg != NULL && (reg->key.sz != len ||
30506640c13bSYuri Pankov strncmp(reg->key.p, name, len) != 0))
3051698f87a4SGarrett D'Amore reg = reg->next;
3052698f87a4SGarrett D'Amore
3053698f87a4SGarrett D'Amore if (NULL == reg) {
3054698f87a4SGarrett D'Amore /* Create a new register. */
3055698f87a4SGarrett D'Amore reg = mandoc_malloc(sizeof(struct roffreg));
30566640c13bSYuri Pankov reg->key.p = mandoc_strndup(name, len);
30576640c13bSYuri Pankov reg->key.sz = len;
3058698f87a4SGarrett D'Amore reg->val = 0;
30596640c13bSYuri Pankov reg->step = 0;
3060698f87a4SGarrett D'Amore reg->next = r->regtab;
3061698f87a4SGarrett D'Amore r->regtab = reg;
3062698f87a4SGarrett D'Amore }
3063698f87a4SGarrett D'Amore
3064698f87a4SGarrett D'Amore if ('+' == sign)
3065698f87a4SGarrett D'Amore reg->val += val;
3066698f87a4SGarrett D'Amore else if ('-' == sign)
3067698f87a4SGarrett D'Amore reg->val -= val;
3068698f87a4SGarrett D'Amore else
3069698f87a4SGarrett D'Amore reg->val = val;
30706640c13bSYuri Pankov if (step != INT_MIN)
30716640c13bSYuri Pankov reg->step = step;
3072698f87a4SGarrett D'Amore }
3073698f87a4SGarrett D'Amore
3074260e9a87SYuri Pankov /*
3075260e9a87SYuri Pankov * Handle some predefined read-only number registers.
3076260e9a87SYuri Pankov * For now, return -1 if the requested register is not predefined;
3077260e9a87SYuri Pankov * in case a predefined read-only register having the value -1
3078260e9a87SYuri Pankov * were to turn up, another special value would have to be chosen.
3079260e9a87SYuri Pankov */
3080260e9a87SYuri Pankov static int
roff_getregro(const struct roff * r,const char * name)3081371584c2SYuri Pankov roff_getregro(const struct roff *r, const char *name)
3082260e9a87SYuri Pankov {
3083260e9a87SYuri Pankov
3084260e9a87SYuri Pankov switch (*name) {
3085371584c2SYuri Pankov case '$': /* Number of arguments of the last macro evaluated. */
3086cec8643bSMichal Nowak return r->mstackpos < 0 ? 0 : r->mstack[r->mstackpos].argc;
3087260e9a87SYuri Pankov case 'A': /* ASCII approximation mode is always off. */
3088371584c2SYuri Pankov return 0;
3089260e9a87SYuri Pankov case 'g': /* Groff compatibility mode is always on. */
3090371584c2SYuri Pankov return 1;
3091260e9a87SYuri Pankov case 'H': /* Fixed horizontal resolution. */
3092371584c2SYuri Pankov return 24;
3093260e9a87SYuri Pankov case 'j': /* Always adjust left margin only. */
3094371584c2SYuri Pankov return 0;
3095260e9a87SYuri Pankov case 'T': /* Some output device is always defined. */
3096371584c2SYuri Pankov return 1;
3097260e9a87SYuri Pankov case 'V': /* Fixed vertical resolution. */
3098371584c2SYuri Pankov return 40;
3099260e9a87SYuri Pankov default:
3100371584c2SYuri Pankov return -1;
3101260e9a87SYuri Pankov }
3102260e9a87SYuri Pankov }
3103260e9a87SYuri Pankov
3104698f87a4SGarrett D'Amore int
roff_getreg(struct roff * r,const char * name)31056640c13bSYuri Pankov roff_getreg(struct roff *r, const char *name)
3106698f87a4SGarrett D'Amore {
31076640c13bSYuri Pankov return roff_getregn(r, name, strlen(name), '\0');
3108698f87a4SGarrett D'Amore }
3109698f87a4SGarrett D'Amore
3110698f87a4SGarrett D'Amore static int
roff_getregn(struct roff * r,const char * name,size_t len,char sign)31116640c13bSYuri Pankov roff_getregn(struct roff *r, const char *name, size_t len, char sign)
3112698f87a4SGarrett D'Amore {
3113698f87a4SGarrett D'Amore struct roffreg *reg;
3114260e9a87SYuri Pankov int val;
3115260e9a87SYuri Pankov
3116260e9a87SYuri Pankov if ('.' == name[0] && 2 == len) {
3117371584c2SYuri Pankov val = roff_getregro(r, name + 1);
3118260e9a87SYuri Pankov if (-1 != val)
3119371584c2SYuri Pankov return val;
3120260e9a87SYuri Pankov }
3121698f87a4SGarrett D'Amore
31226640c13bSYuri Pankov for (reg = r->regtab; reg; reg = reg->next) {
3123698f87a4SGarrett D'Amore if (len == reg->key.sz &&
31246640c13bSYuri Pankov 0 == strncmp(name, reg->key.p, len)) {
31256640c13bSYuri Pankov switch (sign) {
31266640c13bSYuri Pankov case '+':
31276640c13bSYuri Pankov reg->val += reg->step;
31286640c13bSYuri Pankov break;
31296640c13bSYuri Pankov case '-':
31306640c13bSYuri Pankov reg->val -= reg->step;
31316640c13bSYuri Pankov break;
31326640c13bSYuri Pankov default:
31336640c13bSYuri Pankov break;
31346640c13bSYuri Pankov }
3135371584c2SYuri Pankov return reg->val;
31366640c13bSYuri Pankov }
31376640c13bSYuri Pankov }
3138698f87a4SGarrett D'Amore
31396640c13bSYuri Pankov roff_setregn(r, name, len, 0, '\0', INT_MIN);
3140371584c2SYuri Pankov return 0;
3141371584c2SYuri Pankov }
3142371584c2SYuri Pankov
3143371584c2SYuri Pankov static int
roff_hasregn(const struct roff * r,const char * name,size_t len)3144371584c2SYuri Pankov roff_hasregn(const struct roff *r, const char *name, size_t len)
3145371584c2SYuri Pankov {
3146371584c2SYuri Pankov struct roffreg *reg;
3147371584c2SYuri Pankov int val;
3148371584c2SYuri Pankov
3149371584c2SYuri Pankov if ('.' == name[0] && 2 == len) {
3150371584c2SYuri Pankov val = roff_getregro(r, name + 1);
3151371584c2SYuri Pankov if (-1 != val)
3152371584c2SYuri Pankov return 1;
3153371584c2SYuri Pankov }
3154371584c2SYuri Pankov
3155371584c2SYuri Pankov for (reg = r->regtab; reg; reg = reg->next)
3156371584c2SYuri Pankov if (len == reg->key.sz &&
3157371584c2SYuri Pankov 0 == strncmp(name, reg->key.p, len))
3158371584c2SYuri Pankov return 1;
3159371584c2SYuri Pankov
3160371584c2SYuri Pankov return 0;
3161698f87a4SGarrett D'Amore }
3162698f87a4SGarrett D'Amore
3163698f87a4SGarrett D'Amore static void
roff_freereg(struct roffreg * reg)3164698f87a4SGarrett D'Amore roff_freereg(struct roffreg *reg)
3165698f87a4SGarrett D'Amore {
3166698f87a4SGarrett D'Amore struct roffreg *old_reg;
3167698f87a4SGarrett D'Amore
3168698f87a4SGarrett D'Amore while (NULL != reg) {
3169698f87a4SGarrett D'Amore free(reg->key.p);
3170698f87a4SGarrett D'Amore old_reg = reg;
3171698f87a4SGarrett D'Amore reg = reg->next;
3172698f87a4SGarrett D'Amore free(old_reg);
3173698f87a4SGarrett D'Amore }
317495c635efSGarrett D'Amore }
317595c635efSGarrett D'Amore
3176cec8643bSMichal Nowak static int
roff_nr(ROFF_ARGS)317795c635efSGarrett D'Amore roff_nr(ROFF_ARGS)
317895c635efSGarrett D'Amore {
31796640c13bSYuri Pankov char *key, *val, *step;
3180260e9a87SYuri Pankov size_t keysz;
31816640c13bSYuri Pankov int iv, is, len;
3182698f87a4SGarrett D'Amore char sign;
318395c635efSGarrett D'Amore
3184260e9a87SYuri Pankov key = val = buf->buf + pos;
3185260e9a87SYuri Pankov if (*key == '\0')
3186371584c2SYuri Pankov return ROFF_IGN;
3187260e9a87SYuri Pankov
3188260e9a87SYuri Pankov keysz = roff_getname(r, &val, ln, pos);
3189cec8643bSMichal Nowak if (key[keysz] == '\\' || key[keysz] == '\t')
3190371584c2SYuri Pankov return ROFF_IGN;
319195c635efSGarrett D'Amore
3192698f87a4SGarrett D'Amore sign = *val;
3193260e9a87SYuri Pankov if (sign == '+' || sign == '-')
3194698f87a4SGarrett D'Amore val++;
3195698f87a4SGarrett D'Amore
31966640c13bSYuri Pankov len = 0;
31976640c13bSYuri Pankov if (roff_evalnum(r, ln, val, &len, &iv, ROFFNUM_SCALE) == 0)
31986640c13bSYuri Pankov return ROFF_IGN;
319995c635efSGarrett D'Amore
32006640c13bSYuri Pankov step = val + len;
32016640c13bSYuri Pankov while (isspace((unsigned char)*step))
32026640c13bSYuri Pankov step++;
32036640c13bSYuri Pankov if (roff_evalnum(r, ln, step, NULL, &is, 0) == 0)
32046640c13bSYuri Pankov is = INT_MIN;
32056640c13bSYuri Pankov
32066640c13bSYuri Pankov roff_setregn(r, key, keysz, iv, sign, is);
3207371584c2SYuri Pankov return ROFF_IGN;
320895c635efSGarrett D'Amore }
320995c635efSGarrett D'Amore
3210cec8643bSMichal Nowak static int
roff_rr(ROFF_ARGS)3211260e9a87SYuri Pankov roff_rr(ROFF_ARGS)
3212260e9a87SYuri Pankov {
3213260e9a87SYuri Pankov struct roffreg *reg, **prev;
3214260e9a87SYuri Pankov char *name, *cp;
3215260e9a87SYuri Pankov size_t namesz;
3216260e9a87SYuri Pankov
3217260e9a87SYuri Pankov name = cp = buf->buf + pos;
3218260e9a87SYuri Pankov if (*name == '\0')
3219371584c2SYuri Pankov return ROFF_IGN;
3220260e9a87SYuri Pankov namesz = roff_getname(r, &cp, ln, pos);
3221260e9a87SYuri Pankov name[namesz] = '\0';
3222260e9a87SYuri Pankov
3223260e9a87SYuri Pankov prev = &r->regtab;
3224260e9a87SYuri Pankov while (1) {
3225260e9a87SYuri Pankov reg = *prev;
3226260e9a87SYuri Pankov if (reg == NULL || !strcmp(name, reg->key.p))
3227260e9a87SYuri Pankov break;
3228260e9a87SYuri Pankov prev = ®->next;
3229260e9a87SYuri Pankov }
3230260e9a87SYuri Pankov if (reg != NULL) {
3231260e9a87SYuri Pankov *prev = reg->next;
3232260e9a87SYuri Pankov free(reg->key.p);
3233260e9a87SYuri Pankov free(reg);
3234260e9a87SYuri Pankov }
3235371584c2SYuri Pankov return ROFF_IGN;
3236260e9a87SYuri Pankov }
3237260e9a87SYuri Pankov
3238371584c2SYuri Pankov /* --- handler functions for roff requests -------------------------------- */
3239371584c2SYuri Pankov
3240cec8643bSMichal Nowak static int
roff_rm(ROFF_ARGS)324195c635efSGarrett D'Amore roff_rm(ROFF_ARGS)
324295c635efSGarrett D'Amore {
324395c635efSGarrett D'Amore const char *name;
324495c635efSGarrett D'Amore char *cp;
3245260e9a87SYuri Pankov size_t namesz;
324695c635efSGarrett D'Amore
3247260e9a87SYuri Pankov cp = buf->buf + pos;
3248260e9a87SYuri Pankov while (*cp != '\0') {
3249260e9a87SYuri Pankov name = cp;
3250260e9a87SYuri Pankov namesz = roff_getname(r, &cp, ln, (int)(cp - buf->buf));
3251260e9a87SYuri Pankov roff_setstrn(&r->strtab, name, namesz, NULL, 0, 0);
3252c66b8046SYuri Pankov roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0);
3253cec8643bSMichal Nowak if (name[namesz] == '\\' || name[namesz] == '\t')
3254260e9a87SYuri Pankov break;
325595c635efSGarrett D'Amore }
3256371584c2SYuri Pankov return ROFF_IGN;
325795c635efSGarrett D'Amore }
325895c635efSGarrett D'Amore
3259cec8643bSMichal Nowak static int
roff_it(ROFF_ARGS)3260698f87a4SGarrett D'Amore roff_it(ROFF_ARGS)
3261698f87a4SGarrett D'Amore {
3262698f87a4SGarrett D'Amore int iv;
3263698f87a4SGarrett D'Amore
3264698f87a4SGarrett D'Amore /* Parse the number of lines. */
3265260e9a87SYuri Pankov
3266260e9a87SYuri Pankov if ( ! roff_evalnum(r, ln, buf->buf, &pos, &iv, 0)) {
3267cec8643bSMichal Nowak mandoc_msg(MANDOCERR_IT_NONUM,
3268cec8643bSMichal Nowak ln, ppos, "%s", buf->buf + 1);
3269371584c2SYuri Pankov return ROFF_IGN;
3270698f87a4SGarrett D'Amore }
3271698f87a4SGarrett D'Amore
3272260e9a87SYuri Pankov while (isspace((unsigned char)buf->buf[pos]))
3273260e9a87SYuri Pankov pos++;
3274260e9a87SYuri Pankov
3275260e9a87SYuri Pankov /*
3276260e9a87SYuri Pankov * Arm the input line trap.
3277260e9a87SYuri Pankov * Special-casing "an-trap" is an ugly workaround to cope
3278260e9a87SYuri Pankov * with DocBook stupidly fiddling with man(7) internals.
3279260e9a87SYuri Pankov */
3280260e9a87SYuri Pankov
3281698f87a4SGarrett D'Amore roffit_lines = iv;
3282260e9a87SYuri Pankov roffit_macro = mandoc_strdup(iv != 1 ||
3283260e9a87SYuri Pankov strcmp(buf->buf + pos, "an-trap") ?
3284260e9a87SYuri Pankov buf->buf + pos : "br");
3285371584c2SYuri Pankov return ROFF_IGN;
3286698f87a4SGarrett D'Amore }
3287698f87a4SGarrett D'Amore
3288cec8643bSMichal Nowak static int
roff_Dd(ROFF_ARGS)3289698f87a4SGarrett D'Amore roff_Dd(ROFF_ARGS)
3290698f87a4SGarrett D'Amore {
3291c66b8046SYuri Pankov int mask;
3292c66b8046SYuri Pankov enum roff_tok t, te;
3293698f87a4SGarrett D'Amore
3294c66b8046SYuri Pankov switch (tok) {
3295c66b8046SYuri Pankov case ROFF_Dd:
3296c66b8046SYuri Pankov tok = MDOC_Dd;
3297c66b8046SYuri Pankov te = MDOC_MAX;
3298260e9a87SYuri Pankov if (r->format == 0)
3299260e9a87SYuri Pankov r->format = MPARSE_MDOC;
3300c66b8046SYuri Pankov mask = MPARSE_MDOC | MPARSE_QUICK;
3301c66b8046SYuri Pankov break;
3302c66b8046SYuri Pankov case ROFF_TH:
3303c66b8046SYuri Pankov tok = MAN_TH;
3304c66b8046SYuri Pankov te = MAN_MAX;
3305260e9a87SYuri Pankov if (r->format == 0)
3306260e9a87SYuri Pankov r->format = MPARSE_MAN;
3307c66b8046SYuri Pankov mask = MPARSE_QUICK;
3308c66b8046SYuri Pankov break;
3309c66b8046SYuri Pankov default:
3310c66b8046SYuri Pankov abort();
3311c66b8046SYuri Pankov }
3312c66b8046SYuri Pankov if ((r->options & mask) == 0)
3313c66b8046SYuri Pankov for (t = tok; t < te; t++)
3314c66b8046SYuri Pankov roff_setstr(r, roff_name[t], NULL, 0);
3315371584c2SYuri Pankov return ROFF_CONT;
3316698f87a4SGarrett D'Amore }
3317698f87a4SGarrett D'Amore
3318cec8643bSMichal Nowak static int
roff_TE(ROFF_ARGS)331995c635efSGarrett D'Amore roff_TE(ROFF_ARGS)
332095c635efSGarrett D'Amore {
3321cec8643bSMichal Nowak r->man->flags &= ~ROFF_NONOFILL;
3322c66b8046SYuri Pankov if (r->tbl == NULL) {
3323cec8643bSMichal Nowak mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "TE");
3324c66b8046SYuri Pankov return ROFF_IGN;
3325c66b8046SYuri Pankov }
3326cec8643bSMichal Nowak if (tbl_end(r->tbl, 0) == 0) {
3327c66b8046SYuri Pankov r->tbl = NULL;
3328260e9a87SYuri Pankov free(buf->buf);
3329260e9a87SYuri Pankov buf->buf = mandoc_strdup(".sp");
3330260e9a87SYuri Pankov buf->sz = 4;
33316640c13bSYuri Pankov *offs = 0;
3332371584c2SYuri Pankov return ROFF_REPARSE;
3333260e9a87SYuri Pankov }
3334c66b8046SYuri Pankov r->tbl = NULL;
3335371584c2SYuri Pankov return ROFF_IGN;
333695c635efSGarrett D'Amore }
333795c635efSGarrett D'Amore
3338cec8643bSMichal Nowak static int
roff_T_(ROFF_ARGS)333995c635efSGarrett D'Amore roff_T_(ROFF_ARGS)
334095c635efSGarrett D'Amore {
334195c635efSGarrett D'Amore
334295c635efSGarrett D'Amore if (NULL == r->tbl)
3343cec8643bSMichal Nowak mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "T&");
334495c635efSGarrett D'Amore else
3345c66b8046SYuri Pankov tbl_restart(ln, ppos, r->tbl);
334695c635efSGarrett D'Amore
3347371584c2SYuri Pankov return ROFF_IGN;
334895c635efSGarrett D'Amore }
334995c635efSGarrett D'Amore
3350260e9a87SYuri Pankov /*
3351260e9a87SYuri Pankov * Handle in-line equation delimiters.
3352260e9a87SYuri Pankov */
3353cec8643bSMichal Nowak static int
roff_eqndelim(struct roff * r,struct buf * buf,int pos)3354260e9a87SYuri Pankov roff_eqndelim(struct roff *r, struct buf *buf, int pos)
335595c635efSGarrett D'Amore {
3356260e9a87SYuri Pankov char *cp1, *cp2;
3357260e9a87SYuri Pankov const char *bef_pr, *bef_nl, *mac, *aft_nl, *aft_pr;
335895c635efSGarrett D'Amore
3359260e9a87SYuri Pankov /*
3360260e9a87SYuri Pankov * Outside equations, look for an opening delimiter.
3361260e9a87SYuri Pankov * If we are inside an equation, we already know it is
3362260e9a87SYuri Pankov * in-line, or this function wouldn't have been called;
3363260e9a87SYuri Pankov * so look for a closing delimiter.
3364260e9a87SYuri Pankov */
3365260e9a87SYuri Pankov
3366260e9a87SYuri Pankov cp1 = buf->buf + pos;
3367260e9a87SYuri Pankov cp2 = strchr(cp1, r->eqn == NULL ?
3368260e9a87SYuri Pankov r->last_eqn->odelim : r->last_eqn->cdelim);
3369260e9a87SYuri Pankov if (cp2 == NULL)
3370371584c2SYuri Pankov return ROFF_CONT;
3371260e9a87SYuri Pankov
3372260e9a87SYuri Pankov *cp2++ = '\0';
3373260e9a87SYuri Pankov bef_pr = bef_nl = aft_nl = aft_pr = "";
3374260e9a87SYuri Pankov
3375260e9a87SYuri Pankov /* Handle preceding text, protecting whitespace. */
3376260e9a87SYuri Pankov
3377260e9a87SYuri Pankov if (*buf->buf != '\0') {
3378260e9a87SYuri Pankov if (r->eqn == NULL)
3379260e9a87SYuri Pankov bef_pr = "\\&";
3380260e9a87SYuri Pankov bef_nl = "\n";
338195c635efSGarrett D'Amore }
338295c635efSGarrett D'Amore
3383260e9a87SYuri Pankov /*
3384260e9a87SYuri Pankov * Prepare replacing the delimiter with an equation macro
3385260e9a87SYuri Pankov * and drop leading white space from the equation.
3386260e9a87SYuri Pankov */
3387260e9a87SYuri Pankov
3388260e9a87SYuri Pankov if (r->eqn == NULL) {
3389260e9a87SYuri Pankov while (*cp2 == ' ')
3390260e9a87SYuri Pankov cp2++;
3391260e9a87SYuri Pankov mac = ".EQ";
3392260e9a87SYuri Pankov } else
3393260e9a87SYuri Pankov mac = ".EN";
3394260e9a87SYuri Pankov
3395260e9a87SYuri Pankov /* Handle following text, protecting whitespace. */
3396260e9a87SYuri Pankov
3397260e9a87SYuri Pankov if (*cp2 != '\0') {
3398260e9a87SYuri Pankov aft_nl = "\n";
3399260e9a87SYuri Pankov if (r->eqn != NULL)
3400260e9a87SYuri Pankov aft_pr = "\\&";
3401260e9a87SYuri Pankov }
3402260e9a87SYuri Pankov
3403260e9a87SYuri Pankov /* Do the actual replacement. */
3404260e9a87SYuri Pankov
3405260e9a87SYuri Pankov buf->sz = mandoc_asprintf(&cp1, "%s%s%s%s%s%s%s", buf->buf,
3406260e9a87SYuri Pankov bef_pr, bef_nl, mac, aft_nl, aft_pr, cp2) + 1;
3407260e9a87SYuri Pankov free(buf->buf);
3408260e9a87SYuri Pankov buf->buf = cp1;
3409260e9a87SYuri Pankov
3410260e9a87SYuri Pankov /* Toggle the in-line state of the eqn subsystem. */
3411260e9a87SYuri Pankov
3412260e9a87SYuri Pankov r->eqn_inline = r->eqn == NULL;
3413371584c2SYuri Pankov return ROFF_REPARSE;
3414260e9a87SYuri Pankov }
3415260e9a87SYuri Pankov
3416cec8643bSMichal Nowak static int
roff_EQ(ROFF_ARGS)3417260e9a87SYuri Pankov roff_EQ(ROFF_ARGS)
341895c635efSGarrett D'Amore {
3419c66b8046SYuri Pankov struct roff_node *n;
3420c66b8046SYuri Pankov
3421cec8643bSMichal Nowak if (r->man->meta.macroset == MACROSET_MAN)
3422c66b8046SYuri Pankov man_breakscope(r->man, ROFF_EQ);
3423c66b8046SYuri Pankov n = roff_node_alloc(r->man, ln, ppos, ROFFT_EQN, TOKEN_NONE);
3424c66b8046SYuri Pankov if (ln > r->man->last->line)
3425c66b8046SYuri Pankov n->flags |= NODE_LINE;
3426cec8643bSMichal Nowak n->eqn = eqn_box_new();
3427c66b8046SYuri Pankov roff_node_append(r->man, n);
3428c66b8046SYuri Pankov r->man->next = ROFF_NEXT_SIBLING;
342995c635efSGarrett D'Amore
3430260e9a87SYuri Pankov assert(r->eqn == NULL);
3431c66b8046SYuri Pankov if (r->last_eqn == NULL)
3432cec8643bSMichal Nowak r->last_eqn = eqn_alloc();
3433c66b8046SYuri Pankov else
3434c66b8046SYuri Pankov eqn_reset(r->last_eqn);
3435c66b8046SYuri Pankov r->eqn = r->last_eqn;
3436c66b8046SYuri Pankov r->eqn->node = n;
343795c635efSGarrett D'Amore
3438260e9a87SYuri Pankov if (buf->buf[pos] != '\0')
3439cec8643bSMichal Nowak mandoc_msg(MANDOCERR_ARG_SKIP, ln, pos,
3440260e9a87SYuri Pankov ".EQ %s", buf->buf + pos);
344195c635efSGarrett D'Amore
3442371584c2SYuri Pankov return ROFF_IGN;
344395c635efSGarrett D'Amore }
344495c635efSGarrett D'Amore
3445cec8643bSMichal Nowak static int
roff_EN(ROFF_ARGS)344695c635efSGarrett D'Amore roff_EN(ROFF_ARGS)
344795c635efSGarrett D'Amore {
3448c66b8046SYuri Pankov if (r->eqn != NULL) {
3449c66b8046SYuri Pankov eqn_parse(r->eqn);
3450c66b8046SYuri Pankov r->eqn = NULL;
3451c66b8046SYuri Pankov } else
3452cec8643bSMichal Nowak mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "EN");
3453c66b8046SYuri Pankov if (buf->buf[pos] != '\0')
3454cec8643bSMichal Nowak mandoc_msg(MANDOCERR_ARG_SKIP, ln, pos,
3455c66b8046SYuri Pankov "EN %s", buf->buf + pos);
3456371584c2SYuri Pankov return ROFF_IGN;
345795c635efSGarrett D'Amore }
345895c635efSGarrett D'Amore
3459cec8643bSMichal Nowak static int
roff_TS(ROFF_ARGS)346095c635efSGarrett D'Amore roff_TS(ROFF_ARGS)
346195c635efSGarrett D'Amore {
3462c66b8046SYuri Pankov if (r->tbl != NULL) {
3463cec8643bSMichal Nowak mandoc_msg(MANDOCERR_BLK_BROKEN, ln, ppos, "TS breaks TS");
3464cec8643bSMichal Nowak tbl_end(r->tbl, 0);
346595c635efSGarrett D'Amore }
3466cec8643bSMichal Nowak r->man->flags |= ROFF_NONOFILL;
3467cec8643bSMichal Nowak r->tbl = tbl_alloc(ppos, ln, r->last_tbl);
3468cec8643bSMichal Nowak if (r->last_tbl == NULL)
3469c66b8046SYuri Pankov r->first_tbl = r->tbl;
3470c66b8046SYuri Pankov r->last_tbl = r->tbl;
3471371584c2SYuri Pankov return ROFF_IGN;
3472698f87a4SGarrett D'Amore }
3473698f87a4SGarrett D'Amore
3474cec8643bSMichal Nowak static int
roff_noarg(ROFF_ARGS)3475cec8643bSMichal Nowak roff_noarg(ROFF_ARGS)
3476cec8643bSMichal Nowak {
3477cec8643bSMichal Nowak if (r->man->flags & (MAN_BLINE | MAN_ELINE))
3478cec8643bSMichal Nowak man_breakscope(r->man, tok);
3479cec8643bSMichal Nowak if (tok == ROFF_brp)
3480cec8643bSMichal Nowak tok = ROFF_br;
3481cec8643bSMichal Nowak roff_elem_alloc(r->man, ln, ppos, tok);
3482cec8643bSMichal Nowak if (buf->buf[pos] != '\0')
3483cec8643bSMichal Nowak mandoc_msg(MANDOCERR_ARG_SKIP, ln, pos,
3484cec8643bSMichal Nowak "%s %s", roff_name[tok], buf->buf + pos);
3485cec8643bSMichal Nowak if (tok == ROFF_nf)
3486cec8643bSMichal Nowak r->man->flags |= ROFF_NOFILL;
3487cec8643bSMichal Nowak else if (tok == ROFF_fi)
3488cec8643bSMichal Nowak r->man->flags &= ~ROFF_NOFILL;
3489cec8643bSMichal Nowak r->man->last->flags |= NODE_LINE | NODE_VALID | NODE_ENDED;
3490cec8643bSMichal Nowak r->man->next = ROFF_NEXT_SIBLING;
3491cec8643bSMichal Nowak return ROFF_IGN;
3492cec8643bSMichal Nowak }
3493cec8643bSMichal Nowak
3494cec8643bSMichal Nowak static int
roff_onearg(ROFF_ARGS)3495c66b8046SYuri Pankov roff_onearg(ROFF_ARGS)
3496260e9a87SYuri Pankov {
3497c66b8046SYuri Pankov struct roff_node *n;
3498c66b8046SYuri Pankov char *cp;
3499c66b8046SYuri Pankov int npos;
3500260e9a87SYuri Pankov
3501c66b8046SYuri Pankov if (r->man->flags & (MAN_BLINE | MAN_ELINE) &&
3502c66b8046SYuri Pankov (tok == ROFF_ce || tok == ROFF_rj || tok == ROFF_sp ||
3503c66b8046SYuri Pankov tok == ROFF_ti))
3504c66b8046SYuri Pankov man_breakscope(r->man, tok);
3505c66b8046SYuri Pankov
3506c66b8046SYuri Pankov if (roffce_node != NULL && (tok == ROFF_ce || tok == ROFF_rj)) {
3507c66b8046SYuri Pankov r->man->last = roffce_node;
3508c66b8046SYuri Pankov r->man->next = ROFF_NEXT_SIBLING;
3509c66b8046SYuri Pankov }
3510c66b8046SYuri Pankov
3511c66b8046SYuri Pankov roff_elem_alloc(r->man, ln, ppos, tok);
3512c66b8046SYuri Pankov n = r->man->last;
3513c66b8046SYuri Pankov
3514c66b8046SYuri Pankov cp = buf->buf + pos;
3515c66b8046SYuri Pankov if (*cp != '\0') {
3516c66b8046SYuri Pankov while (*cp != '\0' && *cp != ' ')
3517c66b8046SYuri Pankov cp++;
3518c66b8046SYuri Pankov while (*cp == ' ')
3519c66b8046SYuri Pankov *cp++ = '\0';
3520c66b8046SYuri Pankov if (*cp != '\0')
3521cec8643bSMichal Nowak mandoc_msg(MANDOCERR_ARG_EXCESS,
3522cec8643bSMichal Nowak ln, (int)(cp - buf->buf),
3523c66b8046SYuri Pankov "%s ... %s", roff_name[tok], cp);
3524c66b8046SYuri Pankov roff_word_alloc(r->man, ln, pos, buf->buf + pos);
3525c66b8046SYuri Pankov }
3526c66b8046SYuri Pankov
3527c66b8046SYuri Pankov if (tok == ROFF_ce || tok == ROFF_rj) {
3528c66b8046SYuri Pankov if (r->man->last->type == ROFFT_ELEM) {
3529c66b8046SYuri Pankov roff_word_alloc(r->man, ln, pos, "1");
3530c66b8046SYuri Pankov r->man->last->flags |= NODE_NOSRC;
3531c66b8046SYuri Pankov }
3532c66b8046SYuri Pankov npos = 0;
3533c66b8046SYuri Pankov if (roff_evalnum(r, ln, r->man->last->string, &npos,
3534c66b8046SYuri Pankov &roffce_lines, 0) == 0) {
3535cec8643bSMichal Nowak mandoc_msg(MANDOCERR_CE_NONUM,
3536cec8643bSMichal Nowak ln, pos, "ce %s", buf->buf + pos);
3537c66b8046SYuri Pankov roffce_lines = 1;
3538c66b8046SYuri Pankov }
3539c66b8046SYuri Pankov if (roffce_lines < 1) {
3540c66b8046SYuri Pankov r->man->last = r->man->last->parent;
3541c66b8046SYuri Pankov roffce_node = NULL;
3542c66b8046SYuri Pankov roffce_lines = 0;
3543c66b8046SYuri Pankov } else
3544c66b8046SYuri Pankov roffce_node = r->man->last->parent;
3545c66b8046SYuri Pankov } else {
3546c66b8046SYuri Pankov n->flags |= NODE_VALID | NODE_ENDED;
3547c66b8046SYuri Pankov r->man->last = n;
3548c66b8046SYuri Pankov }
3549c66b8046SYuri Pankov n->flags |= NODE_LINE;
3550c66b8046SYuri Pankov r->man->next = ROFF_NEXT_SIBLING;
3551c66b8046SYuri Pankov return ROFF_IGN;
3552c66b8046SYuri Pankov }
3553c66b8046SYuri Pankov
3554cec8643bSMichal Nowak static int
roff_manyarg(ROFF_ARGS)3555c66b8046SYuri Pankov roff_manyarg(ROFF_ARGS)
3556c66b8046SYuri Pankov {
3557c66b8046SYuri Pankov struct roff_node *n;
3558c66b8046SYuri Pankov char *sp, *ep;
3559c66b8046SYuri Pankov
3560c66b8046SYuri Pankov roff_elem_alloc(r->man, ln, ppos, tok);
3561c66b8046SYuri Pankov n = r->man->last;
3562c66b8046SYuri Pankov
3563c66b8046SYuri Pankov for (sp = ep = buf->buf + pos; *sp != '\0'; sp = ep) {
3564c66b8046SYuri Pankov while (*ep != '\0' && *ep != ' ')
3565c66b8046SYuri Pankov ep++;
3566c66b8046SYuri Pankov while (*ep == ' ')
3567c66b8046SYuri Pankov *ep++ = '\0';
3568c66b8046SYuri Pankov roff_word_alloc(r->man, ln, sp - buf->buf, sp);
3569c66b8046SYuri Pankov }
3570c66b8046SYuri Pankov
3571c66b8046SYuri Pankov n->flags |= NODE_LINE | NODE_VALID | NODE_ENDED;
3572c66b8046SYuri Pankov r->man->last = n;
3573c66b8046SYuri Pankov r->man->next = ROFF_NEXT_SIBLING;
3574c66b8046SYuri Pankov return ROFF_IGN;
3575c66b8046SYuri Pankov }
3576c66b8046SYuri Pankov
3577cec8643bSMichal Nowak static int
roff_als(ROFF_ARGS)3578c66b8046SYuri Pankov roff_als(ROFF_ARGS)
3579c66b8046SYuri Pankov {
3580c66b8046SYuri Pankov char *oldn, *newn, *end, *value;
3581c66b8046SYuri Pankov size_t oldsz, newsz, valsz;
3582c66b8046SYuri Pankov
3583c66b8046SYuri Pankov newn = oldn = buf->buf + pos;
3584c66b8046SYuri Pankov if (*newn == '\0')
3585c66b8046SYuri Pankov return ROFF_IGN;
3586c66b8046SYuri Pankov
3587c66b8046SYuri Pankov newsz = roff_getname(r, &oldn, ln, pos);
3588cec8643bSMichal Nowak if (newn[newsz] == '\\' || newn[newsz] == '\t' || *oldn == '\0')
3589c66b8046SYuri Pankov return ROFF_IGN;
3590c66b8046SYuri Pankov
3591c66b8046SYuri Pankov end = oldn;
3592c66b8046SYuri Pankov oldsz = roff_getname(r, &end, ln, oldn - buf->buf);
3593c66b8046SYuri Pankov if (oldsz == 0)
3594c66b8046SYuri Pankov return ROFF_IGN;
3595c66b8046SYuri Pankov
3596cec8643bSMichal Nowak valsz = mandoc_asprintf(&value, ".%.*s \\$@\\\"\n",
3597c66b8046SYuri Pankov (int)oldsz, oldn);
3598c66b8046SYuri Pankov roff_setstrn(&r->strtab, newn, newsz, value, valsz, 0);
3599c66b8046SYuri Pankov roff_setstrn(&r->rentab, newn, newsz, NULL, 0, 0);
3600c66b8046SYuri Pankov free(value);
3601c66b8046SYuri Pankov return ROFF_IGN;
3602c66b8046SYuri Pankov }
3603c66b8046SYuri Pankov
3604*4d131170SRobert Mustacchi /*
3605*4d131170SRobert Mustacchi * The .break request only makes sense inside conditionals,
3606*4d131170SRobert Mustacchi * and that case is already handled in roff_cond_sub().
3607*4d131170SRobert Mustacchi */
3608*4d131170SRobert Mustacchi static int
roff_break(ROFF_ARGS)3609*4d131170SRobert Mustacchi roff_break(ROFF_ARGS)
3610*4d131170SRobert Mustacchi {
3611*4d131170SRobert Mustacchi mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, pos, "break");
3612*4d131170SRobert Mustacchi return ROFF_IGN;
3613*4d131170SRobert Mustacchi }
3614*4d131170SRobert Mustacchi
3615cec8643bSMichal Nowak static int
roff_cc(ROFF_ARGS)3616698f87a4SGarrett D'Amore roff_cc(ROFF_ARGS)
3617698f87a4SGarrett D'Amore {
3618698f87a4SGarrett D'Amore const char *p;
3619698f87a4SGarrett D'Amore
3620260e9a87SYuri Pankov p = buf->buf + pos;
3621698f87a4SGarrett D'Amore
3622260e9a87SYuri Pankov if (*p == '\0' || (r->control = *p++) == '.')
3623c66b8046SYuri Pankov r->control = '\0';
3624698f87a4SGarrett D'Amore
3625260e9a87SYuri Pankov if (*p != '\0')
3626cec8643bSMichal Nowak mandoc_msg(MANDOCERR_ARG_EXCESS,
3627260e9a87SYuri Pankov ln, p - buf->buf, "cc ... %s", p);
3628698f87a4SGarrett D'Amore
3629371584c2SYuri Pankov return ROFF_IGN;
363095c635efSGarrett D'Amore }
363195c635efSGarrett D'Amore
3632cec8643bSMichal Nowak static int
roff_char(ROFF_ARGS)3633cec8643bSMichal Nowak roff_char(ROFF_ARGS)
3634cec8643bSMichal Nowak {
3635cec8643bSMichal Nowak const char *p, *kp, *vp;
3636cec8643bSMichal Nowak size_t ksz, vsz;
3637cec8643bSMichal Nowak int font;
3638cec8643bSMichal Nowak
3639cec8643bSMichal Nowak /* Parse the character to be replaced. */
3640cec8643bSMichal Nowak
3641cec8643bSMichal Nowak kp = buf->buf + pos;
3642cec8643bSMichal Nowak p = kp + 1;
3643cec8643bSMichal Nowak if (*kp == '\0' || (*kp == '\\' &&
3644cec8643bSMichal Nowak mandoc_escape(&p, NULL, NULL) != ESCAPE_SPECIAL) ||
3645cec8643bSMichal Nowak (*p != ' ' && *p != '\0')) {
3646cec8643bSMichal Nowak mandoc_msg(MANDOCERR_CHAR_ARG, ln, pos, "char %s", kp);
3647cec8643bSMichal Nowak return ROFF_IGN;
3648cec8643bSMichal Nowak }
3649cec8643bSMichal Nowak ksz = p - kp;
3650cec8643bSMichal Nowak while (*p == ' ')
3651cec8643bSMichal Nowak p++;
3652cec8643bSMichal Nowak
3653cec8643bSMichal Nowak /*
3654cec8643bSMichal Nowak * If the replacement string contains a font escape sequence,
3655cec8643bSMichal Nowak * we have to restore the font at the end.
3656cec8643bSMichal Nowak */
3657cec8643bSMichal Nowak
3658cec8643bSMichal Nowak vp = p;
3659cec8643bSMichal Nowak vsz = strlen(p);
3660cec8643bSMichal Nowak font = 0;
3661cec8643bSMichal Nowak while (*p != '\0') {
3662cec8643bSMichal Nowak if (*p++ != '\\')
3663cec8643bSMichal Nowak continue;
3664cec8643bSMichal Nowak switch (mandoc_escape(&p, NULL, NULL)) {
3665cec8643bSMichal Nowak case ESCAPE_FONT:
3666cec8643bSMichal Nowak case ESCAPE_FONTROMAN:
3667cec8643bSMichal Nowak case ESCAPE_FONTITALIC:
3668cec8643bSMichal Nowak case ESCAPE_FONTBOLD:
3669cec8643bSMichal Nowak case ESCAPE_FONTBI:
3670*4d131170SRobert Mustacchi case ESCAPE_FONTCR:
3671*4d131170SRobert Mustacchi case ESCAPE_FONTCB:
3672*4d131170SRobert Mustacchi case ESCAPE_FONTCI:
3673cec8643bSMichal Nowak case ESCAPE_FONTPREV:
3674cec8643bSMichal Nowak font++;
3675cec8643bSMichal Nowak break;
3676cec8643bSMichal Nowak default:
3677cec8643bSMichal Nowak break;
3678cec8643bSMichal Nowak }
3679cec8643bSMichal Nowak }
3680cec8643bSMichal Nowak if (font > 1)
3681cec8643bSMichal Nowak mandoc_msg(MANDOCERR_CHAR_FONT,
3682cec8643bSMichal Nowak ln, (int)(vp - buf->buf), "%s", vp);
3683cec8643bSMichal Nowak
3684cec8643bSMichal Nowak /*
3685cec8643bSMichal Nowak * Approximate the effect of .char using the .tr tables.
3686cec8643bSMichal Nowak * XXX In groff, .char and .tr interact differently.
3687cec8643bSMichal Nowak */
3688cec8643bSMichal Nowak
3689cec8643bSMichal Nowak if (ksz == 1) {
3690cec8643bSMichal Nowak if (r->xtab == NULL)
3691cec8643bSMichal Nowak r->xtab = mandoc_calloc(128, sizeof(*r->xtab));
3692cec8643bSMichal Nowak assert((unsigned int)*kp < 128);
3693cec8643bSMichal Nowak free(r->xtab[(int)*kp].p);
3694cec8643bSMichal Nowak r->xtab[(int)*kp].sz = mandoc_asprintf(&r->xtab[(int)*kp].p,
3695cec8643bSMichal Nowak "%s%s", vp, font ? "\fP" : "");
3696cec8643bSMichal Nowak } else {
3697cec8643bSMichal Nowak roff_setstrn(&r->xmbtab, kp, ksz, vp, vsz, 0);
3698cec8643bSMichal Nowak if (font)
3699cec8643bSMichal Nowak roff_setstrn(&r->xmbtab, kp, ksz, "\\fP", 3, 1);
3700cec8643bSMichal Nowak }
3701cec8643bSMichal Nowak return ROFF_IGN;
3702cec8643bSMichal Nowak }
3703cec8643bSMichal Nowak
3704cec8643bSMichal Nowak static int
roff_ec(ROFF_ARGS)3705c66b8046SYuri Pankov roff_ec(ROFF_ARGS)
3706c66b8046SYuri Pankov {
3707c66b8046SYuri Pankov const char *p;
3708c66b8046SYuri Pankov
3709c66b8046SYuri Pankov p = buf->buf + pos;
3710c66b8046SYuri Pankov if (*p == '\0')
3711c66b8046SYuri Pankov r->escape = '\\';
3712c66b8046SYuri Pankov else {
3713c66b8046SYuri Pankov r->escape = *p;
3714c66b8046SYuri Pankov if (*++p != '\0')
3715cec8643bSMichal Nowak mandoc_msg(MANDOCERR_ARG_EXCESS, ln,
3716cec8643bSMichal Nowak (int)(p - buf->buf), "ec ... %s", p);
3717c66b8046SYuri Pankov }
3718c66b8046SYuri Pankov return ROFF_IGN;
3719c66b8046SYuri Pankov }
3720c66b8046SYuri Pankov
3721cec8643bSMichal Nowak static int
roff_eo(ROFF_ARGS)3722c66b8046SYuri Pankov roff_eo(ROFF_ARGS)
3723c66b8046SYuri Pankov {
3724c66b8046SYuri Pankov r->escape = '\0';
3725c66b8046SYuri Pankov if (buf->buf[pos] != '\0')
3726cec8643bSMichal Nowak mandoc_msg(MANDOCERR_ARG_SKIP,
3727c66b8046SYuri Pankov ln, pos, "eo %s", buf->buf + pos);
3728c66b8046SYuri Pankov return ROFF_IGN;
3729c66b8046SYuri Pankov }
3730c66b8046SYuri Pankov
3731cec8643bSMichal Nowak static int
roff_nop(ROFF_ARGS)3732cec8643bSMichal Nowak roff_nop(ROFF_ARGS)
3733cec8643bSMichal Nowak {
3734cec8643bSMichal Nowak while (buf->buf[pos] == ' ')
3735cec8643bSMichal Nowak pos++;
3736cec8643bSMichal Nowak *offs = pos;
3737cec8643bSMichal Nowak return ROFF_RERUN;
3738cec8643bSMichal Nowak }
3739cec8643bSMichal Nowak
3740cec8643bSMichal Nowak static int
roff_tr(ROFF_ARGS)374195c635efSGarrett D'Amore roff_tr(ROFF_ARGS)
374295c635efSGarrett D'Amore {
374395c635efSGarrett D'Amore const char *p, *first, *second;
374495c635efSGarrett D'Amore size_t fsz, ssz;
374595c635efSGarrett D'Amore enum mandoc_esc esc;
374695c635efSGarrett D'Amore
3747260e9a87SYuri Pankov p = buf->buf + pos;
374895c635efSGarrett D'Amore
3749260e9a87SYuri Pankov if (*p == '\0') {
3750cec8643bSMichal Nowak mandoc_msg(MANDOCERR_REQ_EMPTY, ln, ppos, "tr");
3751371584c2SYuri Pankov return ROFF_IGN;
375295c635efSGarrett D'Amore }
375395c635efSGarrett D'Amore
3754260e9a87SYuri Pankov while (*p != '\0') {
375595c635efSGarrett D'Amore fsz = ssz = 1;
375695c635efSGarrett D'Amore
375795c635efSGarrett D'Amore first = p++;
3758260e9a87SYuri Pankov if (*first == '\\') {
375995c635efSGarrett D'Amore esc = mandoc_escape(&p, NULL, NULL);
3760260e9a87SYuri Pankov if (esc == ESCAPE_ERROR) {
3761cec8643bSMichal Nowak mandoc_msg(MANDOCERR_ESC_BAD, ln,
3762cec8643bSMichal Nowak (int)(p - buf->buf), "%s", first);
3763371584c2SYuri Pankov return ROFF_IGN;
376495c635efSGarrett D'Amore }
376595c635efSGarrett D'Amore fsz = (size_t)(p - first);
376695c635efSGarrett D'Amore }
376795c635efSGarrett D'Amore
376895c635efSGarrett D'Amore second = p++;
3769260e9a87SYuri Pankov if (*second == '\\') {
377095c635efSGarrett D'Amore esc = mandoc_escape(&p, NULL, NULL);
3771260e9a87SYuri Pankov if (esc == ESCAPE_ERROR) {
3772cec8643bSMichal Nowak mandoc_msg(MANDOCERR_ESC_BAD, ln,
3773cec8643bSMichal Nowak (int)(p - buf->buf), "%s", second);
3774371584c2SYuri Pankov return ROFF_IGN;
377595c635efSGarrett D'Amore }
377695c635efSGarrett D'Amore ssz = (size_t)(p - second);
3777260e9a87SYuri Pankov } else if (*second == '\0') {
3778cec8643bSMichal Nowak mandoc_msg(MANDOCERR_TR_ODD, ln,
3779cec8643bSMichal Nowak (int)(first - buf->buf), "tr %s", first);
378095c635efSGarrett D'Amore second = " ";
378195c635efSGarrett D'Amore p--;
378295c635efSGarrett D'Amore }
378395c635efSGarrett D'Amore
378495c635efSGarrett D'Amore if (fsz > 1) {
3785260e9a87SYuri Pankov roff_setstrn(&r->xmbtab, first, fsz,
3786260e9a87SYuri Pankov second, ssz, 0);
378795c635efSGarrett D'Amore continue;
378895c635efSGarrett D'Amore }
378995c635efSGarrett D'Amore
3790260e9a87SYuri Pankov if (r->xtab == NULL)
3791260e9a87SYuri Pankov r->xtab = mandoc_calloc(128,
3792260e9a87SYuri Pankov sizeof(struct roffstr));
379395c635efSGarrett D'Amore
379495c635efSGarrett D'Amore free(r->xtab[(int)*first].p);
379595c635efSGarrett D'Amore r->xtab[(int)*first].p = mandoc_strndup(second, ssz);
379695c635efSGarrett D'Amore r->xtab[(int)*first].sz = ssz;
379795c635efSGarrett D'Amore }
379895c635efSGarrett D'Amore
3799371584c2SYuri Pankov return ROFF_IGN;
380095c635efSGarrett D'Amore }
380195c635efSGarrett D'Amore
3802cec8643bSMichal Nowak /*
3803cec8643bSMichal Nowak * Implementation of the .return request.
3804cec8643bSMichal Nowak * There is no need to call roff_userret() from here.
3805cec8643bSMichal Nowak * The read module will call that after rewinding the reader stack
3806cec8643bSMichal Nowak * to the place from where the current macro was called.
3807cec8643bSMichal Nowak */
3808cec8643bSMichal Nowak static int
roff_return(ROFF_ARGS)3809cec8643bSMichal Nowak roff_return(ROFF_ARGS)
3810cec8643bSMichal Nowak {
3811cec8643bSMichal Nowak if (r->mstackpos >= 0)
3812cec8643bSMichal Nowak return ROFF_IGN | ROFF_USERRET;
3813cec8643bSMichal Nowak
3814cec8643bSMichal Nowak mandoc_msg(MANDOCERR_REQ_NOMAC, ln, ppos, "return");
3815cec8643bSMichal Nowak return ROFF_IGN;
3816cec8643bSMichal Nowak }
3817cec8643bSMichal Nowak
3818cec8643bSMichal Nowak static int
roff_rn(ROFF_ARGS)3819c66b8046SYuri Pankov roff_rn(ROFF_ARGS)
3820c66b8046SYuri Pankov {
3821c66b8046SYuri Pankov const char *value;
3822c66b8046SYuri Pankov char *oldn, *newn, *end;
3823c66b8046SYuri Pankov size_t oldsz, newsz;
3824c66b8046SYuri Pankov int deftype;
3825c66b8046SYuri Pankov
3826c66b8046SYuri Pankov oldn = newn = buf->buf + pos;
3827c66b8046SYuri Pankov if (*oldn == '\0')
3828c66b8046SYuri Pankov return ROFF_IGN;
3829c66b8046SYuri Pankov
3830c66b8046SYuri Pankov oldsz = roff_getname(r, &newn, ln, pos);
3831cec8643bSMichal Nowak if (oldn[oldsz] == '\\' || oldn[oldsz] == '\t' || *newn == '\0')
3832c66b8046SYuri Pankov return ROFF_IGN;
3833c66b8046SYuri Pankov
3834c66b8046SYuri Pankov end = newn;
3835c66b8046SYuri Pankov newsz = roff_getname(r, &end, ln, newn - buf->buf);
3836c66b8046SYuri Pankov if (newsz == 0)
3837c66b8046SYuri Pankov return ROFF_IGN;
3838c66b8046SYuri Pankov
3839c66b8046SYuri Pankov deftype = ROFFDEF_ANY;
3840c66b8046SYuri Pankov value = roff_getstrn(r, oldn, oldsz, &deftype);
3841c66b8046SYuri Pankov switch (deftype) {
3842c66b8046SYuri Pankov case ROFFDEF_USER:
3843c66b8046SYuri Pankov roff_setstrn(&r->strtab, newn, newsz, value, strlen(value), 0);
3844c66b8046SYuri Pankov roff_setstrn(&r->strtab, oldn, oldsz, NULL, 0, 0);
3845c66b8046SYuri Pankov roff_setstrn(&r->rentab, newn, newsz, NULL, 0, 0);
3846c66b8046SYuri Pankov break;
3847c66b8046SYuri Pankov case ROFFDEF_PRE:
3848c66b8046SYuri Pankov roff_setstrn(&r->strtab, newn, newsz, value, strlen(value), 0);
3849c66b8046SYuri Pankov roff_setstrn(&r->rentab, newn, newsz, NULL, 0, 0);
3850c66b8046SYuri Pankov break;
3851c66b8046SYuri Pankov case ROFFDEF_REN:
3852c66b8046SYuri Pankov roff_setstrn(&r->rentab, newn, newsz, value, strlen(value), 0);
3853c66b8046SYuri Pankov roff_setstrn(&r->rentab, oldn, oldsz, NULL, 0, 0);
3854c66b8046SYuri Pankov roff_setstrn(&r->strtab, newn, newsz, NULL, 0, 0);
3855c66b8046SYuri Pankov break;
3856c66b8046SYuri Pankov case ROFFDEF_STD:
3857c66b8046SYuri Pankov roff_setstrn(&r->rentab, newn, newsz, oldn, oldsz, 0);
3858c66b8046SYuri Pankov roff_setstrn(&r->strtab, newn, newsz, NULL, 0, 0);
3859c66b8046SYuri Pankov break;
3860c66b8046SYuri Pankov default:
3861c66b8046SYuri Pankov roff_setstrn(&r->strtab, newn, newsz, NULL, 0, 0);
3862c66b8046SYuri Pankov roff_setstrn(&r->rentab, newn, newsz, NULL, 0, 0);
3863c66b8046SYuri Pankov break;
3864c66b8046SYuri Pankov }
3865c66b8046SYuri Pankov return ROFF_IGN;
3866c66b8046SYuri Pankov }
3867c66b8046SYuri Pankov
3868cec8643bSMichal Nowak static int
roff_shift(ROFF_ARGS)3869cec8643bSMichal Nowak roff_shift(ROFF_ARGS)
3870cec8643bSMichal Nowak {
3871cec8643bSMichal Nowak struct mctx *ctx;
3872cec8643bSMichal Nowak int levels, i;
3873cec8643bSMichal Nowak
3874cec8643bSMichal Nowak levels = 1;
3875cec8643bSMichal Nowak if (buf->buf[pos] != '\0' &&
3876cec8643bSMichal Nowak roff_evalnum(r, ln, buf->buf, &pos, &levels, 0) == 0) {
3877cec8643bSMichal Nowak mandoc_msg(MANDOCERR_CE_NONUM,
3878cec8643bSMichal Nowak ln, pos, "shift %s", buf->buf + pos);
3879cec8643bSMichal Nowak levels = 1;
3880cec8643bSMichal Nowak }
3881cec8643bSMichal Nowak if (r->mstackpos < 0) {
3882cec8643bSMichal Nowak mandoc_msg(MANDOCERR_REQ_NOMAC, ln, ppos, "shift");
3883cec8643bSMichal Nowak return ROFF_IGN;
3884cec8643bSMichal Nowak }
3885cec8643bSMichal Nowak ctx = r->mstack + r->mstackpos;
3886cec8643bSMichal Nowak if (levels > ctx->argc) {
3887cec8643bSMichal Nowak mandoc_msg(MANDOCERR_SHIFT,
3888cec8643bSMichal Nowak ln, pos, "%d, but max is %d", levels, ctx->argc);
3889cec8643bSMichal Nowak levels = ctx->argc;
3890cec8643bSMichal Nowak }
3891cec8643bSMichal Nowak if (levels == 0)
3892cec8643bSMichal Nowak return ROFF_IGN;
3893cec8643bSMichal Nowak for (i = 0; i < levels; i++)
3894cec8643bSMichal Nowak free(ctx->argv[i]);
3895cec8643bSMichal Nowak ctx->argc -= levels;
3896cec8643bSMichal Nowak for (i = 0; i < ctx->argc; i++)
3897cec8643bSMichal Nowak ctx->argv[i] = ctx->argv[i + levels];
3898cec8643bSMichal Nowak return ROFF_IGN;
3899cec8643bSMichal Nowak }
3900cec8643bSMichal Nowak
3901cec8643bSMichal Nowak static int
roff_so(ROFF_ARGS)390295c635efSGarrett D'Amore roff_so(ROFF_ARGS)
390395c635efSGarrett D'Amore {
3904260e9a87SYuri Pankov char *name, *cp;
390595c635efSGarrett D'Amore
3906260e9a87SYuri Pankov name = buf->buf + pos;
3907cec8643bSMichal Nowak mandoc_msg(MANDOCERR_SO, ln, ppos, "so %s", name);
390895c635efSGarrett D'Amore
390995c635efSGarrett D'Amore /*
391095c635efSGarrett D'Amore * Handle `so'. Be EXTREMELY careful, as we shouldn't be
391195c635efSGarrett D'Amore * opening anything that's not in our cwd or anything beneath
391295c635efSGarrett D'Amore * it. Thus, explicitly disallow traversing up the file-system
391395c635efSGarrett D'Amore * or using absolute paths.
391495c635efSGarrett D'Amore */
391595c635efSGarrett D'Amore
3916260e9a87SYuri Pankov if (*name == '/' || strstr(name, "../") || strstr(name, "/..")) {
3917cec8643bSMichal Nowak mandoc_msg(MANDOCERR_SO_PATH, ln, ppos, ".so %s", name);
3918260e9a87SYuri Pankov buf->sz = mandoc_asprintf(&cp,
3919260e9a87SYuri Pankov ".sp\nSee the file %s.\n.sp", name) + 1;
3920260e9a87SYuri Pankov free(buf->buf);
3921260e9a87SYuri Pankov buf->buf = cp;
3922260e9a87SYuri Pankov *offs = 0;
3923371584c2SYuri Pankov return ROFF_REPARSE;
392495c635efSGarrett D'Amore }
392595c635efSGarrett D'Amore
392695c635efSGarrett D'Amore *offs = pos;
3927371584c2SYuri Pankov return ROFF_SO;
392895c635efSGarrett D'Amore }
392995c635efSGarrett D'Amore
3930371584c2SYuri Pankov /* --- user defined strings and macros ------------------------------------ */
3931371584c2SYuri Pankov
3932cec8643bSMichal Nowak static int
roff_userdef(ROFF_ARGS)393395c635efSGarrett D'Amore roff_userdef(ROFF_ARGS)
393495c635efSGarrett D'Amore {
3935cec8643bSMichal Nowak struct mctx *ctx;
3936cec8643bSMichal Nowak char *arg, *ap, *dst, *src;
3937cec8643bSMichal Nowak size_t sz;
393895c635efSGarrett D'Amore
3939*4d131170SRobert Mustacchi /* If the macro is empty, ignore it altogether. */
3940*4d131170SRobert Mustacchi
3941*4d131170SRobert Mustacchi if (*r->current_string == '\0')
3942*4d131170SRobert Mustacchi return ROFF_IGN;
3943*4d131170SRobert Mustacchi
3944cec8643bSMichal Nowak /* Initialize a new macro stack context. */
3945260e9a87SYuri Pankov
3946cec8643bSMichal Nowak if (++r->mstackpos == r->mstacksz) {
3947cec8643bSMichal Nowak r->mstack = mandoc_recallocarray(r->mstack,
3948cec8643bSMichal Nowak r->mstacksz, r->mstacksz + 8, sizeof(*r->mstack));
3949cec8643bSMichal Nowak r->mstacksz += 8;
3950371584c2SYuri Pankov }
3951cec8643bSMichal Nowak ctx = r->mstack + r->mstackpos;
3952cec8643bSMichal Nowak ctx->argsz = 0;
3953cec8643bSMichal Nowak ctx->argc = 0;
3954cec8643bSMichal Nowak ctx->argv = NULL;
3955cec8643bSMichal Nowak
3956cec8643bSMichal Nowak /*
3957cec8643bSMichal Nowak * Collect pointers to macro argument strings,
3958cec8643bSMichal Nowak * NUL-terminating them and escaping quotes.
3959cec8643bSMichal Nowak */
3960cec8643bSMichal Nowak
3961cec8643bSMichal Nowak src = buf->buf + pos;
3962cec8643bSMichal Nowak while (*src != '\0') {
3963cec8643bSMichal Nowak if (ctx->argc == ctx->argsz) {
3964cec8643bSMichal Nowak ctx->argsz += 8;
3965cec8643bSMichal Nowak ctx->argv = mandoc_reallocarray(ctx->argv,
3966cec8643bSMichal Nowak ctx->argsz, sizeof(*ctx->argv));
3967371584c2SYuri Pankov }
3968cec8643bSMichal Nowak arg = roff_getarg(r, &src, ln, &pos);
3969cec8643bSMichal Nowak sz = 1; /* For the terminating NUL. */
3970cec8643bSMichal Nowak for (ap = arg; *ap != '\0'; ap++)
3971cec8643bSMichal Nowak sz += *ap == '"' ? 4 : 1;
3972cec8643bSMichal Nowak ctx->argv[ctx->argc++] = dst = mandoc_malloc(sz);
3973cec8643bSMichal Nowak for (ap = arg; *ap != '\0'; ap++) {
3974260e9a87SYuri Pankov if (*ap == '"') {
3975cec8643bSMichal Nowak memcpy(dst, "\\(dq", 4);
3976cec8643bSMichal Nowak dst += 4;
3977260e9a87SYuri Pankov } else
3978cec8643bSMichal Nowak *dst++ = *ap;
3979260e9a87SYuri Pankov }
3980cec8643bSMichal Nowak *dst = '\0';
3981cec8643bSMichal Nowak free(arg);
398295c635efSGarrett D'Amore }
398395c635efSGarrett D'Amore
3984cec8643bSMichal Nowak /* Replace the macro invocation by the macro definition. */
398595c635efSGarrett D'Amore
3986260e9a87SYuri Pankov free(buf->buf);
3987cec8643bSMichal Nowak buf->buf = mandoc_strdup(r->current_string);
3988cec8643bSMichal Nowak buf->sz = strlen(buf->buf) + 1;
3989260e9a87SYuri Pankov *offs = 0;
3990260e9a87SYuri Pankov
3991*4d131170SRobert Mustacchi return buf->buf[buf->sz - 2] == '\n' ?
3992cec8643bSMichal Nowak ROFF_REPARSE | ROFF_USERCALL : ROFF_IGN | ROFF_APPEND;
399395c635efSGarrett D'Amore }
399495c635efSGarrett D'Amore
3995c66b8046SYuri Pankov /*
3996c66b8046SYuri Pankov * Calling a high-level macro that was renamed with .rn.
3997c66b8046SYuri Pankov * r->current_string has already been set up by roff_parse().
3998c66b8046SYuri Pankov */
3999cec8643bSMichal Nowak static int
roff_renamed(ROFF_ARGS)4000c66b8046SYuri Pankov roff_renamed(ROFF_ARGS)
4001c66b8046SYuri Pankov {
4002c66b8046SYuri Pankov char *nbuf;
4003c66b8046SYuri Pankov
4004c66b8046SYuri Pankov buf->sz = mandoc_asprintf(&nbuf, ".%s%s%s", r->current_string,
4005c66b8046SYuri Pankov buf->buf[pos] == '\0' ? "" : " ", buf->buf + pos) + 1;
4006c66b8046SYuri Pankov free(buf->buf);
4007c66b8046SYuri Pankov buf->buf = nbuf;
40086640c13bSYuri Pankov *offs = 0;
4009c66b8046SYuri Pankov return ROFF_CONT;
4010c66b8046SYuri Pankov }
4011c66b8046SYuri Pankov
4012cec8643bSMichal Nowak /*
4013cec8643bSMichal Nowak * Measure the length in bytes of the roff identifier at *cpp
4014cec8643bSMichal Nowak * and advance the pointer to the next word.
4015cec8643bSMichal Nowak */
4016260e9a87SYuri Pankov static size_t
roff_getname(struct roff * r,char ** cpp,int ln,int pos)401795c635efSGarrett D'Amore roff_getname(struct roff *r, char **cpp, int ln, int pos)
401895c635efSGarrett D'Amore {
401995c635efSGarrett D'Amore char *name, *cp;
4020260e9a87SYuri Pankov size_t namesz;
402195c635efSGarrett D'Amore
402295c635efSGarrett D'Amore name = *cpp;
4023cec8643bSMichal Nowak if (*name == '\0')
4024371584c2SYuri Pankov return 0;
402595c635efSGarrett D'Amore
4026cec8643bSMichal Nowak /* Advance cp to the byte after the end of the name. */
4027cec8643bSMichal Nowak
4028260e9a87SYuri Pankov for (cp = name; 1; cp++) {
4029260e9a87SYuri Pankov namesz = cp - name;
4030cec8643bSMichal Nowak if (*cp == '\0')
4031cec8643bSMichal Nowak break;
4032cec8643bSMichal Nowak if (*cp == ' ' || *cp == '\t') {
4033cec8643bSMichal Nowak cp++;
4034260e9a87SYuri Pankov break;
4035260e9a87SYuri Pankov }
4036cec8643bSMichal Nowak if (*cp != '\\')
403795c635efSGarrett D'Amore continue;
4038cec8643bSMichal Nowak if (cp[1] == '{' || cp[1] == '}')
4039260e9a87SYuri Pankov break;
4040cec8643bSMichal Nowak if (*++cp == '\\')
404195c635efSGarrett D'Amore continue;
4042cec8643bSMichal Nowak mandoc_msg(MANDOCERR_NAMESC, ln, pos,
4043260e9a87SYuri Pankov "%.*s", (int)(cp - name + 1), name);
4044260e9a87SYuri Pankov mandoc_escape((const char **)&cp, NULL, NULL);
4045260e9a87SYuri Pankov break;
404695c635efSGarrett D'Amore }
404795c635efSGarrett D'Amore
404895c635efSGarrett D'Amore /* Read past spaces. */
4049cec8643bSMichal Nowak
4050cec8643bSMichal Nowak while (*cp == ' ')
405195c635efSGarrett D'Amore cp++;
405295c635efSGarrett D'Amore
405395c635efSGarrett D'Amore *cpp = cp;
4054371584c2SYuri Pankov return namesz;
405595c635efSGarrett D'Amore }
405695c635efSGarrett D'Amore
405795c635efSGarrett D'Amore /*
405895c635efSGarrett D'Amore * Store *string into the user-defined string called *name.
405995c635efSGarrett D'Amore * To clear an existing entry, call with (*r, *name, NULL, 0).
4060260e9a87SYuri Pankov * append == 0: replace mode
4061260e9a87SYuri Pankov * append == 1: single-line append mode
4062260e9a87SYuri Pankov * append == 2: multiline append mode, append '\n' after each call
406395c635efSGarrett D'Amore */
406495c635efSGarrett D'Amore static void
roff_setstr(struct roff * r,const char * name,const char * string,int append)406595c635efSGarrett D'Amore roff_setstr(struct roff *r, const char *name, const char *string,
4066260e9a87SYuri Pankov int append)
406795c635efSGarrett D'Amore {
4068c66b8046SYuri Pankov size_t namesz;
406995c635efSGarrett D'Amore
4070c66b8046SYuri Pankov namesz = strlen(name);
4071c66b8046SYuri Pankov roff_setstrn(&r->strtab, name, namesz, string,
4072260e9a87SYuri Pankov string ? strlen(string) : 0, append);
4073c66b8046SYuri Pankov roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0);
407495c635efSGarrett D'Amore }
407595c635efSGarrett D'Amore
407695c635efSGarrett D'Amore static void
roff_setstrn(struct roffkv ** r,const char * name,size_t namesz,const char * string,size_t stringsz,int append)407795c635efSGarrett D'Amore roff_setstrn(struct roffkv **r, const char *name, size_t namesz,
4078260e9a87SYuri Pankov const char *string, size_t stringsz, int append)
407995c635efSGarrett D'Amore {
408095c635efSGarrett D'Amore struct roffkv *n;
408195c635efSGarrett D'Amore char *c;
408295c635efSGarrett D'Amore int i;
408395c635efSGarrett D'Amore size_t oldch, newch;
408495c635efSGarrett D'Amore
408595c635efSGarrett D'Amore /* Search for an existing string with the same name. */
408695c635efSGarrett D'Amore n = *r;
408795c635efSGarrett D'Amore
4088260e9a87SYuri Pankov while (n && (namesz != n->key.sz ||
4089260e9a87SYuri Pankov strncmp(n->key.p, name, namesz)))
409095c635efSGarrett D'Amore n = n->next;
409195c635efSGarrett D'Amore
409295c635efSGarrett D'Amore if (NULL == n) {
409395c635efSGarrett D'Amore /* Create a new string table entry. */
409495c635efSGarrett D'Amore n = mandoc_malloc(sizeof(struct roffkv));
409595c635efSGarrett D'Amore n->key.p = mandoc_strndup(name, namesz);
409695c635efSGarrett D'Amore n->key.sz = namesz;
409795c635efSGarrett D'Amore n->val.p = NULL;
409895c635efSGarrett D'Amore n->val.sz = 0;
409995c635efSGarrett D'Amore n->next = *r;
410095c635efSGarrett D'Amore *r = n;
4101260e9a87SYuri Pankov } else if (0 == append) {
410295c635efSGarrett D'Amore free(n->val.p);
410395c635efSGarrett D'Amore n->val.p = NULL;
410495c635efSGarrett D'Amore n->val.sz = 0;
410595c635efSGarrett D'Amore }
410695c635efSGarrett D'Amore
410795c635efSGarrett D'Amore if (NULL == string)
410895c635efSGarrett D'Amore return;
410995c635efSGarrett D'Amore
411095c635efSGarrett D'Amore /*
411195c635efSGarrett D'Amore * One additional byte for the '\n' in multiline mode,
411295c635efSGarrett D'Amore * and one for the terminating '\0'.
411395c635efSGarrett D'Amore */
4114260e9a87SYuri Pankov newch = stringsz + (1 < append ? 2u : 1u);
411595c635efSGarrett D'Amore
411695c635efSGarrett D'Amore if (NULL == n->val.p) {
411795c635efSGarrett D'Amore n->val.p = mandoc_malloc(newch);
411895c635efSGarrett D'Amore *n->val.p = '\0';
411995c635efSGarrett D'Amore oldch = 0;
412095c635efSGarrett D'Amore } else {
412195c635efSGarrett D'Amore oldch = n->val.sz;
412295c635efSGarrett D'Amore n->val.p = mandoc_realloc(n->val.p, oldch + newch);
412395c635efSGarrett D'Amore }
412495c635efSGarrett D'Amore
412595c635efSGarrett D'Amore /* Skip existing content in the destination buffer. */
412695c635efSGarrett D'Amore c = n->val.p + (int)oldch;
412795c635efSGarrett D'Amore
412895c635efSGarrett D'Amore /* Append new content to the destination buffer. */
412995c635efSGarrett D'Amore i = 0;
413095c635efSGarrett D'Amore while (i < (int)stringsz) {
413195c635efSGarrett D'Amore /*
413295c635efSGarrett D'Amore * Rudimentary roff copy mode:
413395c635efSGarrett D'Amore * Handle escaped backslashes.
413495c635efSGarrett D'Amore */
413595c635efSGarrett D'Amore if ('\\' == string[i] && '\\' == string[i + 1])
413695c635efSGarrett D'Amore i++;
413795c635efSGarrett D'Amore *c++ = string[i++];
413895c635efSGarrett D'Amore }
413995c635efSGarrett D'Amore
414095c635efSGarrett D'Amore /* Append terminating bytes. */
4141260e9a87SYuri Pankov if (1 < append)
414295c635efSGarrett D'Amore *c++ = '\n';
414395c635efSGarrett D'Amore
414495c635efSGarrett D'Amore *c = '\0';
414595c635efSGarrett D'Amore n->val.sz = (int)(c - n->val.p);
414695c635efSGarrett D'Amore }
414795c635efSGarrett D'Amore
414895c635efSGarrett D'Amore static const char *
roff_getstrn(struct roff * r,const char * name,size_t len,int * deftype)41496640c13bSYuri Pankov roff_getstrn(struct roff *r, const char *name, size_t len,
4150c66b8046SYuri Pankov int *deftype)
415195c635efSGarrett D'Amore {
415295c635efSGarrett D'Amore const struct roffkv *n;
41536640c13bSYuri Pankov int found, i;
4154c66b8046SYuri Pankov enum roff_tok tok;
415595c635efSGarrett D'Amore
41566640c13bSYuri Pankov found = 0;
4157c66b8046SYuri Pankov for (n = r->strtab; n != NULL; n = n->next) {
41586640c13bSYuri Pankov if (strncmp(name, n->key.p, len) != 0 ||
41596640c13bSYuri Pankov n->key.p[len] != '\0' || n->val.p == NULL)
41606640c13bSYuri Pankov continue;
41616640c13bSYuri Pankov if (*deftype & ROFFDEF_USER) {
4162c66b8046SYuri Pankov *deftype = ROFFDEF_USER;
4163371584c2SYuri Pankov return n->val.p;
41646640c13bSYuri Pankov } else {
41656640c13bSYuri Pankov found = 1;
41666640c13bSYuri Pankov break;
4167c66b8046SYuri Pankov }
4168c66b8046SYuri Pankov }
4169c66b8046SYuri Pankov for (n = r->rentab; n != NULL; n = n->next) {
41706640c13bSYuri Pankov if (strncmp(name, n->key.p, len) != 0 ||
41716640c13bSYuri Pankov n->key.p[len] != '\0' || n->val.p == NULL)
41726640c13bSYuri Pankov continue;
41736640c13bSYuri Pankov if (*deftype & ROFFDEF_REN) {
4174c66b8046SYuri Pankov *deftype = ROFFDEF_REN;
4175c66b8046SYuri Pankov return n->val.p;
41766640c13bSYuri Pankov } else {
41776640c13bSYuri Pankov found = 1;
41786640c13bSYuri Pankov break;
4179c66b8046SYuri Pankov }
4180c66b8046SYuri Pankov }
41816640c13bSYuri Pankov for (i = 0; i < PREDEFS_MAX; i++) {
41826640c13bSYuri Pankov if (strncmp(name, predefs[i].name, len) != 0 ||
41836640c13bSYuri Pankov predefs[i].name[len] != '\0')
41846640c13bSYuri Pankov continue;
41856640c13bSYuri Pankov if (*deftype & ROFFDEF_PRE) {
41866640c13bSYuri Pankov *deftype = ROFFDEF_PRE;
41876640c13bSYuri Pankov return predefs[i].str;
41886640c13bSYuri Pankov } else {
41896640c13bSYuri Pankov found = 1;
41906640c13bSYuri Pankov break;
4191c66b8046SYuri Pankov }
41926640c13bSYuri Pankov }
4193cec8643bSMichal Nowak if (r->man->meta.macroset != MACROSET_MAN) {
4194c66b8046SYuri Pankov for (tok = MDOC_Dd; tok < MDOC_MAX; tok++) {
41956640c13bSYuri Pankov if (strncmp(name, roff_name[tok], len) != 0 ||
41966640c13bSYuri Pankov roff_name[tok][len] != '\0')
41976640c13bSYuri Pankov continue;
41986640c13bSYuri Pankov if (*deftype & ROFFDEF_STD) {
4199c66b8046SYuri Pankov *deftype = ROFFDEF_STD;
4200c66b8046SYuri Pankov return NULL;
42016640c13bSYuri Pankov } else {
42026640c13bSYuri Pankov found = 1;
42036640c13bSYuri Pankov break;
4204c66b8046SYuri Pankov }
4205c66b8046SYuri Pankov }
4206c66b8046SYuri Pankov }
4207cec8643bSMichal Nowak if (r->man->meta.macroset != MACROSET_MDOC) {
4208c66b8046SYuri Pankov for (tok = MAN_TH; tok < MAN_MAX; tok++) {
42096640c13bSYuri Pankov if (strncmp(name, roff_name[tok], len) != 0 ||
42106640c13bSYuri Pankov roff_name[tok][len] != '\0')
42116640c13bSYuri Pankov continue;
42126640c13bSYuri Pankov if (*deftype & ROFFDEF_STD) {
4213c66b8046SYuri Pankov *deftype = ROFFDEF_STD;
4214c66b8046SYuri Pankov return NULL;
42156640c13bSYuri Pankov } else {
42166640c13bSYuri Pankov found = 1;
42176640c13bSYuri Pankov break;
4218c66b8046SYuri Pankov }
4219c66b8046SYuri Pankov }
4220c66b8046SYuri Pankov }
42216640c13bSYuri Pankov
42226640c13bSYuri Pankov if (found == 0 && *deftype != ROFFDEF_ANY) {
42236640c13bSYuri Pankov if (*deftype & ROFFDEF_REN) {
42246640c13bSYuri Pankov /*
42256640c13bSYuri Pankov * This might still be a request,
42266640c13bSYuri Pankov * so do not treat it as undefined yet.
42276640c13bSYuri Pankov */
42286640c13bSYuri Pankov *deftype = ROFFDEF_UNDEF;
42296640c13bSYuri Pankov return NULL;
4230c66b8046SYuri Pankov }
42316640c13bSYuri Pankov
42326640c13bSYuri Pankov /* Using an undefined string defines it to be empty. */
42336640c13bSYuri Pankov
42346640c13bSYuri Pankov roff_setstrn(&r->strtab, name, len, "", 0, 0);
42356640c13bSYuri Pankov roff_setstrn(&r->rentab, name, len, NULL, 0, 0);
42366640c13bSYuri Pankov }
42376640c13bSYuri Pankov
4238c66b8046SYuri Pankov *deftype = 0;
4239371584c2SYuri Pankov return NULL;
424095c635efSGarrett D'Amore }
424195c635efSGarrett D'Amore
424295c635efSGarrett D'Amore static void
roff_freestr(struct roffkv * r)424395c635efSGarrett D'Amore roff_freestr(struct roffkv *r)
424495c635efSGarrett D'Amore {
424595c635efSGarrett D'Amore struct roffkv *n, *nn;
424695c635efSGarrett D'Amore
424795c635efSGarrett D'Amore for (n = r; n; n = nn) {
424895c635efSGarrett D'Amore free(n->key.p);
424995c635efSGarrett D'Amore free(n->val.p);
425095c635efSGarrett D'Amore nn = n->next;
425195c635efSGarrett D'Amore free(n);
425295c635efSGarrett D'Amore }
425395c635efSGarrett D'Amore }
425495c635efSGarrett D'Amore
4255371584c2SYuri Pankov /* --- accessors and utility functions ------------------------------------ */
4256371584c2SYuri Pankov
425795c635efSGarrett D'Amore /*
425895c635efSGarrett D'Amore * Duplicate an input string, making the appropriate character
425995c635efSGarrett D'Amore * conversations (as stipulated by `tr') along the way.
426095c635efSGarrett D'Amore * Returns a heap-allocated string with all the replacements made.
426195c635efSGarrett D'Amore */
426295c635efSGarrett D'Amore char *
roff_strdup(const struct roff * r,const char * p)426395c635efSGarrett D'Amore roff_strdup(const struct roff *r, const char *p)
426495c635efSGarrett D'Amore {
426595c635efSGarrett D'Amore const struct roffkv *cp;
426695c635efSGarrett D'Amore char *res;
426795c635efSGarrett D'Amore const char *pp;
426895c635efSGarrett D'Amore size_t ssz, sz;
426995c635efSGarrett D'Amore enum mandoc_esc esc;
427095c635efSGarrett D'Amore
427195c635efSGarrett D'Amore if (NULL == r->xmbtab && NULL == r->xtab)
4272371584c2SYuri Pankov return mandoc_strdup(p);
427395c635efSGarrett D'Amore else if ('\0' == *p)
4274371584c2SYuri Pankov return mandoc_strdup("");
427595c635efSGarrett D'Amore
427695c635efSGarrett D'Amore /*
427795c635efSGarrett D'Amore * Step through each character looking for term matches
427895c635efSGarrett D'Amore * (remember that a `tr' can be invoked with an escape, which is
427995c635efSGarrett D'Amore * a glyph but the escape is multi-character).
428095c635efSGarrett D'Amore * We only do this if the character hash has been initialised
428195c635efSGarrett D'Amore * and the string is >0 length.
428295c635efSGarrett D'Amore */
428395c635efSGarrett D'Amore
428495c635efSGarrett D'Amore res = NULL;
428595c635efSGarrett D'Amore ssz = 0;
428695c635efSGarrett D'Amore
428795c635efSGarrett D'Amore while ('\0' != *p) {
4288a40ea1a7SYuri Pankov assert((unsigned int)*p < 128);
4289a40ea1a7SYuri Pankov if ('\\' != *p && r->xtab && r->xtab[(unsigned int)*p].p) {
429095c635efSGarrett D'Amore sz = r->xtab[(int)*p].sz;
429195c635efSGarrett D'Amore res = mandoc_realloc(res, ssz + sz + 1);
429295c635efSGarrett D'Amore memcpy(res + ssz, r->xtab[(int)*p].p, sz);
429395c635efSGarrett D'Amore ssz += sz;
429495c635efSGarrett D'Amore p++;
429595c635efSGarrett D'Amore continue;
429695c635efSGarrett D'Amore } else if ('\\' != *p) {
429795c635efSGarrett D'Amore res = mandoc_realloc(res, ssz + 2);
429895c635efSGarrett D'Amore res[ssz++] = *p++;
429995c635efSGarrett D'Amore continue;
430095c635efSGarrett D'Amore }
430195c635efSGarrett D'Amore
430295c635efSGarrett D'Amore /* Search for term matches. */
430395c635efSGarrett D'Amore for (cp = r->xmbtab; cp; cp = cp->next)
430495c635efSGarrett D'Amore if (0 == strncmp(p, cp->key.p, cp->key.sz))
430595c635efSGarrett D'Amore break;
430695c635efSGarrett D'Amore
430795c635efSGarrett D'Amore if (NULL != cp) {
430895c635efSGarrett D'Amore /*
430995c635efSGarrett D'Amore * A match has been found.
431095c635efSGarrett D'Amore * Append the match to the array and move
431195c635efSGarrett D'Amore * forward by its keysize.
431295c635efSGarrett D'Amore */
4313260e9a87SYuri Pankov res = mandoc_realloc(res,
4314260e9a87SYuri Pankov ssz + cp->val.sz + 1);
431595c635efSGarrett D'Amore memcpy(res + ssz, cp->val.p, cp->val.sz);
431695c635efSGarrett D'Amore ssz += cp->val.sz;
431795c635efSGarrett D'Amore p += (int)cp->key.sz;
431895c635efSGarrett D'Amore continue;
431995c635efSGarrett D'Amore }
432095c635efSGarrett D'Amore
432195c635efSGarrett D'Amore /*
432295c635efSGarrett D'Amore * Handle escapes carefully: we need to copy
432395c635efSGarrett D'Amore * over just the escape itself, or else we might
432495c635efSGarrett D'Amore * do replacements within the escape itself.
432595c635efSGarrett D'Amore * Make sure to pass along the bogus string.
432695c635efSGarrett D'Amore */
432795c635efSGarrett D'Amore pp = p++;
432895c635efSGarrett D'Amore esc = mandoc_escape(&p, NULL, NULL);
432995c635efSGarrett D'Amore if (ESCAPE_ERROR == esc) {
433095c635efSGarrett D'Amore sz = strlen(pp);
433195c635efSGarrett D'Amore res = mandoc_realloc(res, ssz + sz + 1);
433295c635efSGarrett D'Amore memcpy(res + ssz, pp, sz);
433395c635efSGarrett D'Amore break;
433495c635efSGarrett D'Amore }
433595c635efSGarrett D'Amore /*
433695c635efSGarrett D'Amore * We bail out on bad escapes.
433795c635efSGarrett D'Amore * No need to warn: we already did so when
4338cec8643bSMichal Nowak * roff_expand() was called.
433995c635efSGarrett D'Amore */
434095c635efSGarrett D'Amore sz = (int)(p - pp);
434195c635efSGarrett D'Amore res = mandoc_realloc(res, ssz + sz + 1);
434295c635efSGarrett D'Amore memcpy(res + ssz, pp, sz);
434395c635efSGarrett D'Amore ssz += sz;
434495c635efSGarrett D'Amore }
434595c635efSGarrett D'Amore
434695c635efSGarrett D'Amore res[(int)ssz] = '\0';
4347371584c2SYuri Pankov return res;
434895c635efSGarrett D'Amore }
4349698f87a4SGarrett D'Amore
4350260e9a87SYuri Pankov int
roff_getformat(const struct roff * r)4351260e9a87SYuri Pankov roff_getformat(const struct roff *r)
4352260e9a87SYuri Pankov {
4353260e9a87SYuri Pankov
4354371584c2SYuri Pankov return r->format;
4355260e9a87SYuri Pankov }
4356260e9a87SYuri Pankov
4357698f87a4SGarrett D'Amore /*
4358698f87a4SGarrett D'Amore * Find out whether a line is a macro line or not.
4359698f87a4SGarrett D'Amore * If it is, adjust the current position and return one; if it isn't,
4360698f87a4SGarrett D'Amore * return zero and don't change the current position.
4361698f87a4SGarrett D'Amore * If the control character has been set with `.cc', then let that grain
4362698f87a4SGarrett D'Amore * precedence.
4363698f87a4SGarrett D'Amore * This is slighly contrary to groff, where using the non-breaking
4364698f87a4SGarrett D'Amore * control character when `cc' has been invoked will cause the
4365698f87a4SGarrett D'Amore * non-breaking macro contents to be printed verbatim.
4366698f87a4SGarrett D'Amore */
4367698f87a4SGarrett D'Amore int
roff_getcontrol(const struct roff * r,const char * cp,int * ppos)4368698f87a4SGarrett D'Amore roff_getcontrol(const struct roff *r, const char *cp, int *ppos)
4369698f87a4SGarrett D'Amore {
4370698f87a4SGarrett D'Amore int pos;
4371698f87a4SGarrett D'Amore
4372698f87a4SGarrett D'Amore pos = *ppos;
4373698f87a4SGarrett D'Amore
4374c66b8046SYuri Pankov if (r->control != '\0' && cp[pos] == r->control)
4375698f87a4SGarrett D'Amore pos++;
4376c66b8046SYuri Pankov else if (r->control != '\0')
4377371584c2SYuri Pankov return 0;
4378698f87a4SGarrett D'Amore else if ('\\' == cp[pos] && '.' == cp[pos + 1])
4379698f87a4SGarrett D'Amore pos += 2;
4380698f87a4SGarrett D'Amore else if ('.' == cp[pos] || '\'' == cp[pos])
4381698f87a4SGarrett D'Amore pos++;
4382698f87a4SGarrett D'Amore else
4383371584c2SYuri Pankov return 0;
4384698f87a4SGarrett D'Amore
4385698f87a4SGarrett D'Amore while (' ' == cp[pos] || '\t' == cp[pos])
4386698f87a4SGarrett D'Amore pos++;
4387698f87a4SGarrett D'Amore
4388698f87a4SGarrett D'Amore *ppos = pos;
4389371584c2SYuri Pankov return 1;
4390698f87a4SGarrett D'Amore }
4391