Bug Summary

File:src/usr.bin/mandoc/roff.c
Warning:line 4132, column 15
Use of memory after it is freed

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name roff.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.bin/mandoc/obj -resource-dir /usr/local/lib/clang/13.0.0 -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -Wno-unused-parameter -fdebug-compilation-dir=/usr/src/usr.bin/mandoc/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/usr.bin/mandoc/roff.c
1/* $OpenBSD: roff.c,v 1.252 2021/10/04 14:18:42 schwarze Exp $ */
2/*
3 * Copyright (c) 2010-2015, 2017-2021 Ingo Schwarze <schwarze@openbsd.org>
4 * Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 *
18 * Implementation of the roff(7) parser for mandoc(1).
19 */
20#include <sys/types.h>
21
22#include <assert.h>
23#include <ctype.h>
24#include <limits.h>
25#include <stddef.h>
26#include <stdint.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30
31#include "mandoc_aux.h"
32#include "mandoc_ohash.h"
33#include "mandoc.h"
34#include "roff.h"
35#include "mandoc_parse.h"
36#include "libmandoc.h"
37#include "roff_int.h"
38#include "tbl_parse.h"
39#include "eqn_parse.h"
40
41/*
42 * ASCII_ESC is used to signal from roff_getarg() to roff_expand()
43 * that an escape sequence resulted from copy-in processing and
44 * needs to be checked or interpolated. As it is used nowhere
45 * else, it is defined here rather than in a header file.
46 */
47#define ASCII_ESC27 27
48
49/* Maximum number of string expansions per line, to break infinite loops. */
50#define EXPAND_LIMIT1000 1000
51
52/* Types of definitions of macros and strings. */
53#define ROFFDEF_USER(1 << 1) (1 << 1) /* User-defined. */
54#define ROFFDEF_PRE(1 << 2) (1 << 2) /* Predefined. */
55#define ROFFDEF_REN(1 << 3) (1 << 3) /* Renamed standard macro. */
56#define ROFFDEF_STD(1 << 4) (1 << 4) /* mdoc(7) or man(7) macro. */
57#define ROFFDEF_ANY((1 << 1) | (1 << 2) | (1 << 3) | (1 <<
4))
(ROFFDEF_USER(1 << 1) | ROFFDEF_PRE(1 << 2) | \
58 ROFFDEF_REN(1 << 3) | ROFFDEF_STD(1 << 4))
59#define ROFFDEF_UNDEF(1 << 5) (1 << 5) /* Completely undefined. */
60
61/* --- data types --------------------------------------------------------- */
62
63/*
64 * An incredibly-simple string buffer.
65 */
66struct roffstr {
67 char *p; /* nil-terminated buffer */
68 size_t sz; /* saved strlen(p) */
69};
70
71/*
72 * A key-value roffstr pair as part of a singly-linked list.
73 */
74struct roffkv {
75 struct roffstr key;
76 struct roffstr val;
77 struct roffkv *next; /* next in list */
78};
79
80/*
81 * A single number register as part of a singly-linked list.
82 */
83struct roffreg {
84 struct roffstr key;
85 int val;
86 int step;
87 struct roffreg *next;
88};
89
90/*
91 * Association of request and macro names with token IDs.
92 */
93struct roffreq {
94 enum roff_tok tok;
95 char name[];
96};
97
98/*
99 * A macro processing context.
100 * More than one is needed when macro calls are nested.
101 */
102struct mctx {
103 char **argv;
104 int argc;
105 int argsz;
106};
107
108struct roff {
109 struct roff_man *man; /* mdoc or man parser */
110 struct roffnode *last; /* leaf of stack */
111 struct mctx *mstack; /* stack of macro contexts */
112 int *rstack; /* stack of inverted `ie' values */
113 struct ohash *reqtab; /* request lookup table */
114 struct roffreg *regtab; /* number registers */
115 struct roffkv *strtab; /* user-defined strings & macros */
116 struct roffkv *rentab; /* renamed strings & macros */
117 struct roffkv *xmbtab; /* multi-byte trans table (`tr') */
118 struct roffstr *xtab; /* single-byte trans table (`tr') */
119 const char *current_string; /* value of last called user macro */
120 struct tbl_node *first_tbl; /* first table parsed */
121 struct tbl_node *last_tbl; /* last table parsed */
122 struct tbl_node *tbl; /* current table being parsed */
123 struct eqn_node *last_eqn; /* equation parser */
124 struct eqn_node *eqn; /* active equation parser */
125 int eqn_inline; /* current equation is inline */
126 int options; /* parse options */
127 int mstacksz; /* current size of mstack */
128 int mstackpos; /* position in mstack */
129 int rstacksz; /* current size limit of rstack */
130 int rstackpos; /* position in rstack */
131 int format; /* current file in mdoc or man format */
132 char control; /* control character */
133 char escape; /* escape character */
134};
135
136/*
137 * A macro definition, condition, or ignored block.
138 */
139struct roffnode {
140 enum roff_tok tok; /* type of node */
141 struct roffnode *parent; /* up one in stack */
142 int line; /* parse line */
143 int col; /* parse col */
144 char *name; /* node name, e.g. macro name */
145 char *end; /* custom end macro of the block */
146 int endspan; /* scope to: 1=eol 2=next line -1=\} */
147 int rule; /* content is: 1=evaluated 0=skipped */
148};
149
150#define ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
struct roff *r, /* parse ctx */ \
151 enum roff_tok tok, /* tok of macro */ \
152 struct buf *buf, /* input buffer */ \
153 int ln, /* parse line */ \
154 int ppos, /* original pos in buffer */ \
155 int pos, /* current pos in buffer */ \
156 int *offs /* reset offset of buffer data */
157
158typedef int (*roffproc)(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
);
159
160struct roffmac {
161 roffproc proc; /* process new macro */
162 roffproc text; /* process as child text of macro */
163 roffproc sub; /* process as child of macro */
164 int flags;
165#define ROFFMAC_STRUCT(1 << 0) (1 << 0) /* always interpret */
166};
167
168struct predef {
169 const char *name; /* predefined input name */
170 const char *str; /* replacement symbol */
171};
172
173#define PREDEF(__name, __str){ (__name), (__str) }, \
174 { (__name), (__str) },
175
176/* --- function prototypes ------------------------------------------------ */
177
178static int roffnode_cleanscope(struct roff *);
179static int roffnode_pop(struct roff *);
180static void roffnode_push(struct roff *, enum roff_tok,
181 const char *, int, int);
182static void roff_addtbl(struct roff_man *, int, struct tbl_node *);
183static int roff_als(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
);
184static int roff_block(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
);
185static int roff_block_text(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
);
186static int roff_block_sub(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
);
187static int roff_break(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
);
188static int roff_cblock(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
);
189static int roff_cc(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
);
190static int roff_ccond(struct roff *, int, int);
191static int roff_char(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
);
192static int roff_cond(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
);
193static int roff_cond_checkend(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
);
194static int roff_cond_text(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
);
195static int roff_cond_sub(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
);
196static int roff_ds(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
);
197static int roff_ec(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
);
198static int roff_eo(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
);
199static int roff_eqndelim(struct roff *, struct buf *, int);
200static int roff_evalcond(struct roff *, int, char *, int *);
201static int roff_evalnum(struct roff *, int,
202 const char *, int *, int *, int);
203static int roff_evalpar(struct roff *, int,
204 const char *, int *, int *, int);
205static int roff_evalstrcond(const char *, int *);
206static int roff_expand(struct roff *, struct buf *,
207 int, int, char);
208static void roff_free1(struct roff *);
209static void roff_freereg(struct roffreg *);
210static void roff_freestr(struct roffkv *);
211static size_t roff_getname(struct roff *, char **, int, int);
212static int roff_getnum(const char *, int *, int *, int);
213static int roff_getop(const char *, int *, char *);
214static int roff_getregn(struct roff *,
215 const char *, size_t, char);
216static int roff_getregro(const struct roff *,
217 const char *name);
218static const char *roff_getstrn(struct roff *,
219 const char *, size_t, int *);
220static int roff_hasregn(const struct roff *,
221 const char *, size_t);
222static int roff_insec(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
);
223static int roff_it(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
);
224static int roff_line_ignore(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
);
225static void roff_man_alloc1(struct roff_man *);
226static void roff_man_free1(struct roff_man *);
227static int roff_manyarg(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
);
228static int roff_noarg(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
);
229static int roff_nop(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
);
230static int roff_nr(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
);
231static int roff_onearg(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
);
232static enum roff_tok roff_parse(struct roff *, char *, int *,
233 int, int);
234static int roff_parsetext(struct roff *, struct buf *,
235 int, int *);
236static int roff_renamed(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
);
237static int roff_return(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
);
238static int roff_rm(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
);
239static int roff_rn(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
);
240static int roff_rr(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
);
241static void roff_setregn(struct roff *, const char *,
242 size_t, int, char, int);
243static void roff_setstr(struct roff *,
244 const char *, const char *, int);
245static void roff_setstrn(struct roffkv **, const char *,
246 size_t, const char *, size_t, int);
247static int roff_shift(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
);
248static int roff_so(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
);
249static int roff_tr(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
);
250static int roff_Dd(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
);
251static int roff_TE(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
);
252static int roff_TS(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
);
253static int roff_EQ(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
);
254static int roff_EN(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
);
255static int roff_T_(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
);
256static int roff_unsupp(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
);
257static int roff_userdef(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
);
258
259/* --- constant data ------------------------------------------------------ */
260
261#define ROFFNUM_SCALE(1 << 0) (1 << 0) /* Honour scaling in roff_getnum(). */
262#define ROFFNUM_WHITE(1 << 1) (1 << 1) /* Skip whitespace in roff_evalnum(). */
263
264const char *__roff_name[MAN_MAX + 1] = {
265 "br", "ce", "fi", "ft",
266 "ll", "mc", "nf",
267 "po", "rj", "sp",
268 "ta", "ti", NULL((void*)0),
269 "ab", "ad", "af", "aln",
270 "als", "am", "am1", "ami",
271 "ami1", "as", "as1", "asciify",
272 "backtrace", "bd", "bleedat", "blm",
273 "box", "boxa", "bp", "BP",
274 "break", "breakchar", "brnl", "brp",
275 "brpnl", "c2", "cc",
276 "cf", "cflags", "ch", "char",
277 "chop", "class", "close", "CL",
278 "color", "composite", "continue", "cp",
279 "cropat", "cs", "cu", "da",
280 "dch", "Dd", "de", "de1",
281 "defcolor", "dei", "dei1", "device",
282 "devicem", "di", "do", "ds",
283 "ds1", "dwh", "dt", "ec",
284 "ecr", "ecs", "el", "em",
285 "EN", "eo", "EP", "EQ",
286 "errprint", "ev", "evc", "ex",
287 "fallback", "fam", "fc", "fchar",
288 "fcolor", "fdeferlig", "feature", "fkern",
289 "fl", "flig", "fp", "fps",
290 "fschar", "fspacewidth", "fspecial", "ftr",
291 "fzoom", "gcolor", "hc", "hcode",
292 "hidechar", "hla", "hlm", "hpf",
293 "hpfa", "hpfcode", "hw", "hy",
294 "hylang", "hylen", "hym", "hypp",
295 "hys", "ie", "if", "ig",
296 "index", "it", "itc", "IX",
297 "kern", "kernafter", "kernbefore", "kernpair",
298 "lc", "lc_ctype", "lds", "length",
299 "letadj", "lf", "lg", "lhang",
300 "linetabs", "lnr", "lnrf", "lpfx",
301 "ls", "lsm", "lt",
302 "mediasize", "minss", "mk", "mso",
303 "na", "ne", "nh", "nhychar",
304 "nm", "nn", "nop", "nr",
305 "nrf", "nroff", "ns", "nx",
306 "open", "opena", "os", "output",
307 "padj", "papersize", "pc", "pev",
308 "pi", "PI", "pl", "pm",
309 "pn", "pnr", "ps",
310 "psbb", "pshape", "pso", "ptr",
311 "pvs", "rchar", "rd", "recursionlimit",
312 "return", "rfschar", "rhang",
313 "rm", "rn", "rnn", "rr",
314 "rs", "rt", "schar", "sentchar",
315 "shc", "shift", "sizes", "so",
316 "spacewidth", "special", "spreadwarn", "ss",
317 "sty", "substring", "sv", "sy",
318 "T&", "tc", "TE",
319 "TH", "tkf", "tl",
320 "tm", "tm1", "tmc", "tr",
321 "track", "transchar", "trf", "trimat",
322 "trin", "trnt", "troff", "TS",
323 "uf", "ul", "unformat", "unwatch",
324 "unwatchn", "vpt", "vs", "warn",
325 "warnscale", "watch", "watchlength", "watchn",
326 "wh", "while", "write", "writec",
327 "writem", "xflag", ".", NULL((void*)0),
328 NULL((void*)0), "text",
329 "Dd", "Dt", "Os", "Sh",
330 "Ss", "Pp", "D1", "Dl",
331 "Bd", "Ed", "Bl", "El",
332 "It", "Ad", "An", "Ap",
333 "Ar", "Cd", "Cm", "Dv",
334 "Er", "Ev", "Ex", "Fa",
335 "Fd", "Fl", "Fn", "Ft",
336 "Ic", "In", "Li", "Nd",
337 "Nm", "Op", "Ot", "Pa",
338 "Rv", "St", "Va", "Vt",
339 "Xr", "%A", "%B", "%D",
340 "%I", "%J", "%N", "%O",
341 "%P", "%R", "%T", "%V",
342 "Ac", "Ao", "Aq", "At",
343 "Bc", "Bf", "Bo", "Bq",
344 "Bsx", "Bx", "Db", "Dc",
345 "Do", "Dq", "Ec", "Ef",
346 "Em", "Eo", "Fx", "Ms",
347 "No", "Ns", "Nx", "Ox",
348 "Pc", "Pf", "Po", "Pq",
349 "Qc", "Ql", "Qo", "Qq",
350 "Re", "Rs", "Sc", "So",
351 "Sq", "Sm", "Sx", "Sy",
352 "Tn", "Ux", "Xc", "Xo",
353 "Fo", "Fc", "Oo", "Oc",
354 "Bk", "Ek", "Bt", "Hf",
355 "Fr", "Ud", "Lb", "Lp",
356 "Lk", "Mt", "Brq", "Bro",
357 "Brc", "%C", "Es", "En",
358 "Dx", "%Q", "%U", "Ta",
359 "Tg", NULL((void*)0),
360 "TH", "SH", "SS", "TP",
361 "TQ",
362 "LP", "PP", "P", "IP",
363 "HP", "SM", "SB", "BI",
364 "IB", "BR", "RB", "R",
365 "B", "I", "IR", "RI",
366 "RE", "RS", "DT", "UC",
367 "PD", "AT", "in",
368 "SY", "YS", "OP",
369 "EX", "EE", "UR",
370 "UE", "MT", "ME", NULL((void*)0)
371};
372const char *const *roff_name = __roff_name;
373
374static struct roffmac roffs[TOKEN_NONE] = {
375 { roff_noarg, NULL((void*)0), NULL((void*)0), 0 }, /* br */
376 { roff_onearg, NULL((void*)0), NULL((void*)0), 0 }, /* ce */
377 { roff_noarg, NULL((void*)0), NULL((void*)0), 0 }, /* fi */
378 { roff_onearg, NULL((void*)0), NULL((void*)0), 0 }, /* ft */
379 { roff_onearg, NULL((void*)0), NULL((void*)0), 0 }, /* ll */
380 { roff_onearg, NULL((void*)0), NULL((void*)0), 0 }, /* mc */
381 { roff_noarg, NULL((void*)0), NULL((void*)0), 0 }, /* nf */
382 { roff_onearg, NULL((void*)0), NULL((void*)0), 0 }, /* po */
383 { roff_onearg, NULL((void*)0), NULL((void*)0), 0 }, /* rj */
384 { roff_onearg, NULL((void*)0), NULL((void*)0), 0 }, /* sp */
385 { roff_manyarg, NULL((void*)0), NULL((void*)0), 0 }, /* ta */
386 { roff_onearg, NULL((void*)0), NULL((void*)0), 0 }, /* ti */
387 { NULL((void*)0), NULL((void*)0), NULL((void*)0), 0 }, /* ROFF_MAX */
388 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* ab */
389 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* ad */
390 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* af */
391 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* aln */
392 { roff_als, NULL((void*)0), NULL((void*)0), 0 }, /* als */
393 { roff_block, roff_block_text, roff_block_sub, 0 }, /* am */
394 { roff_block, roff_block_text, roff_block_sub, 0 }, /* am1 */
395 { roff_block, roff_block_text, roff_block_sub, 0 }, /* ami */
396 { roff_block, roff_block_text, roff_block_sub, 0 }, /* ami1 */
397 { roff_ds, NULL((void*)0), NULL((void*)0), 0 }, /* as */
398 { roff_ds, NULL((void*)0), NULL((void*)0), 0 }, /* as1 */
399 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* asciify */
400 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* backtrace */
401 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* bd */
402 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* bleedat */
403 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* blm */
404 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* box */
405 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* boxa */
406 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* bp */
407 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* BP */
408 { roff_break, NULL((void*)0), NULL((void*)0), 0 }, /* break */
409 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* breakchar */
410 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* brnl */
411 { roff_noarg, NULL((void*)0), NULL((void*)0), 0 }, /* brp */
412 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* brpnl */
413 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* c2 */
414 { roff_cc, NULL((void*)0), NULL((void*)0), 0 }, /* cc */
415 { roff_insec, NULL((void*)0), NULL((void*)0), 0 }, /* cf */
416 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* cflags */
417 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* ch */
418 { roff_char, NULL((void*)0), NULL((void*)0), 0 }, /* char */
419 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* chop */
420 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* class */
421 { roff_insec, NULL((void*)0), NULL((void*)0), 0 }, /* close */
422 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* CL */
423 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* color */
424 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* composite */
425 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* continue */
426 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* cp */
427 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* cropat */
428 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* cs */
429 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* cu */
430 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* da */
431 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* dch */
432 { roff_Dd, NULL((void*)0), NULL((void*)0), 0 }, /* Dd */
433 { roff_block, roff_block_text, roff_block_sub, 0 }, /* de */
434 { roff_block, roff_block_text, roff_block_sub, 0 }, /* de1 */
435 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* defcolor */
436 { roff_block, roff_block_text, roff_block_sub, 0 }, /* dei */
437 { roff_block, roff_block_text, roff_block_sub, 0 }, /* dei1 */
438 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* device */
439 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* devicem */
440 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* di */
441 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* do */
442 { roff_ds, NULL((void*)0), NULL((void*)0), 0 }, /* ds */
443 { roff_ds, NULL((void*)0), NULL((void*)0), 0 }, /* ds1 */
444 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* dwh */
445 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* dt */
446 { roff_ec, NULL((void*)0), NULL((void*)0), 0 }, /* ec */
447 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* ecr */
448 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* ecs */
449 { roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT(1 << 0) }, /* el */
450 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* em */
451 { roff_EN, NULL((void*)0), NULL((void*)0), 0 }, /* EN */
452 { roff_eo, NULL((void*)0), NULL((void*)0), 0 }, /* eo */
453 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* EP */
454 { roff_EQ, NULL((void*)0), NULL((void*)0), 0 }, /* EQ */
455 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* errprint */
456 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* ev */
457 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* evc */
458 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* ex */
459 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* fallback */
460 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* fam */
461 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* fc */
462 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* fchar */
463 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* fcolor */
464 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* fdeferlig */
465 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* feature */
466 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* fkern */
467 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* fl */
468 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* flig */
469 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* fp */
470 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* fps */
471 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* fschar */
472 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* fspacewidth */
473 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* fspecial */
474 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* ftr */
475 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* fzoom */
476 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* gcolor */
477 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* hc */
478 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* hcode */
479 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* hidechar */
480 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* hla */
481 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* hlm */
482 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* hpf */
483 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* hpfa */
484 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* hpfcode */
485 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* hw */
486 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* hy */
487 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* hylang */
488 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* hylen */
489 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* hym */
490 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* hypp */
491 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* hys */
492 { roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT(1 << 0) }, /* ie */
493 { roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT(1 << 0) }, /* if */
494 { roff_block, roff_block_text, roff_block_sub, 0 }, /* ig */
495 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* index */
496 { roff_it, NULL((void*)0), NULL((void*)0), 0 }, /* it */
497 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* itc */
498 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* IX */
499 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* kern */
500 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* kernafter */
501 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* kernbefore */
502 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* kernpair */
503 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* lc */
504 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* lc_ctype */
505 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* lds */
506 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* length */
507 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* letadj */
508 { roff_insec, NULL((void*)0), NULL((void*)0), 0 }, /* lf */
509 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* lg */
510 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* lhang */
511 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* linetabs */
512 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* lnr */
513 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* lnrf */
514 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* lpfx */
515 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* ls */
516 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* lsm */
517 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* lt */
518 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* mediasize */
519 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* minss */
520 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* mk */
521 { roff_insec, NULL((void*)0), NULL((void*)0), 0 }, /* mso */
522 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* na */
523 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* ne */
524 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* nh */
525 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* nhychar */
526 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* nm */
527 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* nn */
528 { roff_nop, NULL((void*)0), NULL((void*)0), 0 }, /* nop */
529 { roff_nr, NULL((void*)0), NULL((void*)0), 0 }, /* nr */
530 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* nrf */
531 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* nroff */
532 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* ns */
533 { roff_insec, NULL((void*)0), NULL((void*)0), 0 }, /* nx */
534 { roff_insec, NULL((void*)0), NULL((void*)0), 0 }, /* open */
535 { roff_insec, NULL((void*)0), NULL((void*)0), 0 }, /* opena */
536 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* os */
537 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* output */
538 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* padj */
539 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* papersize */
540 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* pc */
541 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* pev */
542 { roff_insec, NULL((void*)0), NULL((void*)0), 0 }, /* pi */
543 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* PI */
544 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* pl */
545 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* pm */
546 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* pn */
547 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* pnr */
548 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* ps */
549 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* psbb */
550 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* pshape */
551 { roff_insec, NULL((void*)0), NULL((void*)0), 0 }, /* pso */
552 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* ptr */
553 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* pvs */
554 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* rchar */
555 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* rd */
556 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* recursionlimit */
557 { roff_return, NULL((void*)0), NULL((void*)0), 0 }, /* return */
558 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* rfschar */
559 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* rhang */
560 { roff_rm, NULL((void*)0), NULL((void*)0), 0 }, /* rm */
561 { roff_rn, NULL((void*)0), NULL((void*)0), 0 }, /* rn */
562 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* rnn */
563 { roff_rr, NULL((void*)0), NULL((void*)0), 0 }, /* rr */
564 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* rs */
565 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* rt */
566 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* schar */
567 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* sentchar */
568 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* shc */
569 { roff_shift, NULL((void*)0), NULL((void*)0), 0 }, /* shift */
570 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* sizes */
571 { roff_so, NULL((void*)0), NULL((void*)0), 0 }, /* so */
572 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* spacewidth */
573 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* special */
574 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* spreadwarn */
575 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* ss */
576 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* sty */
577 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* substring */
578 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* sv */
579 { roff_insec, NULL((void*)0), NULL((void*)0), 0 }, /* sy */
580 { roff_T_, NULL((void*)0), NULL((void*)0), 0 }, /* T& */
581 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* tc */
582 { roff_TE, NULL((void*)0), NULL((void*)0), 0 }, /* TE */
583 { roff_Dd, NULL((void*)0), NULL((void*)0), 0 }, /* TH */
584 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* tkf */
585 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* tl */
586 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* tm */
587 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* tm1 */
588 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* tmc */
589 { roff_tr, NULL((void*)0), NULL((void*)0), 0 }, /* tr */
590 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* track */
591 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* transchar */
592 { roff_insec, NULL((void*)0), NULL((void*)0), 0 }, /* trf */
593 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* trimat */
594 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* trin */
595 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* trnt */
596 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* troff */
597 { roff_TS, NULL((void*)0), NULL((void*)0), 0 }, /* TS */
598 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* uf */
599 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* ul */
600 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* unformat */
601 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* unwatch */
602 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* unwatchn */
603 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* vpt */
604 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* vs */
605 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* warn */
606 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* warnscale */
607 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* watch */
608 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* watchlength */
609 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* watchn */
610 { roff_unsupp, NULL((void*)0), NULL((void*)0), 0 }, /* wh */
611 { roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT(1 << 0) }, /*while*/
612 { roff_insec, NULL((void*)0), NULL((void*)0), 0 }, /* write */
613 { roff_insec, NULL((void*)0), NULL((void*)0), 0 }, /* writec */
614 { roff_insec, NULL((void*)0), NULL((void*)0), 0 }, /* writem */
615 { roff_line_ignore, NULL((void*)0), NULL((void*)0), 0 }, /* xflag */
616 { roff_cblock, NULL((void*)0), NULL((void*)0), 0 }, /* . */
617 { roff_renamed, NULL((void*)0), NULL((void*)0), 0 },
618 { roff_userdef, NULL((void*)0), NULL((void*)0), 0 }
619};
620
621/* Array of injected predefined strings. */
622#define PREDEFS_MAX38 38
623static const struct predef predefs[PREDEFS_MAX38] = {
624#include "predefs.in"
625};
626
627static int roffce_lines; /* number of input lines to center */
628static struct roff_node *roffce_node; /* active request */
629static int roffit_lines; /* number of lines to delay */
630static char *roffit_macro; /* nil-terminated macro line */
631
632
633/* --- request table ------------------------------------------------------ */
634
635struct ohash *
636roffhash_alloc(enum roff_tok mintok, enum roff_tok maxtok)
637{
638 struct ohash *htab;
639 struct roffreq *req;
640 enum roff_tok tok;
641 size_t sz;
642 unsigned int slot;
643
644 htab = mandoc_malloc(sizeof(*htab));
645 mandoc_ohash_init(htab, 8, offsetof(struct roffreq, name)__builtin_offsetof(struct roffreq, name));
646
647 for (tok = mintok; tok < maxtok; tok++) {
648 if (roff_name[tok] == NULL((void*)0))
649 continue;
650 sz = strlen(roff_name[tok]);
651 req = mandoc_malloc(sizeof(*req) + sz + 1);
652 req->tok = tok;
653 memcpy(req->name, roff_name[tok], sz + 1);
654 slot = ohash_qlookup(htab, req->name);
655 ohash_insert(htab, slot, req);
656 }
657 return htab;
658}
659
660void
661roffhash_free(struct ohash *htab)
662{
663 struct roffreq *req;
664 unsigned int slot;
665
666 if (htab == NULL((void*)0))
667 return;
668 for (req = ohash_first(htab, &slot); req != NULL((void*)0);
669 req = ohash_next(htab, &slot))
670 free(req);
671 ohash_delete(htab);
672 free(htab);
673}
674
675enum roff_tok
676roffhash_find(struct ohash *htab, const char *name, size_t sz)
677{
678 struct roffreq *req;
679 const char *end;
680
681 if (sz) {
682 end = name + sz;
683 req = ohash_find(htab, ohash_qlookupi(htab, name, &end));
684 } else
685 req = ohash_find(htab, ohash_qlookup(htab, name));
686 return req == NULL((void*)0) ? TOKEN_NONE : req->tok;
687}
688
689/* --- stack of request blocks -------------------------------------------- */
690
691/*
692 * Pop the current node off of the stack of roff instructions currently
693 * pending. Return 1 if it is a loop or 0 otherwise.
694 */
695static int
696roffnode_pop(struct roff *r)
697{
698 struct roffnode *p;
699 int inloop;
700
701 p = r->last;
702 inloop = p->tok == ROFF_while;
703 r->last = p->parent;
704 free(p->name);
705 free(p->end);
706 free(p);
707 return inloop;
708}
709
710/*
711 * Push a roff node onto the instruction stack. This must later be
712 * removed with roffnode_pop().
713 */
714static void
715roffnode_push(struct roff *r, enum roff_tok tok, const char *name,
716 int line, int col)
717{
718 struct roffnode *p;
719
720 p = mandoc_calloc(1, sizeof(struct roffnode));
721 p->tok = tok;
722 if (name)
723 p->name = mandoc_strdup(name);
724 p->parent = r->last;
725 p->line = line;
726 p->col = col;
727 p->rule = p->parent ? p->parent->rule : 0;
728
729 r->last = p;
730}
731
732/* --- roff parser state data management ---------------------------------- */
733
734static void
735roff_free1(struct roff *r)
736{
737 int i;
738
739 tbl_free(r->first_tbl);
740 r->first_tbl = r->last_tbl = r->tbl = NULL((void*)0);
741
742 eqn_free(r->last_eqn);
743 r->last_eqn = r->eqn = NULL((void*)0);
744
745 while (r->mstackpos >= 0)
746 roff_userret(r);
747
748 while (r->last)
749 roffnode_pop(r);
750
751 free (r->rstack);
752 r->rstack = NULL((void*)0);
753 r->rstacksz = 0;
754 r->rstackpos = -1;
755
756 roff_freereg(r->regtab);
757 r->regtab = NULL((void*)0);
758
759 roff_freestr(r->strtab);
760 roff_freestr(r->rentab);
761 roff_freestr(r->xmbtab);
762 r->strtab = r->rentab = r->xmbtab = NULL((void*)0);
763
764 if (r->xtab)
765 for (i = 0; i < 128; i++)
766 free(r->xtab[i].p);
767 free(r->xtab);
768 r->xtab = NULL((void*)0);
769}
770
771void
772roff_reset(struct roff *r)
773{
774 roff_free1(r);
775 r->options |= MPARSE_COMMENT(1 << 7);
776 r->format = r->options & (MPARSE_MDOC(1 << 0) | MPARSE_MAN(1 << 1));
777 r->control = '\0';
778 r->escape = '\\';
779 roffce_lines = 0;
780 roffce_node = NULL((void*)0);
781 roffit_lines = 0;
782 roffit_macro = NULL((void*)0);
783}
784
785void
786roff_free(struct roff *r)
787{
788 int i;
789
790 roff_free1(r);
791 for (i = 0; i < r->mstacksz; i++)
792 free(r->mstack[i].argv);
793 free(r->mstack);
794 roffhash_free(r->reqtab);
795 free(r);
796}
797
798struct roff *
799roff_alloc(int options)
800{
801 struct roff *r;
802
803 r = mandoc_calloc(1, sizeof(struct roff));
804 r->reqtab = roffhash_alloc(0, ROFF_RENAMED);
805 r->options = options | MPARSE_COMMENT(1 << 7);
806 r->format = options & (MPARSE_MDOC(1 << 0) | MPARSE_MAN(1 << 1));
807 r->mstackpos = -1;
808 r->rstackpos = -1;
809 r->escape = '\\';
810 return r;
811}
812
813/* --- syntax tree state data management ---------------------------------- */
814
815static void
816roff_man_free1(struct roff_man *man)
817{
818 if (man->meta.first != NULL((void*)0))
819 roff_node_delete(man, man->meta.first);
820 free(man->meta.msec);
821 free(man->meta.vol);
822 free(man->meta.os);
823 free(man->meta.arch);
824 free(man->meta.title);
825 free(man->meta.name);
826 free(man->meta.date);
827 free(man->meta.sodest);
828}
829
830void
831roff_state_reset(struct roff_man *man)
832{
833 man->last = man->meta.first;
834 man->last_es = NULL((void*)0);
835 man->flags = 0;
836 man->lastsec = man->lastnamed = SEC_NONE;
837 man->next = ROFF_NEXT_CHILD;
838 roff_setreg(man->roff, "nS", 0, '=');
839}
840
841static void
842roff_man_alloc1(struct roff_man *man)
843{
844 memset(&man->meta, 0, sizeof(man->meta));
845 man->meta.first = mandoc_calloc(1, sizeof(*man->meta.first));
846 man->meta.first->type = ROFFT_ROOT;
847 man->meta.macroset = MACROSET_NONE;
848 roff_state_reset(man);
849}
850
851void
852roff_man_reset(struct roff_man *man)
853{
854 roff_man_free1(man);
855 roff_man_alloc1(man);
856}
857
858void
859roff_man_free(struct roff_man *man)
860{
861 roff_man_free1(man);
862 free(man->os_r);
863 free(man);
864}
865
866struct roff_man *
867roff_man_alloc(struct roff *roff, const char *os_s, int quick)
868{
869 struct roff_man *man;
870
871 man = mandoc_calloc(1, sizeof(*man));
872 man->roff = roff;
873 man->os_s = os_s;
874 man->quick = quick;
875 roff_man_alloc1(man);
876 roff->man = man;
877 return man;
878}
879
880/* --- syntax tree handling ----------------------------------------------- */
881
882struct roff_node *
883roff_node_alloc(struct roff_man *man, int line, int pos,
884 enum roff_type type, int tok)
885{
886 struct roff_node *n;
887
888 n = mandoc_calloc(1, sizeof(*n));
889 n->line = line;
890 n->pos = pos;
891 n->tok = tok;
892 n->type = type;
893 n->sec = man->lastsec;
894
895 if (man->flags & MDOC_SYNOPSIS(1 << 7))
896 n->flags |= NODE_SYNPRETTY(1 << 7);
897 else
898 n->flags &= ~NODE_SYNPRETTY(1 << 7);
899 if ((man->flags & (ROFF_NOFILL(1 << 1) | ROFF_NONOFILL(1 << 16))) == ROFF_NOFILL(1 << 1))
900 n->flags |= NODE_NOFILL(1 << 8);
901 else
902 n->flags &= ~NODE_NOFILL(1 << 8);
903 if (man->flags & MDOC_NEWLINE(1 << 3))
904 n->flags |= NODE_LINE(1 << 3);
905 man->flags &= ~MDOC_NEWLINE(1 << 3);
906
907 return n;
908}
909
910void
911roff_node_append(struct roff_man *man, struct roff_node *n)
912{
913
914 switch (man->next) {
915 case ROFF_NEXT_SIBLING:
916 if (man->last->next != NULL((void*)0)) {
917 n->next = man->last->next;
918 man->last->next->prev = n;
919 } else
920 man->last->parent->last = n;
921 man->last->next = n;
922 n->prev = man->last;
923 n->parent = man->last->parent;
924 break;
925 case ROFF_NEXT_CHILD:
926 if (man->last->child != NULL((void*)0)) {
927 n->next = man->last->child;
928 man->last->child->prev = n;
929 } else
930 man->last->last = n;
931 man->last->child = n;
932 n->parent = man->last;
933 break;
934 default:
935 abort();
936 }
937 man->last = n;
938
939 switch (n->type) {
940 case ROFFT_HEAD:
941 n->parent->head = n;
942 break;
943 case ROFFT_BODY:
944 if (n->end != ENDBODY_NOT)
945 return;
946 n->parent->body = n;
947 break;
948 case ROFFT_TAIL:
949 n->parent->tail = n;
950 break;
951 default:
952 return;
953 }
954
955 /*
956 * Copy over the normalised-data pointer of our parent. Not
957 * everybody has one, but copying a null pointer is fine.
958 */
959
960 n->norm = n->parent->norm;
961 assert(n->parent->type == ROFFT_BLOCK)((n->parent->type == ROFFT_BLOCK) ? (void)0 : __assert2
("/usr/src/usr.bin/mandoc/roff.c", 961, __func__, "n->parent->type == ROFFT_BLOCK"
))
;
962}
963
964void
965roff_word_alloc(struct roff_man *man, int line, int pos, const char *word)
966{
967 struct roff_node *n;
968
969 n = roff_node_alloc(man, line, pos, ROFFT_TEXT, TOKEN_NONE);
970 n->string = roff_strdup(man->roff, word);
971 roff_node_append(man, n);
972 n->flags |= NODE_VALID(1 << 0) | NODE_ENDED(1 << 1);
973 man->next = ROFF_NEXT_SIBLING;
974}
975
976void
977roff_word_append(struct roff_man *man, const char *word)
978{
979 struct roff_node *n;
980 char *addstr, *newstr;
981
982 n = man->last;
983 addstr = roff_strdup(man->roff, word);
984 mandoc_asprintf(&newstr, "%s %s", n->string, addstr);
985 free(addstr);
986 free(n->string);
987 n->string = newstr;
988 man->next = ROFF_NEXT_SIBLING;
989}
990
991void
992roff_elem_alloc(struct roff_man *man, int line, int pos, int tok)
993{
994 struct roff_node *n;
995
996 n = roff_node_alloc(man, line, pos, ROFFT_ELEM, tok);
997 roff_node_append(man, n);
998 man->next = ROFF_NEXT_CHILD;
999}
1000
1001struct roff_node *
1002roff_block_alloc(struct roff_man *man, int line, int pos, int tok)
1003{
1004 struct roff_node *n;
1005
1006 n = roff_node_alloc(man, line, pos, ROFFT_BLOCK, tok);
1007 roff_node_append(man, n);
1008 man->next = ROFF_NEXT_CHILD;
1009 return n;
1010}
1011
1012struct roff_node *
1013roff_head_alloc(struct roff_man *man, int line, int pos, int tok)
1014{
1015 struct roff_node *n;
1016
1017 n = roff_node_alloc(man, line, pos, ROFFT_HEAD, tok);
1018 roff_node_append(man, n);
1019 man->next = ROFF_NEXT_CHILD;
1020 return n;
1021}
1022
1023struct roff_node *
1024roff_body_alloc(struct roff_man *man, int line, int pos, int tok)
1025{
1026 struct roff_node *n;
1027
1028 n = roff_node_alloc(man, line, pos, ROFFT_BODY, tok);
1029 roff_node_append(man, n);
1030 man->next = ROFF_NEXT_CHILD;
1031 return n;
1032}
1033
1034static void
1035roff_addtbl(struct roff_man *man, int line, struct tbl_node *tbl)
1036{
1037 struct roff_node *n;
1038 struct tbl_span *span;
1039
1040 if (man->meta.macroset == MACROSET_MAN)
1041 man_breakscope(man, ROFF_TS);
1042 while ((span = tbl_span(tbl)) != NULL((void*)0)) {
1043 n = roff_node_alloc(man, line, 0, ROFFT_TBL, TOKEN_NONE);
1044 n->span = span;
1045 roff_node_append(man, n);
1046 n->flags |= NODE_VALID(1 << 0) | NODE_ENDED(1 << 1);
1047 man->next = ROFF_NEXT_SIBLING;
1048 }
1049}
1050
1051void
1052roff_node_unlink(struct roff_man *man, struct roff_node *n)
1053{
1054
1055 /* Adjust siblings. */
1056
1057 if (n->prev)
1058 n->prev->next = n->next;
1059 if (n->next)
1060 n->next->prev = n->prev;
1061
1062 /* Adjust parent. */
1063
1064 if (n->parent != NULL((void*)0)) {
1065 if (n->parent->child == n)
1066 n->parent->child = n->next;
1067 if (n->parent->last == n)
1068 n->parent->last = n->prev;
1069 }
1070
1071 /* Adjust parse point. */
1072
1073 if (man == NULL((void*)0))
1074 return;
1075 if (man->last == n) {
1076 if (n->prev == NULL((void*)0)) {
1077 man->last = n->parent;
1078 man->next = ROFF_NEXT_CHILD;
1079 } else {
1080 man->last = n->prev;
1081 man->next = ROFF_NEXT_SIBLING;
1082 }
1083 }
1084 if (man->meta.first == n)
1085 man->meta.first = NULL((void*)0);
1086}
1087
1088void
1089roff_node_relink(struct roff_man *man, struct roff_node *n)
1090{
1091 roff_node_unlink(man, n);
1092 n->prev = n->next = NULL((void*)0);
1093 roff_node_append(man, n);
1094}
1095
1096void
1097roff_node_free(struct roff_node *n)
1098{
1099
1100 if (n->args != NULL((void*)0))
1101 mdoc_argv_free(n->args);
1102 if (n->type == ROFFT_BLOCK || n->type == ROFFT_ELEM)
1103 free(n->norm);
1104 eqn_box_free(n->eqn);
1105 free(n->string);
1106 free(n->tag);
1107 free(n);
1108}
1109
1110void
1111roff_node_delete(struct roff_man *man, struct roff_node *n)
1112{
1113
1114 while (n->child != NULL((void*)0))
1115 roff_node_delete(man, n->child);
1116 roff_node_unlink(man, n);
1117 roff_node_free(n);
1118}
1119
1120int
1121roff_node_transparent(struct roff_node *n)
1122{
1123 if (n == NULL((void*)0))
1124 return 0;
1125 if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT(1 << 10))
1126 return 1;
1127 return roff_tok_transparent(n->tok);
1128}
1129
1130int
1131roff_tok_transparent(enum roff_tok tok)
1132{
1133 switch (tok) {
1134 case ROFF_ft:
1135 case ROFF_ll:
1136 case ROFF_mc:
1137 case ROFF_po:
1138 case ROFF_ta:
1139 case MDOC_Db:
1140 case MDOC_Es:
1141 case MDOC_Sm:
1142 case MDOC_Tg:
1143 case MAN_DT:
1144 case MAN_UC:
1145 case MAN_PD:
1146 case MAN_AT:
1147 return 1;
1148 default:
1149 return 0;
1150 }
1151}
1152
1153struct roff_node *
1154roff_node_child(struct roff_node *n)
1155{
1156 for (n = n->child; roff_node_transparent(n); n = n->next)
1157 continue;
1158 return n;
1159}
1160
1161struct roff_node *
1162roff_node_prev(struct roff_node *n)
1163{
1164 do {
1165 n = n->prev;
1166 } while (roff_node_transparent(n));
1167 return n;
1168}
1169
1170struct roff_node *
1171roff_node_next(struct roff_node *n)
1172{
1173 do {
1174 n = n->next;
1175 } while (roff_node_transparent(n));
1176 return n;
1177}
1178
1179void
1180deroff(char **dest, const struct roff_node *n)
1181{
1182 char *cp;
1183 size_t sz;
1184
1185 if (n->string == NULL((void*)0)) {
1186 for (n = n->child; n != NULL((void*)0); n = n->next)
1187 deroff(dest, n);
1188 return;
1189 }
1190
1191 /* Skip leading whitespace. */
1192
1193 for (cp = n->string; *cp != '\0'; cp++) {
1194 if (cp[0] == '\\' && cp[1] != '\0' &&
1195 strchr(" %&0^|~", cp[1]) != NULL((void*)0))
1196 cp++;
1197 else if ( ! isspace((unsigned char)*cp))
1198 break;
1199 }
1200
1201 /* Skip trailing backslash. */
1202
1203 sz = strlen(cp);
1204 if (sz > 0 && cp[sz - 1] == '\\')
1205 sz--;
1206
1207 /* Skip trailing whitespace. */
1208
1209 for (; sz; sz--)
1210 if ( ! isspace((unsigned char)cp[sz-1]))
1211 break;
1212
1213 /* Skip empty strings. */
1214
1215 if (sz == 0)
1216 return;
1217
1218 if (*dest == NULL((void*)0)) {
1219 *dest = mandoc_strndup(cp, sz);
1220 return;
1221 }
1222
1223 mandoc_asprintf(&cp, "%s %*s", *dest, (int)sz, cp);
1224 free(*dest);
1225 *dest = cp;
1226}
1227
1228/* --- main functions of the roff parser ---------------------------------- */
1229
1230/*
1231 * In the current line, expand escape sequences that produce parsable
1232 * input text. Also check the syntax of the remaining escape sequences,
1233 * which typically produce output glyphs or change formatter state.
1234 */
1235static int
1236roff_expand(struct roff *r, struct buf *buf, int ln, int pos, char newesc)
1237{
1238 struct mctx *ctx; /* current macro call context */
1239 char ubuf[24]; /* buffer to print the number */
1240 struct roff_node *n; /* used for header comments */
1241 const char *start; /* start of the string to process */
1242 char *stesc; /* start of an escape sequence ('\\') */
1243 const char *esct; /* type of esccape sequence */
1244 char *ep; /* end of comment string */
1245 const char *stnam; /* start of the name, after "[(*" */
1246 const char *cp; /* end of the name, e.g. before ']' */
1247 const char *res; /* the string to be substituted */
1248 char *nbuf; /* new buffer to copy buf->buf to */
1249 size_t maxl; /* expected length of the escape name */
1250 size_t naml; /* actual length of the escape name */
1251 size_t asz; /* length of the replacement */
1252 size_t rsz; /* length of the rest of the string */
1253 int inaml; /* length returned from mandoc_escape() */
1254 int expand_count; /* to avoid infinite loops */
1255 int npos; /* position in numeric expression */
1256 int arg_complete; /* argument not interrupted by eol */
1257 int quote_args; /* true for \\$@, false for \\$* */
1258 int done; /* no more input available */
1259 int deftype; /* type of definition to paste */
1260 int rcsid; /* kind of RCS id seen */
1261 enum mandocerr err; /* for escape sequence problems */
1262 char sign; /* increment number register */
1263 char term; /* character terminating the escape */
1264
1265 /* Search forward for comments. */
1266
1267 done = 0;
1268 start = buf->buf + pos;
1269 for (stesc = buf->buf + pos; *stesc != '\0'; stesc++) {
1270 if (stesc[0] != newesc || stesc[1] == '\0')
1271 continue;
1272 stesc++;
1273 if (*stesc != '"' && *stesc != '#')
1274 continue;
1275
1276 /* Comment found, look for RCS id. */
1277
1278 rcsid = 0;
1279 if ((cp = strstr(stesc, "$" "OpenBSD")) != NULL((void*)0)) {
1280 rcsid = 1 << MANDOC_OS_OPENBSD;
1281 cp += 8;
1282 } else if ((cp = strstr(stesc, "$" "NetBSD")) != NULL((void*)0)) {
1283 rcsid = 1 << MANDOC_OS_NETBSD;
1284 cp += 7;
1285 }
1286 if (cp != NULL((void*)0) &&
1287 isalnum((unsigned char)*cp) == 0 &&
1288 strchr(cp, '$') != NULL((void*)0)) {
1289 if (r->man->meta.rcsids & rcsid)
1290 mandoc_msg(MANDOCERR_RCS_REP, ln,
1291 (int)(stesc - buf->buf) + 1,
1292 "%s", stesc + 1);
1293 r->man->meta.rcsids |= rcsid;
1294 }
1295
1296 /* Handle trailing whitespace. */
1297
1298 ep = strchr(stesc--, '\0') - 1;
1299 if (*ep == '\n') {
1300 done = 1;
1301 ep--;
1302 }
1303 if (*ep == ' ' || *ep == '\t')
1304 mandoc_msg(MANDOCERR_SPACE_EOL,
1305 ln, (int)(ep - buf->buf), NULL((void*)0));
1306
1307 /*
1308 * Save comments preceding the title macro
1309 * in the syntax tree.
1310 */
1311
1312 if (newesc != ASCII_ESC27 && r->options & MPARSE_COMMENT(1 << 7)) {
1313 while (*ep == ' ' || *ep == '\t')
1314 ep--;
1315 ep[1] = '\0';
1316 n = roff_node_alloc(r->man,
1317 ln, stesc + 1 - buf->buf,
1318 ROFFT_COMMENT, TOKEN_NONE);
1319 n->string = mandoc_strdup(stesc + 2);
1320 roff_node_append(r->man, n);
1321 n->flags |= NODE_VALID(1 << 0) | NODE_ENDED(1 << 1);
1322 r->man->next = ROFF_NEXT_SIBLING;
1323 }
1324
1325 /* Line continuation with comment. */
1326
1327 if (stesc[1] == '#') {
1328 *stesc = '\0';
1329 return ROFF_IGN0x000 | ROFF_APPEND0x010;
1330 }
1331
1332 /* Discard normal comments. */
1333
1334 while (stesc > start && stesc[-1] == ' ' &&
1335 (stesc == start + 1 || stesc[-2] != '\\'))
1336 stesc--;
1337 *stesc = '\0';
1338 break;
1339 }
1340 if (stesc == start)
1341 return ROFF_CONT0x001;
1342 stesc--;
1343
1344 /* Notice the end of the input. */
1345
1346 if (*stesc == '\n') {
1347 *stesc-- = '\0';
1348 done = 1;
1349 }
1350
1351 expand_count = 0;
1352 while (stesc >= start) {
1353 if (*stesc != newesc) {
1354
1355 /*
1356 * If we have a non-standard escape character,
1357 * escape literal backslashes because all
1358 * processing in subsequent functions uses
1359 * the standard escaping rules.
1360 */
1361
1362 if (newesc != ASCII_ESC27 && *stesc == '\\') {
1363 *stesc = '\0';
1364 buf->sz = mandoc_asprintf(&nbuf, "%s\\e%s",
1365 buf->buf, stesc + 1) + 1;
1366 start = nbuf + pos;
1367 stesc = nbuf + (stesc - buf->buf);
1368 free(buf->buf);
1369 buf->buf = nbuf;
1370 }
1371
1372 /* Search backwards for the next escape. */
1373
1374 stesc--;
1375 continue;
1376 }
1377
1378 /* If it is escaped, skip it. */
1379
1380 for (cp = stesc - 1; cp >= start; cp--)
1381 if (*cp != r->escape)
1382 break;
1383
1384 if ((stesc - cp) % 2 == 0) {
1385 while (stesc > cp)
1386 *stesc-- = '\\';
1387 continue;
1388 } else if (stesc[1] != '\0') {
1389 *stesc = '\\';
1390 } else {
1391 *stesc-- = '\0';
1392 if (done)
1393 continue;
1394 else
1395 return ROFF_IGN0x000 | ROFF_APPEND0x010;
1396 }
1397
1398 /* Decide whether to expand or to check only. */
1399
1400 term = '\0';
1401 cp = stesc + 1;
1402 if (*cp == 'E')
1403 cp++;
1404 esct = cp;
1405 switch (*esct) {
1406 case '*':
1407 case '$':
1408 res = NULL((void*)0);
1409 break;
1410 case 'B':
1411 case 'w':
1412 term = cp[1];
1413 /* FALLTHROUGH */
1414 case 'n':
1415 sign = cp[1];
1416 if (sign == '+' || sign == '-')
1417 cp++;
1418 res = ubuf;
1419 break;
1420 default:
1421 err = MANDOCERR_OK;
1422 switch(mandoc_escape(&cp, &stnam, &inaml)) {
1423 case ESCAPE_SPECIAL:
1424 if (mchars_spec2cp(stnam, inaml) >= 0)
1425 break;
1426 /* FALLTHROUGH */
1427 case ESCAPE_ERROR:
1428 err = MANDOCERR_ESC_BAD;
1429 break;
1430 case ESCAPE_UNDEF:
1431 err = MANDOCERR_ESC_UNDEF;
1432 break;
1433 case ESCAPE_UNSUPP:
1434 err = MANDOCERR_ESC_UNSUPP;
1435 break;
1436 default:
1437 break;
1438 }
1439 if (err != MANDOCERR_OK)
1440 mandoc_msg(err, ln, (int)(stesc - buf->buf),
1441 "%.*s", (int)(cp - stesc), stesc);
1442 stesc--;
1443 continue;
1444 }
1445
1446 if (EXPAND_LIMIT1000 < ++expand_count) {
1447 mandoc_msg(MANDOCERR_ROFFLOOP,
1448 ln, (int)(stesc - buf->buf), NULL((void*)0));
1449 return ROFF_IGN0x000;
1450 }
1451
1452 /*
1453 * The third character decides the length
1454 * of the name of the string or register.
1455 * Save a pointer to the name.
1456 */
1457
1458 if (term == '\0') {
1459 switch (*++cp) {
1460 case '\0':
1461 maxl = 0;
1462 break;
1463 case '(':
1464 cp++;
1465 maxl = 2;
1466 break;
1467 case '[':
1468 cp++;
1469 term = ']';
1470 maxl = 0;
1471 break;
1472 default:
1473 maxl = 1;
1474 break;
1475 }
1476 } else {
1477 cp += 2;
1478 maxl = 0;
1479 }
1480 stnam = cp;
1481
1482 /* Advance to the end of the name. */
1483
1484 naml = 0;
1485 arg_complete = 1;
1486 while (maxl == 0 || naml < maxl) {
1487 if (*cp == '\0') {
1488 mandoc_msg(MANDOCERR_ESC_BAD, ln,
1489 (int)(stesc - buf->buf), "%s", stesc);
1490 arg_complete = 0;
1491 break;
1492 }
1493 if (maxl == 0 && *cp == term) {
1494 cp++;
1495 break;
1496 }
1497 if (*cp++ != '\\' || *esct != 'w') {
1498 naml++;
1499 continue;
1500 }
1501 switch (mandoc_escape(&cp, NULL((void*)0), NULL((void*)0))) {
1502 case ESCAPE_SPECIAL:
1503 case ESCAPE_UNICODE:
1504 case ESCAPE_NUMBERED:
1505 case ESCAPE_UNDEF:
1506 case ESCAPE_OVERSTRIKE:
1507 naml++;
1508 break;
1509 default:
1510 break;
1511 }
1512 }
1513
1514 /*
1515 * Retrieve the replacement string; if it is
1516 * undefined, resume searching for escapes.
1517 */
1518
1519 switch (*esct) {
1520 case '*':
1521 if (arg_complete) {
1522 deftype = ROFFDEF_USER(1 << 1) | ROFFDEF_PRE(1 << 2);
1523 res = roff_getstrn(r, stnam, naml, &deftype);
1524
1525 /*
1526 * If not overriden, let \*(.T
1527 * through to the formatters.
1528 */
1529
1530 if (res == NULL((void*)0) && naml == 2 &&
1531 stnam[0] == '.' && stnam[1] == 'T') {
1532 roff_setstrn(&r->strtab,
1533 ".T", 2, NULL((void*)0), 0, 0);
1534 stesc--;
1535 continue;
1536 }
1537 }
1538 break;
1539 case '$':
1540 if (r->mstackpos < 0) {
1541 mandoc_msg(MANDOCERR_ARG_UNDEF, ln,
1542 (int)(stesc - buf->buf), "%.3s", stesc);
1543 break;
1544 }
1545 ctx = r->mstack + r->mstackpos;
1546 npos = esct[1] - '1';
1547 if (npos >= 0 && npos <= 8) {
1548 res = npos < ctx->argc ?
1549 ctx->argv[npos] : "";
1550 break;
1551 }
1552 if (esct[1] == '*')
1553 quote_args = 0;
1554 else if (esct[1] == '@')
1555 quote_args = 1;
1556 else {
1557 mandoc_msg(MANDOCERR_ARG_NONUM, ln,
1558 (int)(stesc - buf->buf), "%.3s", stesc);
1559 break;
1560 }
1561 asz = 0;
1562 for (npos = 0; npos < ctx->argc; npos++) {
1563 if (npos)
1564 asz++; /* blank */
1565 if (quote_args)
1566 asz += 2; /* quotes */
1567 asz += strlen(ctx->argv[npos]);
1568 }
1569 if (asz != 3) {
1570 rsz = buf->sz - (stesc - buf->buf) - 3;
1571 if (asz < 3)
1572 memmove(stesc + asz, stesc + 3, rsz);
1573 buf->sz += asz - 3;
1574 nbuf = mandoc_realloc(buf->buf, buf->sz);
1575 start = nbuf + pos;
1576 stesc = nbuf + (stesc - buf->buf);
1577 buf->buf = nbuf;
1578 if (asz > 3)
1579 memmove(stesc + asz, stesc + 3, rsz);
1580 }
1581 for (npos = 0; npos < ctx->argc; npos++) {
1582 if (npos)
1583 *stesc++ = ' ';
1584 if (quote_args)
1585 *stesc++ = '"';
1586 cp = ctx->argv[npos];
1587 while (*cp != '\0')
1588 *stesc++ = *cp++;
1589 if (quote_args)
1590 *stesc++ = '"';
1591 }
1592 continue;
1593 case 'B':
1594 npos = 0;
1595 ubuf[0] = arg_complete &&
1596 roff_evalnum(r, ln, stnam, &npos,
1597 NULL((void*)0), ROFFNUM_SCALE(1 << 0)) &&
1598 stnam + npos + 1 == cp ? '1' : '0';
1599 ubuf[1] = '\0';
1600 break;
1601 case 'n':
1602 if (arg_complete)
1603 (void)snprintf(ubuf, sizeof(ubuf), "%d",
1604 roff_getregn(r, stnam, naml, sign));
1605 else
1606 ubuf[0] = '\0';
1607 break;
1608 case 'w':
1609 /* use even incomplete args */
1610 (void)snprintf(ubuf, sizeof(ubuf), "%d",
1611 24 * (int)naml);
1612 break;
1613 }
1614
1615 if (res == NULL((void*)0)) {
1616 if (*esct == '*')
1617 mandoc_msg(MANDOCERR_STR_UNDEF,
1618 ln, (int)(stesc - buf->buf),
1619 "%.*s", (int)naml, stnam);
1620 res = "";
1621 } else if (buf->sz + strlen(res) > SHRT_MAX32767) {
1622 mandoc_msg(MANDOCERR_ROFFLOOP,
1623 ln, (int)(stesc - buf->buf), NULL((void*)0));
1624 return ROFF_IGN0x000;
1625 }
1626
1627 /* Replace the escape sequence by the string. */
1628
1629 *stesc = '\0';
1630 buf->sz = mandoc_asprintf(&nbuf, "%s%s%s",
1631 buf->buf, res, cp) + 1;
1632
1633 /* Prepare for the next replacement. */
1634
1635 start = nbuf + pos;
1636 stesc = nbuf + (stesc - buf->buf) + strlen(res);
1637 free(buf->buf);
1638 buf->buf = nbuf;
1639 }
1640 return ROFF_CONT0x001;
1641}
1642
1643/*
1644 * Parse a quoted or unquoted roff-style request or macro argument.
1645 * Return a pointer to the parsed argument, which is either the original
1646 * pointer or advanced by one byte in case the argument is quoted.
1647 * NUL-terminate the argument in place.
1648 * Collapse pairs of quotes inside quoted arguments.
1649 * Advance the argument pointer to the next argument,
1650 * or to the NUL byte terminating the argument line.
1651 */
1652char *
1653roff_getarg(struct roff *r, char **cpp, int ln, int *pos)
1654{
1655 struct buf buf;
1656 char *cp, *start;
1657 int newesc, pairs, quoted, white;
1658
1659 /* Quoting can only start with a new word. */
1660 start = *cpp;
1661 quoted = 0;
1662 if ('"' == *start) {
1663 quoted = 1;
1664 start++;
1665 }
1666
1667 newesc = pairs = white = 0;
1668 for (cp = start; '\0' != *cp; cp++) {
1669
1670 /*
1671 * Move the following text left
1672 * after quoted quotes and after "\\" and "\t".
1673 */
1674 if (pairs)
1675 cp[-pairs] = cp[0];
1676
1677 if ('\\' == cp[0]) {
1678 /*
1679 * In copy mode, translate double to single
1680 * backslashes and backslash-t to literal tabs.
1681 */
1682 switch (cp[1]) {
1683 case 'a':
1684 case 't':
1685 cp[-pairs] = '\t';
1686 pairs++;
1687 cp++;
1688 break;
1689 case '\\':
1690 newesc = 1;
1691 cp[-pairs] = ASCII_ESC27;
1692 pairs++;
1693 cp++;
1694 break;
1695 case ' ':
1696 /* Skip escaped blanks. */
1697 if (0 == quoted)
1698 cp++;
1699 break;
1700 default:
1701 break;
1702 }
1703 } else if (0 == quoted) {
1704 if (' ' == cp[0]) {
1705 /* Unescaped blanks end unquoted args. */
1706 white = 1;
1707 break;
1708 }
1709 } else if ('"' == cp[0]) {
1710 if ('"' == cp[1]) {
1711 /* Quoted quotes collapse. */
1712 pairs++;
1713 cp++;
1714 } else {
1715 /* Unquoted quotes end quoted args. */
1716 quoted = 2;
1717 break;
1718 }
1719 }
1720 }
1721
1722 /* Quoted argument without a closing quote. */
1723 if (1 == quoted)
1724 mandoc_msg(MANDOCERR_ARG_QUOTE, ln, *pos, NULL((void*)0));
1725
1726 /* NUL-terminate this argument and move to the next one. */
1727 if (pairs)
1728 cp[-pairs] = '\0';
1729 if ('\0' != *cp) {
1730 *cp++ = '\0';
1731 while (' ' == *cp)
1732 cp++;
1733 }
1734 *pos += (int)(cp - start) + (quoted ? 1 : 0);
1735 *cpp = cp;
1736
1737 if ('\0' == *cp && (white || ' ' == cp[-1]))
1738 mandoc_msg(MANDOCERR_SPACE_EOL, ln, *pos, NULL((void*)0));
1739
1740 start = mandoc_strdup(start);
1741 if (newesc == 0)
1742 return start;
1743
1744 buf.buf = start;
1745 buf.sz = strlen(start) + 1;
1746 buf.next = NULL((void*)0);
1747 if (roff_expand(r, &buf, ln, 0, ASCII_ESC27) & ROFF_IGN0x000) {
1748 free(buf.buf);
1749 buf.buf = mandoc_strdup("");
1750 }
1751 return buf.buf;
1752}
1753
1754
1755/*
1756 * Process text streams.
1757 */
1758static int
1759roff_parsetext(struct roff *r, struct buf *buf, int pos, int *offs)
1760{
1761 size_t sz;
1762 const char *start;
1763 char *p;
1764 int isz;
1765 enum mandoc_esc esc;
1766
1767 /* Spring the input line trap. */
1768
1769 if (roffit_lines == 1) {
1770 isz = mandoc_asprintf(&p, "%s\n.%s", buf->buf, roffit_macro);
1771 free(buf->buf);
1772 buf->buf = p;
1773 buf->sz = isz + 1;
1774 *offs = 0;
1775 free(roffit_macro);
1776 roffit_lines = 0;
1777 return ROFF_REPARSE0x004;
1778 } else if (roffit_lines > 1)
1779 --roffit_lines;
1780
1781 if (roffce_node != NULL((void*)0) && buf->buf[pos] != '\0') {
1782 if (roffce_lines < 1) {
1783 r->man->last = roffce_node;
1784 r->man->next = ROFF_NEXT_SIBLING;
1785 roffce_lines = 0;
1786 roffce_node = NULL((void*)0);
1787 } else
1788 roffce_lines--;
1789 }
1790
1791 /* Convert all breakable hyphens into ASCII_HYPH. */
1792
1793 start = p = buf->buf + pos;
1794
1795 while (*p != '\0') {
1796 sz = strcspn(p, "-\\");
1797 p += sz;
1798
1799 if (*p == '\0')
1800 break;
1801
1802 if (*p == '\\') {
1803 /* Skip over escapes. */
1804 p++;
1805 esc = mandoc_escape((const char **)&p, NULL((void*)0), NULL((void*)0));
1806 if (esc == ESCAPE_ERROR)
1807 break;
1808 while (*p == '-')
1809 p++;
1810 continue;
1811 } else if (p == start) {
1812 p++;
1813 continue;
1814 }
1815
1816 if (isalpha((unsigned char)p[-1]) &&
1817 isalpha((unsigned char)p[1]))
1818 *p = ASCII_HYPH30;
1819 p++;
1820 }
1821 return ROFF_CONT0x001;
1822}
1823
1824int
1825roff_parseln(struct roff *r, int ln, struct buf *buf, int *offs, size_t len)
1826{
1827 enum roff_tok t;
1828 int e;
1829 int pos; /* parse point */
1830 int spos; /* saved parse point for messages */
1831 int ppos; /* original offset in buf->buf */
1832 int ctl; /* macro line (boolean) */
1833
1834 ppos = pos = *offs;
1835
1836 if (len > 80 && r->tbl == NULL((void*)0) && r->eqn == NULL((void*)0) &&
1837 (r->man->flags & ROFF_NOFILL(1 << 1)) == 0 &&
1838 strchr(" .\\", buf->buf[pos]) == NULL((void*)0) &&
1839 buf->buf[pos] != r->control &&
1840 strcspn(buf->buf, " ") < 80)
1841 mandoc_msg(MANDOCERR_TEXT_LONG, ln, (int)len - 1,
1842 "%.20s...", buf->buf + pos);
1843
1844 /* Handle in-line equation delimiters. */
1845
1846 if (r->tbl == NULL((void*)0) &&
1847 r->last_eqn != NULL((void*)0) && r->last_eqn->delim &&
1848 (r->eqn == NULL((void*)0) || r->eqn_inline)) {
1849 e = roff_eqndelim(r, buf, pos);
1850 if (e == ROFF_REPARSE0x004)
1851 return e;
1852 assert(e == ROFF_CONT)((e == 0x001) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/roff.c"
, 1852, __func__, "e == ROFF_CONT"))
;
1853 }
1854
1855 /* Expand some escape sequences. */
1856
1857 e = roff_expand(r, buf, ln, pos, r->escape);
1858 if ((e & ROFF_MASK0x00f) == ROFF_IGN0x000)
1859 return e;
1860 assert(e == ROFF_CONT)((e == 0x001) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/roff.c"
, 1860, __func__, "e == ROFF_CONT"))
;
1861
1862 ctl = roff_getcontrol(r, buf->buf, &pos);
1863
1864 /*
1865 * First, if a scope is open and we're not a macro, pass the
1866 * text through the macro's filter.
1867 * Equations process all content themselves.
1868 * Tables process almost all content themselves, but we want
1869 * to warn about macros before passing it there.
1870 */
1871
1872 if (r->last != NULL((void*)0) && ! ctl) {
1873 t = r->last->tok;
1874 e = (*roffs[t].text)(r, t, buf, ln, pos, pos, offs);
1875 if ((e & ROFF_MASK0x00f) == ROFF_IGN0x000)
1876 return e;
1877 e &= ~ROFF_MASK0x00f;
1878 } else
1879 e = ROFF_IGN0x000;
1880 if (r->eqn != NULL((void*)0) && strncmp(buf->buf + ppos, ".EN", 3)) {
1881 eqn_read(r->eqn, buf->buf + ppos);
1882 return e;
1883 }
1884 if (r->tbl != NULL((void*)0) && (ctl == 0 || buf->buf[pos] == '\0')) {
1885 tbl_read(r->tbl, ln, buf->buf, ppos);
1886 roff_addtbl(r->man, ln, r->tbl);
1887 return e;
1888 }
1889 if ( ! ctl) {
1890 r->options &= ~MPARSE_COMMENT(1 << 7);
1891 return roff_parsetext(r, buf, pos, offs) | e;
1892 }
1893
1894 /* Skip empty request lines. */
1895
1896 if (buf->buf[pos] == '"') {
1897 mandoc_msg(MANDOCERR_COMMENT_BAD, ln, pos, NULL((void*)0));
1898 return ROFF_IGN0x000;
1899 } else if (buf->buf[pos] == '\0')
1900 return ROFF_IGN0x000;
1901
1902 /*
1903 * If a scope is open, go to the child handler for that macro,
1904 * as it may want to preprocess before doing anything with it.
1905 * Don't do so if an equation is open.
1906 */
1907
1908 if (r->last) {
1909 t = r->last->tok;
1910 return (*roffs[t].sub)(r, t, buf, ln, ppos, pos, offs);
1911 }
1912
1913 /* No scope is open. This is a new request or macro. */
1914
1915 r->options &= ~MPARSE_COMMENT(1 << 7);
1916 spos = pos;
1917 t = roff_parse(r, buf->buf, &pos, ln, ppos);
1918
1919 /* Tables ignore most macros. */
1920
1921 if (r->tbl != NULL((void*)0) && (t == TOKEN_NONE || t == ROFF_TS ||
1922 t == ROFF_br || t == ROFF_ce || t == ROFF_rj || t == ROFF_sp)) {
1923 mandoc_msg(MANDOCERR_TBLMACRO,
1924 ln, pos, "%s", buf->buf + spos);
1925 if (t != TOKEN_NONE)
1926 return ROFF_IGN0x000;
1927 while (buf->buf[pos] != '\0' && buf->buf[pos] != ' ')
1928 pos++;
1929 while (buf->buf[pos] == ' ')
1930 pos++;
1931 tbl_read(r->tbl, ln, buf->buf, pos);
1932 roff_addtbl(r->man, ln, r->tbl);
1933 return ROFF_IGN0x000;
1934 }
1935
1936 /* For now, let high level macros abort .ce mode. */
1937
1938 if (ctl && roffce_node != NULL((void*)0) &&
1939 (t == TOKEN_NONE || t == ROFF_Dd || t == ROFF_EQ ||
1940 t == ROFF_TH || t == ROFF_TS)) {
1941 r->man->last = roffce_node;
1942 r->man->next = ROFF_NEXT_SIBLING;
1943 roffce_lines = 0;
1944 roffce_node = NULL((void*)0);
1945 }
1946
1947 /*
1948 * This is neither a roff request nor a user-defined macro.
1949 * Let the standard macro set parsers handle it.
1950 */
1951
1952 if (t == TOKEN_NONE)
1953 return ROFF_CONT0x001;
1954
1955 /* Execute a roff request or a user defined macro. */
1956
1957 return (*roffs[t].proc)(r, t, buf, ln, spos, pos, offs);
1958}
1959
1960/*
1961 * Internal interface function to tell the roff parser that execution
1962 * of the current macro ended. This is required because macro
1963 * definitions usually do not end with a .return request.
1964 */
1965void
1966roff_userret(struct roff *r)
1967{
1968 struct mctx *ctx;
1969 int i;
1970
1971 assert(r->mstackpos >= 0)((r->mstackpos >= 0) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/roff.c"
, 1971, __func__, "r->mstackpos >= 0"))
;
1972 ctx = r->mstack + r->mstackpos;
1973 for (i = 0; i < ctx->argc; i++)
1974 free(ctx->argv[i]);
1975 ctx->argc = 0;
1976 r->mstackpos--;
1977}
1978
1979void
1980roff_endparse(struct roff *r)
1981{
1982 if (r->last != NULL((void*)0))
1983 mandoc_msg(MANDOCERR_BLK_NOEND, r->last->line,
1984 r->last->col, "%s", roff_name[r->last->tok]);
1985
1986 if (r->eqn != NULL((void*)0)) {
1987 mandoc_msg(MANDOCERR_BLK_NOEND,
1988 r->eqn->node->line, r->eqn->node->pos, "EQ");
1989 eqn_parse(r->eqn);
1990 r->eqn = NULL((void*)0);
1991 }
1992
1993 if (r->tbl != NULL((void*)0)) {
1994 tbl_end(r->tbl, 1);
1995 r->tbl = NULL((void*)0);
1996 }
1997}
1998
1999/*
2000 * Parse a roff node's type from the input buffer. This must be in the
2001 * form of ".foo xxx" in the usual way.
2002 */
2003static enum roff_tok
2004roff_parse(struct roff *r, char *buf, int *pos, int ln, int ppos)
2005{
2006 char *cp;
2007 const char *mac;
2008 size_t maclen;
2009 int deftype;
2010 enum roff_tok t;
2011
2012 cp = buf + *pos;
2013
2014 if ('\0' == *cp || '"' == *cp || '\t' == *cp || ' ' == *cp)
2015 return TOKEN_NONE;
2016
2017 mac = cp;
2018 maclen = roff_getname(r, &cp, ln, ppos);
2019
2020 deftype = ROFFDEF_USER(1 << 1) | ROFFDEF_REN(1 << 3);
2021 r->current_string = roff_getstrn(r, mac, maclen, &deftype);
2022 switch (deftype) {
2023 case ROFFDEF_USER(1 << 1):
2024 t = ROFF_USERDEF;
2025 break;
2026 case ROFFDEF_REN(1 << 3):
2027 t = ROFF_RENAMED;
2028 break;
2029 default:
2030 t = roffhash_find(r->reqtab, mac, maclen);
2031 break;
2032 }
2033 if (t != TOKEN_NONE)
2034 *pos = cp - buf;
2035 else if (deftype == ROFFDEF_UNDEF(1 << 5)) {
2036 /* Using an undefined macro defines it to be empty. */
2037 roff_setstrn(&r->strtab, mac, maclen, "", 0, 0);
2038 roff_setstrn(&r->rentab, mac, maclen, NULL((void*)0), 0, 0);
2039 }
2040 return t;
2041}
2042
2043/* --- handling of request blocks ----------------------------------------- */
2044
2045/*
2046 * Close a macro definition block or an "ignore" block.
2047 */
2048static int
2049roff_cblock(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
)
2050{
2051 int rr;
2052
2053 if (r->last == NULL((void*)0)) {
2054 mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "..");
2055 return ROFF_IGN0x000;
2056 }
2057
2058 switch (r->last->tok) {
2059 case ROFF_am:
2060 case ROFF_ami:
2061 case ROFF_de:
2062 case ROFF_dei:
2063 case ROFF_ig:
2064 break;
2065 case ROFF_am1:
2066 case ROFF_de1:
2067 /* Remapped in roff_block(). */
2068 abort();
2069 default:
2070 mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "..");
2071 return ROFF_IGN0x000;
2072 }
2073
2074 roffnode_pop(r);
2075 roffnode_cleanscope(r);
2076
2077 /*
2078 * If a conditional block with braces is still open,
2079 * check for "\}" block end markers.
2080 */
2081
2082 if (r->last != NULL((void*)0) && r->last->endspan < 0) {
2083 rr = 1; /* If arguments follow "\}", warn about them. */
2084 roff_cond_checkend(r, tok, buf, ln, ppos, pos, &rr);
2085 }
2086
2087 if (buf->buf[pos] != '\0')
2088 mandoc_msg(MANDOCERR_ARG_SKIP, ln, pos,
2089 ".. %s", buf->buf + pos);
2090
2091 return ROFF_IGN0x000;
2092}
2093
2094/*
2095 * Pop all nodes ending at the end of the current input line.
2096 * Return the number of loops ended.
2097 */
2098static int
2099roffnode_cleanscope(struct roff *r)
2100{
2101 int inloop;
2102
2103 inloop = 0;
2104 while (r->last != NULL((void*)0) && r->last->endspan > 0) {
2105 if (--r->last->endspan != 0)
2106 break;
2107 inloop += roffnode_pop(r);
2108 }
2109 return inloop;
2110}
2111
2112/*
2113 * Handle the closing "\}" of a conditional block.
2114 * Apart from generating warnings, this only pops nodes.
2115 * Return the number of loops ended.
2116 */
2117static int
2118roff_ccond(struct roff *r, int ln, int ppos)
2119{
2120 if (NULL((void*)0) == r->last) {
2121 mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "\\}");
2122 return 0;
2123 }
2124
2125 switch (r->last->tok) {
2126 case ROFF_el:
2127 case ROFF_ie:
2128 case ROFF_if:
2129 case ROFF_while:
2130 break;
2131 default:
2132 mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "\\}");
2133 return 0;
2134 }
2135
2136 if (r->last->endspan > -1) {
2137 mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "\\}");
2138 return 0;
2139 }
2140
2141 return roffnode_pop(r) + roffnode_cleanscope(r);
2142}
2143
2144static int
2145roff_block(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
)
2146{
2147 const char *name, *value;
2148 char *call, *cp, *iname, *rname;
2149 size_t csz, namesz, rsz;
2150 int deftype;
2151
2152 /* Ignore groff compatibility mode for now. */
2153
2154 if (tok == ROFF_de1)
2155 tok = ROFF_de;
2156 else if (tok == ROFF_dei1)
2157 tok = ROFF_dei;
2158 else if (tok == ROFF_am1)
2159 tok = ROFF_am;
2160 else if (tok == ROFF_ami1)
2161 tok = ROFF_ami;
2162
2163 /* Parse the macro name argument. */
2164
2165 cp = buf->buf + pos;
2166 if (tok == ROFF_ig) {
2167 iname = NULL((void*)0);
2168 namesz = 0;
2169 } else {
2170 iname = cp;
2171 namesz = roff_getname(r, &cp, ln, ppos);
2172 iname[namesz] = '\0';
2173 }
2174
2175 /* Resolve the macro name argument if it is indirect. */
2176
2177 if (namesz && (tok == ROFF_dei || tok == ROFF_ami)) {
2178 deftype = ROFFDEF_USER(1 << 1);
2179 name = roff_getstrn(r, iname, namesz, &deftype);
2180 if (name == NULL((void*)0)) {
2181 mandoc_msg(MANDOCERR_STR_UNDEF,
2182 ln, (int)(iname - buf->buf),
2183 "%.*s", (int)namesz, iname);
2184 namesz = 0;
2185 } else
2186 namesz = strlen(name);
2187 } else
2188 name = iname;
2189
2190 if (namesz == 0 && tok != ROFF_ig) {
2191 mandoc_msg(MANDOCERR_REQ_EMPTY,
2192 ln, ppos, "%s", roff_name[tok]);
2193 return ROFF_IGN0x000;
2194 }
2195
2196 roffnode_push(r, tok, name, ln, ppos);
2197
2198 /*
2199 * At the beginning of a `de' macro, clear the existing string
2200 * with the same name, if there is one. New content will be
2201 * appended from roff_block_text() in multiline mode.
2202 */
2203
2204 if (tok == ROFF_de || tok == ROFF_dei) {
2205 roff_setstrn(&r->strtab, name, namesz, "", 0, 0);
2206 roff_setstrn(&r->rentab, name, namesz, NULL((void*)0), 0, 0);
2207 } else if (tok == ROFF_am || tok == ROFF_ami) {
2208 deftype = ROFFDEF_ANY((1 << 1) | (1 << 2) | (1 << 3) | (1 <<
4))
;
2209 value = roff_getstrn(r, iname, namesz, &deftype);
2210 switch (deftype) { /* Before appending, ... */
2211 case ROFFDEF_PRE(1 << 2): /* copy predefined to user-defined. */
2212 roff_setstrn(&r->strtab, name, namesz,
2213 value, strlen(value), 0);
2214 break;
2215 case ROFFDEF_REN(1 << 3): /* call original standard macro. */
2216 csz = mandoc_asprintf(&call, ".%.*s \\$* \\\"\n",
2217 (int)strlen(value), value);
2218 roff_setstrn(&r->strtab, name, namesz, call, csz, 0);
2219 roff_setstrn(&r->rentab, name, namesz, NULL((void*)0), 0, 0);
2220 free(call);
2221 break;
2222 case ROFFDEF_STD(1 << 4): /* rename and call standard macro. */
2223 rsz = mandoc_asprintf(&rname, "__%s_renamed", name);
2224 roff_setstrn(&r->rentab, rname, rsz, name, namesz, 0);
2225 csz = mandoc_asprintf(&call, ".%.*s \\$* \\\"\n",
2226 (int)rsz, rname);
2227 roff_setstrn(&r->strtab, name, namesz, call, csz, 0);
2228 free(call);
2229 free(rname);
2230 break;
2231 default:
2232 break;
2233 }
2234 }
2235
2236 if (*cp == '\0')
2237 return ROFF_IGN0x000;
2238
2239 /* Get the custom end marker. */
2240
2241 iname = cp;
2242 namesz = roff_getname(r, &cp, ln, ppos);
2243
2244 /* Resolve the end marker if it is indirect. */
2245
2246 if (namesz && (tok == ROFF_dei || tok == ROFF_ami)) {
2247 deftype = ROFFDEF_USER(1 << 1);
2248 name = roff_getstrn(r, iname, namesz, &deftype);
2249 if (name == NULL((void*)0)) {
2250 mandoc_msg(MANDOCERR_STR_UNDEF,
2251 ln, (int)(iname - buf->buf),
2252 "%.*s", (int)namesz, iname);
2253 namesz = 0;
2254 } else
2255 namesz = strlen(name);
2256 } else
2257 name = iname;
2258
2259 if (namesz)
2260 r->last->end = mandoc_strndup(name, namesz);
2261
2262 if (*cp != '\0')
2263 mandoc_msg(MANDOCERR_ARG_EXCESS,
2264 ln, pos, ".%s ... %s", roff_name[tok], cp);
2265
2266 return ROFF_IGN0x000;
2267}
2268
2269static int
2270roff_block_sub(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
)
2271{
2272 enum roff_tok t;
2273 int i, j;
2274
2275 /*
2276 * First check whether a custom macro exists at this level. If
2277 * it does, then check against it. This is some of groff's
2278 * stranger behaviours. If we encountered a custom end-scope
2279 * tag and that tag also happens to be a "real" macro, then we
2280 * need to try interpreting it again as a real macro. If it's
2281 * not, then return ignore. Else continue.
2282 */
2283
2284 if (r->last->end) {
2285 for (i = pos, j = 0; r->last->end[j]; j++, i++)
2286 if (buf->buf[i] != r->last->end[j])
2287 break;
2288
2289 if (r->last->end[j] == '\0' &&
2290 (buf->buf[i] == '\0' ||
2291 buf->buf[i] == ' ' ||
2292 buf->buf[i] == '\t')) {
2293 roffnode_pop(r);
2294 roffnode_cleanscope(r);
2295
2296 while (buf->buf[i] == ' ' || buf->buf[i] == '\t')
2297 i++;
2298
2299 pos = i;
2300 if (roff_parse(r, buf->buf, &pos, ln, ppos) !=
2301 TOKEN_NONE)
2302 return ROFF_RERUN0x002;
2303 return ROFF_IGN0x000;
2304 }
2305 }
2306
2307 /*
2308 * If we have no custom end-query or lookup failed, then try
2309 * pulling it out of the hashtable.
2310 */
2311
2312 t = roff_parse(r, buf->buf, &pos, ln, ppos);
2313
2314 if (t != ROFF_cblock) {
2315 if (tok != ROFF_ig)
2316 roff_setstr(r, r->last->name, buf->buf + ppos, 2);
2317 return ROFF_IGN0x000;
2318 }
2319
2320 return (*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs);
2321}
2322
2323static int
2324roff_block_text(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
)
2325{
2326
2327 if (tok != ROFF_ig)
2328 roff_setstr(r, r->last->name, buf->buf + pos, 2);
2329
2330 return ROFF_IGN0x000;
2331}
2332
2333/*
2334 * Check for a closing "\}" and handle it.
2335 * In this function, the final "int *offs" argument is used for
2336 * different purposes than elsewhere:
2337 * Input: *offs == 0: caller wants to discard arguments following \}
2338 * *offs == 1: caller wants to preserve text following \}
2339 * Output: *offs = 0: tell caller to discard input line
2340 * *offs = 1: tell caller to use input line
2341 */
2342static int
2343roff_cond_checkend(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
)
2344{
2345 char *ep;
2346 int endloop, irc, rr;
2347
2348 irc = ROFF_IGN0x000;
2349 rr = r->last->rule;
2350 endloop = tok != ROFF_while ? ROFF_IGN0x000 :
2351 rr ? ROFF_LOOPCONT0x200 : ROFF_LOOPEXIT0x400;
2352 if (roffnode_cleanscope(r))
2353 irc |= endloop;
2354
2355 /*
2356 * If "\}" occurs on a macro line without a preceding macro or
2357 * a text line contains nothing else, drop the line completely.
2358 */
2359
2360 ep = buf->buf + pos;
2361 if (ep[0] == '\\' && ep[1] == '}' && (ep[2] == '\0' || *offs == 0))
2362 rr = 0;
2363
2364 /*
2365 * The closing delimiter "\}" rewinds the conditional scope
2366 * but is otherwise ignored when interpreting the line.
2367 */
2368
2369 while ((ep = strchr(ep, '\\')) != NULL((void*)0)) {
2370 switch (ep[1]) {
2371 case '}':
2372 if (ep[2] == '\0')
2373 ep[0] = '\0';
2374 else if (rr)
2375 ep[1] = '&';
2376 else
2377 memmove(ep, ep + 2, strlen(ep + 2) + 1);
2378 if (roff_ccond(r, ln, ep - buf->buf))
2379 irc |= endloop;
2380 break;
2381 case '\0':
2382 ++ep;
2383 break;
2384 default:
2385 ep += 2;
2386 break;
2387 }
2388 }
2389 *offs = rr;
2390 return irc;
2391}
2392
2393/*
2394 * Parse and process a request or macro line in conditional scope.
2395 */
2396static int
2397roff_cond_sub(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
)
2398{
2399 struct roffnode *bl;
2400 int irc, rr;
2401 enum roff_tok t;
2402
2403 rr = 0; /* If arguments follow "\}", skip them. */
2404 irc = roff_cond_checkend(r, tok, buf, ln, ppos, pos, &rr);
2405 t = roff_parse(r, buf->buf, &pos, ln, ppos);
2406
2407 /* For now, let high level macros abort .ce mode. */
2408
2409 if (roffce_node != NULL((void*)0) &&
2410 (t == TOKEN_NONE || t == ROFF_Dd || t == ROFF_EQ ||
2411 t == ROFF_TH || t == ROFF_TS)) {
2412 r->man->last = roffce_node;
2413 r->man->next = ROFF_NEXT_SIBLING;
2414 roffce_lines = 0;
2415 roffce_node = NULL((void*)0);
2416 }
2417
2418 /*
2419 * Fully handle known macros when they are structurally
2420 * required or when the conditional evaluated to true.
2421 */
2422
2423 if (t == ROFF_break) {
2424 if (irc & ROFF_LOOPMASK0xf00)
2425 irc = ROFF_IGN0x000 | ROFF_LOOPEXIT0x400;
2426 else if (rr) {
2427 for (bl = r->last; bl != NULL((void*)0); bl = bl->parent) {
2428 bl->rule = 0;
2429 if (bl->tok == ROFF_while)
2430 break;
2431 }
2432 }
2433 } else if (t != TOKEN_NONE &&
2434 (rr || roffs[t].flags & ROFFMAC_STRUCT(1 << 0)))
2435 irc |= (*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs);
2436 else
2437 irc |= rr ? ROFF_CONT0x001 : ROFF_IGN0x000;
2438 return irc;
2439}
2440
2441/*
2442 * Parse and process a text line in conditional scope.
2443 */
2444static int
2445roff_cond_text(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
)
2446{
2447 int irc, rr;
2448
2449 rr = 1; /* If arguments follow "\}", preserve them. */
2450 irc = roff_cond_checkend(r, tok, buf, ln, ppos, pos, &rr);
2451 if (rr)
2452 irc |= ROFF_CONT0x001;
2453 return irc;
2454}
2455
2456/* --- handling of numeric and conditional expressions -------------------- */
2457
2458/*
2459 * Parse a single signed integer number. Stop at the first non-digit.
2460 * If there is at least one digit, return success and advance the
2461 * parse point, else return failure and let the parse point unchanged.
2462 * Ignore overflows, treat them just like the C language.
2463 */
2464static int
2465roff_getnum(const char *v, int *pos, int *res, int flags)
2466{
2467 int myres, scaled, n, p;
2468
2469 if (NULL((void*)0) == res)
2470 res = &myres;
2471
2472 p = *pos;
2473 n = v[p] == '-';
2474 if (n || v[p] == '+')
2475 p++;
2476
2477 if (flags & ROFFNUM_WHITE(1 << 1))
2478 while (isspace((unsigned char)v[p]))
2479 p++;
2480
2481 for (*res = 0; isdigit((unsigned char)v[p]); p++)
2482 *res = 10 * *res + v[p] - '0';
2483 if (p == *pos + n)
2484 return 0;
2485
2486 if (n)
2487 *res = -*res;
2488
2489 /* Each number may be followed by one optional scaling unit. */
2490
2491 switch (v[p]) {
2492 case 'f':
2493 scaled = *res * 65536;
2494 break;
2495 case 'i':
2496 scaled = *res * 240;
2497 break;
2498 case 'c':
2499 scaled = *res * 240 / 2.54;
2500 break;
2501 case 'v':
2502 case 'P':
2503 scaled = *res * 40;
2504 break;
2505 case 'm':
2506 case 'n':
2507 scaled = *res * 24;
2508 break;
2509 case 'p':
2510 scaled = *res * 10 / 3;
2511 break;
2512 case 'u':
2513 scaled = *res;
2514 break;
2515 case 'M':
2516 scaled = *res * 6 / 25;
2517 break;
2518 default:
2519 scaled = *res;
2520 p--;
2521 break;
2522 }
2523 if (flags & ROFFNUM_SCALE(1 << 0))
2524 *res = scaled;
2525
2526 *pos = p + 1;
2527 return 1;
2528}
2529
2530/*
2531 * Evaluate a string comparison condition.
2532 * The first character is the delimiter.
2533 * Succeed if the string up to its second occurrence
2534 * matches the string up to its third occurence.
2535 * Advance the cursor after the third occurrence
2536 * or lacking that, to the end of the line.
2537 */
2538static int
2539roff_evalstrcond(const char *v, int *pos)
2540{
2541 const char *s1, *s2, *s3;
2542 int match;
2543
2544 match = 0;
2545 s1 = v + *pos; /* initial delimiter */
2546 s2 = s1 + 1; /* for scanning the first string */
2547 s3 = strchr(s2, *s1); /* for scanning the second string */
2548
2549 if (NULL((void*)0) == s3) /* found no middle delimiter */
2550 goto out;
2551
2552 while ('\0' != *++s3) {
2553 if (*s2 != *s3) { /* mismatch */
2554 s3 = strchr(s3, *s1);
2555 break;
2556 }
2557 if (*s3 == *s1) { /* found the final delimiter */
2558 match = 1;
2559 break;
2560 }
2561 s2++;
2562 }
2563
2564out:
2565 if (NULL((void*)0) == s3)
2566 s3 = strchr(s2, '\0');
2567 else if (*s3 != '\0')
2568 s3++;
2569 *pos = s3 - v;
2570 return match;
2571}
2572
2573/*
2574 * Evaluate an optionally negated single character, numerical,
2575 * or string condition.
2576 */
2577static int
2578roff_evalcond(struct roff *r, int ln, char *v, int *pos)
2579{
2580 const char *start, *end;
2581 char *cp, *name;
2582 size_t sz;
2583 int deftype, len, number, savepos, istrue, wanttrue;
2584
2585 if ('!' == v[*pos]) {
2586 wanttrue = 0;
2587 (*pos)++;
2588 } else
2589 wanttrue = 1;
2590
2591 switch (v[*pos]) {
2592 case '\0':
2593 return 0;
2594 case 'n':
2595 case 'o':
2596 (*pos)++;
2597 return wanttrue;
2598 case 'e':
2599 case 't':
2600 case 'v':
2601 (*pos)++;
2602 return !wanttrue;
2603 case 'c':
2604 do {
2605 (*pos)++;
2606 } while (v[*pos] == ' ');
2607
2608 /*
2609 * Quirk for groff compatibility:
2610 * The horizontal tab is neither available nor unavailable.
2611 */
2612
2613 if (v[*pos] == '\t') {
2614 (*pos)++;
2615 return 0;
2616 }
2617
2618 /* Printable ASCII characters are available. */
2619
2620 if (v[*pos] != '\\') {
2621 (*pos)++;
2622 return wanttrue;
2623 }
2624
2625 end = v + ++*pos;
2626 switch (mandoc_escape(&end, &start, &len)) {
2627 case ESCAPE_SPECIAL:
2628 istrue = mchars_spec2cp(start, len) != -1;
2629 break;
2630 case ESCAPE_UNICODE:
2631 istrue = 1;
2632 break;
2633 case ESCAPE_NUMBERED:
2634 istrue = mchars_num2char(start, len) != -1;
2635 break;
2636 default:
2637 istrue = !wanttrue;
2638 break;
2639 }
2640 *pos = end - v;
2641 return istrue == wanttrue;
2642 case 'd':
2643 case 'r':
2644 cp = v + *pos + 1;
2645 while (*cp == ' ')
2646 cp++;
2647 name = cp;
2648 sz = roff_getname(r, &cp, ln, cp - v);
2649 if (sz == 0)
2650 istrue = 0;
2651 else if (v[*pos] == 'r')
2652 istrue = roff_hasregn(r, name, sz);
2653 else {
2654 deftype = ROFFDEF_ANY((1 << 1) | (1 << 2) | (1 << 3) | (1 <<
4))
;
2655 roff_getstrn(r, name, sz, &deftype);
2656 istrue = !!deftype;
2657 }
2658 *pos = (name + sz) - v;
2659 return istrue == wanttrue;
2660 default:
2661 break;
2662 }
2663
2664 savepos = *pos;
2665 if (roff_evalnum(r, ln, v, pos, &number, ROFFNUM_SCALE(1 << 0)))
2666 return (number > 0) == wanttrue;
2667 else if (*pos == savepos)
2668 return roff_evalstrcond(v, pos) == wanttrue;
2669 else
2670 return 0;
2671}
2672
2673static int
2674roff_line_ignore(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
)
2675{
2676
2677 return ROFF_IGN0x000;
2678}
2679
2680static int
2681roff_insec(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
)
2682{
2683
2684 mandoc_msg(MANDOCERR_REQ_INSEC, ln, ppos, "%s", roff_name[tok]);
2685 return ROFF_IGN0x000;
2686}
2687
2688static int
2689roff_unsupp(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
)
2690{
2691
2692 mandoc_msg(MANDOCERR_REQ_UNSUPP, ln, ppos, "%s", roff_name[tok]);
2693 return ROFF_IGN0x000;
2694}
2695
2696static int
2697roff_cond(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
)
2698{
2699 int irc;
2700
2701 roffnode_push(r, tok, NULL((void*)0), ln, ppos);
2702
2703 /*
2704 * An `.el' has no conditional body: it will consume the value
2705 * of the current rstack entry set in prior `ie' calls or
2706 * defaults to DENY.
2707 *
2708 * If we're not an `el', however, then evaluate the conditional.
2709 */
2710
2711 r->last->rule = tok == ROFF_el ?
2712 (r->rstackpos < 0 ? 0 : r->rstack[r->rstackpos--]) :
2713 roff_evalcond(r, ln, buf->buf, &pos);
2714
2715 /*
2716 * An if-else will put the NEGATION of the current evaluated
2717 * conditional into the stack of rules.
2718 */
2719
2720 if (tok == ROFF_ie) {
2721 if (r->rstackpos + 1 == r->rstacksz) {
2722 r->rstacksz += 16;
2723 r->rstack = mandoc_reallocarray(r->rstack,
2724 r->rstacksz, sizeof(int));
2725 }
2726 r->rstack[++r->rstackpos] = !r->last->rule;
2727 }
2728
2729 /* If the parent has false as its rule, then so do we. */
2730
2731 if (r->last->parent && !r->last->parent->rule)
2732 r->last->rule = 0;
2733
2734 /*
2735 * Determine scope.
2736 * If there is nothing on the line after the conditional,
2737 * not even whitespace, use next-line scope.
2738 * Except that .while does not support next-line scope.
2739 */
2740
2741 if (buf->buf[pos] == '\0' && tok != ROFF_while) {
2742 r->last->endspan = 2;
2743 goto out;
2744 }
2745
2746 while (buf->buf[pos] == ' ')
2747 pos++;
2748
2749 /* An opening brace requests multiline scope. */
2750
2751 if (buf->buf[pos] == '\\' && buf->buf[pos + 1] == '{') {
2752 r->last->endspan = -1;
2753 pos += 2;
2754 while (buf->buf[pos] == ' ')
2755 pos++;
2756 goto out;
2757 }
2758
2759 /*
2760 * Anything else following the conditional causes
2761 * single-line scope. Warn if the scope contains
2762 * nothing but trailing whitespace.
2763 */
2764
2765 if (buf->buf[pos] == '\0')
2766 mandoc_msg(MANDOCERR_COND_EMPTY,
2767 ln, ppos, "%s", roff_name[tok]);
2768
2769 r->last->endspan = 1;
2770
2771out:
2772 *offs = pos;
2773 irc = ROFF_RERUN0x002;
2774 if (tok == ROFF_while)
2775 irc |= ROFF_WHILE0x100;
2776 return irc;
2777}
2778
2779static int
2780roff_ds(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
)
2781{
2782 char *string;
2783 const char *name;
2784 size_t namesz;
2785
2786 /* Ignore groff compatibility mode for now. */
2787
2788 if (tok == ROFF_ds1)
2789 tok = ROFF_ds;
2790 else if (tok == ROFF_as1)
2791 tok = ROFF_as;
2792
2793 /*
2794 * The first word is the name of the string.
2795 * If it is empty or terminated by an escape sequence,
2796 * abort the `ds' request without defining anything.
2797 */
2798
2799 name = string = buf->buf + pos;
2800 if (*name == '\0')
2801 return ROFF_IGN0x000;
2802
2803 namesz = roff_getname(r, &string, ln, pos);
2804 switch (name[namesz]) {
2805 case '\\':
2806 return ROFF_IGN0x000;
2807 case '\t':
2808 string = buf->buf + pos + namesz;
2809 break;
2810 default:
2811 break;
2812 }
2813
2814 /* Read past the initial double-quote, if any. */
2815 if (*string == '"')
2816 string++;
2817
2818 /* The rest is the value. */
2819 roff_setstrn(&r->strtab, name, namesz, string, strlen(string),
2820 ROFF_as == tok);
2821 roff_setstrn(&r->rentab, name, namesz, NULL((void*)0), 0, 0);
2822 return ROFF_IGN0x000;
2823}
2824
2825/*
2826 * Parse a single operator, one or two characters long.
2827 * If the operator is recognized, return success and advance the
2828 * parse point, else return failure and let the parse point unchanged.
2829 */
2830static int
2831roff_getop(const char *v, int *pos, char *res)
2832{
2833
2834 *res = v[*pos];
2835
2836 switch (*res) {
2837 case '+':
2838 case '-':
2839 case '*':
2840 case '/':
2841 case '%':
2842 case '&':
2843 case ':':
2844 break;
2845 case '<':
2846 switch (v[*pos + 1]) {
2847 case '=':
2848 *res = 'l';
2849 (*pos)++;
2850 break;
2851 case '>':
2852 *res = '!';
2853 (*pos)++;
2854 break;
2855 case '?':
2856 *res = 'i';
2857 (*pos)++;
2858 break;
2859 default:
2860 break;
2861 }
2862 break;
2863 case '>':
2864 switch (v[*pos + 1]) {
2865 case '=':
2866 *res = 'g';
2867 (*pos)++;
2868 break;
2869 case '?':
2870 *res = 'a';
2871 (*pos)++;
2872 break;
2873 default:
2874 break;
2875 }
2876 break;
2877 case '=':
2878 if ('=' == v[*pos + 1])
2879 (*pos)++;
2880 break;
2881 default:
2882 return 0;
2883 }
2884 (*pos)++;
2885
2886 return *res;
2887}
2888
2889/*
2890 * Evaluate either a parenthesized numeric expression
2891 * or a single signed integer number.
2892 */
2893static int
2894roff_evalpar(struct roff *r, int ln,
2895 const char *v, int *pos, int *res, int flags)
2896{
2897
2898 if ('(' != v[*pos])
2899 return roff_getnum(v, pos, res, flags);
2900
2901 (*pos)++;
2902 if ( ! roff_evalnum(r, ln, v, pos, res, flags | ROFFNUM_WHITE(1 << 1)))
2903 return 0;
2904
2905 /*
2906 * Omission of the closing parenthesis
2907 * is an error in validation mode,
2908 * but ignored in evaluation mode.
2909 */
2910
2911 if (')' == v[*pos])
2912 (*pos)++;
2913 else if (NULL((void*)0) == res)
2914 return 0;
2915
2916 return 1;
2917}
2918
2919/*
2920 * Evaluate a complete numeric expression.
2921 * Proceed left to right, there is no concept of precedence.
2922 */
2923static int
2924roff_evalnum(struct roff *r, int ln, const char *v,
2925 int *pos, int *res, int flags)
2926{
2927 int mypos, operand2;
2928 char operator;
2929
2930 if (NULL((void*)0) == pos) {
2931 mypos = 0;
2932 pos = &mypos;
2933 }
2934
2935 if (flags & ROFFNUM_WHITE(1 << 1))
2936 while (isspace((unsigned char)v[*pos]))
2937 (*pos)++;
2938
2939 if ( ! roff_evalpar(r, ln, v, pos, res, flags))
2940 return 0;
2941
2942 while (1) {
2943 if (flags & ROFFNUM_WHITE(1 << 1))
2944 while (isspace((unsigned char)v[*pos]))
2945 (*pos)++;
2946
2947 if ( ! roff_getop(v, pos, &operator))
2948 break;
2949
2950 if (flags & ROFFNUM_WHITE(1 << 1))
2951 while (isspace((unsigned char)v[*pos]))
2952 (*pos)++;
2953
2954 if ( ! roff_evalpar(r, ln, v, pos, &operand2, flags))
2955 return 0;
2956
2957 if (flags & ROFFNUM_WHITE(1 << 1))
2958 while (isspace((unsigned char)v[*pos]))
2959 (*pos)++;
2960
2961 if (NULL((void*)0) == res)
2962 continue;
2963
2964 switch (operator) {
2965 case '+':
2966 *res += operand2;
2967 break;
2968 case '-':
2969 *res -= operand2;
2970 break;
2971 case '*':
2972 *res *= operand2;
2973 break;
2974 case '/':
2975 if (operand2 == 0) {
2976 mandoc_msg(MANDOCERR_DIVZERO,
2977 ln, *pos, "%s", v);
2978 *res = 0;
2979 break;
2980 }
2981 *res /= operand2;
2982 break;
2983 case '%':
2984 if (operand2 == 0) {
2985 mandoc_msg(MANDOCERR_DIVZERO,
2986 ln, *pos, "%s", v);
2987 *res = 0;
2988 break;
2989 }
2990 *res %= operand2;
2991 break;
2992 case '<':
2993 *res = *res < operand2;
2994 break;
2995 case '>':
2996 *res = *res > operand2;
2997 break;
2998 case 'l':
2999 *res = *res <= operand2;
3000 break;
3001 case 'g':
3002 *res = *res >= operand2;
3003 break;
3004 case '=':
3005 *res = *res == operand2;
3006 break;
3007 case '!':
3008 *res = *res != operand2;
3009 break;
3010 case '&':
3011 *res = *res && operand2;
3012 break;
3013 case ':':
3014 *res = *res || operand2;
3015 break;
3016 case 'i':
3017 if (operand2 < *res)
3018 *res = operand2;
3019 break;
3020 case 'a':
3021 if (operand2 > *res)
3022 *res = operand2;
3023 break;
3024 default:
3025 abort();
3026 }
3027 }
3028 return 1;
3029}
3030
3031/* --- register management ------------------------------------------------ */
3032
3033void
3034roff_setreg(struct roff *r, const char *name, int val, char sign)
3035{
3036 roff_setregn(r, name, strlen(name), val, sign, INT_MIN(-2147483647 -1));
3037}
3038
3039static void
3040roff_setregn(struct roff *r, const char *name, size_t len,
3041 int val, char sign, int step)
3042{
3043 struct roffreg *reg;
3044
3045 /* Search for an existing register with the same name. */
3046 reg = r->regtab;
3047
3048 while (reg != NULL((void*)0) && (reg->key.sz != len ||
3049 strncmp(reg->key.p, name, len) != 0))
3050 reg = reg->next;
3051
3052 if (NULL((void*)0) == reg) {
3053 /* Create a new register. */
3054 reg = mandoc_malloc(sizeof(struct roffreg));
3055 reg->key.p = mandoc_strndup(name, len);
3056 reg->key.sz = len;
3057 reg->val = 0;
3058 reg->step = 0;
3059 reg->next = r->regtab;
3060 r->regtab = reg;
3061 }
3062
3063 if ('+' == sign)
3064 reg->val += val;
3065 else if ('-' == sign)
3066 reg->val -= val;
3067 else
3068 reg->val = val;
3069 if (step != INT_MIN(-2147483647 -1))
3070 reg->step = step;
3071}
3072
3073/*
3074 * Handle some predefined read-only number registers.
3075 * For now, return -1 if the requested register is not predefined;
3076 * in case a predefined read-only register having the value -1
3077 * were to turn up, another special value would have to be chosen.
3078 */
3079static int
3080roff_getregro(const struct roff *r, const char *name)
3081{
3082
3083 switch (*name) {
3084 case '$': /* Number of arguments of the last macro evaluated. */
3085 return r->mstackpos < 0 ? 0 : r->mstack[r->mstackpos].argc;
3086 case 'A': /* ASCII approximation mode is always off. */
3087 return 0;
3088 case 'g': /* Groff compatibility mode is always on. */
3089 return 1;
3090 case 'H': /* Fixed horizontal resolution. */
3091 return 24;
3092 case 'j': /* Always adjust left margin only. */
3093 return 0;
3094 case 'T': /* Some output device is always defined. */
3095 return 1;
3096 case 'V': /* Fixed vertical resolution. */
3097 return 40;
3098 default:
3099 return -1;
3100 }
3101}
3102
3103int
3104roff_getreg(struct roff *r, const char *name)
3105{
3106 return roff_getregn(r, name, strlen(name), '\0');
3107}
3108
3109static int
3110roff_getregn(struct roff *r, const char *name, size_t len, char sign)
3111{
3112 struct roffreg *reg;
3113 int val;
3114
3115 if ('.' == name[0] && 2 == len) {
3116 val = roff_getregro(r, name + 1);
3117 if (-1 != val)
3118 return val;
3119 }
3120
3121 for (reg = r->regtab; reg; reg = reg->next) {
3122 if (len == reg->key.sz &&
3123 0 == strncmp(name, reg->key.p, len)) {
3124 switch (sign) {
3125 case '+':
3126 reg->val += reg->step;
3127 break;
3128 case '-':
3129 reg->val -= reg->step;
3130 break;
3131 default:
3132 break;
3133 }
3134 return reg->val;
3135 }
3136 }
3137
3138 roff_setregn(r, name, len, 0, '\0', INT_MIN(-2147483647 -1));
3139 return 0;
3140}
3141
3142static int
3143roff_hasregn(const struct roff *r, const char *name, size_t len)
3144{
3145 struct roffreg *reg;
3146 int val;
3147
3148 if ('.' == name[0] && 2 == len) {
3149 val = roff_getregro(r, name + 1);
3150 if (-1 != val)
3151 return 1;
3152 }
3153
3154 for (reg = r->regtab; reg; reg = reg->next)
3155 if (len == reg->key.sz &&
3156 0 == strncmp(name, reg->key.p, len))
3157 return 1;
3158
3159 return 0;
3160}
3161
3162static void
3163roff_freereg(struct roffreg *reg)
3164{
3165 struct roffreg *old_reg;
3166
3167 while (NULL((void*)0) != reg) {
3168 free(reg->key.p);
3169 old_reg = reg;
3170 reg = reg->next;
3171 free(old_reg);
3172 }
3173}
3174
3175static int
3176roff_nr(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
)
3177{
3178 char *key, *val, *step;
3179 size_t keysz;
3180 int iv, is, len;
3181 char sign;
3182
3183 key = val = buf->buf + pos;
3184 if (*key == '\0')
3185 return ROFF_IGN0x000;
3186
3187 keysz = roff_getname(r, &val, ln, pos);
3188 if (key[keysz] == '\\' || key[keysz] == '\t')
3189 return ROFF_IGN0x000;
3190
3191 sign = *val;
3192 if (sign == '+' || sign == '-')
3193 val++;
3194
3195 len = 0;
3196 if (roff_evalnum(r, ln, val, &len, &iv, ROFFNUM_SCALE(1 << 0)) == 0)
3197 return ROFF_IGN0x000;
3198
3199 step = val + len;
3200 while (isspace((unsigned char)*step))
3201 step++;
3202 if (roff_evalnum(r, ln, step, NULL((void*)0), &is, 0) == 0)
3203 is = INT_MIN(-2147483647 -1);
3204
3205 roff_setregn(r, key, keysz, iv, sign, is);
3206 return ROFF_IGN0x000;
3207}
3208
3209static int
3210roff_rr(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
)
3211{
3212 struct roffreg *reg, **prev;
3213 char *name, *cp;
3214 size_t namesz;
3215
3216 name = cp = buf->buf + pos;
3217 if (*name == '\0')
3218 return ROFF_IGN0x000;
3219 namesz = roff_getname(r, &cp, ln, pos);
3220 name[namesz] = '\0';
3221
3222 prev = &r->regtab;
3223 while (1) {
3224 reg = *prev;
3225 if (reg == NULL((void*)0) || !strcmp(name, reg->key.p))
3226 break;
3227 prev = &reg->next;
3228 }
3229 if (reg != NULL((void*)0)) {
3230 *prev = reg->next;
3231 free(reg->key.p);
3232 free(reg);
3233 }
3234 return ROFF_IGN0x000;
3235}
3236
3237/* --- handler functions for roff requests -------------------------------- */
3238
3239static int
3240roff_rm(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
)
3241{
3242 const char *name;
3243 char *cp;
3244 size_t namesz;
3245
3246 cp = buf->buf + pos;
3247 while (*cp != '\0') {
3248 name = cp;
3249 namesz = roff_getname(r, &cp, ln, (int)(cp - buf->buf));
3250 roff_setstrn(&r->strtab, name, namesz, NULL((void*)0), 0, 0);
3251 roff_setstrn(&r->rentab, name, namesz, NULL((void*)0), 0, 0);
3252 if (name[namesz] == '\\' || name[namesz] == '\t')
3253 break;
3254 }
3255 return ROFF_IGN0x000;
3256}
3257
3258static int
3259roff_it(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
)
3260{
3261 int iv;
3262
3263 /* Parse the number of lines. */
3264
3265 if ( ! roff_evalnum(r, ln, buf->buf, &pos, &iv, 0)) {
3266 mandoc_msg(MANDOCERR_IT_NONUM,
3267 ln, ppos, "%s", buf->buf + 1);
3268 return ROFF_IGN0x000;
3269 }
3270
3271 while (isspace((unsigned char)buf->buf[pos]))
3272 pos++;
3273
3274 /*
3275 * Arm the input line trap.
3276 * Special-casing "an-trap" is an ugly workaround to cope
3277 * with DocBook stupidly fiddling with man(7) internals.
3278 */
3279
3280 roffit_lines = iv;
3281 roffit_macro = mandoc_strdup(iv != 1 ||
3282 strcmp(buf->buf + pos, "an-trap") ?
3283 buf->buf + pos : "br");
3284 return ROFF_IGN0x000;
3285}
3286
3287static int
3288roff_Dd(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
)
3289{
3290 int mask;
3291 enum roff_tok t, te;
3292
3293 switch (tok) {
3294 case ROFF_Dd:
3295 tok = MDOC_Dd;
3296 te = MDOC_MAX;
3297 if (r->format == 0)
3298 r->format = MPARSE_MDOC(1 << 0);
3299 mask = MPARSE_MDOC(1 << 0) | MPARSE_QUICK(1 << 3);
3300 break;
3301 case ROFF_TH:
3302 tok = MAN_TH;
3303 te = MAN_MAX;
3304 if (r->format == 0)
3305 r->format = MPARSE_MAN(1 << 1);
3306 mask = MPARSE_QUICK(1 << 3);
3307 break;
3308 default:
3309 abort();
3310 }
3311 if ((r->options & mask) == 0)
3312 for (t = tok; t < te; t++)
3313 roff_setstr(r, roff_name[t], NULL((void*)0), 0);
3314 return ROFF_CONT0x001;
3315}
3316
3317static int
3318roff_TE(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
)
3319{
3320 r->man->flags &= ~ROFF_NONOFILL(1 << 16);
3321 if (r->tbl == NULL((void*)0)) {
3322 mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "TE");
3323 return ROFF_IGN0x000;
3324 }
3325 if (tbl_end(r->tbl, 0) == 0) {
3326 r->tbl = NULL((void*)0);
3327 free(buf->buf);
3328 buf->buf = mandoc_strdup(".sp");
3329 buf->sz = 4;
3330 *offs = 0;
3331 return ROFF_REPARSE0x004;
3332 }
3333 r->tbl = NULL((void*)0);
3334 return ROFF_IGN0x000;
3335}
3336
3337static int
3338roff_T_(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
)
3339{
3340
3341 if (NULL((void*)0) == r->tbl)
3342 mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "T&");
3343 else
3344 tbl_restart(ln, ppos, r->tbl);
3345
3346 return ROFF_IGN0x000;
3347}
3348
3349/*
3350 * Handle in-line equation delimiters.
3351 */
3352static int
3353roff_eqndelim(struct roff *r, struct buf *buf, int pos)
3354{
3355 char *cp1, *cp2;
3356 const char *bef_pr, *bef_nl, *mac, *aft_nl, *aft_pr;
3357
3358 /*
3359 * Outside equations, look for an opening delimiter.
3360 * If we are inside an equation, we already know it is
3361 * in-line, or this function wouldn't have been called;
3362 * so look for a closing delimiter.
3363 */
3364
3365 cp1 = buf->buf + pos;
3366 cp2 = strchr(cp1, r->eqn == NULL((void*)0) ?
3367 r->last_eqn->odelim : r->last_eqn->cdelim);
3368 if (cp2 == NULL((void*)0))
3369 return ROFF_CONT0x001;
3370
3371 *cp2++ = '\0';
3372 bef_pr = bef_nl = aft_nl = aft_pr = "";
3373
3374 /* Handle preceding text, protecting whitespace. */
3375
3376 if (*buf->buf != '\0') {
3377 if (r->eqn == NULL((void*)0))
3378 bef_pr = "\\&";
3379 bef_nl = "\n";
3380 }
3381
3382 /*
3383 * Prepare replacing the delimiter with an equation macro
3384 * and drop leading white space from the equation.
3385 */
3386
3387 if (r->eqn == NULL((void*)0)) {
3388 while (*cp2 == ' ')
3389 cp2++;
3390 mac = ".EQ";
3391 } else
3392 mac = ".EN";
3393
3394 /* Handle following text, protecting whitespace. */
3395
3396 if (*cp2 != '\0') {
3397 aft_nl = "\n";
3398 if (r->eqn != NULL((void*)0))
3399 aft_pr = "\\&";
3400 }
3401
3402 /* Do the actual replacement. */
3403
3404 buf->sz = mandoc_asprintf(&cp1, "%s%s%s%s%s%s%s", buf->buf,
3405 bef_pr, bef_nl, mac, aft_nl, aft_pr, cp2) + 1;
3406 free(buf->buf);
3407 buf->buf = cp1;
3408
3409 /* Toggle the in-line state of the eqn subsystem. */
3410
3411 r->eqn_inline = r->eqn == NULL((void*)0);
3412 return ROFF_REPARSE0x004;
3413}
3414
3415static int
3416roff_EQ(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
)
3417{
3418 struct roff_node *n;
3419
3420 if (r->man->meta.macroset == MACROSET_MAN)
3421 man_breakscope(r->man, ROFF_EQ);
3422 n = roff_node_alloc(r->man, ln, ppos, ROFFT_EQN, TOKEN_NONE);
3423 if (ln > r->man->last->line)
3424 n->flags |= NODE_LINE(1 << 3);
3425 n->eqn = eqn_box_new();
3426 roff_node_append(r->man, n);
3427 r->man->next = ROFF_NEXT_SIBLING;
3428
3429 assert(r->eqn == NULL)((r->eqn == ((void*)0)) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/roff.c"
, 3429, __func__, "r->eqn == NULL"))
;
3430 if (r->last_eqn == NULL((void*)0))
3431 r->last_eqn = eqn_alloc();
3432 else
3433 eqn_reset(r->last_eqn);
3434 r->eqn = r->last_eqn;
3435 r->eqn->node = n;
3436
3437 if (buf->buf[pos] != '\0')
3438 mandoc_msg(MANDOCERR_ARG_SKIP, ln, pos,
3439 ".EQ %s", buf->buf + pos);
3440
3441 return ROFF_IGN0x000;
3442}
3443
3444static int
3445roff_EN(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
)
3446{
3447 if (r->eqn != NULL((void*)0)) {
3448 eqn_parse(r->eqn);
3449 r->eqn = NULL((void*)0);
3450 } else
3451 mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "EN");
3452 if (buf->buf[pos] != '\0')
3453 mandoc_msg(MANDOCERR_ARG_SKIP, ln, pos,
3454 "EN %s", buf->buf + pos);
3455 return ROFF_IGN0x000;
3456}
3457
3458static int
3459roff_TS(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
)
3460{
3461 if (r->tbl != NULL((void*)0)) {
3462 mandoc_msg(MANDOCERR_BLK_BROKEN, ln, ppos, "TS breaks TS");
3463 tbl_end(r->tbl, 0);
3464 }
3465 r->man->flags |= ROFF_NONOFILL(1 << 16);
3466 r->tbl = tbl_alloc(ppos, ln, r->last_tbl);
3467 if (r->last_tbl == NULL((void*)0))
3468 r->first_tbl = r->tbl;
3469 r->last_tbl = r->tbl;
3470 return ROFF_IGN0x000;
3471}
3472
3473static int
3474roff_noarg(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
)
3475{
3476 if (r->man->flags & (MAN_BLINE(1 << 12) | MAN_ELINE(1 << 11)))
3477 man_breakscope(r->man, tok);
3478 if (tok == ROFF_brp)
3479 tok = ROFF_br;
3480 roff_elem_alloc(r->man, ln, ppos, tok);
3481 if (buf->buf[pos] != '\0')
3482 mandoc_msg(MANDOCERR_ARG_SKIP, ln, pos,
3483 "%s %s", roff_name[tok], buf->buf + pos);
3484 if (tok == ROFF_nf)
3485 r->man->flags |= ROFF_NOFILL(1 << 1);
3486 else if (tok == ROFF_fi)
3487 r->man->flags &= ~ROFF_NOFILL(1 << 1);
3488 r->man->last->flags |= NODE_LINE(1 << 3) | NODE_VALID(1 << 0) | NODE_ENDED(1 << 1);
3489 r->man->next = ROFF_NEXT_SIBLING;
3490 return ROFF_IGN0x000;
3491}
3492
3493static int
3494roff_onearg(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
)
3495{
3496 struct roff_node *n;
3497 char *cp;
3498 int npos;
3499
3500 if (r->man->flags & (MAN_BLINE(1 << 12) | MAN_ELINE(1 << 11)) &&
3501 (tok == ROFF_ce || tok == ROFF_rj || tok == ROFF_sp ||
3502 tok == ROFF_ti))
3503 man_breakscope(r->man, tok);
3504
3505 if (roffce_node != NULL((void*)0) && (tok == ROFF_ce || tok == ROFF_rj)) {
3506 r->man->last = roffce_node;
3507 r->man->next = ROFF_NEXT_SIBLING;
3508 }
3509
3510 roff_elem_alloc(r->man, ln, ppos, tok);
3511 n = r->man->last;
3512
3513 cp = buf->buf + pos;
3514 if (*cp != '\0') {
3515 while (*cp != '\0' && *cp != ' ')
3516 cp++;
3517 while (*cp == ' ')
3518 *cp++ = '\0';
3519 if (*cp != '\0')
3520 mandoc_msg(MANDOCERR_ARG_EXCESS,
3521 ln, (int)(cp - buf->buf),
3522 "%s ... %s", roff_name[tok], cp);
3523 roff_word_alloc(r->man, ln, pos, buf->buf + pos);
3524 }
3525
3526 if (tok == ROFF_ce || tok == ROFF_rj) {
3527 if (r->man->last->type == ROFFT_ELEM) {
3528 roff_word_alloc(r->man, ln, pos, "1");
3529 r->man->last->flags |= NODE_NOSRC(1 << 9);
3530 }
3531 npos = 0;
3532 if (roff_evalnum(r, ln, r->man->last->string, &npos,
3533 &roffce_lines, 0) == 0) {
3534 mandoc_msg(MANDOCERR_CE_NONUM,
3535 ln, pos, "ce %s", buf->buf + pos);
3536 roffce_lines = 1;
3537 }
3538 if (roffce_lines < 1) {
3539 r->man->last = r->man->last->parent;
3540 roffce_node = NULL((void*)0);
3541 roffce_lines = 0;
3542 } else
3543 roffce_node = r->man->last->parent;
3544 } else {
3545 n->flags |= NODE_VALID(1 << 0) | NODE_ENDED(1 << 1);
3546 r->man->last = n;
3547 }
3548 n->flags |= NODE_LINE(1 << 3);
3549 r->man->next = ROFF_NEXT_SIBLING;
3550 return ROFF_IGN0x000;
3551}
3552
3553static int
3554roff_manyarg(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
)
3555{
3556 struct roff_node *n;
3557 char *sp, *ep;
3558
3559 roff_elem_alloc(r->man, ln, ppos, tok);
3560 n = r->man->last;
3561
3562 for (sp = ep = buf->buf + pos; *sp != '\0'; sp = ep) {
3563 while (*ep != '\0' && *ep != ' ')
3564 ep++;
3565 while (*ep == ' ')
3566 *ep++ = '\0';
3567 roff_word_alloc(r->man, ln, sp - buf->buf, sp);
3568 }
3569
3570 n->flags |= NODE_LINE(1 << 3) | NODE_VALID(1 << 0) | NODE_ENDED(1 << 1);
3571 r->man->last = n;
3572 r->man->next = ROFF_NEXT_SIBLING;
3573 return ROFF_IGN0x000;
3574}
3575
3576static int
3577roff_als(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
)
3578{
3579 char *oldn, *newn, *end, *value;
3580 size_t oldsz, newsz, valsz;
3581
3582 newn = oldn = buf->buf + pos;
3583 if (*newn == '\0')
3584 return ROFF_IGN0x000;
3585
3586 newsz = roff_getname(r, &oldn, ln, pos);
3587 if (newn[newsz] == '\\' || newn[newsz] == '\t' || *oldn == '\0')
3588 return ROFF_IGN0x000;
3589
3590 end = oldn;
3591 oldsz = roff_getname(r, &end, ln, oldn - buf->buf);
3592 if (oldsz == 0)
3593 return ROFF_IGN0x000;
3594
3595 valsz = mandoc_asprintf(&value, ".%.*s \\$@\\\"\n",
3596 (int)oldsz, oldn);
3597 roff_setstrn(&r->strtab, newn, newsz, value, valsz, 0);
3598 roff_setstrn(&r->rentab, newn, newsz, NULL((void*)0), 0, 0);
3599 free(value);
3600 return ROFF_IGN0x000;
3601}
3602
3603/*
3604 * The .break request only makes sense inside conditionals,
3605 * and that case is already handled in roff_cond_sub().
3606 */
3607static int
3608roff_break(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
)
3609{
3610 mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, pos, "break");
3611 return ROFF_IGN0x000;
3612}
3613
3614static int
3615roff_cc(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
)
3616{
3617 const char *p;
3618
3619 p = buf->buf + pos;
3620
3621 if (*p == '\0' || (r->control = *p++) == '.')
3622 r->control = '\0';
3623
3624 if (*p != '\0')
3625 mandoc_msg(MANDOCERR_ARG_EXCESS,
3626 ln, p - buf->buf, "cc ... %s", p);
3627
3628 return ROFF_IGN0x000;
3629}
3630
3631static int
3632roff_char(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
)
3633{
3634 const char *p, *kp, *vp;
3635 size_t ksz, vsz;
3636 int font;
3637
3638 /* Parse the character to be replaced. */
3639
3640 kp = buf->buf + pos;
3641 p = kp + 1;
3642 if (*kp == '\0' || (*kp == '\\' &&
3643 mandoc_escape(&p, NULL((void*)0), NULL((void*)0)) != ESCAPE_SPECIAL) ||
3644 (*p != ' ' && *p != '\0')) {
3645 mandoc_msg(MANDOCERR_CHAR_ARG, ln, pos, "char %s", kp);
3646 return ROFF_IGN0x000;
3647 }
3648 ksz = p - kp;
3649 while (*p == ' ')
3650 p++;
3651
3652 /*
3653 * If the replacement string contains a font escape sequence,
3654 * we have to restore the font at the end.
3655 */
3656
3657 vp = p;
3658 vsz = strlen(p);
3659 font = 0;
3660 while (*p != '\0') {
3661 if (*p++ != '\\')
3662 continue;
3663 switch (mandoc_escape(&p, NULL((void*)0), NULL((void*)0))) {
3664 case ESCAPE_FONT:
3665 case ESCAPE_FONTROMAN:
3666 case ESCAPE_FONTITALIC:
3667 case ESCAPE_FONTBOLD:
3668 case ESCAPE_FONTBI:
3669 case ESCAPE_FONTCR:
3670 case ESCAPE_FONTCB:
3671 case ESCAPE_FONTCI:
3672 case ESCAPE_FONTPREV:
3673 font++;
3674 break;
3675 default:
3676 break;
3677 }
3678 }
3679 if (font > 1)
3680 mandoc_msg(MANDOCERR_CHAR_FONT,
3681 ln, (int)(vp - buf->buf), "%s", vp);
3682
3683 /*
3684 * Approximate the effect of .char using the .tr tables.
3685 * XXX In groff, .char and .tr interact differently.
3686 */
3687
3688 if (ksz == 1) {
3689 if (r->xtab == NULL((void*)0))
3690 r->xtab = mandoc_calloc(128, sizeof(*r->xtab));
3691 assert((unsigned int)*kp < 128)(((unsigned int)*kp < 128) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/roff.c"
, 3691, __func__, "(unsigned int)*kp < 128"))
;
3692 free(r->xtab[(int)*kp].p);
3693 r->xtab[(int)*kp].sz = mandoc_asprintf(&r->xtab[(int)*kp].p,
3694 "%s%s", vp, font ? "\fP" : "");
3695 } else {
3696 roff_setstrn(&r->xmbtab, kp, ksz, vp, vsz, 0);
3697 if (font)
3698 roff_setstrn(&r->xmbtab, kp, ksz, "\\fP", 3, 1);
3699 }
3700 return ROFF_IGN0x000;
3701}
3702
3703static int
3704roff_ec(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
)
3705{
3706 const char *p;
3707
3708 p = buf->buf + pos;
3709 if (*p == '\0')
3710 r->escape = '\\';
3711 else {
3712 r->escape = *p;
3713 if (*++p != '\0')
3714 mandoc_msg(MANDOCERR_ARG_EXCESS, ln,
3715 (int)(p - buf->buf), "ec ... %s", p);
3716 }
3717 return ROFF_IGN0x000;
3718}
3719
3720static int
3721roff_eo(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
)
3722{
3723 r->escape = '\0';
3724 if (buf->buf[pos] != '\0')
3725 mandoc_msg(MANDOCERR_ARG_SKIP,
3726 ln, pos, "eo %s", buf->buf + pos);
3727 return ROFF_IGN0x000;
3728}
3729
3730static int
3731roff_nop(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
)
3732{
3733 while (buf->buf[pos] == ' ')
3734 pos++;
3735 *offs = pos;
3736 return ROFF_RERUN0x002;
3737}
3738
3739static int
3740roff_tr(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
)
3741{
3742 const char *p, *first, *second;
3743 size_t fsz, ssz;
3744 enum mandoc_esc esc;
3745
3746 p = buf->buf + pos;
3747
3748 if (*p == '\0') {
3749 mandoc_msg(MANDOCERR_REQ_EMPTY, ln, ppos, "tr");
3750 return ROFF_IGN0x000;
3751 }
3752
3753 while (*p != '\0') {
3754 fsz = ssz = 1;
3755
3756 first = p++;
3757 if (*first == '\\') {
3758 esc = mandoc_escape(&p, NULL((void*)0), NULL((void*)0));
3759 if (esc == ESCAPE_ERROR) {
3760 mandoc_msg(MANDOCERR_ESC_BAD, ln,
3761 (int)(p - buf->buf), "%s", first);
3762 return ROFF_IGN0x000;
3763 }
3764 fsz = (size_t)(p - first);
3765 }
3766
3767 second = p++;
3768 if (*second == '\\') {
3769 esc = mandoc_escape(&p, NULL((void*)0), NULL((void*)0));
3770 if (esc == ESCAPE_ERROR) {
3771 mandoc_msg(MANDOCERR_ESC_BAD, ln,
3772 (int)(p - buf->buf), "%s", second);
3773 return ROFF_IGN0x000;
3774 }
3775 ssz = (size_t)(p - second);
3776 } else if (*second == '\0') {
3777 mandoc_msg(MANDOCERR_TR_ODD, ln,
3778 (int)(first - buf->buf), "tr %s", first);
3779 second = " ";
3780 p--;
3781 }
3782
3783 if (fsz > 1) {
3784 roff_setstrn(&r->xmbtab, first, fsz,
3785 second, ssz, 0);
3786 continue;
3787 }
3788
3789 if (r->xtab == NULL((void*)0))
3790 r->xtab = mandoc_calloc(128,
3791 sizeof(struct roffstr));
3792
3793 free(r->xtab[(int)*first].p);
3794 r->xtab[(int)*first].p = mandoc_strndup(second, ssz);
3795 r->xtab[(int)*first].sz = ssz;
3796 }
3797
3798 return ROFF_IGN0x000;
3799}
3800
3801/*
3802 * Implementation of the .return request.
3803 * There is no need to call roff_userret() from here.
3804 * The read module will call that after rewinding the reader stack
3805 * to the place from where the current macro was called.
3806 */
3807static int
3808roff_return(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
)
3809{
3810 if (r->mstackpos >= 0)
3811 return ROFF_IGN0x000 | ROFF_USERRET0x040;
3812
3813 mandoc_msg(MANDOCERR_REQ_NOMAC, ln, ppos, "return");
3814 return ROFF_IGN0x000;
3815}
3816
3817static int
3818roff_rn(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
)
3819{
3820 const char *value;
3821 char *oldn, *newn, *end;
3822 size_t oldsz, newsz;
3823 int deftype;
3824
3825 oldn = newn = buf->buf + pos;
3826 if (*oldn == '\0')
1
Assuming the condition is false
2
Taking false branch
3827 return ROFF_IGN0x000;
3828
3829 oldsz = roff_getname(r, &newn, ln, pos);
3830 if (oldn[oldsz] == '\\' || oldn[oldsz] == '\t' || *newn == '\0')
3
Assuming the condition is false
4
Assuming the condition is false
5
Assuming the condition is false
6
Taking false branch
3831 return ROFF_IGN0x000;
3832
3833 end = newn;
3834 newsz = roff_getname(r, &end, ln, newn - buf->buf);
3835 if (newsz == 0)
7
Assuming 'newsz' is not equal to 0
8
Taking false branch
3836 return ROFF_IGN0x000;
3837
3838 deftype = ROFFDEF_ANY((1 << 1) | (1 << 2) | (1 << 3) | (1 <<
4))
;
3839 value = roff_getstrn(r, oldn, oldsz, &deftype);
3840 switch (deftype) {
9
Control jumps to 'case 2:' at line 3841
3841 case ROFFDEF_USER(1 << 1):
3842 roff_setstrn(&r->strtab, newn, newsz, value, strlen(value), 0);
10
Calling 'roff_setstrn'
3843 roff_setstrn(&r->strtab, oldn, oldsz, NULL((void*)0), 0, 0);
3844 roff_setstrn(&r->rentab, newn, newsz, NULL((void*)0), 0, 0);
3845 break;
3846 case ROFFDEF_PRE(1 << 2):
3847 roff_setstrn(&r->strtab, newn, newsz, value, strlen(value), 0);
3848 roff_setstrn(&r->rentab, newn, newsz, NULL((void*)0), 0, 0);
3849 break;
3850 case ROFFDEF_REN(1 << 3):
3851 roff_setstrn(&r->rentab, newn, newsz, value, strlen(value), 0);
3852 roff_setstrn(&r->rentab, oldn, oldsz, NULL((void*)0), 0, 0);
3853 roff_setstrn(&r->strtab, newn, newsz, NULL((void*)0), 0, 0);
3854 break;
3855 case ROFFDEF_STD(1 << 4):
3856 roff_setstrn(&r->rentab, newn, newsz, oldn, oldsz, 0);
3857 roff_setstrn(&r->strtab, newn, newsz, NULL((void*)0), 0, 0);
3858 break;
3859 default:
3860 roff_setstrn(&r->strtab, newn, newsz, NULL((void*)0), 0, 0);
3861 roff_setstrn(&r->rentab, newn, newsz, NULL((void*)0), 0, 0);
3862 break;
3863 }
3864 return ROFF_IGN0x000;
3865}
3866
3867static int
3868roff_shift(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
)
3869{
3870 struct mctx *ctx;
3871 int levels, i;
3872
3873 levels = 1;
3874 if (buf->buf[pos] != '\0' &&
3875 roff_evalnum(r, ln, buf->buf, &pos, &levels, 0) == 0) {
3876 mandoc_msg(MANDOCERR_CE_NONUM,
3877 ln, pos, "shift %s", buf->buf + pos);
3878 levels = 1;
3879 }
3880 if (r->mstackpos < 0) {
3881 mandoc_msg(MANDOCERR_REQ_NOMAC, ln, ppos, "shift");
3882 return ROFF_IGN0x000;
3883 }
3884 ctx = r->mstack + r->mstackpos;
3885 if (levels > ctx->argc) {
3886 mandoc_msg(MANDOCERR_SHIFT,
3887 ln, pos, "%d, but max is %d", levels, ctx->argc);
3888 levels = ctx->argc;
3889 }
3890 if (levels == 0)
3891 return ROFF_IGN0x000;
3892 for (i = 0; i < levels; i++)
3893 free(ctx->argv[i]);
3894 ctx->argc -= levels;
3895 for (i = 0; i < ctx->argc; i++)
3896 ctx->argv[i] = ctx->argv[i + levels];
3897 return ROFF_IGN0x000;
3898}
3899
3900static int
3901roff_so(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
)
3902{
3903 char *name, *cp;
3904
3905 name = buf->buf + pos;
3906 mandoc_msg(MANDOCERR_SO, ln, ppos, "so %s", name);
3907
3908 /*
3909 * Handle `so'. Be EXTREMELY careful, as we shouldn't be
3910 * opening anything that's not in our cwd or anything beneath
3911 * it. Thus, explicitly disallow traversing up the file-system
3912 * or using absolute paths.
3913 */
3914
3915 if (*name == '/' || strstr(name, "../") || strstr(name, "/..")) {
3916 mandoc_msg(MANDOCERR_SO_PATH, ln, ppos, ".so %s", name);
3917 buf->sz = mandoc_asprintf(&cp,
3918 ".sp\nSee the file %s.\n.sp", name) + 1;
3919 free(buf->buf);
3920 buf->buf = cp;
3921 *offs = 0;
3922 return ROFF_REPARSE0x004;
3923 }
3924
3925 *offs = pos;
3926 return ROFF_SO0x008;
3927}
3928
3929/* --- user defined strings and macros ------------------------------------ */
3930
3931static int
3932roff_userdef(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
)
3933{
3934 struct mctx *ctx;
3935 char *arg, *ap, *dst, *src;
3936 size_t sz;
3937
3938 /* If the macro is empty, ignore it altogether. */
3939
3940 if (*r->current_string == '\0')
3941 return ROFF_IGN0x000;
3942
3943 /* Initialize a new macro stack context. */
3944
3945 if (++r->mstackpos == r->mstacksz) {
3946 r->mstack = mandoc_recallocarray(r->mstack,
3947 r->mstacksz, r->mstacksz + 8, sizeof(*r->mstack));
3948 r->mstacksz += 8;
3949 }
3950 ctx = r->mstack + r->mstackpos;
3951 ctx->argc = 0;
3952
3953 /*
3954 * Collect pointers to macro argument strings,
3955 * NUL-terminating them and escaping quotes.
3956 */
3957
3958 src = buf->buf + pos;
3959 while (*src != '\0') {
3960 if (ctx->argc == ctx->argsz) {
3961 ctx->argsz += 8;
3962 ctx->argv = mandoc_reallocarray(ctx->argv,
3963 ctx->argsz, sizeof(*ctx->argv));
3964 }
3965 arg = roff_getarg(r, &src, ln, &pos);
3966 sz = 1; /* For the terminating NUL. */
3967 for (ap = arg; *ap != '\0'; ap++)
3968 sz += *ap == '"' ? 4 : 1;
3969 ctx->argv[ctx->argc++] = dst = mandoc_malloc(sz);
3970 for (ap = arg; *ap != '\0'; ap++) {
3971 if (*ap == '"') {
3972 memcpy(dst, "\\(dq", 4);
3973 dst += 4;
3974 } else
3975 *dst++ = *ap;
3976 }
3977 *dst = '\0';
3978 free(arg);
3979 }
3980
3981 /* Replace the macro invocation by the macro definition. */
3982
3983 free(buf->buf);
3984 buf->buf = mandoc_strdup(r->current_string);
3985 buf->sz = strlen(buf->buf) + 1;
3986 *offs = 0;
3987
3988 return buf->buf[buf->sz - 2] == '\n' ?
3989 ROFF_REPARSE0x004 | ROFF_USERCALL0x020 : ROFF_IGN0x000 | ROFF_APPEND0x010;
3990}
3991
3992/*
3993 * Calling a high-level macro that was renamed with .rn.
3994 * r->current_string has already been set up by roff_parse().
3995 */
3996static int
3997roff_renamed(ROFF_ARGSstruct roff *r, enum roff_tok tok, struct buf *buf, int ln, int
ppos, int pos, int *offs
)
3998{
3999 char *nbuf;
4000
4001 buf->sz = mandoc_asprintf(&nbuf, ".%s%s%s", r->current_string,
4002 buf->buf[pos] == '\0' ? "" : " ", buf->buf + pos) + 1;
4003 free(buf->buf);
4004 buf->buf = nbuf;
4005 *offs = 0;
4006 return ROFF_CONT0x001;
4007}
4008
4009/*
4010 * Measure the length in bytes of the roff identifier at *cpp
4011 * and advance the pointer to the next word.
4012 */
4013static size_t
4014roff_getname(struct roff *r, char **cpp, int ln, int pos)
4015{
4016 char *name, *cp;
4017 size_t namesz;
4018
4019 name = *cpp;
4020 if (*name == '\0')
4021 return 0;
4022
4023 /* Advance cp to the byte after the end of the name. */
4024
4025 for (cp = name; 1; cp++) {
4026 namesz = cp - name;
4027 if (*cp == '\0')
4028 break;
4029 if (*cp == ' ' || *cp == '\t') {
4030 cp++;
4031 break;
4032 }
4033 if (*cp != '\\')
4034 continue;
4035 if (cp[1] == '{' || cp[1] == '}')
4036 break;
4037 if (*++cp == '\\')
4038 continue;
4039 mandoc_msg(MANDOCERR_NAMESC, ln, pos,
4040 "%.*s", (int)(cp - name + 1), name);
4041 mandoc_escape((const char **)&cp, NULL((void*)0), NULL((void*)0));
4042 break;
4043 }
4044
4045 /* Read past spaces. */
4046
4047 while (*cp == ' ')
4048 cp++;
4049
4050 *cpp = cp;
4051 return namesz;
4052}
4053
4054/*
4055 * Store *string into the user-defined string called *name.
4056 * To clear an existing entry, call with (*r, *name, NULL, 0).
4057 * append == 0: replace mode
4058 * append == 1: single-line append mode
4059 * append == 2: multiline append mode, append '\n' after each call
4060 */
4061static void
4062roff_setstr(struct roff *r, const char *name, const char *string,
4063 int append)
4064{
4065 size_t namesz;
4066
4067 namesz = strlen(name);
4068 roff_setstrn(&r->strtab, name, namesz, string,
4069 string ? strlen(string) : 0, append);
4070 roff_setstrn(&r->rentab, name, namesz, NULL((void*)0), 0, 0);
4071}
4072
4073static void
4074roff_setstrn(struct roffkv **r, const char *name, size_t namesz,
4075 const char *string, size_t stringsz, int append)
4076{
4077 struct roffkv *n;
4078 char *c;
4079 int i;
4080 size_t oldch, newch;
4081
4082 /* Search for an existing string with the same name. */
4083 n = *r;
4084
4085 while (n
10.1
'n' is non-null
&& (namesz != n->key.sz ||
11
Assuming 'namesz' is equal to field 'sz'
12
Loop condition is false. Execution continues on line 4089
4086 strncmp(n->key.p, name, namesz)))
4087 n = n->next;
4088
4089 if (NULL((void*)0) == n
12.1
'n' is not equal to NULL
) {
13
Taking false branch
4090 /* Create a new string table entry. */
4091 n = mandoc_malloc(sizeof(struct roffkv));
4092 n->key.p = mandoc_strndup(name, namesz);
4093 n->key.sz = namesz;
4094 n->val.p = NULL((void*)0);
4095 n->val.sz = 0;
4096 n->next = *r;
4097 *r = n;
4098 } else if (0 == append
13.1
'append' is equal to 0
) {
14
Taking true branch
4099 free(n->val.p);
15
Memory is released
4100 n->val.p = NULL((void*)0);
4101 n->val.sz = 0;
4102 }
4103
4104 if (NULL((void*)0) == string
15.1
'string' is not equal to NULL
)
16
Taking false branch
4105 return;
4106
4107 /*
4108 * One additional byte for the '\n' in multiline mode,
4109 * and one for the terminating '\0'.
4110 */
4111 newch = stringsz + (1 < append
16.1
'append' is <= 1
? 2u : 1u);
17
'?' condition is false
4112
4113 if (NULL((void*)0) == n->val.p) {
18
Taking true branch
4114 n->val.p = mandoc_malloc(newch);
4115 *n->val.p = '\0';
4116 oldch = 0;
4117 } else {
4118 oldch = n->val.sz;
4119 n->val.p = mandoc_realloc(n->val.p, oldch + newch);
4120 }
4121
4122 /* Skip existing content in the destination buffer. */
4123 c = n->val.p + (int)oldch;
4124
4125 /* Append new content to the destination buffer. */
4126 i = 0;
4127 while (i < (int)stringsz) {
19
Assuming 'i' is < 'stringsz'
20
Loop condition is true. Entering loop body
4128 /*
4129 * Rudimentary roff copy mode:
4130 * Handle escaped backslashes.
4131 */
4132 if ('\\' == string[i] && '\\' == string[i + 1])
21
Use of memory after it is freed
4133 i++;
4134 *c++ = string[i++];
4135 }
4136
4137 /* Append terminating bytes. */
4138 if (1 < append)
4139 *c++ = '\n';
4140
4141 *c = '\0';
4142 n->val.sz = (int)(c - n->val.p);
4143}
4144
4145static const char *
4146roff_getstrn(struct roff *r, const char *name, size_t len,
4147 int *deftype)
4148{
4149 const struct roffkv *n;
4150 int found, i;
4151 enum roff_tok tok;
4152
4153 found = 0;
4154 for (n = r->strtab; n != NULL((void*)0); n = n->next) {
4155 if (strncmp(name, n->key.p, len) != 0 ||
4156 n->key.p[len] != '\0' || n->val.p == NULL((void*)0))
4157 continue;
4158 if (*deftype & ROFFDEF_USER(1 << 1)) {
4159 *deftype = ROFFDEF_USER(1 << 1);
4160 return n->val.p;
4161 } else {
4162 found = 1;
4163 break;
4164 }
4165 }
4166 for (n = r->rentab; n != NULL((void*)0); n = n->next) {
4167 if (strncmp(name, n->key.p, len) != 0 ||
4168 n->key.p[len] != '\0' || n->val.p == NULL((void*)0))
4169 continue;
4170 if (*deftype & ROFFDEF_REN(1 << 3)) {
4171 *deftype = ROFFDEF_REN(1 << 3);
4172 return n->val.p;
4173 } else {
4174 found = 1;
4175 break;
4176 }
4177 }
4178 for (i = 0; i < PREDEFS_MAX38; i++) {
4179 if (strncmp(name, predefs[i].name, len) != 0 ||
4180 predefs[i].name[len] != '\0')
4181 continue;
4182 if (*deftype & ROFFDEF_PRE(1 << 2)) {
4183 *deftype = ROFFDEF_PRE(1 << 2);
4184 return predefs[i].str;
4185 } else {
4186 found = 1;
4187 break;
4188 }
4189 }
4190 if (r->man->meta.macroset != MACROSET_MAN) {
4191 for (tok = MDOC_Dd; tok < MDOC_MAX; tok++) {
4192 if (strncmp(name, roff_name[tok], len) != 0 ||
4193 roff_name[tok][len] != '\0')
4194 continue;
4195 if (*deftype & ROFFDEF_STD(1 << 4)) {
4196 *deftype = ROFFDEF_STD(1 << 4);
4197 return NULL((void*)0);
4198 } else {
4199 found = 1;
4200 break;
4201 }
4202 }
4203 }
4204 if (r->man->meta.macroset != MACROSET_MDOC) {
4205 for (tok = MAN_TH; tok < MAN_MAX; tok++) {
4206 if (strncmp(name, roff_name[tok], len) != 0 ||
4207 roff_name[tok][len] != '\0')
4208 continue;
4209 if (*deftype & ROFFDEF_STD(1 << 4)) {
4210 *deftype = ROFFDEF_STD(1 << 4);
4211 return NULL((void*)0);
4212 } else {
4213 found = 1;
4214 break;
4215 }
4216 }
4217 }
4218
4219 if (found == 0 && *deftype != ROFFDEF_ANY((1 << 1) | (1 << 2) | (1 << 3) | (1 <<
4))
) {
4220 if (*deftype & ROFFDEF_REN(1 << 3)) {
4221 /*
4222 * This might still be a request,
4223 * so do not treat it as undefined yet.
4224 */
4225 *deftype = ROFFDEF_UNDEF(1 << 5);
4226 return NULL((void*)0);
4227 }
4228
4229 /* Using an undefined string defines it to be empty. */
4230
4231 roff_setstrn(&r->strtab, name, len, "", 0, 0);
4232 roff_setstrn(&r->rentab, name, len, NULL((void*)0), 0, 0);
4233 }
4234
4235 *deftype = 0;
4236 return NULL((void*)0);
4237}
4238
4239static void
4240roff_freestr(struct roffkv *r)
4241{
4242 struct roffkv *n, *nn;
4243
4244 for (n = r; n; n = nn) {
4245 free(n->key.p);
4246 free(n->val.p);
4247 nn = n->next;
4248 free(n);
4249 }
4250}
4251
4252/* --- accessors and utility functions ------------------------------------ */
4253
4254/*
4255 * Duplicate an input string, making the appropriate character
4256 * conversations (as stipulated by `tr') along the way.
4257 * Returns a heap-allocated string with all the replacements made.
4258 */
4259char *
4260roff_strdup(const struct roff *r, const char *p)
4261{
4262 const struct roffkv *cp;
4263 char *res;
4264 const char *pp;
4265 size_t ssz, sz;
4266 enum mandoc_esc esc;
4267
4268 if (NULL((void*)0) == r->xmbtab && NULL((void*)0) == r->xtab)
4269 return mandoc_strdup(p);
4270 else if ('\0' == *p)
4271 return mandoc_strdup("");
4272
4273 /*
4274 * Step through each character looking for term matches
4275 * (remember that a `tr' can be invoked with an escape, which is
4276 * a glyph but the escape is multi-character).
4277 * We only do this if the character hash has been initialised
4278 * and the string is >0 length.
4279 */
4280
4281 res = NULL((void*)0);
4282 ssz = 0;
4283
4284 while ('\0' != *p) {
4285 assert((unsigned int)*p < 128)(((unsigned int)*p < 128) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/roff.c"
, 4285, __func__, "(unsigned int)*p < 128"))
;
4286 if ('\\' != *p && r->xtab && r->xtab[(unsigned int)*p].p) {
4287 sz = r->xtab[(int)*p].sz;
4288 res = mandoc_realloc(res, ssz + sz + 1);
4289 memcpy(res + ssz, r->xtab[(int)*p].p, sz);
4290 ssz += sz;
4291 p++;
4292 continue;
4293 } else if ('\\' != *p) {
4294 res = mandoc_realloc(res, ssz + 2);
4295 res[ssz++] = *p++;
4296 continue;
4297 }
4298
4299 /* Search for term matches. */
4300 for (cp = r->xmbtab; cp; cp = cp->next)
4301 if (0 == strncmp(p, cp->key.p, cp->key.sz))
4302 break;
4303
4304 if (NULL((void*)0) != cp) {
4305 /*
4306 * A match has been found.
4307 * Append the match to the array and move
4308 * forward by its keysize.
4309 */
4310 res = mandoc_realloc(res,
4311 ssz + cp->val.sz + 1);
4312 memcpy(res + ssz, cp->val.p, cp->val.sz);
4313 ssz += cp->val.sz;
4314 p += (int)cp->key.sz;
4315 continue;
4316 }
4317
4318 /*
4319 * Handle escapes carefully: we need to copy
4320 * over just the escape itself, or else we might
4321 * do replacements within the escape itself.
4322 * Make sure to pass along the bogus string.
4323 */
4324 pp = p++;
4325 esc = mandoc_escape(&p, NULL((void*)0), NULL((void*)0));
4326 if (ESCAPE_ERROR == esc) {
4327 sz = strlen(pp);
4328 res = mandoc_realloc(res, ssz + sz + 1);
4329 memcpy(res + ssz, pp, sz);
4330 break;
4331 }
4332 /*
4333 * We bail out on bad escapes.
4334 * No need to warn: we already did so when
4335 * roff_expand() was called.
4336 */
4337 sz = (int)(p - pp);
4338 res = mandoc_realloc(res, ssz + sz + 1);
4339 memcpy(res + ssz, pp, sz);
4340 ssz += sz;
4341 }
4342
4343 res[(int)ssz] = '\0';
4344 return res;
4345}
4346
4347int
4348roff_getformat(const struct roff *r)
4349{
4350
4351 return r->format;
4352}
4353
4354/*
4355 * Find out whether a line is a macro line or not.
4356 * If it is, adjust the current position and return one; if it isn't,
4357 * return zero and don't change the current position.
4358 * If the control character has been set with `.cc', then let that grain
4359 * precedence.
4360 * This is slighly contrary to groff, where using the non-breaking
4361 * control character when `cc' has been invoked will cause the
4362 * non-breaking macro contents to be printed verbatim.
4363 */
4364int
4365roff_getcontrol(const struct roff *r, const char *cp, int *ppos)
4366{
4367 int pos;
4368
4369 pos = *ppos;
4370
4371 if (r->control != '\0' && cp[pos] == r->control)
4372 pos++;
4373 else if (r->control != '\0')
4374 return 0;
4375 else if ('\\' == cp[pos] && '.' == cp[pos + 1])
4376 pos += 2;
4377 else if ('.' == cp[pos] || '\'' == cp[pos])
4378 pos++;
4379 else
4380 return 0;
4381
4382 while (' ' == cp[pos] || '\t' == cp[pos])
4383 pos++;
4384
4385 *ppos = pos;
4386 return 1;
4387}