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