]> git.cameronkatri.com Git - mandoc.git/blob - roff.c
Minimal implementation of the roff(7) .ce request (center a number
[mandoc.git] / roff.c
1 /* $Id: roff.c,v 1.305 2017/06/06 15:01:04 schwarze Exp $ */
2 /*
3 * Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2010-2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 #include "config.h"
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.h"
32 #include "mandoc_aux.h"
33 #include "mandoc_ohash.h"
34 #include "roff.h"
35 #include "libmandoc.h"
36 #include "roff_int.h"
37 #include "libroff.h"
38
39 /* Maximum number of string expansions per line, to break infinite loops. */
40 #define EXPAND_LIMIT 1000
41
42 /* --- data types --------------------------------------------------------- */
43
44 /*
45 * An incredibly-simple string buffer.
46 */
47 struct roffstr {
48 char *p; /* nil-terminated buffer */
49 size_t sz; /* saved strlen(p) */
50 };
51
52 /*
53 * A key-value roffstr pair as part of a singly-linked list.
54 */
55 struct roffkv {
56 struct roffstr key;
57 struct roffstr val;
58 struct roffkv *next; /* next in list */
59 };
60
61 /*
62 * A single number register as part of a singly-linked list.
63 */
64 struct roffreg {
65 struct roffstr key;
66 int val;
67 struct roffreg *next;
68 };
69
70 /*
71 * Association of request and macro names with token IDs.
72 */
73 struct roffreq {
74 enum roff_tok tok;
75 char name[];
76 };
77
78 struct roff {
79 struct mparse *parse; /* parse point */
80 struct roff_man *man; /* mdoc or man parser */
81 struct roffnode *last; /* leaf of stack */
82 int *rstack; /* stack of inverted `ie' values */
83 struct ohash *reqtab; /* request lookup table */
84 struct roffreg *regtab; /* number registers */
85 struct roffkv *strtab; /* user-defined strings & macros */
86 struct roffkv *xmbtab; /* multi-byte trans table (`tr') */
87 struct roffstr *xtab; /* single-byte trans table (`tr') */
88 const char *current_string; /* value of last called user macro */
89 struct tbl_node *first_tbl; /* first table parsed */
90 struct tbl_node *last_tbl; /* last table parsed */
91 struct tbl_node *tbl; /* current table being parsed */
92 struct eqn_node *last_eqn; /* last equation parsed */
93 struct eqn_node *first_eqn; /* first equation parsed */
94 struct eqn_node *eqn; /* current equation being parsed */
95 int eqn_inline; /* current equation is inline */
96 int options; /* parse options */
97 int rstacksz; /* current size limit of rstack */
98 int rstackpos; /* position in rstack */
99 int format; /* current file in mdoc or man format */
100 int argc; /* number of args of the last macro */
101 char control; /* control character */
102 char escape; /* escape character */
103 };
104
105 struct roffnode {
106 enum roff_tok tok; /* type of node */
107 struct roffnode *parent; /* up one in stack */
108 int line; /* parse line */
109 int col; /* parse col */
110 char *name; /* node name, e.g. macro name */
111 char *end; /* end-rules: custom token */
112 int endspan; /* end-rules: next-line or infty */
113 int rule; /* current evaluation rule */
114 };
115
116 #define ROFF_ARGS struct roff *r, /* parse ctx */ \
117 enum roff_tok tok, /* tok of macro */ \
118 struct buf *buf, /* input buffer */ \
119 int ln, /* parse line */ \
120 int ppos, /* original pos in buffer */ \
121 int pos, /* current pos in buffer */ \
122 int *offs /* reset offset of buffer data */
123
124 typedef enum rofferr (*roffproc)(ROFF_ARGS);
125
126 struct roffmac {
127 roffproc proc; /* process new macro */
128 roffproc text; /* process as child text of macro */
129 roffproc sub; /* process as child of macro */
130 int flags;
131 #define ROFFMAC_STRUCT (1 << 0) /* always interpret */
132 };
133
134 struct predef {
135 const char *name; /* predefined input name */
136 const char *str; /* replacement symbol */
137 };
138
139 #define PREDEF(__name, __str) \
140 { (__name), (__str) },
141
142 /* --- function prototypes ------------------------------------------------ */
143
144 static void roffnode_cleanscope(struct roff *);
145 static void roffnode_pop(struct roff *);
146 static void roffnode_push(struct roff *, enum roff_tok,
147 const char *, int, int);
148 static enum rofferr roff_block(ROFF_ARGS);
149 static enum rofferr roff_block_text(ROFF_ARGS);
150 static enum rofferr roff_block_sub(ROFF_ARGS);
151 static enum rofferr roff_br(ROFF_ARGS);
152 static enum rofferr roff_cblock(ROFF_ARGS);
153 static enum rofferr roff_cc(ROFF_ARGS);
154 static void roff_ccond(struct roff *, int, int);
155 static enum rofferr roff_cond(ROFF_ARGS);
156 static enum rofferr roff_cond_text(ROFF_ARGS);
157 static enum rofferr roff_cond_sub(ROFF_ARGS);
158 static enum rofferr roff_ds(ROFF_ARGS);
159 static enum rofferr roff_ec(ROFF_ARGS);
160 static enum rofferr roff_eo(ROFF_ARGS);
161 static enum rofferr roff_eqndelim(struct roff *, struct buf *, int);
162 static int roff_evalcond(struct roff *r, int, char *, int *);
163 static int roff_evalnum(struct roff *, int,
164 const char *, int *, int *, int);
165 static int roff_evalpar(struct roff *, int,
166 const char *, int *, int *, int);
167 static int roff_evalstrcond(const char *, int *);
168 static void roff_free1(struct roff *);
169 static void roff_freereg(struct roffreg *);
170 static void roff_freestr(struct roffkv *);
171 static size_t roff_getname(struct roff *, char **, int, int);
172 static int roff_getnum(const char *, int *, int *, int);
173 static int roff_getop(const char *, int *, char *);
174 static int roff_getregn(const struct roff *,
175 const char *, size_t);
176 static int roff_getregro(const struct roff *,
177 const char *name);
178 static const char *roff_getstrn(const struct roff *,
179 const char *, size_t);
180 static int roff_hasregn(const struct roff *,
181 const char *, size_t);
182 static enum rofferr roff_insec(ROFF_ARGS);
183 static enum rofferr roff_it(ROFF_ARGS);
184 static enum rofferr roff_line_ignore(ROFF_ARGS);
185 static void roff_man_alloc1(struct roff_man *);
186 static void roff_man_free1(struct roff_man *);
187 static enum rofferr roff_manyarg(ROFF_ARGS);
188 static enum rofferr roff_nr(ROFF_ARGS);
189 static enum rofferr roff_onearg(ROFF_ARGS);
190 static enum roff_tok roff_parse(struct roff *, char *, int *,
191 int, int);
192 static enum rofferr roff_parsetext(struct roff *, struct buf *,
193 int, int *);
194 static enum rofferr roff_res(struct roff *, struct buf *, int, int);
195 static enum rofferr roff_rm(ROFF_ARGS);
196 static enum rofferr roff_rr(ROFF_ARGS);
197 static void roff_setstr(struct roff *,
198 const char *, const char *, int);
199 static void roff_setstrn(struct roffkv **, const char *,
200 size_t, const char *, size_t, int);
201 static enum rofferr roff_so(ROFF_ARGS);
202 static enum rofferr roff_tr(ROFF_ARGS);
203 static enum rofferr roff_Dd(ROFF_ARGS);
204 static enum rofferr roff_TH(ROFF_ARGS);
205 static enum rofferr roff_TE(ROFF_ARGS);
206 static enum rofferr roff_TS(ROFF_ARGS);
207 static enum rofferr roff_EQ(ROFF_ARGS);
208 static enum rofferr roff_EN(ROFF_ARGS);
209 static enum rofferr roff_T_(ROFF_ARGS);
210 static enum rofferr roff_unsupp(ROFF_ARGS);
211 static enum rofferr roff_userdef(ROFF_ARGS);
212
213 /* --- constant data ------------------------------------------------------ */
214
215 #define ROFFNUM_SCALE (1 << 0) /* Honour scaling in roff_getnum(). */
216 #define ROFFNUM_WHITE (1 << 1) /* Skip whitespace in roff_evalnum(). */
217
218 const char *__roff_name[MAN_MAX + 1] = {
219 "br", "ce", "ft", "ll",
220 "mc", "sp", "ta", "ti",
221 NULL,
222 "ab", "ad", "af", "aln",
223 "als", "am", "am1", "ami",
224 "ami1", "as", "as1", "asciify",
225 "backtrace", "bd", "bleedat", "blm",
226 "box", "boxa", "bp", "BP",
227 "break", "breakchar", "brnl", "brp",
228 "brpnl", "c2", "cc",
229 "cf", "cflags", "ch", "char",
230 "chop", "class", "close", "CL",
231 "color", "composite", "continue", "cp",
232 "cropat", "cs", "cu", "da",
233 "dch", "Dd", "de", "de1",
234 "defcolor", "dei", "dei1", "device",
235 "devicem", "di", "do", "ds",
236 "ds1", "dwh", "dt", "ec",
237 "ecr", "ecs", "el", "em",
238 "EN", "eo", "EP", "EQ",
239 "errprint", "ev", "evc", "ex",
240 "fallback", "fam", "fc", "fchar",
241 "fcolor", "fdeferlig", "feature", "fkern",
242 "fl", "flig", "fp", "fps",
243 "fschar", "fspacewidth", "fspecial", "ftr",
244 "fzoom", "gcolor", "hc", "hcode",
245 "hidechar", "hla", "hlm", "hpf",
246 "hpfa", "hpfcode", "hw", "hy",
247 "hylang", "hylen", "hym", "hypp",
248 "hys", "ie", "if", "ig",
249 "index", "it", "itc", "IX",
250 "kern", "kernafter", "kernbefore", "kernpair",
251 "lc", "lc_ctype", "lds", "length",
252 "letadj", "lf", "lg", "lhang",
253 "linetabs", "lnr", "lnrf", "lpfx",
254 "ls", "lsm", "lt",
255 "mediasize", "minss", "mk", "mso",
256 "na", "ne", "nh", "nhychar",
257 "nm", "nn", "nop", "nr",
258 "nrf", "nroff", "ns", "nx",
259 "open", "opena", "os", "output",
260 "padj", "papersize", "pc", "pev",
261 "pi", "PI", "pl", "pm",
262 "pn", "pnr", "po", "ps",
263 "psbb", "pshape", "pso", "ptr",
264 "pvs", "rchar", "rd", "recursionlimit",
265 "return", "rfschar", "rhang", "rj",
266 "rm", "rn", "rnn", "rr",
267 "rs", "rt", "schar", "sentchar",
268 "shc", "shift", "sizes", "so",
269 "spacewidth", "special", "spreadwarn", "ss",
270 "sty", "substring", "sv", "sy",
271 "T&", "tc", "TE",
272 "TH", "tkf", "tl",
273 "tm", "tm1", "tmc", "tr",
274 "track", "transchar", "trf", "trimat",
275 "trin", "trnt", "troff", "TS",
276 "uf", "ul", "unformat", "unwatch",
277 "unwatchn", "vpt", "vs", "warn",
278 "warnscale", "watch", "watchlength", "watchn",
279 "wh", "while", "write", "writec",
280 "writem", "xflag", ".", NULL,
281 "text",
282 "Dd", "Dt", "Os", "Sh",
283 "Ss", "Pp", "D1", "Dl",
284 "Bd", "Ed", "Bl", "El",
285 "It", "Ad", "An", "Ap",
286 "Ar", "Cd", "Cm", "Dv",
287 "Er", "Ev", "Ex", "Fa",
288 "Fd", "Fl", "Fn", "Ft",
289 "Ic", "In", "Li", "Nd",
290 "Nm", "Op", "Ot", "Pa",
291 "Rv", "St", "Va", "Vt",
292 "Xr", "%A", "%B", "%D",
293 "%I", "%J", "%N", "%O",
294 "%P", "%R", "%T", "%V",
295 "Ac", "Ao", "Aq", "At",
296 "Bc", "Bf", "Bo", "Bq",
297 "Bsx", "Bx", "Db", "Dc",
298 "Do", "Dq", "Ec", "Ef",
299 "Em", "Eo", "Fx", "Ms",
300 "No", "Ns", "Nx", "Ox",
301 "Pc", "Pf", "Po", "Pq",
302 "Qc", "Ql", "Qo", "Qq",
303 "Re", "Rs", "Sc", "So",
304 "Sq", "Sm", "Sx", "Sy",
305 "Tn", "Ux", "Xc", "Xo",
306 "Fo", "Fc", "Oo", "Oc",
307 "Bk", "Ek", "Bt", "Hf",
308 "Fr", "Ud", "Lb", "Lp",
309 "Lk", "Mt", "Brq", "Bro",
310 "Brc", "%C", "Es", "En",
311 "Dx", "%Q", "%U", "Ta",
312 NULL,
313 "TH", "SH", "SS", "TP",
314 "LP", "PP", "P", "IP",
315 "HP", "SM", "SB", "BI",
316 "IB", "BR", "RB", "R",
317 "B", "I", "IR", "RI",
318 "nf", "fi",
319 "RE", "RS", "DT", "UC",
320 "PD", "AT", "in",
321 "OP", "EX", "EE", "UR",
322 "UE", NULL
323 };
324 const char *const *roff_name = __roff_name;
325
326 static struct roffmac roffs[TOKEN_NONE] = {
327 { roff_br, NULL, NULL, 0 }, /* br */
328 { roff_onearg, NULL, NULL, 0 }, /* ce */
329 { roff_onearg, NULL, NULL, 0 }, /* ft */
330 { roff_onearg, NULL, NULL, 0 }, /* ll */
331 { roff_onearg, NULL, NULL, 0 }, /* mc */
332 { roff_onearg, NULL, NULL, 0 }, /* sp */
333 { roff_manyarg, NULL, NULL, 0 }, /* ta */
334 { roff_onearg, NULL, NULL, 0 }, /* ti */
335 { NULL, NULL, NULL, 0 }, /* ROFF_MAX */
336 { roff_unsupp, NULL, NULL, 0 }, /* ab */
337 { roff_line_ignore, NULL, NULL, 0 }, /* ad */
338 { roff_line_ignore, NULL, NULL, 0 }, /* af */
339 { roff_unsupp, NULL, NULL, 0 }, /* aln */
340 { roff_unsupp, NULL, NULL, 0 }, /* als */
341 { roff_block, roff_block_text, roff_block_sub, 0 }, /* am */
342 { roff_block, roff_block_text, roff_block_sub, 0 }, /* am1 */
343 { roff_block, roff_block_text, roff_block_sub, 0 }, /* ami */
344 { roff_block, roff_block_text, roff_block_sub, 0 }, /* ami1 */
345 { roff_ds, NULL, NULL, 0 }, /* as */
346 { roff_ds, NULL, NULL, 0 }, /* as1 */
347 { roff_unsupp, NULL, NULL, 0 }, /* asciify */
348 { roff_line_ignore, NULL, NULL, 0 }, /* backtrace */
349 { roff_line_ignore, NULL, NULL, 0 }, /* bd */
350 { roff_line_ignore, NULL, NULL, 0 }, /* bleedat */
351 { roff_unsupp, NULL, NULL, 0 }, /* blm */
352 { roff_unsupp, NULL, NULL, 0 }, /* box */
353 { roff_unsupp, NULL, NULL, 0 }, /* boxa */
354 { roff_line_ignore, NULL, NULL, 0 }, /* bp */
355 { roff_unsupp, NULL, NULL, 0 }, /* BP */
356 { roff_unsupp, NULL, NULL, 0 }, /* break */
357 { roff_line_ignore, NULL, NULL, 0 }, /* breakchar */
358 { roff_line_ignore, NULL, NULL, 0 }, /* brnl */
359 { roff_br, NULL, NULL, 0 }, /* brp */
360 { roff_line_ignore, NULL, NULL, 0 }, /* brpnl */
361 { roff_unsupp, NULL, NULL, 0 }, /* c2 */
362 { roff_cc, NULL, NULL, 0 }, /* cc */
363 { roff_insec, NULL, NULL, 0 }, /* cf */
364 { roff_line_ignore, NULL, NULL, 0 }, /* cflags */
365 { roff_line_ignore, NULL, NULL, 0 }, /* ch */
366 { roff_unsupp, NULL, NULL, 0 }, /* char */
367 { roff_unsupp, NULL, NULL, 0 }, /* chop */
368 { roff_line_ignore, NULL, NULL, 0 }, /* class */
369 { roff_insec, NULL, NULL, 0 }, /* close */
370 { roff_unsupp, NULL, NULL, 0 }, /* CL */
371 { roff_line_ignore, NULL, NULL, 0 }, /* color */
372 { roff_unsupp, NULL, NULL, 0 }, /* composite */
373 { roff_unsupp, NULL, NULL, 0 }, /* continue */
374 { roff_line_ignore, NULL, NULL, 0 }, /* cp */
375 { roff_line_ignore, NULL, NULL, 0 }, /* cropat */
376 { roff_line_ignore, NULL, NULL, 0 }, /* cs */
377 { roff_line_ignore, NULL, NULL, 0 }, /* cu */
378 { roff_unsupp, NULL, NULL, 0 }, /* da */
379 { roff_unsupp, NULL, NULL, 0 }, /* dch */
380 { roff_Dd, NULL, NULL, 0 }, /* Dd */
381 { roff_block, roff_block_text, roff_block_sub, 0 }, /* de */
382 { roff_block, roff_block_text, roff_block_sub, 0 }, /* de1 */
383 { roff_line_ignore, NULL, NULL, 0 }, /* defcolor */
384 { roff_block, roff_block_text, roff_block_sub, 0 }, /* dei */
385 { roff_block, roff_block_text, roff_block_sub, 0 }, /* dei1 */
386 { roff_unsupp, NULL, NULL, 0 }, /* device */
387 { roff_unsupp, NULL, NULL, 0 }, /* devicem */
388 { roff_unsupp, NULL, NULL, 0 }, /* di */
389 { roff_unsupp, NULL, NULL, 0 }, /* do */
390 { roff_ds, NULL, NULL, 0 }, /* ds */
391 { roff_ds, NULL, NULL, 0 }, /* ds1 */
392 { roff_unsupp, NULL, NULL, 0 }, /* dwh */
393 { roff_unsupp, NULL, NULL, 0 }, /* dt */
394 { roff_ec, NULL, NULL, 0 }, /* ec */
395 { roff_unsupp, NULL, NULL, 0 }, /* ecr */
396 { roff_unsupp, NULL, NULL, 0 }, /* ecs */
397 { roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT }, /* el */
398 { roff_unsupp, NULL, NULL, 0 }, /* em */
399 { roff_EN, NULL, NULL, 0 }, /* EN */
400 { roff_eo, NULL, NULL, 0 }, /* eo */
401 { roff_unsupp, NULL, NULL, 0 }, /* EP */
402 { roff_EQ, NULL, NULL, 0 }, /* EQ */
403 { roff_line_ignore, NULL, NULL, 0 }, /* errprint */
404 { roff_unsupp, NULL, NULL, 0 }, /* ev */
405 { roff_unsupp, NULL, NULL, 0 }, /* evc */
406 { roff_unsupp, NULL, NULL, 0 }, /* ex */
407 { roff_line_ignore, NULL, NULL, 0 }, /* fallback */
408 { roff_line_ignore, NULL, NULL, 0 }, /* fam */
409 { roff_unsupp, NULL, NULL, 0 }, /* fc */
410 { roff_unsupp, NULL, NULL, 0 }, /* fchar */
411 { roff_line_ignore, NULL, NULL, 0 }, /* fcolor */
412 { roff_line_ignore, NULL, NULL, 0 }, /* fdeferlig */
413 { roff_line_ignore, NULL, NULL, 0 }, /* feature */
414 { roff_line_ignore, NULL, NULL, 0 }, /* fkern */
415 { roff_line_ignore, NULL, NULL, 0 }, /* fl */
416 { roff_line_ignore, NULL, NULL, 0 }, /* flig */
417 { roff_line_ignore, NULL, NULL, 0 }, /* fp */
418 { roff_line_ignore, NULL, NULL, 0 }, /* fps */
419 { roff_unsupp, NULL, NULL, 0 }, /* fschar */
420 { roff_line_ignore, NULL, NULL, 0 }, /* fspacewidth */
421 { roff_line_ignore, NULL, NULL, 0 }, /* fspecial */
422 { roff_line_ignore, NULL, NULL, 0 }, /* ftr */
423 { roff_line_ignore, NULL, NULL, 0 }, /* fzoom */
424 { roff_line_ignore, NULL, NULL, 0 }, /* gcolor */
425 { roff_line_ignore, NULL, NULL, 0 }, /* hc */
426 { roff_line_ignore, NULL, NULL, 0 }, /* hcode */
427 { roff_line_ignore, NULL, NULL, 0 }, /* hidechar */
428 { roff_line_ignore, NULL, NULL, 0 }, /* hla */
429 { roff_line_ignore, NULL, NULL, 0 }, /* hlm */
430 { roff_line_ignore, NULL, NULL, 0 }, /* hpf */
431 { roff_line_ignore, NULL, NULL, 0 }, /* hpfa */
432 { roff_line_ignore, NULL, NULL, 0 }, /* hpfcode */
433 { roff_line_ignore, NULL, NULL, 0 }, /* hw */
434 { roff_line_ignore, NULL, NULL, 0 }, /* hy */
435 { roff_line_ignore, NULL, NULL, 0 }, /* hylang */
436 { roff_line_ignore, NULL, NULL, 0 }, /* hylen */
437 { roff_line_ignore, NULL, NULL, 0 }, /* hym */
438 { roff_line_ignore, NULL, NULL, 0 }, /* hypp */
439 { roff_line_ignore, NULL, NULL, 0 }, /* hys */
440 { roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT }, /* ie */
441 { roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT }, /* if */
442 { roff_block, roff_block_text, roff_block_sub, 0 }, /* ig */
443 { roff_unsupp, NULL, NULL, 0 }, /* index */
444 { roff_it, NULL, NULL, 0 }, /* it */
445 { roff_unsupp, NULL, NULL, 0 }, /* itc */
446 { roff_line_ignore, NULL, NULL, 0 }, /* IX */
447 { roff_line_ignore, NULL, NULL, 0 }, /* kern */
448 { roff_line_ignore, NULL, NULL, 0 }, /* kernafter */
449 { roff_line_ignore, NULL, NULL, 0 }, /* kernbefore */
450 { roff_line_ignore, NULL, NULL, 0 }, /* kernpair */
451 { roff_unsupp, NULL, NULL, 0 }, /* lc */
452 { roff_unsupp, NULL, NULL, 0 }, /* lc_ctype */
453 { roff_unsupp, NULL, NULL, 0 }, /* lds */
454 { roff_unsupp, NULL, NULL, 0 }, /* length */
455 { roff_line_ignore, NULL, NULL, 0 }, /* letadj */
456 { roff_insec, NULL, NULL, 0 }, /* lf */
457 { roff_line_ignore, NULL, NULL, 0 }, /* lg */
458 { roff_line_ignore, NULL, NULL, 0 }, /* lhang */
459 { roff_unsupp, NULL, NULL, 0 }, /* linetabs */
460 { roff_unsupp, NULL, NULL, 0 }, /* lnr */
461 { roff_unsupp, NULL, NULL, 0 }, /* lnrf */
462 { roff_unsupp, NULL, NULL, 0 }, /* lpfx */
463 { roff_line_ignore, NULL, NULL, 0 }, /* ls */
464 { roff_unsupp, NULL, NULL, 0 }, /* lsm */
465 { roff_line_ignore, NULL, NULL, 0 }, /* lt */
466 { roff_line_ignore, NULL, NULL, 0 }, /* mediasize */
467 { roff_line_ignore, NULL, NULL, 0 }, /* minss */
468 { roff_line_ignore, NULL, NULL, 0 }, /* mk */
469 { roff_insec, NULL, NULL, 0 }, /* mso */
470 { roff_line_ignore, NULL, NULL, 0 }, /* na */
471 { roff_line_ignore, NULL, NULL, 0 }, /* ne */
472 { roff_line_ignore, NULL, NULL, 0 }, /* nh */
473 { roff_line_ignore, NULL, NULL, 0 }, /* nhychar */
474 { roff_unsupp, NULL, NULL, 0 }, /* nm */
475 { roff_unsupp, NULL, NULL, 0 }, /* nn */
476 { roff_unsupp, NULL, NULL, 0 }, /* nop */
477 { roff_nr, NULL, NULL, 0 }, /* nr */
478 { roff_unsupp, NULL, NULL, 0 }, /* nrf */
479 { roff_line_ignore, NULL, NULL, 0 }, /* nroff */
480 { roff_line_ignore, NULL, NULL, 0 }, /* ns */
481 { roff_insec, NULL, NULL, 0 }, /* nx */
482 { roff_insec, NULL, NULL, 0 }, /* open */
483 { roff_insec, NULL, NULL, 0 }, /* opena */
484 { roff_line_ignore, NULL, NULL, 0 }, /* os */
485 { roff_unsupp, NULL, NULL, 0 }, /* output */
486 { roff_line_ignore, NULL, NULL, 0 }, /* padj */
487 { roff_line_ignore, NULL, NULL, 0 }, /* papersize */
488 { roff_line_ignore, NULL, NULL, 0 }, /* pc */
489 { roff_line_ignore, NULL, NULL, 0 }, /* pev */
490 { roff_insec, NULL, NULL, 0 }, /* pi */
491 { roff_unsupp, NULL, NULL, 0 }, /* PI */
492 { roff_line_ignore, NULL, NULL, 0 }, /* pl */
493 { roff_line_ignore, NULL, NULL, 0 }, /* pm */
494 { roff_line_ignore, NULL, NULL, 0 }, /* pn */
495 { roff_line_ignore, NULL, NULL, 0 }, /* pnr */
496 { roff_line_ignore, NULL, NULL, 0 }, /* po */
497 { roff_line_ignore, NULL, NULL, 0 }, /* ps */
498 { roff_unsupp, NULL, NULL, 0 }, /* psbb */
499 { roff_unsupp, NULL, NULL, 0 }, /* pshape */
500 { roff_insec, NULL, NULL, 0 }, /* pso */
501 { roff_line_ignore, NULL, NULL, 0 }, /* ptr */
502 { roff_line_ignore, NULL, NULL, 0 }, /* pvs */
503 { roff_unsupp, NULL, NULL, 0 }, /* rchar */
504 { roff_line_ignore, NULL, NULL, 0 }, /* rd */
505 { roff_line_ignore, NULL, NULL, 0 }, /* recursionlimit */
506 { roff_unsupp, NULL, NULL, 0 }, /* return */
507 { roff_unsupp, NULL, NULL, 0 }, /* rfschar */
508 { roff_line_ignore, NULL, NULL, 0 }, /* rhang */
509 { roff_line_ignore, NULL, NULL, 0 }, /* rj */
510 { roff_rm, NULL, NULL, 0 }, /* rm */
511 { roff_unsupp, NULL, NULL, 0 }, /* rn */
512 { roff_unsupp, NULL, NULL, 0 }, /* rnn */
513 { roff_rr, NULL, NULL, 0 }, /* rr */
514 { roff_line_ignore, NULL, NULL, 0 }, /* rs */
515 { roff_line_ignore, NULL, NULL, 0 }, /* rt */
516 { roff_unsupp, NULL, NULL, 0 }, /* schar */
517 { roff_line_ignore, NULL, NULL, 0 }, /* sentchar */
518 { roff_line_ignore, NULL, NULL, 0 }, /* shc */
519 { roff_unsupp, NULL, NULL, 0 }, /* shift */
520 { roff_line_ignore, NULL, NULL, 0 }, /* sizes */
521 { roff_so, NULL, NULL, 0 }, /* so */
522 { roff_line_ignore, NULL, NULL, 0 }, /* spacewidth */
523 { roff_line_ignore, NULL, NULL, 0 }, /* special */
524 { roff_line_ignore, NULL, NULL, 0 }, /* spreadwarn */
525 { roff_line_ignore, NULL, NULL, 0 }, /* ss */
526 { roff_line_ignore, NULL, NULL, 0 }, /* sty */
527 { roff_unsupp, NULL, NULL, 0 }, /* substring */
528 { roff_line_ignore, NULL, NULL, 0 }, /* sv */
529 { roff_insec, NULL, NULL, 0 }, /* sy */
530 { roff_T_, NULL, NULL, 0 }, /* T& */
531 { roff_unsupp, NULL, NULL, 0 }, /* tc */
532 { roff_TE, NULL, NULL, 0 }, /* TE */
533 { roff_TH, NULL, NULL, 0 }, /* TH */
534 { roff_line_ignore, NULL, NULL, 0 }, /* tkf */
535 { roff_unsupp, NULL, NULL, 0 }, /* tl */
536 { roff_line_ignore, NULL, NULL, 0 }, /* tm */
537 { roff_line_ignore, NULL, NULL, 0 }, /* tm1 */
538 { roff_line_ignore, NULL, NULL, 0 }, /* tmc */
539 { roff_tr, NULL, NULL, 0 }, /* tr */
540 { roff_line_ignore, NULL, NULL, 0 }, /* track */
541 { roff_line_ignore, NULL, NULL, 0 }, /* transchar */
542 { roff_insec, NULL, NULL, 0 }, /* trf */
543 { roff_line_ignore, NULL, NULL, 0 }, /* trimat */
544 { roff_unsupp, NULL, NULL, 0 }, /* trin */
545 { roff_unsupp, NULL, NULL, 0 }, /* trnt */
546 { roff_line_ignore, NULL, NULL, 0 }, /* troff */
547 { roff_TS, NULL, NULL, 0 }, /* TS */
548 { roff_line_ignore, NULL, NULL, 0 }, /* uf */
549 { roff_line_ignore, NULL, NULL, 0 }, /* ul */
550 { roff_unsupp, NULL, NULL, 0 }, /* unformat */
551 { roff_line_ignore, NULL, NULL, 0 }, /* unwatch */
552 { roff_line_ignore, NULL, NULL, 0 }, /* unwatchn */
553 { roff_line_ignore, NULL, NULL, 0 }, /* vpt */
554 { roff_line_ignore, NULL, NULL, 0 }, /* vs */
555 { roff_line_ignore, NULL, NULL, 0 }, /* warn */
556 { roff_line_ignore, NULL, NULL, 0 }, /* warnscale */
557 { roff_line_ignore, NULL, NULL, 0 }, /* watch */
558 { roff_line_ignore, NULL, NULL, 0 }, /* watchlength */
559 { roff_line_ignore, NULL, NULL, 0 }, /* watchn */
560 { roff_unsupp, NULL, NULL, 0 }, /* wh */
561 { roff_unsupp, NULL, NULL, 0 }, /* while */
562 { roff_insec, NULL, NULL, 0 }, /* write */
563 { roff_insec, NULL, NULL, 0 }, /* writec */
564 { roff_insec, NULL, NULL, 0 }, /* writem */
565 { roff_line_ignore, NULL, NULL, 0 }, /* xflag */
566 { roff_cblock, NULL, NULL, 0 }, /* . */
567 { roff_userdef, NULL, NULL, 0 }
568 };
569
570 /* not currently implemented: Ds em Eq LP Me PP pp Or Rd Sf SH */
571 const char *const __mdoc_reserved[] = {
572 "Ac", "Ad", "An", "Ao", "Ap", "Aq", "Ar", "At",
573 "Bc", "Bd", "Bf", "Bk", "Bl", "Bo", "Bq",
574 "Brc", "Bro", "Brq", "Bsx", "Bt", "Bx",
575 "Cd", "Cm", "Db", "Dc", "Dd", "Dl", "Do", "Dq",
576 "Dt", "Dv", "Dx", "D1",
577 "Ec", "Ed", "Ef", "Ek", "El", "Em",
578 "En", "Eo", "Er", "Es", "Ev", "Ex",
579 "Fa", "Fc", "Fd", "Fl", "Fn", "Fo", "Fr", "Ft", "Fx",
580 "Hf", "Ic", "In", "It", "Lb", "Li", "Lk", "Lp",
581 "Ms", "Mt", "Nd", "Nm", "No", "Ns", "Nx",
582 "Oc", "Oo", "Op", "Os", "Ot", "Ox",
583 "Pa", "Pc", "Pf", "Po", "Pp", "Pq",
584 "Qc", "Ql", "Qo", "Qq", "Re", "Rs", "Rv",
585 "Sc", "Sh", "Sm", "So", "Sq",
586 "Ss", "St", "Sx", "Sy",
587 "Ta", "Tn", "Ud", "Ux", "Va", "Vt", "Xc", "Xo", "Xr",
588 "%A", "%B", "%C", "%D", "%I", "%J", "%N", "%O",
589 "%P", "%Q", "%R", "%T", "%U", "%V",
590 NULL
591 };
592
593 /* not currently implemented: BT DE DS ME MT PT SY TQ YS */
594 const char *const __man_reserved[] = {
595 "AT", "B", "BI", "BR", "DT",
596 "EE", "EN", "EQ", "EX", "HP", "I", "IB", "IP", "IR",
597 "LP", "OP", "P", "PD", "PP",
598 "R", "RB", "RE", "RI", "RS", "SB", "SH", "SM", "SS",
599 "TE", "TH", "TP", "TS", "T&", "UC", "UE", "UR",
600 NULL
601 };
602
603 /* Array of injected predefined strings. */
604 #define PREDEFS_MAX 38
605 static const struct predef predefs[PREDEFS_MAX] = {
606 #include "predefs.in"
607 };
608
609 static int roffce_lines; /* number of input lines to center */
610 static struct roff_node *roffce_node; /* active request */
611 static int roffit_lines; /* number of lines to delay */
612 static char *roffit_macro; /* nil-terminated macro line */
613
614
615 /* --- request table ------------------------------------------------------ */
616
617 struct ohash *
618 roffhash_alloc(enum roff_tok mintok, enum roff_tok maxtok)
619 {
620 struct ohash *htab;
621 struct roffreq *req;
622 enum roff_tok tok;
623 size_t sz;
624 unsigned int slot;
625
626 htab = mandoc_malloc(sizeof(*htab));
627 mandoc_ohash_init(htab, 8, offsetof(struct roffreq, name));
628
629 for (tok = mintok; tok < maxtok; tok++) {
630 if (roff_name[tok] == NULL)
631 continue;
632 sz = strlen(roff_name[tok]);
633 req = mandoc_malloc(sizeof(*req) + sz + 1);
634 req->tok = tok;
635 memcpy(req->name, roff_name[tok], sz + 1);
636 slot = ohash_qlookup(htab, req->name);
637 ohash_insert(htab, slot, req);
638 }
639 return htab;
640 }
641
642 void
643 roffhash_free(struct ohash *htab)
644 {
645 struct roffreq *req;
646 unsigned int slot;
647
648 if (htab == NULL)
649 return;
650 for (req = ohash_first(htab, &slot); req != NULL;
651 req = ohash_next(htab, &slot))
652 free(req);
653 ohash_delete(htab);
654 free(htab);
655 }
656
657 enum roff_tok
658 roffhash_find(struct ohash *htab, const char *name, size_t sz)
659 {
660 struct roffreq *req;
661 const char *end;
662
663 if (sz) {
664 end = name + sz;
665 req = ohash_find(htab, ohash_qlookupi(htab, name, &end));
666 } else
667 req = ohash_find(htab, ohash_qlookup(htab, name));
668 return req == NULL ? TOKEN_NONE : req->tok;
669 }
670
671 /* --- stack of request blocks -------------------------------------------- */
672
673 /*
674 * Pop the current node off of the stack of roff instructions currently
675 * pending.
676 */
677 static void
678 roffnode_pop(struct roff *r)
679 {
680 struct roffnode *p;
681
682 assert(r->last);
683 p = r->last;
684
685 r->last = r->last->parent;
686 free(p->name);
687 free(p->end);
688 free(p);
689 }
690
691 /*
692 * Push a roff node onto the instruction stack. This must later be
693 * removed with roffnode_pop().
694 */
695 static void
696 roffnode_push(struct roff *r, enum roff_tok tok, const char *name,
697 int line, int col)
698 {
699 struct roffnode *p;
700
701 p = mandoc_calloc(1, sizeof(struct roffnode));
702 p->tok = tok;
703 if (name)
704 p->name = mandoc_strdup(name);
705 p->parent = r->last;
706 p->line = line;
707 p->col = col;
708 p->rule = p->parent ? p->parent->rule : 0;
709
710 r->last = p;
711 }
712
713 /* --- roff parser state data management ---------------------------------- */
714
715 static void
716 roff_free1(struct roff *r)
717 {
718 struct tbl_node *tbl;
719 struct eqn_node *e;
720 int i;
721
722 while (NULL != (tbl = r->first_tbl)) {
723 r->first_tbl = tbl->next;
724 tbl_free(tbl);
725 }
726 r->first_tbl = r->last_tbl = r->tbl = NULL;
727
728 while (NULL != (e = r->first_eqn)) {
729 r->first_eqn = e->next;
730 eqn_free(e);
731 }
732 r->first_eqn = r->last_eqn = r->eqn = NULL;
733
734 while (r->last)
735 roffnode_pop(r);
736
737 free (r->rstack);
738 r->rstack = NULL;
739 r->rstacksz = 0;
740 r->rstackpos = -1;
741
742 roff_freereg(r->regtab);
743 r->regtab = NULL;
744
745 roff_freestr(r->strtab);
746 roff_freestr(r->xmbtab);
747 r->strtab = r->xmbtab = NULL;
748
749 if (r->xtab)
750 for (i = 0; i < 128; i++)
751 free(r->xtab[i].p);
752 free(r->xtab);
753 r->xtab = NULL;
754 }
755
756 void
757 roff_reset(struct roff *r)
758 {
759 roff_free1(r);
760 r->format = r->options & (MPARSE_MDOC | MPARSE_MAN);
761 r->control = '\0';
762 r->escape = '\\';
763 }
764
765 void
766 roff_free(struct roff *r)
767 {
768 roff_free1(r);
769 roffhash_free(r->reqtab);
770 free(r);
771 }
772
773 struct roff *
774 roff_alloc(struct mparse *parse, int options)
775 {
776 struct roff *r;
777
778 r = mandoc_calloc(1, sizeof(struct roff));
779 r->parse = parse;
780 r->reqtab = roffhash_alloc(0, ROFF_USERDEF);
781 r->options = options;
782 r->format = options & (MPARSE_MDOC | MPARSE_MAN);
783 r->rstackpos = -1;
784 r->escape = '\\';
785 return r;
786 }
787
788 /* --- syntax tree state data management ---------------------------------- */
789
790 static void
791 roff_man_free1(struct roff_man *man)
792 {
793
794 if (man->first != NULL)
795 roff_node_delete(man, man->first);
796 free(man->meta.msec);
797 free(man->meta.vol);
798 free(man->meta.os);
799 free(man->meta.arch);
800 free(man->meta.title);
801 free(man->meta.name);
802 free(man->meta.date);
803 }
804
805 static void
806 roff_man_alloc1(struct roff_man *man)
807 {
808
809 memset(&man->meta, 0, sizeof(man->meta));
810 man->first = mandoc_calloc(1, sizeof(*man->first));
811 man->first->type = ROFFT_ROOT;
812 man->last = man->first;
813 man->last_es = NULL;
814 man->flags = 0;
815 man->macroset = MACROSET_NONE;
816 man->lastsec = man->lastnamed = SEC_NONE;
817 man->next = ROFF_NEXT_CHILD;
818 }
819
820 void
821 roff_man_reset(struct roff_man *man)
822 {
823
824 roff_man_free1(man);
825 roff_man_alloc1(man);
826 }
827
828 void
829 roff_man_free(struct roff_man *man)
830 {
831
832 roff_man_free1(man);
833 free(man);
834 }
835
836 struct roff_man *
837 roff_man_alloc(struct roff *roff, struct mparse *parse,
838 const char *defos, int quick)
839 {
840 struct roff_man *man;
841
842 man = mandoc_calloc(1, sizeof(*man));
843 man->parse = parse;
844 man->roff = roff;
845 man->defos = defos;
846 man->quick = quick;
847 roff_man_alloc1(man);
848 roff->man = man;
849 return man;
850 }
851
852 /* --- syntax tree handling ----------------------------------------------- */
853
854 struct roff_node *
855 roff_node_alloc(struct roff_man *man, int line, int pos,
856 enum roff_type type, int tok)
857 {
858 struct roff_node *n;
859
860 n = mandoc_calloc(1, sizeof(*n));
861 n->line = line;
862 n->pos = pos;
863 n->tok = tok;
864 n->type = type;
865 n->sec = man->lastsec;
866
867 if (man->flags & MDOC_SYNOPSIS)
868 n->flags |= NODE_SYNPRETTY;
869 else
870 n->flags &= ~NODE_SYNPRETTY;
871 if (man->flags & MDOC_NEWLINE)
872 n->flags |= NODE_LINE;
873 man->flags &= ~MDOC_NEWLINE;
874
875 return n;
876 }
877
878 void
879 roff_node_append(struct roff_man *man, struct roff_node *n)
880 {
881
882 switch (man->next) {
883 case ROFF_NEXT_SIBLING:
884 if (man->last->next != NULL) {
885 n->next = man->last->next;
886 man->last->next->prev = n;
887 } else
888 man->last->parent->last = n;
889 man->last->next = n;
890 n->prev = man->last;
891 n->parent = man->last->parent;
892 break;
893 case ROFF_NEXT_CHILD:
894 if (man->last->child != NULL) {
895 n->next = man->last->child;
896 man->last->child->prev = n;
897 } else
898 man->last->last = n;
899 man->last->child = n;
900 n->parent = man->last;
901 break;
902 default:
903 abort();
904 }
905 man->last = n;
906
907 switch (n->type) {
908 case ROFFT_HEAD:
909 n->parent->head = n;
910 break;
911 case ROFFT_BODY:
912 if (n->end != ENDBODY_NOT)
913 return;
914 n->parent->body = n;
915 break;
916 case ROFFT_TAIL:
917 n->parent->tail = n;
918 break;
919 default:
920 return;
921 }
922
923 /*
924 * Copy over the normalised-data pointer of our parent. Not
925 * everybody has one, but copying a null pointer is fine.
926 */
927
928 n->norm = n->parent->norm;
929 assert(n->parent->type == ROFFT_BLOCK);
930 }
931
932 void
933 roff_word_alloc(struct roff_man *man, int line, int pos, const char *word)
934 {
935 struct roff_node *n;
936
937 n = roff_node_alloc(man, line, pos, ROFFT_TEXT, TOKEN_NONE);
938 n->string = roff_strdup(man->roff, word);
939 roff_node_append(man, n);
940 n->flags |= NODE_VALID | NODE_ENDED;
941 man->next = ROFF_NEXT_SIBLING;
942 }
943
944 void
945 roff_word_append(struct roff_man *man, const char *word)
946 {
947 struct roff_node *n;
948 char *addstr, *newstr;
949
950 n = man->last;
951 addstr = roff_strdup(man->roff, word);
952 mandoc_asprintf(&newstr, "%s %s", n->string, addstr);
953 free(addstr);
954 free(n->string);
955 n->string = newstr;
956 man->next = ROFF_NEXT_SIBLING;
957 }
958
959 void
960 roff_elem_alloc(struct roff_man *man, int line, int pos, int tok)
961 {
962 struct roff_node *n;
963
964 n = roff_node_alloc(man, line, pos, ROFFT_ELEM, tok);
965 roff_node_append(man, n);
966 man->next = ROFF_NEXT_CHILD;
967 }
968
969 struct roff_node *
970 roff_block_alloc(struct roff_man *man, int line, int pos, int tok)
971 {
972 struct roff_node *n;
973
974 n = roff_node_alloc(man, line, pos, ROFFT_BLOCK, tok);
975 roff_node_append(man, n);
976 man->next = ROFF_NEXT_CHILD;
977 return n;
978 }
979
980 struct roff_node *
981 roff_head_alloc(struct roff_man *man, int line, int pos, int tok)
982 {
983 struct roff_node *n;
984
985 n = roff_node_alloc(man, line, pos, ROFFT_HEAD, tok);
986 roff_node_append(man, n);
987 man->next = ROFF_NEXT_CHILD;
988 return n;
989 }
990
991 struct roff_node *
992 roff_body_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_BODY, tok);
997 roff_node_append(man, n);
998 man->next = ROFF_NEXT_CHILD;
999 return n;
1000 }
1001
1002 void
1003 roff_addeqn(struct roff_man *man, const struct eqn *eqn)
1004 {
1005 struct roff_node *n;
1006
1007 n = roff_node_alloc(man, eqn->ln, eqn->pos, ROFFT_EQN, TOKEN_NONE);
1008 n->eqn = eqn;
1009 if (eqn->ln > man->last->line)
1010 n->flags |= NODE_LINE;
1011 roff_node_append(man, n);
1012 man->next = ROFF_NEXT_SIBLING;
1013 }
1014
1015 void
1016 roff_addtbl(struct roff_man *man, const struct tbl_span *tbl)
1017 {
1018 struct roff_node *n;
1019
1020 if (man->macroset == MACROSET_MAN)
1021 man_breakscope(man, ROFF_TS);
1022 n = roff_node_alloc(man, tbl->line, 0, ROFFT_TBL, TOKEN_NONE);
1023 n->span = tbl;
1024 roff_node_append(man, n);
1025 n->flags |= NODE_VALID | NODE_ENDED;
1026 man->next = ROFF_NEXT_SIBLING;
1027 }
1028
1029 void
1030 roff_node_unlink(struct roff_man *man, struct roff_node *n)
1031 {
1032
1033 /* Adjust siblings. */
1034
1035 if (n->prev)
1036 n->prev->next = n->next;
1037 if (n->next)
1038 n->next->prev = n->prev;
1039
1040 /* Adjust parent. */
1041
1042 if (n->parent != NULL) {
1043 if (n->parent->child == n)
1044 n->parent->child = n->next;
1045 if (n->parent->last == n)
1046 n->parent->last = n->prev;
1047 }
1048
1049 /* Adjust parse point. */
1050
1051 if (man == NULL)
1052 return;
1053 if (man->last == n) {
1054 if (n->prev == NULL) {
1055 man->last = n->parent;
1056 man->next = ROFF_NEXT_CHILD;
1057 } else {
1058 man->last = n->prev;
1059 man->next = ROFF_NEXT_SIBLING;
1060 }
1061 }
1062 if (man->first == n)
1063 man->first = NULL;
1064 }
1065
1066 void
1067 roff_node_free(struct roff_node *n)
1068 {
1069
1070 if (n->args != NULL)
1071 mdoc_argv_free(n->args);
1072 if (n->type == ROFFT_BLOCK || n->type == ROFFT_ELEM)
1073 free(n->norm);
1074 free(n->string);
1075 free(n);
1076 }
1077
1078 void
1079 roff_node_delete(struct roff_man *man, struct roff_node *n)
1080 {
1081
1082 while (n->child != NULL)
1083 roff_node_delete(man, n->child);
1084 roff_node_unlink(man, n);
1085 roff_node_free(n);
1086 }
1087
1088 void
1089 deroff(char **dest, const struct roff_node *n)
1090 {
1091 char *cp;
1092 size_t sz;
1093
1094 if (n->type != ROFFT_TEXT) {
1095 for (n = n->child; n != NULL; n = n->next)
1096 deroff(dest, n);
1097 return;
1098 }
1099
1100 /* Skip leading whitespace. */
1101
1102 for (cp = n->string; *cp != '\0'; cp++) {
1103 if (cp[0] == '\\' && cp[1] != '\0' &&
1104 strchr(" %&0^|~", cp[1]) != NULL)
1105 cp++;
1106 else if ( ! isspace((unsigned char)*cp))
1107 break;
1108 }
1109
1110 /* Skip trailing backslash. */
1111
1112 sz = strlen(cp);
1113 if (sz > 0 && cp[sz - 1] == '\\')
1114 sz--;
1115
1116 /* Skip trailing whitespace. */
1117
1118 for (; sz; sz--)
1119 if ( ! isspace((unsigned char)cp[sz-1]))
1120 break;
1121
1122 /* Skip empty strings. */
1123
1124 if (sz == 0)
1125 return;
1126
1127 if (*dest == NULL) {
1128 *dest = mandoc_strndup(cp, sz);
1129 return;
1130 }
1131
1132 mandoc_asprintf(&cp, "%s %*s", *dest, (int)sz, cp);
1133 free(*dest);
1134 *dest = cp;
1135 }
1136
1137 /* --- main functions of the roff parser ---------------------------------- */
1138
1139 /*
1140 * In the current line, expand escape sequences that tend to get
1141 * used in numerical expressions and conditional requests.
1142 * Also check the syntax of the remaining escape sequences.
1143 */
1144 static enum rofferr
1145 roff_res(struct roff *r, struct buf *buf, int ln, int pos)
1146 {
1147 char ubuf[24]; /* buffer to print the number */
1148 const char *start; /* start of the string to process */
1149 char *stesc; /* start of an escape sequence ('\\') */
1150 const char *stnam; /* start of the name, after "[(*" */
1151 const char *cp; /* end of the name, e.g. before ']' */
1152 const char *res; /* the string to be substituted */
1153 char *nbuf; /* new buffer to copy buf->buf to */
1154 size_t maxl; /* expected length of the escape name */
1155 size_t naml; /* actual length of the escape name */
1156 enum mandoc_esc esc; /* type of the escape sequence */
1157 int inaml; /* length returned from mandoc_escape() */
1158 int expand_count; /* to avoid infinite loops */
1159 int npos; /* position in numeric expression */
1160 int arg_complete; /* argument not interrupted by eol */
1161 int done; /* no more input available */
1162 char term; /* character terminating the escape */
1163
1164 /* Search forward for comments. */
1165
1166 done = 0;
1167 start = buf->buf + pos;
1168 for (stesc = buf->buf + pos; *stesc != '\0'; stesc++) {
1169 if (stesc[0] != r->escape || stesc[1] == '\0')
1170 continue;
1171 stesc++;
1172 if (*stesc != '"' && *stesc != '#')
1173 continue;
1174 cp = strchr(stesc--, '\0') - 1;
1175 if (*cp == '\n') {
1176 done = 1;
1177 cp--;
1178 }
1179 if (*cp == ' ' || *cp == '\t')
1180 mandoc_msg(MANDOCERR_SPACE_EOL, r->parse,
1181 ln, cp - buf->buf, NULL);
1182 while (stesc > start && stesc[-1] == ' ')
1183 stesc--;
1184 *stesc = '\0';
1185 break;
1186 }
1187 if (stesc == start)
1188 return ROFF_CONT;
1189 stesc--;
1190
1191 /* Notice the end of the input. */
1192
1193 if (*stesc == '\n') {
1194 *stesc-- = '\0';
1195 done = 1;
1196 }
1197
1198 expand_count = 0;
1199 while (stesc >= start) {
1200
1201 /* Search backwards for the next backslash. */
1202
1203 if (*stesc != r->escape) {
1204 if (*stesc == '\\') {
1205 *stesc = '\0';
1206 buf->sz = mandoc_asprintf(&nbuf, "%s\\e%s",
1207 buf->buf, stesc + 1) + 1;
1208 start = nbuf + pos;
1209 stesc = nbuf + (stesc - buf->buf);
1210 free(buf->buf);
1211 buf->buf = nbuf;
1212 }
1213 stesc--;
1214 continue;
1215 }
1216
1217 /* If it is escaped, skip it. */
1218
1219 for (cp = stesc - 1; cp >= start; cp--)
1220 if (*cp != r->escape)
1221 break;
1222
1223 if ((stesc - cp) % 2 == 0) {
1224 while (stesc > cp)
1225 *stesc-- = '\\';
1226 continue;
1227 } else if (stesc[1] != '\0') {
1228 *stesc = '\\';
1229 } else {
1230 *stesc-- = '\0';
1231 if (done)
1232 continue;
1233 else
1234 return ROFF_APPEND;
1235 }
1236
1237 /* Decide whether to expand or to check only. */
1238
1239 term = '\0';
1240 cp = stesc + 1;
1241 switch (*cp) {
1242 case '*':
1243 res = NULL;
1244 break;
1245 case 'B':
1246 case 'w':
1247 term = cp[1];
1248 /* FALLTHROUGH */
1249 case 'n':
1250 res = ubuf;
1251 break;
1252 default:
1253 esc = mandoc_escape(&cp, &stnam, &inaml);
1254 if (esc == ESCAPE_ERROR ||
1255 (esc == ESCAPE_SPECIAL &&
1256 mchars_spec2cp(stnam, inaml) < 0))
1257 mandoc_vmsg(MANDOCERR_ESC_BAD,
1258 r->parse, ln, (int)(stesc - buf->buf),
1259 "%.*s", (int)(cp - stesc), stesc);
1260 stesc--;
1261 continue;
1262 }
1263
1264 if (EXPAND_LIMIT < ++expand_count) {
1265 mandoc_msg(MANDOCERR_ROFFLOOP, r->parse,
1266 ln, (int)(stesc - buf->buf), NULL);
1267 return ROFF_IGN;
1268 }
1269
1270 /*
1271 * The third character decides the length
1272 * of the name of the string or register.
1273 * Save a pointer to the name.
1274 */
1275
1276 if (term == '\0') {
1277 switch (*++cp) {
1278 case '\0':
1279 maxl = 0;
1280 break;
1281 case '(':
1282 cp++;
1283 maxl = 2;
1284 break;
1285 case '[':
1286 cp++;
1287 term = ']';
1288 maxl = 0;
1289 break;
1290 default:
1291 maxl = 1;
1292 break;
1293 }
1294 } else {
1295 cp += 2;
1296 maxl = 0;
1297 }
1298 stnam = cp;
1299
1300 /* Advance to the end of the name. */
1301
1302 naml = 0;
1303 arg_complete = 1;
1304 while (maxl == 0 || naml < maxl) {
1305 if (*cp == '\0') {
1306 mandoc_msg(MANDOCERR_ESC_BAD, r->parse,
1307 ln, (int)(stesc - buf->buf), stesc);
1308 arg_complete = 0;
1309 break;
1310 }
1311 if (maxl == 0 && *cp == term) {
1312 cp++;
1313 break;
1314 }
1315 if (*cp++ != '\\' || stesc[1] != 'w') {
1316 naml++;
1317 continue;
1318 }
1319 switch (mandoc_escape(&cp, NULL, NULL)) {
1320 case ESCAPE_SPECIAL:
1321 case ESCAPE_UNICODE:
1322 case ESCAPE_NUMBERED:
1323 case ESCAPE_OVERSTRIKE:
1324 naml++;
1325 break;
1326 default:
1327 break;
1328 }
1329 }
1330
1331 /*
1332 * Retrieve the replacement string; if it is
1333 * undefined, resume searching for escapes.
1334 */
1335
1336 switch (stesc[1]) {
1337 case '*':
1338 if (arg_complete)
1339 res = roff_getstrn(r, stnam, naml);
1340 break;
1341 case 'B':
1342 npos = 0;
1343 ubuf[0] = arg_complete &&
1344 roff_evalnum(r, ln, stnam, &npos,
1345 NULL, ROFFNUM_SCALE) &&
1346 stnam + npos + 1 == cp ? '1' : '0';
1347 ubuf[1] = '\0';
1348 break;
1349 case 'n':
1350 if (arg_complete)
1351 (void)snprintf(ubuf, sizeof(ubuf), "%d",
1352 roff_getregn(r, stnam, naml));
1353 else
1354 ubuf[0] = '\0';
1355 break;
1356 case 'w':
1357 /* use even incomplete args */
1358 (void)snprintf(ubuf, sizeof(ubuf), "%d",
1359 24 * (int)naml);
1360 break;
1361 }
1362
1363 if (res == NULL) {
1364 mandoc_vmsg(MANDOCERR_STR_UNDEF,
1365 r->parse, ln, (int)(stesc - buf->buf),
1366 "%.*s", (int)naml, stnam);
1367 res = "";
1368 } else if (buf->sz + strlen(res) > SHRT_MAX) {
1369 mandoc_msg(MANDOCERR_ROFFLOOP, r->parse,
1370 ln, (int)(stesc - buf->buf), NULL);
1371 return ROFF_IGN;
1372 }
1373
1374 /* Replace the escape sequence by the string. */
1375
1376 *stesc = '\0';
1377 buf->sz = mandoc_asprintf(&nbuf, "%s%s%s",
1378 buf->buf, res, cp) + 1;
1379
1380 /* Prepare for the next replacement. */
1381
1382 start = nbuf + pos;
1383 stesc = nbuf + (stesc - buf->buf) + strlen(res);
1384 free(buf->buf);
1385 buf->buf = nbuf;
1386 }
1387 return ROFF_CONT;
1388 }
1389
1390 /*
1391 * Process text streams.
1392 */
1393 static enum rofferr
1394 roff_parsetext(struct roff *r, struct buf *buf, int pos, int *offs)
1395 {
1396 size_t sz;
1397 const char *start;
1398 char *p;
1399 int isz;
1400 enum mandoc_esc esc;
1401
1402 /* Spring the input line trap. */
1403
1404 if (roffit_lines == 1) {
1405 isz = mandoc_asprintf(&p, "%s\n.%s", buf->buf, roffit_macro);
1406 free(buf->buf);
1407 buf->buf = p;
1408 buf->sz = isz + 1;
1409 *offs = 0;
1410 free(roffit_macro);
1411 roffit_lines = 0;
1412 return ROFF_REPARSE;
1413 } else if (roffit_lines > 1)
1414 --roffit_lines;
1415
1416 if (roffce_node != NULL && buf->buf[pos] != '\0') {
1417 if (roffce_lines < 1) {
1418 r->man->last = roffce_node;
1419 r->man->next = ROFF_NEXT_SIBLING;
1420 roffce_lines = 0;
1421 roffce_node = NULL;
1422 } else
1423 roffce_lines--;
1424 }
1425
1426 /* Convert all breakable hyphens into ASCII_HYPH. */
1427
1428 start = p = buf->buf + pos;
1429
1430 while (*p != '\0') {
1431 sz = strcspn(p, "-\\");
1432 p += sz;
1433
1434 if (*p == '\0')
1435 break;
1436
1437 if (*p == '\\') {
1438 /* Skip over escapes. */
1439 p++;
1440 esc = mandoc_escape((const char **)&p, NULL, NULL);
1441 if (esc == ESCAPE_ERROR)
1442 break;
1443 while (*p == '-')
1444 p++;
1445 continue;
1446 } else if (p == start) {
1447 p++;
1448 continue;
1449 }
1450
1451 if (isalpha((unsigned char)p[-1]) &&
1452 isalpha((unsigned char)p[1]))
1453 *p = ASCII_HYPH;
1454 p++;
1455 }
1456 return ROFF_CONT;
1457 }
1458
1459 enum rofferr
1460 roff_parseln(struct roff *r, int ln, struct buf *buf, int *offs)
1461 {
1462 enum roff_tok t;
1463 enum rofferr e;
1464 int pos; /* parse point */
1465 int spos; /* saved parse point for messages */
1466 int ppos; /* original offset in buf->buf */
1467 int ctl; /* macro line (boolean) */
1468
1469 ppos = pos = *offs;
1470
1471 /* Handle in-line equation delimiters. */
1472
1473 if (r->tbl == NULL &&
1474 r->last_eqn != NULL && r->last_eqn->delim &&
1475 (r->eqn == NULL || r->eqn_inline)) {
1476 e = roff_eqndelim(r, buf, pos);
1477 if (e == ROFF_REPARSE)
1478 return e;
1479 assert(e == ROFF_CONT);
1480 }
1481
1482 /* Expand some escape sequences. */
1483
1484 e = roff_res(r, buf, ln, pos);
1485 if (e == ROFF_IGN || e == ROFF_APPEND)
1486 return e;
1487 assert(e == ROFF_CONT);
1488
1489 ctl = roff_getcontrol(r, buf->buf, &pos);
1490
1491 /*
1492 * First, if a scope is open and we're not a macro, pass the
1493 * text through the macro's filter.
1494 * Equations process all content themselves.
1495 * Tables process almost all content themselves, but we want
1496 * to warn about macros before passing it there.
1497 */
1498
1499 if (r->last != NULL && ! ctl) {
1500 t = r->last->tok;
1501 e = (*roffs[t].text)(r, t, buf, ln, pos, pos, offs);
1502 if (e == ROFF_IGN)
1503 return e;
1504 assert(e == ROFF_CONT);
1505 }
1506 if (r->eqn != NULL)
1507 return eqn_read(&r->eqn, ln, buf->buf, ppos, offs);
1508 if (r->tbl != NULL && ( ! ctl || buf->buf[pos] == '\0'))
1509 return tbl_read(r->tbl, ln, buf->buf, ppos);
1510 if ( ! ctl)
1511 return roff_parsetext(r, buf, pos, offs);
1512
1513 /* Skip empty request lines. */
1514
1515 if (buf->buf[pos] == '"') {
1516 mandoc_msg(MANDOCERR_COMMENT_BAD, r->parse,
1517 ln, pos, NULL);
1518 return ROFF_IGN;
1519 } else if (buf->buf[pos] == '\0')
1520 return ROFF_IGN;
1521
1522 /*
1523 * If a scope is open, go to the child handler for that macro,
1524 * as it may want to preprocess before doing anything with it.
1525 * Don't do so if an equation is open.
1526 */
1527
1528 if (r->last) {
1529 t = r->last->tok;
1530 return (*roffs[t].sub)(r, t, buf, ln, ppos, pos, offs);
1531 }
1532
1533 /* No scope is open. This is a new request or macro. */
1534
1535 spos = pos;
1536 t = roff_parse(r, buf->buf, &pos, ln, ppos);
1537
1538 /* Tables ignore most macros. */
1539
1540 if (r->tbl != NULL && (t == TOKEN_NONE || t == ROFF_TS)) {
1541 mandoc_msg(MANDOCERR_TBLMACRO, r->parse,
1542 ln, pos, buf->buf + spos);
1543 if (t == ROFF_TS)
1544 return ROFF_IGN;
1545 while (buf->buf[pos] != '\0' && buf->buf[pos] != ' ')
1546 pos++;
1547 while (buf->buf[pos] == ' ')
1548 pos++;
1549 return tbl_read(r->tbl, ln, buf->buf, pos);
1550 }
1551
1552 /* For now, let high level macros abort .ce mode. */
1553
1554 if (ctl && roffce_node != NULL &&
1555 (t == TOKEN_NONE || t == ROFF_EQ || t == ROFF_TS)) {
1556 r->man->last = roffce_node;
1557 r->man->next = ROFF_NEXT_SIBLING;
1558 roffce_lines = 0;
1559 roffce_node = NULL;
1560 }
1561
1562 /*
1563 * This is neither a roff request nor a user-defined macro.
1564 * Let the standard macro set parsers handle it.
1565 */
1566
1567 if (t == TOKEN_NONE)
1568 return ROFF_CONT;
1569
1570 /* Execute a roff request or a user defined macro. */
1571
1572 return (*roffs[t].proc)(r, t, buf, ln, spos, pos, offs);
1573 }
1574
1575 void
1576 roff_endparse(struct roff *r)
1577 {
1578
1579 if (r->last)
1580 mandoc_msg(MANDOCERR_BLK_NOEND, r->parse,
1581 r->last->line, r->last->col,
1582 roff_name[r->last->tok]);
1583
1584 if (r->eqn) {
1585 mandoc_msg(MANDOCERR_BLK_NOEND, r->parse,
1586 r->eqn->eqn.ln, r->eqn->eqn.pos, "EQ");
1587 eqn_end(&r->eqn);
1588 }
1589
1590 if (r->tbl) {
1591 mandoc_msg(MANDOCERR_BLK_NOEND, r->parse,
1592 r->tbl->line, r->tbl->pos, "TS");
1593 tbl_end(&r->tbl);
1594 }
1595 }
1596
1597 /*
1598 * Parse a roff node's type from the input buffer. This must be in the
1599 * form of ".foo xxx" in the usual way.
1600 */
1601 static enum roff_tok
1602 roff_parse(struct roff *r, char *buf, int *pos, int ln, int ppos)
1603 {
1604 char *cp;
1605 const char *mac;
1606 size_t maclen;
1607 enum roff_tok t;
1608
1609 cp = buf + *pos;
1610
1611 if ('\0' == *cp || '"' == *cp || '\t' == *cp || ' ' == *cp)
1612 return TOKEN_NONE;
1613
1614 mac = cp;
1615 maclen = roff_getname(r, &cp, ln, ppos);
1616
1617 t = (r->current_string = roff_getstrn(r, mac, maclen))
1618 ? ROFF_USERDEF : roffhash_find(r->reqtab, mac, maclen);
1619
1620 if (t != TOKEN_NONE)
1621 *pos = cp - buf;
1622
1623 return t;
1624 }
1625
1626 /* --- handling of request blocks ----------------------------------------- */
1627
1628 static enum rofferr
1629 roff_cblock(ROFF_ARGS)
1630 {
1631
1632 /*
1633 * A block-close `..' should only be invoked as a child of an
1634 * ignore macro, otherwise raise a warning and just ignore it.
1635 */
1636
1637 if (r->last == NULL) {
1638 mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
1639 ln, ppos, "..");
1640 return ROFF_IGN;
1641 }
1642
1643 switch (r->last->tok) {
1644 case ROFF_am:
1645 /* ROFF_am1 is remapped to ROFF_am in roff_block(). */
1646 case ROFF_ami:
1647 case ROFF_de:
1648 /* ROFF_de1 is remapped to ROFF_de in roff_block(). */
1649 case ROFF_dei:
1650 case ROFF_ig:
1651 break;
1652 default:
1653 mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
1654 ln, ppos, "..");
1655 return ROFF_IGN;
1656 }
1657
1658 if (buf->buf[pos] != '\0')
1659 mandoc_vmsg(MANDOCERR_ARG_SKIP, r->parse, ln, pos,
1660 ".. %s", buf->buf + pos);
1661
1662 roffnode_pop(r);
1663 roffnode_cleanscope(r);
1664 return ROFF_IGN;
1665
1666 }
1667
1668 static void
1669 roffnode_cleanscope(struct roff *r)
1670 {
1671
1672 while (r->last) {
1673 if (--r->last->endspan != 0)
1674 break;
1675 roffnode_pop(r);
1676 }
1677 }
1678
1679 static void
1680 roff_ccond(struct roff *r, int ln, int ppos)
1681 {
1682
1683 if (NULL == r->last) {
1684 mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
1685 ln, ppos, "\\}");
1686 return;
1687 }
1688
1689 switch (r->last->tok) {
1690 case ROFF_el:
1691 case ROFF_ie:
1692 case ROFF_if:
1693 break;
1694 default:
1695 mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
1696 ln, ppos, "\\}");
1697 return;
1698 }
1699
1700 if (r->last->endspan > -1) {
1701 mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
1702 ln, ppos, "\\}");
1703 return;
1704 }
1705
1706 roffnode_pop(r);
1707 roffnode_cleanscope(r);
1708 return;
1709 }
1710
1711 static enum rofferr
1712 roff_block(ROFF_ARGS)
1713 {
1714 const char *name;
1715 char *iname, *cp;
1716 size_t namesz;
1717
1718 /* Ignore groff compatibility mode for now. */
1719
1720 if (tok == ROFF_de1)
1721 tok = ROFF_de;
1722 else if (tok == ROFF_dei1)
1723 tok = ROFF_dei;
1724 else if (tok == ROFF_am1)
1725 tok = ROFF_am;
1726 else if (tok == ROFF_ami1)
1727 tok = ROFF_ami;
1728
1729 /* Parse the macro name argument. */
1730
1731 cp = buf->buf + pos;
1732 if (tok == ROFF_ig) {
1733 iname = NULL;
1734 namesz = 0;
1735 } else {
1736 iname = cp;
1737 namesz = roff_getname(r, &cp, ln, ppos);
1738 iname[namesz] = '\0';
1739 }
1740
1741 /* Resolve the macro name argument if it is indirect. */
1742
1743 if (namesz && (tok == ROFF_dei || tok == ROFF_ami)) {
1744 if ((name = roff_getstrn(r, iname, namesz)) == NULL) {
1745 mandoc_vmsg(MANDOCERR_STR_UNDEF,
1746 r->parse, ln, (int)(iname - buf->buf),
1747 "%.*s", (int)namesz, iname);
1748 namesz = 0;
1749 } else
1750 namesz = strlen(name);
1751 } else
1752 name = iname;
1753
1754 if (namesz == 0 && tok != ROFF_ig) {
1755 mandoc_msg(MANDOCERR_REQ_EMPTY, r->parse,
1756 ln, ppos, roff_name[tok]);
1757 return ROFF_IGN;
1758 }
1759
1760 roffnode_push(r, tok, name, ln, ppos);
1761
1762 /*
1763 * At the beginning of a `de' macro, clear the existing string
1764 * with the same name, if there is one. New content will be
1765 * appended from roff_block_text() in multiline mode.
1766 */
1767
1768 if (tok == ROFF_de || tok == ROFF_dei)
1769 roff_setstrn(&r->strtab, name, namesz, "", 0, 0);
1770
1771 if (*cp == '\0')
1772 return ROFF_IGN;
1773
1774 /* Get the custom end marker. */
1775
1776 iname = cp;
1777 namesz = roff_getname(r, &cp, ln, ppos);
1778
1779 /* Resolve the end marker if it is indirect. */
1780
1781 if (namesz && (tok == ROFF_dei || tok == ROFF_ami)) {
1782 if ((name = roff_getstrn(r, iname, namesz)) == NULL) {
1783 mandoc_vmsg(MANDOCERR_STR_UNDEF,
1784 r->parse, ln, (int)(iname - buf->buf),
1785 "%.*s", (int)namesz, iname);
1786 namesz = 0;
1787 } else
1788 namesz = strlen(name);
1789 } else
1790 name = iname;
1791
1792 if (namesz)
1793 r->last->end = mandoc_strndup(name, namesz);
1794
1795 if (*cp != '\0')
1796 mandoc_vmsg(MANDOCERR_ARG_EXCESS, r->parse,
1797 ln, pos, ".%s ... %s", roff_name[tok], cp);
1798
1799 return ROFF_IGN;
1800 }
1801
1802 static enum rofferr
1803 roff_block_sub(ROFF_ARGS)
1804 {
1805 enum roff_tok t;
1806 int i, j;
1807
1808 /*
1809 * First check whether a custom macro exists at this level. If
1810 * it does, then check against it. This is some of groff's
1811 * stranger behaviours. If we encountered a custom end-scope
1812 * tag and that tag also happens to be a "real" macro, then we
1813 * need to try interpreting it again as a real macro. If it's
1814 * not, then return ignore. Else continue.
1815 */
1816
1817 if (r->last->end) {
1818 for (i = pos, j = 0; r->last->end[j]; j++, i++)
1819 if (buf->buf[i] != r->last->end[j])
1820 break;
1821
1822 if (r->last->end[j] == '\0' &&
1823 (buf->buf[i] == '\0' ||
1824 buf->buf[i] == ' ' ||
1825 buf->buf[i] == '\t')) {
1826 roffnode_pop(r);
1827 roffnode_cleanscope(r);
1828
1829 while (buf->buf[i] == ' ' || buf->buf[i] == '\t')
1830 i++;
1831
1832 pos = i;
1833 if (roff_parse(r, buf->buf, &pos, ln, ppos) !=
1834 TOKEN_NONE)
1835 return ROFF_RERUN;
1836 return ROFF_IGN;
1837 }
1838 }
1839
1840 /*
1841 * If we have no custom end-query or lookup failed, then try
1842 * pulling it out of the hashtable.
1843 */
1844
1845 t = roff_parse(r, buf->buf, &pos, ln, ppos);
1846
1847 if (t != ROFF_cblock) {
1848 if (tok != ROFF_ig)
1849 roff_setstr(r, r->last->name, buf->buf + ppos, 2);
1850 return ROFF_IGN;
1851 }
1852
1853 return (*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs);
1854 }
1855
1856 static enum rofferr
1857 roff_block_text(ROFF_ARGS)
1858 {
1859
1860 if (tok != ROFF_ig)
1861 roff_setstr(r, r->last->name, buf->buf + pos, 2);
1862
1863 return ROFF_IGN;
1864 }
1865
1866 static enum rofferr
1867 roff_cond_sub(ROFF_ARGS)
1868 {
1869 enum roff_tok t;
1870 char *ep;
1871 int rr;
1872
1873 rr = r->last->rule;
1874 roffnode_cleanscope(r);
1875 t = roff_parse(r, buf->buf, &pos, ln, ppos);
1876
1877 /*
1878 * Fully handle known macros when they are structurally
1879 * required or when the conditional evaluated to true.
1880 */
1881
1882 if (t != TOKEN_NONE && (rr || roffs[t].flags & ROFFMAC_STRUCT))
1883 return (*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs);
1884
1885 /*
1886 * If `\}' occurs on a macro line without a preceding macro,
1887 * drop the line completely.
1888 */
1889
1890 ep = buf->buf + pos;
1891 if (ep[0] == '\\' && ep[1] == '}')
1892 rr = 0;
1893
1894 /* Always check for the closing delimiter `\}'. */
1895
1896 while ((ep = strchr(ep, '\\')) != NULL) {
1897 if (*(++ep) == '}') {
1898 *ep = '&';
1899 roff_ccond(r, ln, ep - buf->buf - 1);
1900 }
1901 if (*ep != '\0')
1902 ++ep;
1903 }
1904 return rr ? ROFF_CONT : ROFF_IGN;
1905 }
1906
1907 static enum rofferr
1908 roff_cond_text(ROFF_ARGS)
1909 {
1910 char *ep;
1911 int rr;
1912
1913 rr = r->last->rule;
1914 roffnode_cleanscope(r);
1915
1916 ep = buf->buf + pos;
1917 while ((ep = strchr(ep, '\\')) != NULL) {
1918 if (*(++ep) == '}') {
1919 *ep = '&';
1920 roff_ccond(r, ln, ep - buf->buf - 1);
1921 }
1922 if (*ep != '\0')
1923 ++ep;
1924 }
1925 return rr ? ROFF_CONT : ROFF_IGN;
1926 }
1927
1928 /* --- handling of numeric and conditional expressions -------------------- */
1929
1930 /*
1931 * Parse a single signed integer number. Stop at the first non-digit.
1932 * If there is at least one digit, return success and advance the
1933 * parse point, else return failure and let the parse point unchanged.
1934 * Ignore overflows, treat them just like the C language.
1935 */
1936 static int
1937 roff_getnum(const char *v, int *pos, int *res, int flags)
1938 {
1939 int myres, scaled, n, p;
1940
1941 if (NULL == res)
1942 res = &myres;
1943
1944 p = *pos;
1945 n = v[p] == '-';
1946 if (n || v[p] == '+')
1947 p++;
1948
1949 if (flags & ROFFNUM_WHITE)
1950 while (isspace((unsigned char)v[p]))
1951 p++;
1952
1953 for (*res = 0; isdigit((unsigned char)v[p]); p++)
1954 *res = 10 * *res + v[p] - '0';
1955 if (p == *pos + n)
1956 return 0;
1957
1958 if (n)
1959 *res = -*res;
1960
1961 /* Each number may be followed by one optional scaling unit. */
1962
1963 switch (v[p]) {
1964 case 'f':
1965 scaled = *res * 65536;
1966 break;
1967 case 'i':
1968 scaled = *res * 240;
1969 break;
1970 case 'c':
1971 scaled = *res * 240 / 2.54;
1972 break;
1973 case 'v':
1974 case 'P':
1975 scaled = *res * 40;
1976 break;
1977 case 'm':
1978 case 'n':
1979 scaled = *res * 24;
1980 break;
1981 case 'p':
1982 scaled = *res * 10 / 3;
1983 break;
1984 case 'u':
1985 scaled = *res;
1986 break;
1987 case 'M':
1988 scaled = *res * 6 / 25;
1989 break;
1990 default:
1991 scaled = *res;
1992 p--;
1993 break;
1994 }
1995 if (flags & ROFFNUM_SCALE)
1996 *res = scaled;
1997
1998 *pos = p + 1;
1999 return 1;
2000 }
2001
2002 /*
2003 * Evaluate a string comparison condition.
2004 * The first character is the delimiter.
2005 * Succeed if the string up to its second occurrence
2006 * matches the string up to its third occurence.
2007 * Advance the cursor after the third occurrence
2008 * or lacking that, to the end of the line.
2009 */
2010 static int
2011 roff_evalstrcond(const char *v, int *pos)
2012 {
2013 const char *s1, *s2, *s3;
2014 int match;
2015
2016 match = 0;
2017 s1 = v + *pos; /* initial delimiter */
2018 s2 = s1 + 1; /* for scanning the first string */
2019 s3 = strchr(s2, *s1); /* for scanning the second string */
2020
2021 if (NULL == s3) /* found no middle delimiter */
2022 goto out;
2023
2024 while ('\0' != *++s3) {
2025 if (*s2 != *s3) { /* mismatch */
2026 s3 = strchr(s3, *s1);
2027 break;
2028 }
2029 if (*s3 == *s1) { /* found the final delimiter */
2030 match = 1;
2031 break;
2032 }
2033 s2++;
2034 }
2035
2036 out:
2037 if (NULL == s3)
2038 s3 = strchr(s2, '\0');
2039 else if (*s3 != '\0')
2040 s3++;
2041 *pos = s3 - v;
2042 return match;
2043 }
2044
2045 /*
2046 * Evaluate an optionally negated single character, numerical,
2047 * or string condition.
2048 */
2049 static int
2050 roff_evalcond(struct roff *r, int ln, char *v, int *pos)
2051 {
2052 char *cp, *name;
2053 size_t sz;
2054 int number, savepos, wanttrue;
2055
2056 if ('!' == v[*pos]) {
2057 wanttrue = 0;
2058 (*pos)++;
2059 } else
2060 wanttrue = 1;
2061
2062 switch (v[*pos]) {
2063 case '\0':
2064 return 0;
2065 case 'n':
2066 case 'o':
2067 (*pos)++;
2068 return wanttrue;
2069 case 'c':
2070 case 'd':
2071 case 'e':
2072 case 't':
2073 case 'v':
2074 (*pos)++;
2075 return !wanttrue;
2076 case 'r':
2077 cp = name = v + ++*pos;
2078 sz = roff_getname(r, &cp, ln, *pos);
2079 *pos = cp - v;
2080 return (sz && roff_hasregn(r, name, sz)) == wanttrue;
2081 default:
2082 break;
2083 }
2084
2085 savepos = *pos;
2086 if (roff_evalnum(r, ln, v, pos, &number, ROFFNUM_SCALE))
2087 return (number > 0) == wanttrue;
2088 else if (*pos == savepos)
2089 return roff_evalstrcond(v, pos) == wanttrue;
2090 else
2091 return 0;
2092 }
2093
2094 static enum rofferr
2095 roff_line_ignore(ROFF_ARGS)
2096 {
2097
2098 return ROFF_IGN;
2099 }
2100
2101 static enum rofferr
2102 roff_insec(ROFF_ARGS)
2103 {
2104
2105 mandoc_msg(MANDOCERR_REQ_INSEC, r->parse,
2106 ln, ppos, roff_name[tok]);
2107 return ROFF_IGN;
2108 }
2109
2110 static enum rofferr
2111 roff_unsupp(ROFF_ARGS)
2112 {
2113
2114 mandoc_msg(MANDOCERR_REQ_UNSUPP, r->parse,
2115 ln, ppos, roff_name[tok]);
2116 return ROFF_IGN;
2117 }
2118
2119 static enum rofferr
2120 roff_cond(ROFF_ARGS)
2121 {
2122
2123 roffnode_push(r, tok, NULL, ln, ppos);
2124
2125 /*
2126 * An `.el' has no conditional body: it will consume the value
2127 * of the current rstack entry set in prior `ie' calls or
2128 * defaults to DENY.
2129 *
2130 * If we're not an `el', however, then evaluate the conditional.
2131 */
2132
2133 r->last->rule = tok == ROFF_el ?
2134 (r->rstackpos < 0 ? 0 : r->rstack[r->rstackpos--]) :
2135 roff_evalcond(r, ln, buf->buf, &pos);
2136
2137 /*
2138 * An if-else will put the NEGATION of the current evaluated
2139 * conditional into the stack of rules.
2140 */
2141
2142 if (tok == ROFF_ie) {
2143 if (r->rstackpos + 1 == r->rstacksz) {
2144 r->rstacksz += 16;
2145 r->rstack = mandoc_reallocarray(r->rstack,
2146 r->rstacksz, sizeof(int));
2147 }
2148 r->rstack[++r->rstackpos] = !r->last->rule;
2149 }
2150
2151 /* If the parent has false as its rule, then so do we. */
2152
2153 if (r->last->parent && !r->last->parent->rule)
2154 r->last->rule = 0;
2155
2156 /*
2157 * Determine scope.
2158 * If there is nothing on the line after the conditional,
2159 * not even whitespace, use next-line scope.
2160 */
2161
2162 if (buf->buf[pos] == '\0') {
2163 r->last->endspan = 2;
2164 goto out;
2165 }
2166
2167 while (buf->buf[pos] == ' ')
2168 pos++;
2169
2170 /* An opening brace requests multiline scope. */
2171
2172 if (buf->buf[pos] == '\\' && buf->buf[pos + 1] == '{') {
2173 r->last->endspan = -1;
2174 pos += 2;
2175 while (buf->buf[pos] == ' ')
2176 pos++;
2177 goto out;
2178 }
2179
2180 /*
2181 * Anything else following the conditional causes
2182 * single-line scope. Warn if the scope contains
2183 * nothing but trailing whitespace.
2184 */
2185
2186 if (buf->buf[pos] == '\0')
2187 mandoc_msg(MANDOCERR_COND_EMPTY, r->parse,
2188 ln, ppos, roff_name[tok]);
2189
2190 r->last->endspan = 1;
2191
2192 out:
2193 *offs = pos;
2194 return ROFF_RERUN;
2195 }
2196
2197 static enum rofferr
2198 roff_ds(ROFF_ARGS)
2199 {
2200 char *string;
2201 const char *name;
2202 size_t namesz;
2203
2204 /* Ignore groff compatibility mode for now. */
2205
2206 if (tok == ROFF_ds1)
2207 tok = ROFF_ds;
2208 else if (tok == ROFF_as1)
2209 tok = ROFF_as;
2210
2211 /*
2212 * The first word is the name of the string.
2213 * If it is empty or terminated by an escape sequence,
2214 * abort the `ds' request without defining anything.
2215 */
2216
2217 name = string = buf->buf + pos;
2218 if (*name == '\0')
2219 return ROFF_IGN;
2220
2221 namesz = roff_getname(r, &string, ln, pos);
2222 if (name[namesz] == '\\')
2223 return ROFF_IGN;
2224
2225 /* Read past the initial double-quote, if any. */
2226 if (*string == '"')
2227 string++;
2228
2229 /* The rest is the value. */
2230 roff_setstrn(&r->strtab, name, namesz, string, strlen(string),
2231 ROFF_as == tok);
2232 return ROFF_IGN;
2233 }
2234
2235 /*
2236 * Parse a single operator, one or two characters long.
2237 * If the operator is recognized, return success and advance the
2238 * parse point, else return failure and let the parse point unchanged.
2239 */
2240 static int
2241 roff_getop(const char *v, int *pos, char *res)
2242 {
2243
2244 *res = v[*pos];
2245
2246 switch (*res) {
2247 case '+':
2248 case '-':
2249 case '*':
2250 case '/':
2251 case '%':
2252 case '&':
2253 case ':':
2254 break;
2255 case '<':
2256 switch (v[*pos + 1]) {
2257 case '=':
2258 *res = 'l';
2259 (*pos)++;
2260 break;
2261 case '>':
2262 *res = '!';
2263 (*pos)++;
2264 break;
2265 case '?':
2266 *res = 'i';
2267 (*pos)++;
2268 break;
2269 default:
2270 break;
2271 }
2272 break;
2273 case '>':
2274 switch (v[*pos + 1]) {
2275 case '=':
2276 *res = 'g';
2277 (*pos)++;
2278 break;
2279 case '?':
2280 *res = 'a';
2281 (*pos)++;
2282 break;
2283 default:
2284 break;
2285 }
2286 break;
2287 case '=':
2288 if ('=' == v[*pos + 1])
2289 (*pos)++;
2290 break;
2291 default:
2292 return 0;
2293 }
2294 (*pos)++;
2295
2296 return *res;
2297 }
2298
2299 /*
2300 * Evaluate either a parenthesized numeric expression
2301 * or a single signed integer number.
2302 */
2303 static int
2304 roff_evalpar(struct roff *r, int ln,
2305 const char *v, int *pos, int *res, int flags)
2306 {
2307
2308 if ('(' != v[*pos])
2309 return roff_getnum(v, pos, res, flags);
2310
2311 (*pos)++;
2312 if ( ! roff_evalnum(r, ln, v, pos, res, flags | ROFFNUM_WHITE))
2313 return 0;
2314
2315 /*
2316 * Omission of the closing parenthesis
2317 * is an error in validation mode,
2318 * but ignored in evaluation mode.
2319 */
2320
2321 if (')' == v[*pos])
2322 (*pos)++;
2323 else if (NULL == res)
2324 return 0;
2325
2326 return 1;
2327 }
2328
2329 /*
2330 * Evaluate a complete numeric expression.
2331 * Proceed left to right, there is no concept of precedence.
2332 */
2333 static int
2334 roff_evalnum(struct roff *r, int ln, const char *v,
2335 int *pos, int *res, int flags)
2336 {
2337 int mypos, operand2;
2338 char operator;
2339
2340 if (NULL == pos) {
2341 mypos = 0;
2342 pos = &mypos;
2343 }
2344
2345 if (flags & ROFFNUM_WHITE)
2346 while (isspace((unsigned char)v[*pos]))
2347 (*pos)++;
2348
2349 if ( ! roff_evalpar(r, ln, v, pos, res, flags))
2350 return 0;
2351
2352 while (1) {
2353 if (flags & ROFFNUM_WHITE)
2354 while (isspace((unsigned char)v[*pos]))
2355 (*pos)++;
2356
2357 if ( ! roff_getop(v, pos, &operator))
2358 break;
2359
2360 if (flags & ROFFNUM_WHITE)
2361 while (isspace((unsigned char)v[*pos]))
2362 (*pos)++;
2363
2364 if ( ! roff_evalpar(r, ln, v, pos, &operand2, flags))
2365 return 0;
2366
2367 if (flags & ROFFNUM_WHITE)
2368 while (isspace((unsigned char)v[*pos]))
2369 (*pos)++;
2370
2371 if (NULL == res)
2372 continue;
2373
2374 switch (operator) {
2375 case '+':
2376 *res += operand2;
2377 break;
2378 case '-':
2379 *res -= operand2;
2380 break;
2381 case '*':
2382 *res *= operand2;
2383 break;
2384 case '/':
2385 if (operand2 == 0) {
2386 mandoc_msg(MANDOCERR_DIVZERO,
2387 r->parse, ln, *pos, v);
2388 *res = 0;
2389 break;
2390 }
2391 *res /= operand2;
2392 break;
2393 case '%':
2394 if (operand2 == 0) {
2395 mandoc_msg(MANDOCERR_DIVZERO,
2396 r->parse, ln, *pos, v);
2397 *res = 0;
2398 break;
2399 }
2400 *res %= operand2;
2401 break;
2402 case '<':
2403 *res = *res < operand2;
2404 break;
2405 case '>':
2406 *res = *res > operand2;
2407 break;
2408 case 'l':
2409 *res = *res <= operand2;
2410 break;
2411 case 'g':
2412 *res = *res >= operand2;
2413 break;
2414 case '=':
2415 *res = *res == operand2;
2416 break;
2417 case '!':
2418 *res = *res != operand2;
2419 break;
2420 case '&':
2421 *res = *res && operand2;
2422 break;
2423 case ':':
2424 *res = *res || operand2;
2425 break;
2426 case 'i':
2427 if (operand2 < *res)
2428 *res = operand2;
2429 break;
2430 case 'a':
2431 if (operand2 > *res)
2432 *res = operand2;
2433 break;
2434 default:
2435 abort();
2436 }
2437 }
2438 return 1;
2439 }
2440
2441 /* --- register management ------------------------------------------------ */
2442
2443 void
2444 roff_setreg(struct roff *r, const char *name, int val, char sign)
2445 {
2446 struct roffreg *reg;
2447
2448 /* Search for an existing register with the same name. */
2449 reg = r->regtab;
2450
2451 while (reg && strcmp(name, reg->key.p))
2452 reg = reg->next;
2453
2454 if (NULL == reg) {
2455 /* Create a new register. */
2456 reg = mandoc_malloc(sizeof(struct roffreg));
2457 reg->key.p = mandoc_strdup(name);
2458 reg->key.sz = strlen(name);
2459 reg->val = 0;
2460 reg->next = r->regtab;
2461 r->regtab = reg;
2462 }
2463
2464 if ('+' == sign)
2465 reg->val += val;
2466 else if ('-' == sign)
2467 reg->val -= val;
2468 else
2469 reg->val = val;
2470 }
2471
2472 /*
2473 * Handle some predefined read-only number registers.
2474 * For now, return -1 if the requested register is not predefined;
2475 * in case a predefined read-only register having the value -1
2476 * were to turn up, another special value would have to be chosen.
2477 */
2478 static int
2479 roff_getregro(const struct roff *r, const char *name)
2480 {
2481
2482 switch (*name) {
2483 case '$': /* Number of arguments of the last macro evaluated. */
2484 return r->argc;
2485 case 'A': /* ASCII approximation mode is always off. */
2486 return 0;
2487 case 'g': /* Groff compatibility mode is always on. */
2488 return 1;
2489 case 'H': /* Fixed horizontal resolution. */
2490 return 24;
2491 case 'j': /* Always adjust left margin only. */
2492 return 0;
2493 case 'T': /* Some output device is always defined. */
2494 return 1;
2495 case 'V': /* Fixed vertical resolution. */
2496 return 40;
2497 default:
2498 return -1;
2499 }
2500 }
2501
2502 int
2503 roff_getreg(const struct roff *r, const char *name)
2504 {
2505 struct roffreg *reg;
2506 int val;
2507
2508 if ('.' == name[0] && '\0' != name[1] && '\0' == name[2]) {
2509 val = roff_getregro(r, name + 1);
2510 if (-1 != val)
2511 return val;
2512 }
2513
2514 for (reg = r->regtab; reg; reg = reg->next)
2515 if (0 == strcmp(name, reg->key.p))
2516 return reg->val;
2517
2518 return 0;
2519 }
2520
2521 static int
2522 roff_getregn(const struct roff *r, const char *name, size_t len)
2523 {
2524 struct roffreg *reg;
2525 int val;
2526
2527 if ('.' == name[0] && 2 == len) {
2528 val = roff_getregro(r, name + 1);
2529 if (-1 != val)
2530 return val;
2531 }
2532
2533 for (reg = r->regtab; reg; reg = reg->next)
2534 if (len == reg->key.sz &&
2535 0 == strncmp(name, reg->key.p, len))
2536 return reg->val;
2537
2538 return 0;
2539 }
2540
2541 static int
2542 roff_hasregn(const struct roff *r, const char *name, size_t len)
2543 {
2544 struct roffreg *reg;
2545 int val;
2546
2547 if ('.' == name[0] && 2 == len) {
2548 val = roff_getregro(r, name + 1);
2549 if (-1 != val)
2550 return 1;
2551 }
2552
2553 for (reg = r->regtab; reg; reg = reg->next)
2554 if (len == reg->key.sz &&
2555 0 == strncmp(name, reg->key.p, len))
2556 return 1;
2557
2558 return 0;
2559 }
2560
2561 static void
2562 roff_freereg(struct roffreg *reg)
2563 {
2564 struct roffreg *old_reg;
2565
2566 while (NULL != reg) {
2567 free(reg->key.p);
2568 old_reg = reg;
2569 reg = reg->next;
2570 free(old_reg);
2571 }
2572 }
2573
2574 static enum rofferr
2575 roff_nr(ROFF_ARGS)
2576 {
2577 char *key, *val;
2578 size_t keysz;
2579 int iv;
2580 char sign;
2581
2582 key = val = buf->buf + pos;
2583 if (*key == '\0')
2584 return ROFF_IGN;
2585
2586 keysz = roff_getname(r, &val, ln, pos);
2587 if (key[keysz] == '\\')
2588 return ROFF_IGN;
2589 key[keysz] = '\0';
2590
2591 sign = *val;
2592 if (sign == '+' || sign == '-')
2593 val++;
2594
2595 if (roff_evalnum(r, ln, val, NULL, &iv, ROFFNUM_SCALE))
2596 roff_setreg(r, key, iv, sign);
2597
2598 return ROFF_IGN;
2599 }
2600
2601 static enum rofferr
2602 roff_rr(ROFF_ARGS)
2603 {
2604 struct roffreg *reg, **prev;
2605 char *name, *cp;
2606 size_t namesz;
2607
2608 name = cp = buf->buf + pos;
2609 if (*name == '\0')
2610 return ROFF_IGN;
2611 namesz = roff_getname(r, &cp, ln, pos);
2612 name[namesz] = '\0';
2613
2614 prev = &r->regtab;
2615 while (1) {
2616 reg = *prev;
2617 if (reg == NULL || !strcmp(name, reg->key.p))
2618 break;
2619 prev = &reg->next;
2620 }
2621 if (reg != NULL) {
2622 *prev = reg->next;
2623 free(reg->key.p);
2624 free(reg);
2625 }
2626 return ROFF_IGN;
2627 }
2628
2629 /* --- handler functions for roff requests -------------------------------- */
2630
2631 static enum rofferr
2632 roff_rm(ROFF_ARGS)
2633 {
2634 const char *name;
2635 char *cp;
2636 size_t namesz;
2637
2638 cp = buf->buf + pos;
2639 while (*cp != '\0') {
2640 name = cp;
2641 namesz = roff_getname(r, &cp, ln, (int)(cp - buf->buf));
2642 roff_setstrn(&r->strtab, name, namesz, NULL, 0, 0);
2643 if (name[namesz] == '\\')
2644 break;
2645 }
2646 return ROFF_IGN;
2647 }
2648
2649 static enum rofferr
2650 roff_it(ROFF_ARGS)
2651 {
2652 int iv;
2653
2654 /* Parse the number of lines. */
2655
2656 if ( ! roff_evalnum(r, ln, buf->buf, &pos, &iv, 0)) {
2657 mandoc_msg(MANDOCERR_IT_NONUM, r->parse,
2658 ln, ppos, buf->buf + 1);
2659 return ROFF_IGN;
2660 }
2661
2662 while (isspace((unsigned char)buf->buf[pos]))
2663 pos++;
2664
2665 /*
2666 * Arm the input line trap.
2667 * Special-casing "an-trap" is an ugly workaround to cope
2668 * with DocBook stupidly fiddling with man(7) internals.
2669 */
2670
2671 roffit_lines = iv;
2672 roffit_macro = mandoc_strdup(iv != 1 ||
2673 strcmp(buf->buf + pos, "an-trap") ?
2674 buf->buf + pos : "br");
2675 return ROFF_IGN;
2676 }
2677
2678 static enum rofferr
2679 roff_Dd(ROFF_ARGS)
2680 {
2681 const char *const *cp;
2682
2683 if ((r->options & (MPARSE_MDOC | MPARSE_QUICK)) == 0)
2684 for (cp = __mdoc_reserved; *cp; cp++)
2685 roff_setstr(r, *cp, NULL, 0);
2686
2687 if (r->format == 0)
2688 r->format = MPARSE_MDOC;
2689
2690 return ROFF_CONT;
2691 }
2692
2693 static enum rofferr
2694 roff_TH(ROFF_ARGS)
2695 {
2696 const char *const *cp;
2697
2698 if ((r->options & MPARSE_QUICK) == 0)
2699 for (cp = __man_reserved; *cp; cp++)
2700 roff_setstr(r, *cp, NULL, 0);
2701
2702 if (r->format == 0)
2703 r->format = MPARSE_MAN;
2704
2705 return ROFF_CONT;
2706 }
2707
2708 static enum rofferr
2709 roff_TE(ROFF_ARGS)
2710 {
2711
2712 if (NULL == r->tbl)
2713 mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
2714 ln, ppos, "TE");
2715 else if ( ! tbl_end(&r->tbl)) {
2716 free(buf->buf);
2717 buf->buf = mandoc_strdup(".sp");
2718 buf->sz = 4;
2719 return ROFF_REPARSE;
2720 }
2721 return ROFF_IGN;
2722 }
2723
2724 static enum rofferr
2725 roff_T_(ROFF_ARGS)
2726 {
2727
2728 if (NULL == r->tbl)
2729 mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
2730 ln, ppos, "T&");
2731 else
2732 tbl_restart(ln, ppos, r->tbl);
2733
2734 return ROFF_IGN;
2735 }
2736
2737 /*
2738 * Handle in-line equation delimiters.
2739 */
2740 static enum rofferr
2741 roff_eqndelim(struct roff *r, struct buf *buf, int pos)
2742 {
2743 char *cp1, *cp2;
2744 const char *bef_pr, *bef_nl, *mac, *aft_nl, *aft_pr;
2745
2746 /*
2747 * Outside equations, look for an opening delimiter.
2748 * If we are inside an equation, we already know it is
2749 * in-line, or this function wouldn't have been called;
2750 * so look for a closing delimiter.
2751 */
2752
2753 cp1 = buf->buf + pos;
2754 cp2 = strchr(cp1, r->eqn == NULL ?
2755 r->last_eqn->odelim : r->last_eqn->cdelim);
2756 if (cp2 == NULL)
2757 return ROFF_CONT;
2758
2759 *cp2++ = '\0';
2760 bef_pr = bef_nl = aft_nl = aft_pr = "";
2761
2762 /* Handle preceding text, protecting whitespace. */
2763
2764 if (*buf->buf != '\0') {
2765 if (r->eqn == NULL)
2766 bef_pr = "\\&";
2767 bef_nl = "\n";
2768 }
2769
2770 /*
2771 * Prepare replacing the delimiter with an equation macro
2772 * and drop leading white space from the equation.
2773 */
2774
2775 if (r->eqn == NULL) {
2776 while (*cp2 == ' ')
2777 cp2++;
2778 mac = ".EQ";
2779 } else
2780 mac = ".EN";
2781
2782 /* Handle following text, protecting whitespace. */
2783
2784 if (*cp2 != '\0') {
2785 aft_nl = "\n";
2786 if (r->eqn != NULL)
2787 aft_pr = "\\&";
2788 }
2789
2790 /* Do the actual replacement. */
2791
2792 buf->sz = mandoc_asprintf(&cp1, "%s%s%s%s%s%s%s", buf->buf,
2793 bef_pr, bef_nl, mac, aft_nl, aft_pr, cp2) + 1;
2794 free(buf->buf);
2795 buf->buf = cp1;
2796
2797 /* Toggle the in-line state of the eqn subsystem. */
2798
2799 r->eqn_inline = r->eqn == NULL;
2800 return ROFF_REPARSE;
2801 }
2802
2803 static enum rofferr
2804 roff_EQ(ROFF_ARGS)
2805 {
2806 struct eqn_node *e;
2807
2808 assert(r->eqn == NULL);
2809 e = eqn_alloc(ppos, ln, r->parse);
2810
2811 if (r->last_eqn) {
2812 r->last_eqn->next = e;
2813 e->delim = r->last_eqn->delim;
2814 e->odelim = r->last_eqn->odelim;
2815 e->cdelim = r->last_eqn->cdelim;
2816 } else
2817 r->first_eqn = r->last_eqn = e;
2818
2819 r->eqn = r->last_eqn = e;
2820
2821 if (buf->buf[pos] != '\0')
2822 mandoc_vmsg(MANDOCERR_ARG_SKIP, r->parse, ln, pos,
2823 ".EQ %s", buf->buf + pos);
2824
2825 return ROFF_IGN;
2826 }
2827
2828 static enum rofferr
2829 roff_EN(ROFF_ARGS)
2830 {
2831
2832 mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, ln, ppos, "EN");
2833 return ROFF_IGN;
2834 }
2835
2836 static enum rofferr
2837 roff_TS(ROFF_ARGS)
2838 {
2839 struct tbl_node *tbl;
2840
2841 if (r->tbl) {
2842 mandoc_msg(MANDOCERR_BLK_BROKEN, r->parse,
2843 ln, ppos, "TS breaks TS");
2844 tbl_end(&r->tbl);
2845 }
2846
2847 tbl = tbl_alloc(ppos, ln, r->parse);
2848
2849 if (r->last_tbl)
2850 r->last_tbl->next = tbl;
2851 else
2852 r->first_tbl = r->last_tbl = tbl;
2853
2854 r->tbl = r->last_tbl = tbl;
2855 return ROFF_IGN;
2856 }
2857
2858 static enum rofferr
2859 roff_onearg(ROFF_ARGS)
2860 {
2861 struct roff_node *n;
2862 char *cp;
2863 int npos;
2864
2865 if (r->man->flags & (MAN_BLINE | MAN_ELINE) &&
2866 (tok == ROFF_sp || tok == ROFF_ti))
2867 man_breakscope(r->man, tok);
2868
2869 if (tok == ROFF_ce && roffce_node != NULL) {
2870 r->man->last = roffce_node;
2871 r->man->next = ROFF_NEXT_SIBLING;
2872 }
2873
2874 roff_elem_alloc(r->man, ln, ppos, tok);
2875 n = r->man->last;
2876
2877 cp = buf->buf + pos;
2878 if (*cp != '\0') {
2879 while (*cp != '\0' && *cp != ' ')
2880 cp++;
2881 while (*cp == ' ')
2882 *cp++ = '\0';
2883 if (*cp != '\0')
2884 mandoc_vmsg(MANDOCERR_ARG_EXCESS,
2885 r->parse, ln, cp - buf->buf,
2886 "%s ... %s", roff_name[tok], cp);
2887 roff_word_alloc(r->man, ln, pos, buf->buf + pos);
2888 }
2889
2890 if (tok == ROFF_ce) {
2891 if (r->man->last->tok == ROFF_ce) {
2892 roff_word_alloc(r->man, ln, pos, "1");
2893 r->man->last->flags |= NODE_NOSRC;
2894 }
2895 npos = 0;
2896 if (roff_evalnum(r, ln, r->man->last->string, &npos,
2897 &roffce_lines, 0) == 0) {
2898 mandoc_vmsg(MANDOCERR_CE_NONUM,
2899 r->parse, ln, pos, "ce %s", buf->buf + pos);
2900 roffce_lines = 1;
2901 }
2902 if (roffce_lines < 1) {
2903 r->man->last = r->man->last->parent;
2904 roffce_node = NULL;
2905 roffce_lines = 0;
2906 } else
2907 roffce_node = r->man->last->parent;
2908 } else {
2909 n->flags |= NODE_VALID | NODE_ENDED;
2910 r->man->last = n;
2911 }
2912 n->flags |= NODE_LINE;
2913 r->man->next = ROFF_NEXT_SIBLING;
2914 return ROFF_IGN;
2915 }
2916
2917 static enum rofferr
2918 roff_manyarg(ROFF_ARGS)
2919 {
2920 struct roff_node *n;
2921 char *sp, *ep;
2922
2923 roff_elem_alloc(r->man, ln, ppos, tok);
2924 n = r->man->last;
2925
2926 for (sp = ep = buf->buf + pos; *sp != '\0'; sp = ep) {
2927 while (*ep != '\0' && *ep != ' ')
2928 ep++;
2929 while (*ep == ' ')
2930 *ep++ = '\0';
2931 roff_word_alloc(r->man, ln, sp - buf->buf, sp);
2932 }
2933
2934 n->flags |= NODE_LINE | NODE_VALID | NODE_ENDED;
2935 r->man->last = n;
2936 r->man->next = ROFF_NEXT_SIBLING;
2937 return ROFF_IGN;
2938 }
2939
2940 static enum rofferr
2941 roff_br(ROFF_ARGS)
2942 {
2943 if (r->man->flags & (MAN_BLINE | MAN_ELINE))
2944 man_breakscope(r->man, ROFF_br);
2945 roff_elem_alloc(r->man, ln, ppos, ROFF_br);
2946 if (buf->buf[pos] != '\0')
2947 mandoc_vmsg(MANDOCERR_ARG_SKIP, r->parse, ln, pos,
2948 "%s %s", roff_name[tok], buf->buf + pos);
2949 r->man->last->flags |= NODE_LINE | NODE_VALID | NODE_ENDED;
2950 r->man->next = ROFF_NEXT_SIBLING;
2951 return ROFF_IGN;
2952 }
2953
2954 static enum rofferr
2955 roff_cc(ROFF_ARGS)
2956 {
2957 const char *p;
2958
2959 p = buf->buf + pos;
2960
2961 if (*p == '\0' || (r->control = *p++) == '.')
2962 r->control = '\0';
2963
2964 if (*p != '\0')
2965 mandoc_vmsg(MANDOCERR_ARG_EXCESS, r->parse,
2966 ln, p - buf->buf, "cc ... %s", p);
2967
2968 return ROFF_IGN;
2969 }
2970
2971 static enum rofferr
2972 roff_ec(ROFF_ARGS)
2973 {
2974 const char *p;
2975
2976 p = buf->buf + pos;
2977 if (*p == '\0')
2978 r->escape = '\\';
2979 else {
2980 r->escape = *p;
2981 if (*++p != '\0')
2982 mandoc_vmsg(MANDOCERR_ARG_EXCESS, r->parse,
2983 ln, p - buf->buf, "ec ... %s", p);
2984 }
2985 return ROFF_IGN;
2986 }
2987
2988 static enum rofferr
2989 roff_eo(ROFF_ARGS)
2990 {
2991 r->escape = '\0';
2992 if (buf->buf[pos] != '\0')
2993 mandoc_vmsg(MANDOCERR_ARG_SKIP, r->parse,
2994 ln, pos, "eo %s", buf->buf + pos);
2995 return ROFF_IGN;
2996 }
2997
2998 static enum rofferr
2999 roff_tr(ROFF_ARGS)
3000 {
3001 const char *p, *first, *second;
3002 size_t fsz, ssz;
3003 enum mandoc_esc esc;
3004
3005 p = buf->buf + pos;
3006
3007 if (*p == '\0') {
3008 mandoc_msg(MANDOCERR_REQ_EMPTY, r->parse, ln, ppos, "tr");
3009 return ROFF_IGN;
3010 }
3011
3012 while (*p != '\0') {
3013 fsz = ssz = 1;
3014
3015 first = p++;
3016 if (*first == '\\') {
3017 esc = mandoc_escape(&p, NULL, NULL);
3018 if (esc == ESCAPE_ERROR) {
3019 mandoc_msg(MANDOCERR_ESC_BAD, r->parse,
3020 ln, (int)(p - buf->buf), first);
3021 return ROFF_IGN;
3022 }
3023 fsz = (size_t)(p - first);
3024 }
3025
3026 second = p++;
3027 if (*second == '\\') {
3028 esc = mandoc_escape(&p, NULL, NULL);
3029 if (esc == ESCAPE_ERROR) {
3030 mandoc_msg(MANDOCERR_ESC_BAD, r->parse,
3031 ln, (int)(p - buf->buf), second);
3032 return ROFF_IGN;
3033 }
3034 ssz = (size_t)(p - second);
3035 } else if (*second == '\0') {
3036 mandoc_vmsg(MANDOCERR_TR_ODD, r->parse,
3037 ln, first - buf->buf, "tr %s", first);
3038 second = " ";
3039 p--;
3040 }
3041
3042 if (fsz > 1) {
3043 roff_setstrn(&r->xmbtab, first, fsz,
3044 second, ssz, 0);
3045 continue;
3046 }
3047
3048 if (r->xtab == NULL)
3049 r->xtab = mandoc_calloc(128,
3050 sizeof(struct roffstr));
3051
3052 free(r->xtab[(int)*first].p);
3053 r->xtab[(int)*first].p = mandoc_strndup(second, ssz);
3054 r->xtab[(int)*first].sz = ssz;
3055 }
3056
3057 return ROFF_IGN;
3058 }
3059
3060 static enum rofferr
3061 roff_so(ROFF_ARGS)
3062 {
3063 char *name, *cp;
3064
3065 name = buf->buf + pos;
3066 mandoc_vmsg(MANDOCERR_SO, r->parse, ln, ppos, "so %s", name);
3067
3068 /*
3069 * Handle `so'. Be EXTREMELY careful, as we shouldn't be
3070 * opening anything that's not in our cwd or anything beneath
3071 * it. Thus, explicitly disallow traversing up the file-system
3072 * or using absolute paths.
3073 */
3074
3075 if (*name == '/' || strstr(name, "../") || strstr(name, "/..")) {
3076 mandoc_vmsg(MANDOCERR_SO_PATH, r->parse, ln, ppos,
3077 ".so %s", name);
3078 buf->sz = mandoc_asprintf(&cp,
3079 ".sp\nSee the file %s.\n.sp", name) + 1;
3080 free(buf->buf);
3081 buf->buf = cp;
3082 *offs = 0;
3083 return ROFF_REPARSE;
3084 }
3085
3086 *offs = pos;
3087 return ROFF_SO;
3088 }
3089
3090 /* --- user defined strings and macros ------------------------------------ */
3091
3092 static enum rofferr
3093 roff_userdef(ROFF_ARGS)
3094 {
3095 const char *arg[9], *ap;
3096 char *cp, *n1, *n2;
3097 int expand_count, i, ib, ie;
3098 size_t asz, rsz;
3099
3100 /*
3101 * Collect pointers to macro argument strings
3102 * and NUL-terminate them.
3103 */
3104
3105 r->argc = 0;
3106 cp = buf->buf + pos;
3107 for (i = 0; i < 9; i++) {
3108 if (*cp == '\0')
3109 arg[i] = "";
3110 else {
3111 arg[i] = mandoc_getarg(r->parse, &cp, ln, &pos);
3112 r->argc = i + 1;
3113 }
3114 }
3115
3116 /*
3117 * Expand macro arguments.
3118 */
3119
3120 buf->sz = strlen(r->current_string) + 1;
3121 n1 = n2 = cp = mandoc_malloc(buf->sz);
3122 memcpy(n1, r->current_string, buf->sz);
3123 expand_count = 0;
3124 while (*cp != '\0') {
3125
3126 /* Scan ahead for the next argument invocation. */
3127
3128 if (*cp++ != '\\')
3129 continue;
3130 if (*cp++ != '$')
3131 continue;
3132 if (*cp == '*') { /* \\$* inserts all arguments */
3133 ib = 0;
3134 ie = r->argc - 1;
3135 } else { /* \\$1 .. \\$9 insert one argument */
3136 ib = ie = *cp - '1';
3137 if (ib < 0 || ib > 8)
3138 continue;
3139 }
3140 cp -= 2;
3141
3142 /*
3143 * Prevent infinite recursion.
3144 */
3145
3146 if (cp >= n2)
3147 expand_count = 1;
3148 else if (++expand_count > EXPAND_LIMIT) {
3149 mandoc_msg(MANDOCERR_ROFFLOOP, r->parse,
3150 ln, (int)(cp - n1), NULL);
3151 free(buf->buf);
3152 buf->buf = n1;
3153 return ROFF_IGN;
3154 }
3155
3156 /*
3157 * Determine the size of the expanded argument,
3158 * taking escaping of quotes into account.
3159 */
3160
3161 asz = ie > ib ? ie - ib : 0; /* for blanks */
3162 for (i = ib; i <= ie; i++) {
3163 for (ap = arg[i]; *ap != '\0'; ap++) {
3164 asz++;
3165 if (*ap == '"')
3166 asz += 3;
3167 }
3168 }
3169 if (asz != 3) {
3170
3171 /*
3172 * Determine the size of the rest of the
3173 * unexpanded macro, including the NUL.
3174 */
3175
3176 rsz = buf->sz - (cp - n1) - 3;
3177
3178 /*
3179 * When shrinking, move before
3180 * releasing the storage.
3181 */
3182
3183 if (asz < 3)
3184 memmove(cp + asz, cp + 3, rsz);
3185
3186 /*
3187 * Resize the storage for the macro
3188 * and readjust the parse pointer.
3189 */
3190
3191 buf->sz += asz - 3;
3192 n2 = mandoc_realloc(n1, buf->sz);
3193 cp = n2 + (cp - n1);
3194 n1 = n2;
3195
3196 /*
3197 * When growing, make room
3198 * for the expanded argument.
3199 */
3200
3201 if (asz > 3)
3202 memmove(cp + asz, cp + 3, rsz);
3203 }
3204
3205 /* Copy the expanded argument, escaping quotes. */
3206
3207 n2 = cp;
3208 for (i = ib; i <= ie; i++) {
3209 for (ap = arg[i]; *ap != '\0'; ap++) {
3210 if (*ap == '"') {
3211 memcpy(n2, "\\(dq", 4);
3212 n2 += 4;
3213 } else
3214 *n2++ = *ap;
3215 }
3216 if (i < ie)
3217 *n2++ = ' ';
3218 }
3219 }
3220
3221 /*
3222 * Replace the macro invocation
3223 * by the expanded macro.
3224 */
3225
3226 free(buf->buf);
3227 buf->buf = n1;
3228 *offs = 0;
3229
3230 return buf->sz > 1 && buf->buf[buf->sz - 2] == '\n' ?
3231 ROFF_REPARSE : ROFF_APPEND;
3232 }
3233
3234 static size_t
3235 roff_getname(struct roff *r, char **cpp, int ln, int pos)
3236 {
3237 char *name, *cp;
3238 size_t namesz;
3239
3240 name = *cpp;
3241 if ('\0' == *name)
3242 return 0;
3243
3244 /* Read until end of name and terminate it with NUL. */
3245 for (cp = name; 1; cp++) {
3246 if ('\0' == *cp || ' ' == *cp) {
3247 namesz = cp - name;
3248 break;
3249 }
3250 if ('\\' != *cp)
3251 continue;
3252 namesz = cp - name;
3253 if ('{' == cp[1] || '}' == cp[1])
3254 break;
3255 cp++;
3256 if ('\\' == *cp)
3257 continue;
3258 mandoc_vmsg(MANDOCERR_NAMESC, r->parse, ln, pos,
3259 "%.*s", (int)(cp - name + 1), name);
3260 mandoc_escape((const char **)&cp, NULL, NULL);
3261 break;
3262 }
3263
3264 /* Read past spaces. */
3265 while (' ' == *cp)
3266 cp++;
3267
3268 *cpp = cp;
3269 return namesz;
3270 }
3271
3272 /*
3273 * Store *string into the user-defined string called *name.
3274 * To clear an existing entry, call with (*r, *name, NULL, 0).
3275 * append == 0: replace mode
3276 * append == 1: single-line append mode
3277 * append == 2: multiline append mode, append '\n' after each call
3278 */
3279 static void
3280 roff_setstr(struct roff *r, const char *name, const char *string,
3281 int append)
3282 {
3283
3284 roff_setstrn(&r->strtab, name, strlen(name), string,
3285 string ? strlen(string) : 0, append);
3286 }
3287
3288 static void
3289 roff_setstrn(struct roffkv **r, const char *name, size_t namesz,
3290 const char *string, size_t stringsz, int append)
3291 {
3292 struct roffkv *n;
3293 char *c;
3294 int i;
3295 size_t oldch, newch;
3296
3297 /* Search for an existing string with the same name. */
3298 n = *r;
3299
3300 while (n && (namesz != n->key.sz ||
3301 strncmp(n->key.p, name, namesz)))
3302 n = n->next;
3303
3304 if (NULL == n) {
3305 /* Create a new string table entry. */
3306 n = mandoc_malloc(sizeof(struct roffkv));
3307 n->key.p = mandoc_strndup(name, namesz);
3308 n->key.sz = namesz;
3309 n->val.p = NULL;
3310 n->val.sz = 0;
3311 n->next = *r;
3312 *r = n;
3313 } else if (0 == append) {
3314 free(n->val.p);
3315 n->val.p = NULL;
3316 n->val.sz = 0;
3317 }
3318
3319 if (NULL == string)
3320 return;
3321
3322 /*
3323 * One additional byte for the '\n' in multiline mode,
3324 * and one for the terminating '\0'.
3325 */
3326 newch = stringsz + (1 < append ? 2u : 1u);
3327
3328 if (NULL == n->val.p) {
3329 n->val.p = mandoc_malloc(newch);
3330 *n->val.p = '\0';
3331 oldch = 0;
3332 } else {
3333 oldch = n->val.sz;
3334 n->val.p = mandoc_realloc(n->val.p, oldch + newch);
3335 }
3336
3337 /* Skip existing content in the destination buffer. */
3338 c = n->val.p + (int)oldch;
3339
3340 /* Append new content to the destination buffer. */
3341 i = 0;
3342 while (i < (int)stringsz) {
3343 /*
3344 * Rudimentary roff copy mode:
3345 * Handle escaped backslashes.
3346 */
3347 if ('\\' == string[i] && '\\' == string[i + 1])
3348 i++;
3349 *c++ = string[i++];
3350 }
3351
3352 /* Append terminating bytes. */
3353 if (1 < append)
3354 *c++ = '\n';
3355
3356 *c = '\0';
3357 n->val.sz = (int)(c - n->val.p);
3358 }
3359
3360 static const char *
3361 roff_getstrn(const struct roff *r, const char *name, size_t len)
3362 {
3363 const struct roffkv *n;
3364 int i;
3365
3366 for (n = r->strtab; n; n = n->next)
3367 if (0 == strncmp(name, n->key.p, len) &&
3368 '\0' == n->key.p[(int)len])
3369 return n->val.p;
3370
3371 for (i = 0; i < PREDEFS_MAX; i++)
3372 if (0 == strncmp(name, predefs[i].name, len) &&
3373 '\0' == predefs[i].name[(int)len])
3374 return predefs[i].str;
3375
3376 return NULL;
3377 }
3378
3379 static void
3380 roff_freestr(struct roffkv *r)
3381 {
3382 struct roffkv *n, *nn;
3383
3384 for (n = r; n; n = nn) {
3385 free(n->key.p);
3386 free(n->val.p);
3387 nn = n->next;
3388 free(n);
3389 }
3390 }
3391
3392 /* --- accessors and utility functions ------------------------------------ */
3393
3394 const struct tbl_span *
3395 roff_span(const struct roff *r)
3396 {
3397
3398 return r->tbl ? tbl_span(r->tbl) : NULL;
3399 }
3400
3401 const struct eqn *
3402 roff_eqn(const struct roff *r)
3403 {
3404
3405 return r->last_eqn ? &r->last_eqn->eqn : NULL;
3406 }
3407
3408 /*
3409 * Duplicate an input string, making the appropriate character
3410 * conversations (as stipulated by `tr') along the way.
3411 * Returns a heap-allocated string with all the replacements made.
3412 */
3413 char *
3414 roff_strdup(const struct roff *r, const char *p)
3415 {
3416 const struct roffkv *cp;
3417 char *res;
3418 const char *pp;
3419 size_t ssz, sz;
3420 enum mandoc_esc esc;
3421
3422 if (NULL == r->xmbtab && NULL == r->xtab)
3423 return mandoc_strdup(p);
3424 else if ('\0' == *p)
3425 return mandoc_strdup("");
3426
3427 /*
3428 * Step through each character looking for term matches
3429 * (remember that a `tr' can be invoked with an escape, which is
3430 * a glyph but the escape is multi-character).
3431 * We only do this if the character hash has been initialised
3432 * and the string is >0 length.
3433 */
3434
3435 res = NULL;
3436 ssz = 0;
3437
3438 while ('\0' != *p) {
3439 assert((unsigned int)*p < 128);
3440 if ('\\' != *p && r->xtab && r->xtab[(unsigned int)*p].p) {
3441 sz = r->xtab[(int)*p].sz;
3442 res = mandoc_realloc(res, ssz + sz + 1);
3443 memcpy(res + ssz, r->xtab[(int)*p].p, sz);
3444 ssz += sz;
3445 p++;
3446 continue;
3447 } else if ('\\' != *p) {
3448 res = mandoc_realloc(res, ssz + 2);
3449 res[ssz++] = *p++;
3450 continue;
3451 }
3452
3453 /* Search for term matches. */
3454 for (cp = r->xmbtab; cp; cp = cp->next)
3455 if (0 == strncmp(p, cp->key.p, cp->key.sz))
3456 break;
3457
3458 if (NULL != cp) {
3459 /*
3460 * A match has been found.
3461 * Append the match to the array and move
3462 * forward by its keysize.
3463 */
3464 res = mandoc_realloc(res,
3465 ssz + cp->val.sz + 1);
3466 memcpy(res + ssz, cp->val.p, cp->val.sz);
3467 ssz += cp->val.sz;
3468 p += (int)cp->key.sz;
3469 continue;
3470 }
3471
3472 /*
3473 * Handle escapes carefully: we need to copy
3474 * over just the escape itself, or else we might
3475 * do replacements within the escape itself.
3476 * Make sure to pass along the bogus string.
3477 */
3478 pp = p++;
3479 esc = mandoc_escape(&p, NULL, NULL);
3480 if (ESCAPE_ERROR == esc) {
3481 sz = strlen(pp);
3482 res = mandoc_realloc(res, ssz + sz + 1);
3483 memcpy(res + ssz, pp, sz);
3484 break;
3485 }
3486 /*
3487 * We bail out on bad escapes.
3488 * No need to warn: we already did so when
3489 * roff_res() was called.
3490 */
3491 sz = (int)(p - pp);
3492 res = mandoc_realloc(res, ssz + sz + 1);
3493 memcpy(res + ssz, pp, sz);
3494 ssz += sz;
3495 }
3496
3497 res[(int)ssz] = '\0';
3498 return res;
3499 }
3500
3501 int
3502 roff_getformat(const struct roff *r)
3503 {
3504
3505 return r->format;
3506 }
3507
3508 /*
3509 * Find out whether a line is a macro line or not.
3510 * If it is, adjust the current position and return one; if it isn't,
3511 * return zero and don't change the current position.
3512 * If the control character has been set with `.cc', then let that grain
3513 * precedence.
3514 * This is slighly contrary to groff, where using the non-breaking
3515 * control character when `cc' has been invoked will cause the
3516 * non-breaking macro contents to be printed verbatim.
3517 */
3518 int
3519 roff_getcontrol(const struct roff *r, const char *cp, int *ppos)
3520 {
3521 int pos;
3522
3523 pos = *ppos;
3524
3525 if (r->control != '\0' && cp[pos] == r->control)
3526 pos++;
3527 else if (r->control != '\0')
3528 return 0;
3529 else if ('\\' == cp[pos] && '.' == cp[pos + 1])
3530 pos += 2;
3531 else if ('.' == cp[pos] || '\'' == cp[pos])
3532 pos++;
3533 else
3534 return 0;
3535
3536 while (' ' == cp[pos] || '\t' == cp[pos])
3537 pos++;
3538
3539 *ppos = pos;
3540 return 1;
3541 }