]>
git.cameronkatri.com Git - mandoc.git/blob - roff.c
7bed94293f04a895bd66738090d6d5106fbdb813
1 /* $Id: roff.c,v 1.384 2022/04/28 16:21:10 schwarze Exp $ */
3 * Copyright (c) 2010-2015, 2017-2022 Ingo Schwarze <schwarze@openbsd.org>
4 * Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
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.
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.
18 * Implementation of the roff(7) parser for mandoc(1).
22 #include <sys/types.h>
33 #include "mandoc_aux.h"
34 #include "mandoc_ohash.h"
37 #include "mandoc_parse.h"
38 #include "libmandoc.h"
40 #include "tbl_parse.h"
41 #include "eqn_parse.h"
44 * ASCII_ESC is used to signal from roff_getarg() to roff_expand()
45 * that an escape sequence resulted from copy-in processing and
46 * needs to be checked or interpolated. As it is used nowhere
47 * else, it is defined here rather than in a header file.
51 /* Maximum number of string expansions per line, to break infinite loops. */
52 #define EXPAND_LIMIT 1000
54 /* Types of definitions of macros and strings. */
55 #define ROFFDEF_USER (1 << 1) /* User-defined. */
56 #define ROFFDEF_PRE (1 << 2) /* Predefined. */
57 #define ROFFDEF_REN (1 << 3) /* Renamed standard macro. */
58 #define ROFFDEF_STD (1 << 4) /* mdoc(7) or man(7) macro. */
59 #define ROFFDEF_ANY (ROFFDEF_USER | ROFFDEF_PRE | \
60 ROFFDEF_REN | ROFFDEF_STD)
61 #define ROFFDEF_UNDEF (1 << 5) /* Completely undefined. */
63 /* --- data types --------------------------------------------------------- */
66 * An incredibly-simple string buffer.
69 char *p
; /* nil-terminated buffer */
70 size_t sz
; /* saved strlen(p) */
74 * A key-value roffstr pair as part of a singly-linked list.
79 struct roffkv
*next
; /* next in list */
83 * A single number register as part of a singly-linked list.
93 * Association of request and macro names with token IDs.
101 * A macro processing context.
102 * More than one is needed when macro calls are nested.
111 struct roff_man
*man
; /* mdoc or man parser */
112 struct roffnode
*last
; /* leaf of stack */
113 struct mctx
*mstack
; /* stack of macro contexts */
114 int *rstack
; /* stack of inverted `ie' values */
115 struct ohash
*reqtab
; /* request lookup table */
116 struct roffreg
*regtab
; /* number registers */
117 struct roffkv
*strtab
; /* user-defined strings & macros */
118 struct roffkv
*rentab
; /* renamed strings & macros */
119 struct roffkv
*xmbtab
; /* multi-byte trans table (`tr') */
120 struct roffstr
*xtab
; /* single-byte trans table (`tr') */
121 const char *current_string
; /* value of last called user macro */
122 struct tbl_node
*first_tbl
; /* first table parsed */
123 struct tbl_node
*last_tbl
; /* last table parsed */
124 struct tbl_node
*tbl
; /* current table being parsed */
125 struct eqn_node
*last_eqn
; /* equation parser */
126 struct eqn_node
*eqn
; /* active equation parser */
127 int eqn_inline
; /* current equation is inline */
128 int options
; /* parse options */
129 int mstacksz
; /* current size of mstack */
130 int mstackpos
; /* position in mstack */
131 int rstacksz
; /* current size limit of rstack */
132 int rstackpos
; /* position in rstack */
133 int format
; /* current file in mdoc or man format */
134 char control
; /* control character */
135 char escape
; /* escape character */
139 * A macro definition, condition, or ignored block.
142 enum roff_tok tok
; /* type of node */
143 struct roffnode
*parent
; /* up one in stack */
144 int line
; /* parse line */
145 int col
; /* parse col */
146 char *name
; /* node name, e.g. macro name */
147 char *end
; /* custom end macro of the block */
148 int endspan
; /* scope to: 1=eol 2=next line -1=\} */
149 int rule
; /* content is: 1=evaluated 0=skipped */
152 #define ROFF_ARGS struct roff *r, /* parse ctx */ \
153 enum roff_tok tok, /* tok of macro */ \
154 struct buf *buf, /* input buffer */ \
155 int ln, /* parse line */ \
156 int ppos, /* original pos in buffer */ \
157 int pos, /* current pos in buffer */ \
158 int *offs /* reset offset of buffer data */
160 typedef int (*roffproc
)(ROFF_ARGS
);
163 roffproc proc
; /* process new macro */
164 roffproc text
; /* process as child text of macro */
165 roffproc sub
; /* process as child of macro */
167 #define ROFFMAC_STRUCT (1 << 0) /* always interpret */
171 const char *name
; /* predefined input name */
172 const char *str
; /* replacement symbol */
175 #define PREDEF(__name, __str) \
176 { (__name), (__str) },
178 /* --- function prototypes ------------------------------------------------ */
180 static int roffnode_cleanscope(struct roff
*);
181 static int roffnode_pop(struct roff
*);
182 static void roffnode_push(struct roff
*, enum roff_tok
,
183 const char *, int, int);
184 static void roff_addtbl(struct roff_man
*, int, struct tbl_node
*);
185 static int roff_als(ROFF_ARGS
);
186 static int roff_block(ROFF_ARGS
);
187 static int roff_block_text(ROFF_ARGS
);
188 static int roff_block_sub(ROFF_ARGS
);
189 static int roff_break(ROFF_ARGS
);
190 static int roff_cblock(ROFF_ARGS
);
191 static int roff_cc(ROFF_ARGS
);
192 static int roff_ccond(struct roff
*, int, int);
193 static int roff_char(ROFF_ARGS
);
194 static int roff_cond(ROFF_ARGS
);
195 static int roff_cond_checkend(ROFF_ARGS
);
196 static int roff_cond_text(ROFF_ARGS
);
197 static int roff_cond_sub(ROFF_ARGS
);
198 static int roff_ds(ROFF_ARGS
);
199 static int roff_ec(ROFF_ARGS
);
200 static int roff_eo(ROFF_ARGS
);
201 static int roff_eqndelim(struct roff
*, struct buf
*, int);
202 static int roff_evalcond(struct roff
*, int, char *, int *);
203 static int roff_evalnum(struct roff
*, int,
204 const char *, int *, int *, int);
205 static int roff_evalpar(struct roff
*, int,
206 const char *, int *, int *, int);
207 static int roff_evalstrcond(const char *, int *);
208 static int roff_expand(struct roff
*, struct buf
*,
210 static void roff_free1(struct roff
*);
211 static void roff_freereg(struct roffreg
*);
212 static void roff_freestr(struct roffkv
*);
213 static size_t roff_getname(struct roff
*, char **, int, int);
214 static int roff_getnum(const char *, int *, int *, int);
215 static int roff_getop(const char *, int *, char *);
216 static int roff_getregn(struct roff
*,
217 const char *, size_t, char);
218 static int roff_getregro(const struct roff
*,
220 static const char *roff_getstrn(struct roff
*,
221 const char *, size_t, int *);
222 static int roff_hasregn(const struct roff
*,
223 const char *, size_t);
224 static int roff_insec(ROFF_ARGS
);
225 static int roff_it(ROFF_ARGS
);
226 static int roff_line_ignore(ROFF_ARGS
);
227 static void roff_man_alloc1(struct roff_man
*);
228 static void roff_man_free1(struct roff_man
*);
229 static int roff_manyarg(ROFF_ARGS
);
230 static int roff_mc(ROFF_ARGS
);
231 static int roff_noarg(ROFF_ARGS
);
232 static int roff_nop(ROFF_ARGS
);
233 static int roff_nr(ROFF_ARGS
);
234 static int roff_onearg(ROFF_ARGS
);
235 static enum roff_tok
roff_parse(struct roff
*, char *, int *,
237 static int roff_parsetext(struct roff
*, struct buf
*,
239 static int roff_renamed(ROFF_ARGS
);
240 static int roff_return(ROFF_ARGS
);
241 static int roff_rm(ROFF_ARGS
);
242 static int roff_rn(ROFF_ARGS
);
243 static int roff_rr(ROFF_ARGS
);
244 static void roff_setregn(struct roff
*, const char *,
245 size_t, int, char, int);
246 static void roff_setstr(struct roff
*,
247 const char *, const char *, int);
248 static void roff_setstrn(struct roffkv
**, const char *,
249 size_t, const char *, size_t, int);
250 static int roff_shift(ROFF_ARGS
);
251 static int roff_so(ROFF_ARGS
);
252 static int roff_tr(ROFF_ARGS
);
253 static int roff_Dd(ROFF_ARGS
);
254 static int roff_TE(ROFF_ARGS
);
255 static int roff_TS(ROFF_ARGS
);
256 static int roff_EQ(ROFF_ARGS
);
257 static int roff_EN(ROFF_ARGS
);
258 static int roff_T_(ROFF_ARGS
);
259 static int roff_unsupp(ROFF_ARGS
);
260 static int roff_userdef(ROFF_ARGS
);
262 /* --- constant data ------------------------------------------------------ */
264 #define ROFFNUM_SCALE (1 << 0) /* Honour scaling in roff_getnum(). */
265 #define ROFFNUM_WHITE (1 << 1) /* Skip whitespace in roff_evalnum(). */
267 const char *__roff_name
[MAN_MAX
+ 1] = {
268 "br", "ce", "fi", "ft",
272 "ab", "ad", "af", "aln",
273 "als", "am", "am1", "ami",
274 "ami1", "as", "as1", "asciify",
275 "backtrace", "bd", "bleedat", "blm",
276 "box", "boxa", "bp", "BP",
277 "break", "breakchar", "brnl", "brp",
279 "cf", "cflags", "ch", "char",
280 "chop", "class", "close", "CL",
281 "color", "composite", "continue", "cp",
282 "cropat", "cs", "cu", "da",
283 "dch", "Dd", "de", "de1",
284 "defcolor", "dei", "dei1", "device",
285 "devicem", "di", "do", "ds",
286 "ds1", "dwh", "dt", "ec",
287 "ecr", "ecs", "el", "em",
288 "EN", "eo", "EP", "EQ",
289 "errprint", "ev", "evc", "ex",
290 "fallback", "fam", "fc", "fchar",
291 "fcolor", "fdeferlig", "feature", "fkern",
292 "fl", "flig", "fp", "fps",
293 "fschar", "fspacewidth", "fspecial", "ftr",
294 "fzoom", "gcolor", "hc", "hcode",
295 "hidechar", "hla", "hlm", "hpf",
296 "hpfa", "hpfcode", "hw", "hy",
297 "hylang", "hylen", "hym", "hypp",
298 "hys", "ie", "if", "ig",
299 "index", "it", "itc", "IX",
300 "kern", "kernafter", "kernbefore", "kernpair",
301 "lc", "lc_ctype", "lds", "length",
302 "letadj", "lf", "lg", "lhang",
303 "linetabs", "lnr", "lnrf", "lpfx",
305 "mediasize", "minss", "mk", "mso",
306 "na", "ne", "nh", "nhychar",
307 "nm", "nn", "nop", "nr",
308 "nrf", "nroff", "ns", "nx",
309 "open", "opena", "os", "output",
310 "padj", "papersize", "pc", "pev",
311 "pi", "PI", "pl", "pm",
313 "psbb", "pshape", "pso", "ptr",
314 "pvs", "rchar", "rd", "recursionlimit",
315 "return", "rfschar", "rhang",
316 "rm", "rn", "rnn", "rr",
317 "rs", "rt", "schar", "sentchar",
318 "shc", "shift", "sizes", "so",
319 "spacewidth", "special", "spreadwarn", "ss",
320 "sty", "substring", "sv", "sy",
323 "tm", "tm1", "tmc", "tr",
324 "track", "transchar", "trf", "trimat",
325 "trin", "trnt", "troff", "TS",
326 "uf", "ul", "unformat", "unwatch",
327 "unwatchn", "vpt", "vs", "warn",
328 "warnscale", "watch", "watchlength", "watchn",
329 "wh", "while", "write", "writec",
330 "writem", "xflag", ".", NULL
,
332 "Dd", "Dt", "Os", "Sh",
333 "Ss", "Pp", "D1", "Dl",
334 "Bd", "Ed", "Bl", "El",
335 "It", "Ad", "An", "Ap",
336 "Ar", "Cd", "Cm", "Dv",
337 "Er", "Ev", "Ex", "Fa",
338 "Fd", "Fl", "Fn", "Ft",
339 "Ic", "In", "Li", "Nd",
340 "Nm", "Op", "Ot", "Pa",
341 "Rv", "St", "Va", "Vt",
342 "Xr", "%A", "%B", "%D",
343 "%I", "%J", "%N", "%O",
344 "%P", "%R", "%T", "%V",
345 "Ac", "Ao", "Aq", "At",
346 "Bc", "Bf", "Bo", "Bq",
347 "Bsx", "Bx", "Db", "Dc",
348 "Do", "Dq", "Ec", "Ef",
349 "Em", "Eo", "Fx", "Ms",
350 "No", "Ns", "Nx", "Ox",
351 "Pc", "Pf", "Po", "Pq",
352 "Qc", "Ql", "Qo", "Qq",
353 "Re", "Rs", "Sc", "So",
354 "Sq", "Sm", "Sx", "Sy",
355 "Tn", "Ux", "Xc", "Xo",
356 "Fo", "Fc", "Oo", "Oc",
357 "Bk", "Ek", "Bt", "Hf",
358 "Fr", "Ud", "Lb", "Lp",
359 "Lk", "Mt", "Brq", "Bro",
360 "Brc", "%C", "Es", "En",
361 "Dx", "%Q", "%U", "Ta",
363 "TH", "SH", "SS", "TP",
365 "LP", "PP", "P", "IP",
366 "HP", "SM", "SB", "BI",
367 "IB", "BR", "RB", "R",
368 "B", "I", "IR", "RI",
369 "RE", "RS", "DT", "UC",
373 "UE", "MT", "ME", NULL
375 const char *const *roff_name
= __roff_name
;
377 static struct roffmac roffs
[TOKEN_NONE
] = {
378 { roff_noarg
, NULL
, NULL
, 0 }, /* br */
379 { roff_onearg
, NULL
, NULL
, 0 }, /* ce */
380 { roff_noarg
, NULL
, NULL
, 0 }, /* fi */
381 { roff_onearg
, NULL
, NULL
, 0 }, /* ft */
382 { roff_onearg
, NULL
, NULL
, 0 }, /* ll */
383 { roff_mc
, NULL
, NULL
, 0 }, /* mc */
384 { roff_noarg
, NULL
, NULL
, 0 }, /* nf */
385 { roff_onearg
, NULL
, NULL
, 0 }, /* po */
386 { roff_onearg
, NULL
, NULL
, 0 }, /* rj */
387 { roff_onearg
, NULL
, NULL
, 0 }, /* sp */
388 { roff_manyarg
, NULL
, NULL
, 0 }, /* ta */
389 { roff_onearg
, NULL
, NULL
, 0 }, /* ti */
390 { NULL
, NULL
, NULL
, 0 }, /* ROFF_MAX */
391 { roff_unsupp
, NULL
, NULL
, 0 }, /* ab */
392 { roff_line_ignore
, NULL
, NULL
, 0 }, /* ad */
393 { roff_line_ignore
, NULL
, NULL
, 0 }, /* af */
394 { roff_unsupp
, NULL
, NULL
, 0 }, /* aln */
395 { roff_als
, NULL
, NULL
, 0 }, /* als */
396 { roff_block
, roff_block_text
, roff_block_sub
, 0 }, /* am */
397 { roff_block
, roff_block_text
, roff_block_sub
, 0 }, /* am1 */
398 { roff_block
, roff_block_text
, roff_block_sub
, 0 }, /* ami */
399 { roff_block
, roff_block_text
, roff_block_sub
, 0 }, /* ami1 */
400 { roff_ds
, NULL
, NULL
, 0 }, /* as */
401 { roff_ds
, NULL
, NULL
, 0 }, /* as1 */
402 { roff_unsupp
, NULL
, NULL
, 0 }, /* asciify */
403 { roff_line_ignore
, NULL
, NULL
, 0 }, /* backtrace */
404 { roff_line_ignore
, NULL
, NULL
, 0 }, /* bd */
405 { roff_line_ignore
, NULL
, NULL
, 0 }, /* bleedat */
406 { roff_unsupp
, NULL
, NULL
, 0 }, /* blm */
407 { roff_unsupp
, NULL
, NULL
, 0 }, /* box */
408 { roff_unsupp
, NULL
, NULL
, 0 }, /* boxa */
409 { roff_line_ignore
, NULL
, NULL
, 0 }, /* bp */
410 { roff_unsupp
, NULL
, NULL
, 0 }, /* BP */
411 { roff_break
, NULL
, NULL
, 0 }, /* break */
412 { roff_line_ignore
, NULL
, NULL
, 0 }, /* breakchar */
413 { roff_line_ignore
, NULL
, NULL
, 0 }, /* brnl */
414 { roff_noarg
, NULL
, NULL
, 0 }, /* brp */
415 { roff_line_ignore
, NULL
, NULL
, 0 }, /* brpnl */
416 { roff_unsupp
, NULL
, NULL
, 0 }, /* c2 */
417 { roff_cc
, NULL
, NULL
, 0 }, /* cc */
418 { roff_insec
, NULL
, NULL
, 0 }, /* cf */
419 { roff_line_ignore
, NULL
, NULL
, 0 }, /* cflags */
420 { roff_line_ignore
, NULL
, NULL
, 0 }, /* ch */
421 { roff_char
, NULL
, NULL
, 0 }, /* char */
422 { roff_unsupp
, NULL
, NULL
, 0 }, /* chop */
423 { roff_line_ignore
, NULL
, NULL
, 0 }, /* class */
424 { roff_insec
, NULL
, NULL
, 0 }, /* close */
425 { roff_unsupp
, NULL
, NULL
, 0 }, /* CL */
426 { roff_line_ignore
, NULL
, NULL
, 0 }, /* color */
427 { roff_unsupp
, NULL
, NULL
, 0 }, /* composite */
428 { roff_unsupp
, NULL
, NULL
, 0 }, /* continue */
429 { roff_line_ignore
, NULL
, NULL
, 0 }, /* cp */
430 { roff_line_ignore
, NULL
, NULL
, 0 }, /* cropat */
431 { roff_line_ignore
, NULL
, NULL
, 0 }, /* cs */
432 { roff_line_ignore
, NULL
, NULL
, 0 }, /* cu */
433 { roff_unsupp
, NULL
, NULL
, 0 }, /* da */
434 { roff_unsupp
, NULL
, NULL
, 0 }, /* dch */
435 { roff_Dd
, NULL
, NULL
, 0 }, /* Dd */
436 { roff_block
, roff_block_text
, roff_block_sub
, 0 }, /* de */
437 { roff_block
, roff_block_text
, roff_block_sub
, 0 }, /* de1 */
438 { roff_line_ignore
, NULL
, NULL
, 0 }, /* defcolor */
439 { roff_block
, roff_block_text
, roff_block_sub
, 0 }, /* dei */
440 { roff_block
, roff_block_text
, roff_block_sub
, 0 }, /* dei1 */
441 { roff_unsupp
, NULL
, NULL
, 0 }, /* device */
442 { roff_unsupp
, NULL
, NULL
, 0 }, /* devicem */
443 { roff_unsupp
, NULL
, NULL
, 0 }, /* di */
444 { roff_unsupp
, NULL
, NULL
, 0 }, /* do */
445 { roff_ds
, NULL
, NULL
, 0 }, /* ds */
446 { roff_ds
, NULL
, NULL
, 0 }, /* ds1 */
447 { roff_unsupp
, NULL
, NULL
, 0 }, /* dwh */
448 { roff_unsupp
, NULL
, NULL
, 0 }, /* dt */
449 { roff_ec
, NULL
, NULL
, 0 }, /* ec */
450 { roff_unsupp
, NULL
, NULL
, 0 }, /* ecr */
451 { roff_unsupp
, NULL
, NULL
, 0 }, /* ecs */
452 { roff_cond
, roff_cond_text
, roff_cond_sub
, ROFFMAC_STRUCT
}, /* el */
453 { roff_unsupp
, NULL
, NULL
, 0 }, /* em */
454 { roff_EN
, NULL
, NULL
, 0 }, /* EN */
455 { roff_eo
, NULL
, NULL
, 0 }, /* eo */
456 { roff_unsupp
, NULL
, NULL
, 0 }, /* EP */
457 { roff_EQ
, NULL
, NULL
, 0 }, /* EQ */
458 { roff_line_ignore
, NULL
, NULL
, 0 }, /* errprint */
459 { roff_unsupp
, NULL
, NULL
, 0 }, /* ev */
460 { roff_unsupp
, NULL
, NULL
, 0 }, /* evc */
461 { roff_unsupp
, NULL
, NULL
, 0 }, /* ex */
462 { roff_line_ignore
, NULL
, NULL
, 0 }, /* fallback */
463 { roff_line_ignore
, NULL
, NULL
, 0 }, /* fam */
464 { roff_unsupp
, NULL
, NULL
, 0 }, /* fc */
465 { roff_unsupp
, NULL
, NULL
, 0 }, /* fchar */
466 { roff_line_ignore
, NULL
, NULL
, 0 }, /* fcolor */
467 { roff_line_ignore
, NULL
, NULL
, 0 }, /* fdeferlig */
468 { roff_line_ignore
, NULL
, NULL
, 0 }, /* feature */
469 { roff_line_ignore
, NULL
, NULL
, 0 }, /* fkern */
470 { roff_line_ignore
, NULL
, NULL
, 0 }, /* fl */
471 { roff_line_ignore
, NULL
, NULL
, 0 }, /* flig */
472 { roff_line_ignore
, NULL
, NULL
, 0 }, /* fp */
473 { roff_line_ignore
, NULL
, NULL
, 0 }, /* fps */
474 { roff_unsupp
, NULL
, NULL
, 0 }, /* fschar */
475 { roff_line_ignore
, NULL
, NULL
, 0 }, /* fspacewidth */
476 { roff_line_ignore
, NULL
, NULL
, 0 }, /* fspecial */
477 { roff_line_ignore
, NULL
, NULL
, 0 }, /* ftr */
478 { roff_line_ignore
, NULL
, NULL
, 0 }, /* fzoom */
479 { roff_line_ignore
, NULL
, NULL
, 0 }, /* gcolor */
480 { roff_line_ignore
, NULL
, NULL
, 0 }, /* hc */
481 { roff_line_ignore
, NULL
, NULL
, 0 }, /* hcode */
482 { roff_line_ignore
, NULL
, NULL
, 0 }, /* hidechar */
483 { roff_line_ignore
, NULL
, NULL
, 0 }, /* hla */
484 { roff_line_ignore
, NULL
, NULL
, 0 }, /* hlm */
485 { roff_line_ignore
, NULL
, NULL
, 0 }, /* hpf */
486 { roff_line_ignore
, NULL
, NULL
, 0 }, /* hpfa */
487 { roff_line_ignore
, NULL
, NULL
, 0 }, /* hpfcode */
488 { roff_line_ignore
, NULL
, NULL
, 0 }, /* hw */
489 { roff_line_ignore
, NULL
, NULL
, 0 }, /* hy */
490 { roff_line_ignore
, NULL
, NULL
, 0 }, /* hylang */
491 { roff_line_ignore
, NULL
, NULL
, 0 }, /* hylen */
492 { roff_line_ignore
, NULL
, NULL
, 0 }, /* hym */
493 { roff_line_ignore
, NULL
, NULL
, 0 }, /* hypp */
494 { roff_line_ignore
, NULL
, NULL
, 0 }, /* hys */
495 { roff_cond
, roff_cond_text
, roff_cond_sub
, ROFFMAC_STRUCT
}, /* ie */
496 { roff_cond
, roff_cond_text
, roff_cond_sub
, ROFFMAC_STRUCT
}, /* if */
497 { roff_block
, roff_block_text
, roff_block_sub
, 0 }, /* ig */
498 { roff_unsupp
, NULL
, NULL
, 0 }, /* index */
499 { roff_it
, NULL
, NULL
, 0 }, /* it */
500 { roff_unsupp
, NULL
, NULL
, 0 }, /* itc */
501 { roff_line_ignore
, NULL
, NULL
, 0 }, /* IX */
502 { roff_line_ignore
, NULL
, NULL
, 0 }, /* kern */
503 { roff_line_ignore
, NULL
, NULL
, 0 }, /* kernafter */
504 { roff_line_ignore
, NULL
, NULL
, 0 }, /* kernbefore */
505 { roff_line_ignore
, NULL
, NULL
, 0 }, /* kernpair */
506 { roff_unsupp
, NULL
, NULL
, 0 }, /* lc */
507 { roff_unsupp
, NULL
, NULL
, 0 }, /* lc_ctype */
508 { roff_unsupp
, NULL
, NULL
, 0 }, /* lds */
509 { roff_unsupp
, NULL
, NULL
, 0 }, /* length */
510 { roff_line_ignore
, NULL
, NULL
, 0 }, /* letadj */
511 { roff_insec
, NULL
, NULL
, 0 }, /* lf */
512 { roff_line_ignore
, NULL
, NULL
, 0 }, /* lg */
513 { roff_line_ignore
, NULL
, NULL
, 0 }, /* lhang */
514 { roff_unsupp
, NULL
, NULL
, 0 }, /* linetabs */
515 { roff_unsupp
, NULL
, NULL
, 0 }, /* lnr */
516 { roff_unsupp
, NULL
, NULL
, 0 }, /* lnrf */
517 { roff_unsupp
, NULL
, NULL
, 0 }, /* lpfx */
518 { roff_line_ignore
, NULL
, NULL
, 0 }, /* ls */
519 { roff_unsupp
, NULL
, NULL
, 0 }, /* lsm */
520 { roff_line_ignore
, NULL
, NULL
, 0 }, /* lt */
521 { roff_line_ignore
, NULL
, NULL
, 0 }, /* mediasize */
522 { roff_line_ignore
, NULL
, NULL
, 0 }, /* minss */
523 { roff_line_ignore
, NULL
, NULL
, 0 }, /* mk */
524 { roff_insec
, NULL
, NULL
, 0 }, /* mso */
525 { roff_line_ignore
, NULL
, NULL
, 0 }, /* na */
526 { roff_line_ignore
, NULL
, NULL
, 0 }, /* ne */
527 { roff_line_ignore
, NULL
, NULL
, 0 }, /* nh */
528 { roff_line_ignore
, NULL
, NULL
, 0 }, /* nhychar */
529 { roff_unsupp
, NULL
, NULL
, 0 }, /* nm */
530 { roff_unsupp
, NULL
, NULL
, 0 }, /* nn */
531 { roff_nop
, NULL
, NULL
, 0 }, /* nop */
532 { roff_nr
, NULL
, NULL
, 0 }, /* nr */
533 { roff_unsupp
, NULL
, NULL
, 0 }, /* nrf */
534 { roff_line_ignore
, NULL
, NULL
, 0 }, /* nroff */
535 { roff_line_ignore
, NULL
, NULL
, 0 }, /* ns */
536 { roff_insec
, NULL
, NULL
, 0 }, /* nx */
537 { roff_insec
, NULL
, NULL
, 0 }, /* open */
538 { roff_insec
, NULL
, NULL
, 0 }, /* opena */
539 { roff_line_ignore
, NULL
, NULL
, 0 }, /* os */
540 { roff_unsupp
, NULL
, NULL
, 0 }, /* output */
541 { roff_line_ignore
, NULL
, NULL
, 0 }, /* padj */
542 { roff_line_ignore
, NULL
, NULL
, 0 }, /* papersize */
543 { roff_line_ignore
, NULL
, NULL
, 0 }, /* pc */
544 { roff_line_ignore
, NULL
, NULL
, 0 }, /* pev */
545 { roff_insec
, NULL
, NULL
, 0 }, /* pi */
546 { roff_unsupp
, NULL
, NULL
, 0 }, /* PI */
547 { roff_line_ignore
, NULL
, NULL
, 0 }, /* pl */
548 { roff_line_ignore
, NULL
, NULL
, 0 }, /* pm */
549 { roff_line_ignore
, NULL
, NULL
, 0 }, /* pn */
550 { roff_line_ignore
, NULL
, NULL
, 0 }, /* pnr */
551 { roff_line_ignore
, NULL
, NULL
, 0 }, /* ps */
552 { roff_unsupp
, NULL
, NULL
, 0 }, /* psbb */
553 { roff_unsupp
, NULL
, NULL
, 0 }, /* pshape */
554 { roff_insec
, NULL
, NULL
, 0 }, /* pso */
555 { roff_line_ignore
, NULL
, NULL
, 0 }, /* ptr */
556 { roff_line_ignore
, NULL
, NULL
, 0 }, /* pvs */
557 { roff_unsupp
, NULL
, NULL
, 0 }, /* rchar */
558 { roff_line_ignore
, NULL
, NULL
, 0 }, /* rd */
559 { roff_line_ignore
, NULL
, NULL
, 0 }, /* recursionlimit */
560 { roff_return
, NULL
, NULL
, 0 }, /* return */
561 { roff_unsupp
, NULL
, NULL
, 0 }, /* rfschar */
562 { roff_line_ignore
, NULL
, NULL
, 0 }, /* rhang */
563 { roff_rm
, NULL
, NULL
, 0 }, /* rm */
564 { roff_rn
, NULL
, NULL
, 0 }, /* rn */
565 { roff_unsupp
, NULL
, NULL
, 0 }, /* rnn */
566 { roff_rr
, NULL
, NULL
, 0 }, /* rr */
567 { roff_line_ignore
, NULL
, NULL
, 0 }, /* rs */
568 { roff_line_ignore
, NULL
, NULL
, 0 }, /* rt */
569 { roff_unsupp
, NULL
, NULL
, 0 }, /* schar */
570 { roff_line_ignore
, NULL
, NULL
, 0 }, /* sentchar */
571 { roff_line_ignore
, NULL
, NULL
, 0 }, /* shc */
572 { roff_shift
, NULL
, NULL
, 0 }, /* shift */
573 { roff_line_ignore
, NULL
, NULL
, 0 }, /* sizes */
574 { roff_so
, NULL
, NULL
, 0 }, /* so */
575 { roff_line_ignore
, NULL
, NULL
, 0 }, /* spacewidth */
576 { roff_line_ignore
, NULL
, NULL
, 0 }, /* special */
577 { roff_line_ignore
, NULL
, NULL
, 0 }, /* spreadwarn */
578 { roff_line_ignore
, NULL
, NULL
, 0 }, /* ss */
579 { roff_line_ignore
, NULL
, NULL
, 0 }, /* sty */
580 { roff_unsupp
, NULL
, NULL
, 0 }, /* substring */
581 { roff_line_ignore
, NULL
, NULL
, 0 }, /* sv */
582 { roff_insec
, NULL
, NULL
, 0 }, /* sy */
583 { roff_T_
, NULL
, NULL
, 0 }, /* T& */
584 { roff_unsupp
, NULL
, NULL
, 0 }, /* tc */
585 { roff_TE
, NULL
, NULL
, 0 }, /* TE */
586 { roff_Dd
, NULL
, NULL
, 0 }, /* TH */
587 { roff_line_ignore
, NULL
, NULL
, 0 }, /* tkf */
588 { roff_unsupp
, NULL
, NULL
, 0 }, /* tl */
589 { roff_line_ignore
, NULL
, NULL
, 0 }, /* tm */
590 { roff_line_ignore
, NULL
, NULL
, 0 }, /* tm1 */
591 { roff_line_ignore
, NULL
, NULL
, 0 }, /* tmc */
592 { roff_tr
, NULL
, NULL
, 0 }, /* tr */
593 { roff_line_ignore
, NULL
, NULL
, 0 }, /* track */
594 { roff_line_ignore
, NULL
, NULL
, 0 }, /* transchar */
595 { roff_insec
, NULL
, NULL
, 0 }, /* trf */
596 { roff_line_ignore
, NULL
, NULL
, 0 }, /* trimat */
597 { roff_unsupp
, NULL
, NULL
, 0 }, /* trin */
598 { roff_unsupp
, NULL
, NULL
, 0 }, /* trnt */
599 { roff_line_ignore
, NULL
, NULL
, 0 }, /* troff */
600 { roff_TS
, NULL
, NULL
, 0 }, /* TS */
601 { roff_line_ignore
, NULL
, NULL
, 0 }, /* uf */
602 { roff_line_ignore
, NULL
, NULL
, 0 }, /* ul */
603 { roff_unsupp
, NULL
, NULL
, 0 }, /* unformat */
604 { roff_line_ignore
, NULL
, NULL
, 0 }, /* unwatch */
605 { roff_line_ignore
, NULL
, NULL
, 0 }, /* unwatchn */
606 { roff_line_ignore
, NULL
, NULL
, 0 }, /* vpt */
607 { roff_line_ignore
, NULL
, NULL
, 0 }, /* vs */
608 { roff_line_ignore
, NULL
, NULL
, 0 }, /* warn */
609 { roff_line_ignore
, NULL
, NULL
, 0 }, /* warnscale */
610 { roff_line_ignore
, NULL
, NULL
, 0 }, /* watch */
611 { roff_line_ignore
, NULL
, NULL
, 0 }, /* watchlength */
612 { roff_line_ignore
, NULL
, NULL
, 0 }, /* watchn */
613 { roff_unsupp
, NULL
, NULL
, 0 }, /* wh */
614 { roff_cond
, roff_cond_text
, roff_cond_sub
, ROFFMAC_STRUCT
}, /*while*/
615 { roff_insec
, NULL
, NULL
, 0 }, /* write */
616 { roff_insec
, NULL
, NULL
, 0 }, /* writec */
617 { roff_insec
, NULL
, NULL
, 0 }, /* writem */
618 { roff_line_ignore
, NULL
, NULL
, 0 }, /* xflag */
619 { roff_cblock
, NULL
, NULL
, 0 }, /* . */
620 { roff_renamed
, NULL
, NULL
, 0 },
621 { roff_userdef
, NULL
, NULL
, 0 }
624 /* Array of injected predefined strings. */
625 #define PREDEFS_MAX 38
626 static const struct predef predefs
[PREDEFS_MAX
] = {
627 #include "predefs.in"
630 static int roffce_lines
; /* number of input lines to center */
631 static struct roff_node
*roffce_node
; /* active request */
632 static int roffit_lines
; /* number of lines to delay */
633 static char *roffit_macro
; /* nil-terminated macro line */
636 /* --- request table ------------------------------------------------------ */
639 roffhash_alloc(enum roff_tok mintok
, enum roff_tok maxtok
)
647 htab
= mandoc_malloc(sizeof(*htab
));
648 mandoc_ohash_init(htab
, 8, offsetof(struct roffreq
, name
));
650 for (tok
= mintok
; tok
< maxtok
; tok
++) {
651 if (roff_name
[tok
] == NULL
)
653 sz
= strlen(roff_name
[tok
]);
654 req
= mandoc_malloc(sizeof(*req
) + sz
+ 1);
656 memcpy(req
->name
, roff_name
[tok
], sz
+ 1);
657 slot
= ohash_qlookup(htab
, req
->name
);
658 ohash_insert(htab
, slot
, req
);
664 roffhash_free(struct ohash
*htab
)
671 for (req
= ohash_first(htab
, &slot
); req
!= NULL
;
672 req
= ohash_next(htab
, &slot
))
679 roffhash_find(struct ohash
*htab
, const char *name
, size_t sz
)
686 req
= ohash_find(htab
, ohash_qlookupi(htab
, name
, &end
));
688 req
= ohash_find(htab
, ohash_qlookup(htab
, name
));
689 return req
== NULL
? TOKEN_NONE
: req
->tok
;
692 /* --- stack of request blocks -------------------------------------------- */
695 * Pop the current node off of the stack of roff instructions currently
696 * pending. Return 1 if it is a loop or 0 otherwise.
699 roffnode_pop(struct roff
*r
)
705 inloop
= p
->tok
== ROFF_while
;
714 * Push a roff node onto the instruction stack. This must later be
715 * removed with roffnode_pop().
718 roffnode_push(struct roff
*r
, enum roff_tok tok
, const char *name
,
723 p
= mandoc_calloc(1, sizeof(struct roffnode
));
726 p
->name
= mandoc_strdup(name
);
730 p
->rule
= p
->parent
? p
->parent
->rule
: 0;
735 /* --- roff parser state data management ---------------------------------- */
738 roff_free1(struct roff
*r
)
742 tbl_free(r
->first_tbl
);
743 r
->first_tbl
= r
->last_tbl
= r
->tbl
= NULL
;
745 eqn_free(r
->last_eqn
);
746 r
->last_eqn
= r
->eqn
= NULL
;
748 while (r
->mstackpos
>= 0)
759 roff_freereg(r
->regtab
);
762 roff_freestr(r
->strtab
);
763 roff_freestr(r
->rentab
);
764 roff_freestr(r
->xmbtab
);
765 r
->strtab
= r
->rentab
= r
->xmbtab
= NULL
;
768 for (i
= 0; i
< 128; i
++)
775 roff_reset(struct roff
*r
)
778 r
->options
|= MPARSE_COMMENT
;
779 r
->format
= r
->options
& (MPARSE_MDOC
| MPARSE_MAN
);
789 roff_free(struct roff
*r
)
794 for (i
= 0; i
< r
->mstacksz
; i
++)
795 free(r
->mstack
[i
].argv
);
797 roffhash_free(r
->reqtab
);
802 roff_alloc(int options
)
806 r
= mandoc_calloc(1, sizeof(struct roff
));
807 r
->reqtab
= roffhash_alloc(0, ROFF_RENAMED
);
808 r
->options
= options
| MPARSE_COMMENT
;
809 r
->format
= options
& (MPARSE_MDOC
| MPARSE_MAN
);
816 /* --- syntax tree state data management ---------------------------------- */
819 roff_man_free1(struct roff_man
*man
)
821 if (man
->meta
.first
!= NULL
)
822 roff_node_delete(man
, man
->meta
.first
);
823 free(man
->meta
.msec
);
826 free(man
->meta
.arch
);
827 free(man
->meta
.title
);
828 free(man
->meta
.name
);
829 free(man
->meta
.date
);
830 free(man
->meta
.sodest
);
834 roff_state_reset(struct roff_man
*man
)
836 man
->last
= man
->meta
.first
;
839 man
->lastsec
= man
->lastnamed
= SEC_NONE
;
840 man
->next
= ROFF_NEXT_CHILD
;
841 roff_setreg(man
->roff
, "nS", 0, '=');
845 roff_man_alloc1(struct roff_man
*man
)
847 memset(&man
->meta
, 0, sizeof(man
->meta
));
848 man
->meta
.first
= mandoc_calloc(1, sizeof(*man
->meta
.first
));
849 man
->meta
.first
->type
= ROFFT_ROOT
;
850 man
->meta
.macroset
= MACROSET_NONE
;
851 roff_state_reset(man
);
855 roff_man_reset(struct roff_man
*man
)
858 roff_man_alloc1(man
);
862 roff_man_free(struct roff_man
*man
)
870 roff_man_alloc(struct roff
*roff
, const char *os_s
, int quick
)
872 struct roff_man
*man
;
874 man
= mandoc_calloc(1, sizeof(*man
));
878 roff_man_alloc1(man
);
883 /* --- syntax tree handling ----------------------------------------------- */
886 roff_node_alloc(struct roff_man
*man
, int line
, int pos
,
887 enum roff_type type
, int tok
)
891 n
= mandoc_calloc(1, sizeof(*n
));
896 n
->sec
= man
->lastsec
;
898 if (man
->flags
& MDOC_SYNOPSIS
)
899 n
->flags
|= NODE_SYNPRETTY
;
901 n
->flags
&= ~NODE_SYNPRETTY
;
902 if ((man
->flags
& (ROFF_NOFILL
| ROFF_NONOFILL
)) == ROFF_NOFILL
)
903 n
->flags
|= NODE_NOFILL
;
905 n
->flags
&= ~NODE_NOFILL
;
906 if (man
->flags
& MDOC_NEWLINE
)
907 n
->flags
|= NODE_LINE
;
908 man
->flags
&= ~MDOC_NEWLINE
;
914 roff_node_append(struct roff_man
*man
, struct roff_node
*n
)
918 case ROFF_NEXT_SIBLING
:
919 if (man
->last
->next
!= NULL
) {
920 n
->next
= man
->last
->next
;
921 man
->last
->next
->prev
= n
;
923 man
->last
->parent
->last
= n
;
926 n
->parent
= man
->last
->parent
;
928 case ROFF_NEXT_CHILD
:
929 if (man
->last
->child
!= NULL
) {
930 n
->next
= man
->last
->child
;
931 man
->last
->child
->prev
= n
;
934 man
->last
->child
= n
;
935 n
->parent
= man
->last
;
947 if (n
->end
!= ENDBODY_NOT
)
959 * Copy over the normalised-data pointer of our parent. Not
960 * everybody has one, but copying a null pointer is fine.
963 n
->norm
= n
->parent
->norm
;
964 assert(n
->parent
->type
== ROFFT_BLOCK
);
968 roff_word_alloc(struct roff_man
*man
, int line
, int pos
, const char *word
)
972 n
= roff_node_alloc(man
, line
, pos
, ROFFT_TEXT
, TOKEN_NONE
);
973 n
->string
= roff_strdup(man
->roff
, word
);
974 roff_node_append(man
, n
);
975 n
->flags
|= NODE_VALID
| NODE_ENDED
;
976 man
->next
= ROFF_NEXT_SIBLING
;
980 roff_word_append(struct roff_man
*man
, const char *word
)
983 char *addstr
, *newstr
;
986 addstr
= roff_strdup(man
->roff
, word
);
987 mandoc_asprintf(&newstr
, "%s %s", n
->string
, addstr
);
991 man
->next
= ROFF_NEXT_SIBLING
;
995 roff_elem_alloc(struct roff_man
*man
, int line
, int pos
, int tok
)
999 n
= roff_node_alloc(man
, line
, pos
, ROFFT_ELEM
, tok
);
1000 roff_node_append(man
, n
);
1001 man
->next
= ROFF_NEXT_CHILD
;
1005 roff_block_alloc(struct roff_man
*man
, int line
, int pos
, int tok
)
1007 struct roff_node
*n
;
1009 n
= roff_node_alloc(man
, line
, pos
, ROFFT_BLOCK
, tok
);
1010 roff_node_append(man
, n
);
1011 man
->next
= ROFF_NEXT_CHILD
;
1016 roff_head_alloc(struct roff_man
*man
, int line
, int pos
, int tok
)
1018 struct roff_node
*n
;
1020 n
= roff_node_alloc(man
, line
, pos
, ROFFT_HEAD
, tok
);
1021 roff_node_append(man
, n
);
1022 man
->next
= ROFF_NEXT_CHILD
;
1027 roff_body_alloc(struct roff_man
*man
, int line
, int pos
, int tok
)
1029 struct roff_node
*n
;
1031 n
= roff_node_alloc(man
, line
, pos
, ROFFT_BODY
, tok
);
1032 roff_node_append(man
, n
);
1033 man
->next
= ROFF_NEXT_CHILD
;
1038 roff_addtbl(struct roff_man
*man
, int line
, struct tbl_node
*tbl
)
1040 struct roff_node
*n
;
1041 struct tbl_span
*span
;
1043 if (man
->meta
.macroset
== MACROSET_MAN
)
1044 man_breakscope(man
, ROFF_TS
);
1045 while ((span
= tbl_span(tbl
)) != NULL
) {
1046 n
= roff_node_alloc(man
, line
, 0, ROFFT_TBL
, TOKEN_NONE
);
1048 roff_node_append(man
, n
);
1049 n
->flags
|= NODE_VALID
| NODE_ENDED
;
1050 man
->next
= ROFF_NEXT_SIBLING
;
1055 roff_node_unlink(struct roff_man
*man
, struct roff_node
*n
)
1058 /* Adjust siblings. */
1061 n
->prev
->next
= n
->next
;
1063 n
->next
->prev
= n
->prev
;
1065 /* Adjust parent. */
1067 if (n
->parent
!= NULL
) {
1068 if (n
->parent
->child
== n
)
1069 n
->parent
->child
= n
->next
;
1070 if (n
->parent
->last
== n
)
1071 n
->parent
->last
= n
->prev
;
1074 /* Adjust parse point. */
1078 if (man
->last
== n
) {
1079 if (n
->prev
== NULL
) {
1080 man
->last
= n
->parent
;
1081 man
->next
= ROFF_NEXT_CHILD
;
1083 man
->last
= n
->prev
;
1084 man
->next
= ROFF_NEXT_SIBLING
;
1087 if (man
->meta
.first
== n
)
1088 man
->meta
.first
= NULL
;
1092 roff_node_relink(struct roff_man
*man
, struct roff_node
*n
)
1094 roff_node_unlink(man
, n
);
1095 n
->prev
= n
->next
= NULL
;
1096 roff_node_append(man
, n
);
1100 roff_node_free(struct roff_node
*n
)
1103 if (n
->args
!= NULL
)
1104 mdoc_argv_free(n
->args
);
1105 if (n
->type
== ROFFT_BLOCK
|| n
->type
== ROFFT_ELEM
)
1107 eqn_box_free(n
->eqn
);
1114 roff_node_delete(struct roff_man
*man
, struct roff_node
*n
)
1117 while (n
->child
!= NULL
)
1118 roff_node_delete(man
, n
->child
);
1119 roff_node_unlink(man
, n
);
1124 roff_node_transparent(struct roff_node
*n
)
1128 if (n
->type
== ROFFT_COMMENT
|| n
->flags
& NODE_NOPRT
)
1130 return roff_tok_transparent(n
->tok
);
1134 roff_tok_transparent(enum roff_tok tok
)
1157 roff_node_child(struct roff_node
*n
)
1159 for (n
= n
->child
; roff_node_transparent(n
); n
= n
->next
)
1165 roff_node_prev(struct roff_node
*n
)
1169 } while (roff_node_transparent(n
));
1174 roff_node_next(struct roff_node
*n
)
1178 } while (roff_node_transparent(n
));
1183 deroff(char **dest
, const struct roff_node
*n
)
1188 if (n
->string
== NULL
) {
1189 for (n
= n
->child
; n
!= NULL
; n
= n
->next
)
1194 /* Skip leading whitespace. */
1196 for (cp
= n
->string
; *cp
!= '\0'; cp
++) {
1197 if (cp
[0] == '\\' && cp
[1] != '\0' &&
1198 strchr(" %&0^|~", cp
[1]) != NULL
)
1200 else if ( ! isspace((unsigned char)*cp
))
1204 /* Skip trailing backslash. */
1207 if (sz
> 0 && cp
[sz
- 1] == '\\')
1210 /* Skip trailing whitespace. */
1213 if ( ! isspace((unsigned char)cp
[sz
-1]))
1216 /* Skip empty strings. */
1221 if (*dest
== NULL
) {
1222 *dest
= mandoc_strndup(cp
, sz
);
1226 mandoc_asprintf(&cp
, "%s %*s", *dest
, (int)sz
, cp
);
1231 /* --- main functions of the roff parser ---------------------------------- */
1234 * In the current line, expand escape sequences that produce parsable
1235 * input text. Also check the syntax of the remaining escape sequences,
1236 * which typically produce output glyphs or change formatter state.
1239 roff_expand(struct roff
*r
, struct buf
*buf
, int ln
, int pos
, char newesc
)
1241 struct mctx
*ctx
; /* current macro call context */
1242 char ubuf
[24]; /* buffer to print the number */
1243 struct roff_node
*n
; /* used for header comments */
1244 const char *start
; /* start of the string to process */
1245 char *stesc
; /* start of an escape sequence ('\\') */
1246 const char *esct
; /* type of esccape sequence */
1247 char *ep
; /* end of comment string */
1248 const char *stnam
; /* start of the name, after "[(*" */
1249 const char *cp
; /* end of the name, e.g. before ']' */
1250 const char *res
; /* the string to be substituted */
1251 char *nbuf
; /* new buffer to copy buf->buf to */
1252 size_t maxl
; /* expected length of the escape name */
1253 size_t naml
; /* actual length of the escape name */
1254 size_t asz
; /* length of the replacement */
1255 size_t rsz
; /* length of the rest of the string */
1256 int inaml
; /* length returned from mandoc_escape() */
1257 int expand_count
; /* to avoid infinite loops */
1258 int npos
; /* position in numeric expression */
1259 int arg_complete
; /* argument not interrupted by eol */
1260 int quote_args
; /* true for \\$@, false for \\$* */
1261 int done
; /* no more input available */
1262 int deftype
; /* type of definition to paste */
1263 int rcsid
; /* kind of RCS id seen */
1264 enum mandocerr err
; /* for escape sequence problems */
1265 char sign
; /* increment number register */
1266 char term
; /* character terminating the escape */
1268 /* Search forward for comments. */
1271 start
= buf
->buf
+ pos
;
1272 for (stesc
= buf
->buf
+ pos
; *stesc
!= '\0'; stesc
++) {
1273 if (stesc
[0] != newesc
|| stesc
[1] == '\0')
1276 if (*stesc
!= '"' && *stesc
!= '#')
1279 /* Comment found, look for RCS id. */
1282 if ((cp
= strstr(stesc
, "$" "OpenBSD")) != NULL
) {
1283 rcsid
= 1 << MANDOC_OS_OPENBSD
;
1285 } else if ((cp
= strstr(stesc
, "$" "NetBSD")) != NULL
) {
1286 rcsid
= 1 << MANDOC_OS_NETBSD
;
1290 isalnum((unsigned char)*cp
) == 0 &&
1291 strchr(cp
, '$') != NULL
) {
1292 if (r
->man
->meta
.rcsids
& rcsid
)
1293 mandoc_msg(MANDOCERR_RCS_REP
, ln
,
1294 (int)(stesc
- buf
->buf
) + 1,
1296 r
->man
->meta
.rcsids
|= rcsid
;
1299 /* Handle trailing whitespace. */
1301 ep
= strchr(stesc
--, '\0') - 1;
1306 if (*ep
== ' ' || *ep
== '\t')
1307 mandoc_msg(MANDOCERR_SPACE_EOL
,
1308 ln
, (int)(ep
- buf
->buf
), NULL
);
1311 * Save comments preceding the title macro
1312 * in the syntax tree.
1315 if (newesc
!= ASCII_ESC
&& r
->options
& MPARSE_COMMENT
) {
1316 while (*ep
== ' ' || *ep
== '\t')
1319 n
= roff_node_alloc(r
->man
,
1320 ln
, stesc
+ 1 - buf
->buf
,
1321 ROFFT_COMMENT
, TOKEN_NONE
);
1322 n
->string
= mandoc_strdup(stesc
+ 2);
1323 roff_node_append(r
->man
, n
);
1324 n
->flags
|= NODE_VALID
| NODE_ENDED
;
1325 r
->man
->next
= ROFF_NEXT_SIBLING
;
1328 /* Line continuation with comment. */
1330 if (stesc
[1] == '#') {
1332 return ROFF_IGN
| ROFF_APPEND
;
1335 /* Discard normal comments. */
1337 while (stesc
> start
&& stesc
[-1] == ' ' &&
1338 (stesc
== start
+ 1 || stesc
[-2] != '\\'))
1347 /* Notice the end of the input. */
1349 if (*stesc
== '\n') {
1355 while (stesc
>= start
) {
1356 if (*stesc
!= newesc
) {
1359 * If we have a non-standard escape character,
1360 * escape literal backslashes because all
1361 * processing in subsequent functions uses
1362 * the standard escaping rules.
1365 if (newesc
!= ASCII_ESC
&& *stesc
== '\\') {
1367 buf
->sz
= mandoc_asprintf(&nbuf
, "%s\\e%s",
1368 buf
->buf
, stesc
+ 1) + 1;
1370 stesc
= nbuf
+ (stesc
- buf
->buf
);
1375 /* Search backwards for the next escape. */
1381 /* If it is escaped, skip it. */
1383 for (cp
= stesc
- 1; cp
>= start
; cp
--)
1384 if (*cp
!= r
->escape
)
1387 if ((stesc
- cp
) % 2 == 0) {
1391 } else if (stesc
[1] != '\0') {
1398 return ROFF_IGN
| ROFF_APPEND
;
1401 /* Decide whether to expand or to check only. */
1419 if (sign
== '+' || sign
== '-')
1425 switch(mandoc_escape(&cp
, &stnam
, &inaml
)) {
1426 case ESCAPE_SPECIAL
:
1427 if (mchars_spec2cp(stnam
, inaml
) >= 0)
1431 err
= MANDOCERR_ESC_BAD
;
1434 err
= MANDOCERR_ESC_UNDEF
;
1437 err
= MANDOCERR_ESC_UNSUPP
;
1442 if (err
!= MANDOCERR_OK
)
1443 mandoc_msg(err
, ln
, (int)(stesc
- buf
->buf
),
1444 "%.*s", (int)(cp
- stesc
), stesc
);
1449 if (EXPAND_LIMIT
< ++expand_count
) {
1450 mandoc_msg(MANDOCERR_ROFFLOOP
,
1451 ln
, (int)(stesc
- buf
->buf
), NULL
);
1456 * The third character decides the length
1457 * of the name of the string or register.
1458 * Save a pointer to the name.
1485 /* Advance to the end of the name. */
1489 while (maxl
== 0 || naml
< maxl
) {
1491 mandoc_msg(MANDOCERR_ESC_BAD
, ln
,
1492 (int)(stesc
- buf
->buf
), "%s", stesc
);
1496 if (maxl
== 0 && *cp
== term
) {
1500 if (*cp
++ != '\\' || *esct
!= 'w') {
1504 switch (mandoc_escape(&cp
, NULL
, NULL
)) {
1505 case ESCAPE_SPECIAL
:
1506 case ESCAPE_UNICODE
:
1507 case ESCAPE_NUMBERED
:
1509 case ESCAPE_OVERSTRIKE
:
1518 * Retrieve the replacement string; if it is
1519 * undefined, resume searching for escapes.
1525 deftype
= ROFFDEF_USER
| ROFFDEF_PRE
;
1526 res
= roff_getstrn(r
, stnam
, naml
, &deftype
);
1529 * If not overriden, let \*(.T
1530 * through to the formatters.
1533 if (res
== NULL
&& naml
== 2 &&
1534 stnam
[0] == '.' && stnam
[1] == 'T') {
1535 roff_setstrn(&r
->strtab
,
1536 ".T", 2, NULL
, 0, 0);
1543 if (r
->mstackpos
< 0) {
1544 mandoc_msg(MANDOCERR_ARG_UNDEF
, ln
,
1545 (int)(stesc
- buf
->buf
), "%.3s", stesc
);
1548 ctx
= r
->mstack
+ r
->mstackpos
;
1549 npos
= esct
[1] - '1';
1550 if (npos
>= 0 && npos
<= 8) {
1551 res
= npos
< ctx
->argc
?
1552 ctx
->argv
[npos
] : "";
1557 else if (esct
[1] == '@')
1560 mandoc_msg(MANDOCERR_ARG_NONUM
, ln
,
1561 (int)(stesc
- buf
->buf
), "%.3s", stesc
);
1565 for (npos
= 0; npos
< ctx
->argc
; npos
++) {
1569 asz
+= 2; /* quotes */
1570 asz
+= strlen(ctx
->argv
[npos
]);
1573 rsz
= buf
->sz
- (stesc
- buf
->buf
) - 3;
1575 memmove(stesc
+ asz
, stesc
+ 3, rsz
);
1577 nbuf
= mandoc_realloc(buf
->buf
, buf
->sz
);
1579 stesc
= nbuf
+ (stesc
- buf
->buf
);
1582 memmove(stesc
+ asz
, stesc
+ 3, rsz
);
1584 for (npos
= 0; npos
< ctx
->argc
; npos
++) {
1589 cp
= ctx
->argv
[npos
];
1598 ubuf
[0] = arg_complete
&&
1599 roff_evalnum(r
, ln
, stnam
, &npos
,
1600 NULL
, ROFFNUM_SCALE
) &&
1601 stnam
+ npos
+ 1 == cp
? '1' : '0';
1606 (void)snprintf(ubuf
, sizeof(ubuf
), "%d",
1607 roff_getregn(r
, stnam
, naml
, sign
));
1612 /* use even incomplete args */
1613 (void)snprintf(ubuf
, sizeof(ubuf
), "%d",
1620 mandoc_msg(MANDOCERR_STR_UNDEF
,
1621 ln
, (int)(stesc
- buf
->buf
),
1622 "%.*s", (int)naml
, stnam
);
1624 } else if (buf
->sz
+ strlen(res
) > SHRT_MAX
) {
1625 mandoc_msg(MANDOCERR_ROFFLOOP
,
1626 ln
, (int)(stesc
- buf
->buf
), NULL
);
1630 /* Replace the escape sequence by the string. */
1633 buf
->sz
= mandoc_asprintf(&nbuf
, "%s%s%s",
1634 buf
->buf
, res
, cp
) + 1;
1636 /* Prepare for the next replacement. */
1639 stesc
= nbuf
+ (stesc
- buf
->buf
) + strlen(res
);
1647 * Parse a quoted or unquoted roff-style request or macro argument.
1648 * Return a pointer to the parsed argument, which is either the original
1649 * pointer or advanced by one byte in case the argument is quoted.
1650 * NUL-terminate the argument in place.
1651 * Collapse pairs of quotes inside quoted arguments.
1652 * Advance the argument pointer to the next argument,
1653 * or to the NUL byte terminating the argument line.
1656 roff_getarg(struct roff
*r
, char **cpp
, int ln
, int *pos
)
1660 int newesc
, pairs
, quoted
, white
;
1662 /* Quoting can only start with a new word. */
1665 if ('"' == *start
) {
1670 newesc
= pairs
= white
= 0;
1671 for (cp
= start
; '\0' != *cp
; cp
++) {
1674 * Move the following text left
1675 * after quoted quotes and after "\\" and "\t".
1680 if ('\\' == cp
[0]) {
1682 * In copy mode, translate double to single
1683 * backslashes and backslash-t to literal tabs.
1694 cp
[-pairs
] = ASCII_ESC
;
1699 /* Skip escaped blanks. */
1706 } else if (0 == quoted
) {
1708 /* Unescaped blanks end unquoted args. */
1712 } else if ('"' == cp
[0]) {
1714 /* Quoted quotes collapse. */
1718 /* Unquoted quotes end quoted args. */
1725 /* Quoted argument without a closing quote. */
1727 mandoc_msg(MANDOCERR_ARG_QUOTE
, ln
, *pos
, NULL
);
1729 /* NUL-terminate this argument and move to the next one. */
1737 *pos
+= (int)(cp
- start
) + (quoted
? 1 : 0);
1740 if ('\0' == *cp
&& (white
|| ' ' == cp
[-1]))
1741 mandoc_msg(MANDOCERR_SPACE_EOL
, ln
, *pos
, NULL
);
1743 start
= mandoc_strdup(start
);
1748 buf
.sz
= strlen(start
) + 1;
1750 if (roff_expand(r
, &buf
, ln
, 0, ASCII_ESC
) & ROFF_IGN
) {
1752 buf
.buf
= mandoc_strdup("");
1759 * Process text streams.
1762 roff_parsetext(struct roff
*r
, struct buf
*buf
, int pos
, int *offs
)
1768 enum mandoc_esc esc
;
1770 /* Spring the input line trap. */
1772 if (roffit_lines
== 1) {
1773 isz
= mandoc_asprintf(&p
, "%s\n.%s", buf
->buf
, roffit_macro
);
1780 return ROFF_REPARSE
;
1781 } else if (roffit_lines
> 1)
1784 if (roffce_node
!= NULL
&& buf
->buf
[pos
] != '\0') {
1785 if (roffce_lines
< 1) {
1786 r
->man
->last
= roffce_node
;
1787 r
->man
->next
= ROFF_NEXT_SIBLING
;
1794 /* Convert all breakable hyphens into ASCII_HYPH. */
1796 start
= p
= buf
->buf
+ pos
;
1798 while (*p
!= '\0') {
1799 sz
= strcspn(p
, "-\\");
1806 /* Skip over escapes. */
1808 esc
= mandoc_escape((const char **)&p
, NULL
, NULL
);
1809 if (esc
== ESCAPE_ERROR
)
1814 } else if (p
== start
) {
1819 if (isalpha((unsigned char)p
[-1]) &&
1820 isalpha((unsigned char)p
[1]))
1828 roff_parseln(struct roff
*r
, int ln
, struct buf
*buf
, int *offs
, size_t len
)
1832 int pos
; /* parse point */
1833 int spos
; /* saved parse point for messages */
1834 int ppos
; /* original offset in buf->buf */
1835 int ctl
; /* macro line (boolean) */
1839 if (len
> 80 && r
->tbl
== NULL
&& r
->eqn
== NULL
&&
1840 (r
->man
->flags
& ROFF_NOFILL
) == 0 &&
1841 strchr(" .\\", buf
->buf
[pos
]) == NULL
&&
1842 buf
->buf
[pos
] != r
->control
&&
1843 strcspn(buf
->buf
, " ") < 80)
1844 mandoc_msg(MANDOCERR_TEXT_LONG
, ln
, (int)len
- 1,
1845 "%.20s...", buf
->buf
+ pos
);
1847 /* Handle in-line equation delimiters. */
1849 if (r
->tbl
== NULL
&&
1850 r
->last_eqn
!= NULL
&& r
->last_eqn
->delim
&&
1851 (r
->eqn
== NULL
|| r
->eqn_inline
)) {
1852 e
= roff_eqndelim(r
, buf
, pos
);
1853 if (e
== ROFF_REPARSE
)
1855 assert(e
== ROFF_CONT
);
1858 /* Expand some escape sequences. */
1860 e
= roff_expand(r
, buf
, ln
, pos
, r
->escape
);
1861 if ((e
& ROFF_MASK
) == ROFF_IGN
)
1863 assert(e
== ROFF_CONT
);
1865 ctl
= roff_getcontrol(r
, buf
->buf
, &pos
);
1868 * First, if a scope is open and we're not a macro, pass the
1869 * text through the macro's filter.
1870 * Equations process all content themselves.
1871 * Tables process almost all content themselves, but we want
1872 * to warn about macros before passing it there.
1875 if (r
->last
!= NULL
&& ! ctl
) {
1877 e
= (*roffs
[t
].text
)(r
, t
, buf
, ln
, pos
, pos
, offs
);
1878 if ((e
& ROFF_MASK
) == ROFF_IGN
)
1883 if (r
->eqn
!= NULL
&& strncmp(buf
->buf
+ ppos
, ".EN", 3)) {
1884 eqn_read(r
->eqn
, buf
->buf
+ ppos
);
1887 if (r
->tbl
!= NULL
&& (ctl
== 0 || buf
->buf
[pos
] == '\0')) {
1888 tbl_read(r
->tbl
, ln
, buf
->buf
, ppos
);
1889 roff_addtbl(r
->man
, ln
, r
->tbl
);
1893 r
->options
&= ~MPARSE_COMMENT
;
1894 return roff_parsetext(r
, buf
, pos
, offs
) | e
;
1897 /* Skip empty request lines. */
1899 if (buf
->buf
[pos
] == '"') {
1900 mandoc_msg(MANDOCERR_COMMENT_BAD
, ln
, pos
, NULL
);
1902 } else if (buf
->buf
[pos
] == '\0')
1906 * If a scope is open, go to the child handler for that macro,
1907 * as it may want to preprocess before doing anything with it.
1908 * Don't do so if an equation is open.
1913 return (*roffs
[t
].sub
)(r
, t
, buf
, ln
, ppos
, pos
, offs
);
1916 /* No scope is open. This is a new request or macro. */
1918 r
->options
&= ~MPARSE_COMMENT
;
1920 t
= roff_parse(r
, buf
->buf
, &pos
, ln
, ppos
);
1922 /* Tables ignore most macros. */
1924 if (r
->tbl
!= NULL
&& (t
== TOKEN_NONE
|| t
== ROFF_TS
||
1925 t
== ROFF_br
|| t
== ROFF_ce
|| t
== ROFF_rj
|| t
== ROFF_sp
)) {
1926 mandoc_msg(MANDOCERR_TBLMACRO
,
1927 ln
, pos
, "%s", buf
->buf
+ spos
);
1928 if (t
!= TOKEN_NONE
)
1930 while (buf
->buf
[pos
] != '\0' && buf
->buf
[pos
] != ' ')
1932 while (buf
->buf
[pos
] == ' ')
1934 tbl_read(r
->tbl
, ln
, buf
->buf
, pos
);
1935 roff_addtbl(r
->man
, ln
, r
->tbl
);
1939 /* For now, let high level macros abort .ce mode. */
1941 if (ctl
&& roffce_node
!= NULL
&&
1942 (t
== TOKEN_NONE
|| t
== ROFF_Dd
|| t
== ROFF_EQ
||
1943 t
== ROFF_TH
|| t
== ROFF_TS
)) {
1944 r
->man
->last
= roffce_node
;
1945 r
->man
->next
= ROFF_NEXT_SIBLING
;
1951 * This is neither a roff request nor a user-defined macro.
1952 * Let the standard macro set parsers handle it.
1955 if (t
== TOKEN_NONE
)
1958 /* Execute a roff request or a user defined macro. */
1960 return (*roffs
[t
].proc
)(r
, t
, buf
, ln
, spos
, pos
, offs
);
1964 * Internal interface function to tell the roff parser that execution
1965 * of the current macro ended. This is required because macro
1966 * definitions usually do not end with a .return request.
1969 roff_userret(struct roff
*r
)
1974 assert(r
->mstackpos
>= 0);
1975 ctx
= r
->mstack
+ r
->mstackpos
;
1976 for (i
= 0; i
< ctx
->argc
; i
++)
1983 roff_endparse(struct roff
*r
)
1985 if (r
->last
!= NULL
)
1986 mandoc_msg(MANDOCERR_BLK_NOEND
, r
->last
->line
,
1987 r
->last
->col
, "%s", roff_name
[r
->last
->tok
]);
1989 if (r
->eqn
!= NULL
) {
1990 mandoc_msg(MANDOCERR_BLK_NOEND
,
1991 r
->eqn
->node
->line
, r
->eqn
->node
->pos
, "EQ");
1996 if (r
->tbl
!= NULL
) {
2003 * Parse a roff node's type from the input buffer. This must be in the
2004 * form of ".foo xxx" in the usual way.
2006 static enum roff_tok
2007 roff_parse(struct roff
*r
, char *buf
, int *pos
, int ln
, int ppos
)
2017 if ('\0' == *cp
|| '"' == *cp
|| '\t' == *cp
|| ' ' == *cp
)
2021 maclen
= roff_getname(r
, &cp
, ln
, ppos
);
2023 deftype
= ROFFDEF_USER
| ROFFDEF_REN
;
2024 r
->current_string
= roff_getstrn(r
, mac
, maclen
, &deftype
);
2033 t
= roffhash_find(r
->reqtab
, mac
, maclen
);
2036 if (t
!= TOKEN_NONE
)
2038 else if (deftype
== ROFFDEF_UNDEF
) {
2039 /* Using an undefined macro defines it to be empty. */
2040 roff_setstrn(&r
->strtab
, mac
, maclen
, "", 0, 0);
2041 roff_setstrn(&r
->rentab
, mac
, maclen
, NULL
, 0, 0);
2046 /* --- handling of request blocks ----------------------------------------- */
2049 * Close a macro definition block or an "ignore" block.
2052 roff_cblock(ROFF_ARGS
)
2056 if (r
->last
== NULL
) {
2057 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, ln
, ppos
, "..");
2061 switch (r
->last
->tok
) {
2070 /* Remapped in roff_block(). */
2073 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, ln
, ppos
, "..");
2078 roffnode_cleanscope(r
);
2081 * If a conditional block with braces is still open,
2082 * check for "\}" block end markers.
2085 if (r
->last
!= NULL
&& r
->last
->endspan
< 0) {
2086 rr
= 1; /* If arguments follow "\}", warn about them. */
2087 roff_cond_checkend(r
, tok
, buf
, ln
, ppos
, pos
, &rr
);
2090 if (buf
->buf
[pos
] != '\0')
2091 mandoc_msg(MANDOCERR_ARG_SKIP
, ln
, pos
,
2092 ".. %s", buf
->buf
+ pos
);
2098 * Pop all nodes ending at the end of the current input line.
2099 * Return the number of loops ended.
2102 roffnode_cleanscope(struct roff
*r
)
2107 while (r
->last
!= NULL
&& r
->last
->endspan
> 0) {
2108 if (--r
->last
->endspan
!= 0)
2110 inloop
+= roffnode_pop(r
);
2116 * Handle the closing "\}" of a conditional block.
2117 * Apart from generating warnings, this only pops nodes.
2118 * Return the number of loops ended.
2121 roff_ccond(struct roff
*r
, int ln
, int ppos
)
2123 if (NULL
== r
->last
) {
2124 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, ln
, ppos
, "\\}");
2128 switch (r
->last
->tok
) {
2135 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, ln
, ppos
, "\\}");
2139 if (r
->last
->endspan
> -1) {
2140 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, ln
, ppos
, "\\}");
2144 return roffnode_pop(r
) + roffnode_cleanscope(r
);
2148 roff_block(ROFF_ARGS
)
2150 const char *name
, *value
;
2151 char *call
, *cp
, *iname
, *rname
;
2152 size_t csz
, namesz
, rsz
;
2155 /* Ignore groff compatibility mode for now. */
2157 if (tok
== ROFF_de1
)
2159 else if (tok
== ROFF_dei1
)
2161 else if (tok
== ROFF_am1
)
2163 else if (tok
== ROFF_ami1
)
2166 /* Parse the macro name argument. */
2168 cp
= buf
->buf
+ pos
;
2169 if (tok
== ROFF_ig
) {
2174 namesz
= roff_getname(r
, &cp
, ln
, ppos
);
2175 iname
[namesz
] = '\0';
2178 /* Resolve the macro name argument if it is indirect. */
2180 if (namesz
&& (tok
== ROFF_dei
|| tok
== ROFF_ami
)) {
2181 deftype
= ROFFDEF_USER
;
2182 name
= roff_getstrn(r
, iname
, namesz
, &deftype
);
2184 mandoc_msg(MANDOCERR_STR_UNDEF
,
2185 ln
, (int)(iname
- buf
->buf
),
2186 "%.*s", (int)namesz
, iname
);
2189 namesz
= strlen(name
);
2193 if (namesz
== 0 && tok
!= ROFF_ig
) {
2194 mandoc_msg(MANDOCERR_REQ_EMPTY
,
2195 ln
, ppos
, "%s", roff_name
[tok
]);
2199 roffnode_push(r
, tok
, name
, ln
, ppos
);
2202 * At the beginning of a `de' macro, clear the existing string
2203 * with the same name, if there is one. New content will be
2204 * appended from roff_block_text() in multiline mode.
2207 if (tok
== ROFF_de
|| tok
== ROFF_dei
) {
2208 roff_setstrn(&r
->strtab
, name
, namesz
, "", 0, 0);
2209 roff_setstrn(&r
->rentab
, name
, namesz
, NULL
, 0, 0);
2210 } else if (tok
== ROFF_am
|| tok
== ROFF_ami
) {
2211 deftype
= ROFFDEF_ANY
;
2212 value
= roff_getstrn(r
, iname
, namesz
, &deftype
);
2213 switch (deftype
) { /* Before appending, ... */
2214 case ROFFDEF_PRE
: /* copy predefined to user-defined. */
2215 roff_setstrn(&r
->strtab
, name
, namesz
,
2216 value
, strlen(value
), 0);
2218 case ROFFDEF_REN
: /* call original standard macro. */
2219 csz
= mandoc_asprintf(&call
, ".%.*s \\$* \\\"\n",
2220 (int)strlen(value
), value
);
2221 roff_setstrn(&r
->strtab
, name
, namesz
, call
, csz
, 0);
2222 roff_setstrn(&r
->rentab
, name
, namesz
, NULL
, 0, 0);
2225 case ROFFDEF_STD
: /* rename and call standard macro. */
2226 rsz
= mandoc_asprintf(&rname
, "__%s_renamed", name
);
2227 roff_setstrn(&r
->rentab
, rname
, rsz
, name
, namesz
, 0);
2228 csz
= mandoc_asprintf(&call
, ".%.*s \\$* \\\"\n",
2230 roff_setstrn(&r
->strtab
, name
, namesz
, call
, csz
, 0);
2242 /* Get the custom end marker. */
2245 namesz
= roff_getname(r
, &cp
, ln
, ppos
);
2247 /* Resolve the end marker if it is indirect. */
2249 if (namesz
&& (tok
== ROFF_dei
|| tok
== ROFF_ami
)) {
2250 deftype
= ROFFDEF_USER
;
2251 name
= roff_getstrn(r
, iname
, namesz
, &deftype
);
2253 mandoc_msg(MANDOCERR_STR_UNDEF
,
2254 ln
, (int)(iname
- buf
->buf
),
2255 "%.*s", (int)namesz
, iname
);
2258 namesz
= strlen(name
);
2263 r
->last
->end
= mandoc_strndup(name
, namesz
);
2266 mandoc_msg(MANDOCERR_ARG_EXCESS
,
2267 ln
, pos
, ".%s ... %s", roff_name
[tok
], cp
);
2273 roff_block_sub(ROFF_ARGS
)
2279 * First check whether a custom macro exists at this level. If
2280 * it does, then check against it. This is some of groff's
2281 * stranger behaviours. If we encountered a custom end-scope
2282 * tag and that tag also happens to be a "real" macro, then we
2283 * need to try interpreting it again as a real macro. If it's
2284 * not, then return ignore. Else continue.
2288 for (i
= pos
, j
= 0; r
->last
->end
[j
]; j
++, i
++)
2289 if (buf
->buf
[i
] != r
->last
->end
[j
])
2292 if (r
->last
->end
[j
] == '\0' &&
2293 (buf
->buf
[i
] == '\0' ||
2294 buf
->buf
[i
] == ' ' ||
2295 buf
->buf
[i
] == '\t')) {
2297 roffnode_cleanscope(r
);
2299 while (buf
->buf
[i
] == ' ' || buf
->buf
[i
] == '\t')
2303 if (roff_parse(r
, buf
->buf
, &pos
, ln
, ppos
) !=
2311 * If we have no custom end-query or lookup failed, then try
2312 * pulling it out of the hashtable.
2315 t
= roff_parse(r
, buf
->buf
, &pos
, ln
, ppos
);
2317 if (t
!= ROFF_cblock
) {
2319 roff_setstr(r
, r
->last
->name
, buf
->buf
+ ppos
, 2);
2323 return (*roffs
[t
].proc
)(r
, t
, buf
, ln
, ppos
, pos
, offs
);
2327 roff_block_text(ROFF_ARGS
)
2331 roff_setstr(r
, r
->last
->name
, buf
->buf
+ pos
, 2);
2337 * Check for a closing "\}" and handle it.
2338 * In this function, the final "int *offs" argument is used for
2339 * different purposes than elsewhere:
2340 * Input: *offs == 0: caller wants to discard arguments following \}
2341 * *offs == 1: caller wants to preserve text following \}
2342 * Output: *offs = 0: tell caller to discard input line
2343 * *offs = 1: tell caller to use input line
2346 roff_cond_checkend(ROFF_ARGS
)
2349 int endloop
, irc
, rr
;
2353 endloop
= tok
!= ROFF_while
? ROFF_IGN
:
2354 rr
? ROFF_LOOPCONT
: ROFF_LOOPEXIT
;
2355 if (roffnode_cleanscope(r
))
2359 * If "\}" occurs on a macro line without a preceding macro or
2360 * a text line contains nothing else, drop the line completely.
2363 ep
= buf
->buf
+ pos
;
2364 if (ep
[0] == '\\' && ep
[1] == '}' && (ep
[2] == '\0' || *offs
== 0))
2368 * The closing delimiter "\}" rewinds the conditional scope
2369 * but is otherwise ignored when interpreting the line.
2372 while ((ep
= strchr(ep
, '\\')) != NULL
) {
2380 memmove(ep
, ep
+ 2, strlen(ep
+ 2) + 1);
2381 if (roff_ccond(r
, ln
, ep
- buf
->buf
))
2397 * Parse and process a request or macro line in conditional scope.
2400 roff_cond_sub(ROFF_ARGS
)
2402 struct roffnode
*bl
;
2406 rr
= 0; /* If arguments follow "\}", skip them. */
2407 irc
= roff_cond_checkend(r
, tok
, buf
, ln
, ppos
, pos
, &rr
);
2408 t
= roff_parse(r
, buf
->buf
, &pos
, ln
, ppos
);
2410 /* For now, let high level macros abort .ce mode. */
2412 if (roffce_node
!= NULL
&&
2413 (t
== TOKEN_NONE
|| t
== ROFF_Dd
|| t
== ROFF_EQ
||
2414 t
== ROFF_TH
|| t
== ROFF_TS
)) {
2415 r
->man
->last
= roffce_node
;
2416 r
->man
->next
= ROFF_NEXT_SIBLING
;
2422 * Fully handle known macros when they are structurally
2423 * required or when the conditional evaluated to true.
2426 if (t
== ROFF_break
) {
2427 if (irc
& ROFF_LOOPMASK
)
2428 irc
= ROFF_IGN
| ROFF_LOOPEXIT
;
2430 for (bl
= r
->last
; bl
!= NULL
; bl
= bl
->parent
) {
2432 if (bl
->tok
== ROFF_while
)
2436 } else if (t
!= TOKEN_NONE
&&
2437 (rr
|| roffs
[t
].flags
& ROFFMAC_STRUCT
)) {
2438 irc
|= (*roffs
[t
].proc
)(r
, t
, buf
, ln
, ppos
, pos
, offs
);
2439 if (irc
& ROFF_WHILE
)
2440 irc
&= ~(ROFF_LOOPCONT
| ROFF_LOOPEXIT
);
2442 irc
|= rr
? ROFF_CONT
: ROFF_IGN
;
2447 * Parse and process a text line in conditional scope.
2450 roff_cond_text(ROFF_ARGS
)
2454 rr
= 1; /* If arguments follow "\}", preserve them. */
2455 irc
= roff_cond_checkend(r
, tok
, buf
, ln
, ppos
, pos
, &rr
);
2461 /* --- handling of numeric and conditional expressions -------------------- */
2464 * Parse a single signed integer number. Stop at the first non-digit.
2465 * If there is at least one digit, return success and advance the
2466 * parse point, else return failure and let the parse point unchanged.
2467 * Ignore overflows, treat them just like the C language.
2470 roff_getnum(const char *v
, int *pos
, int *res
, int flags
)
2472 int myres
, scaled
, n
, p
;
2479 if (n
|| v
[p
] == '+')
2482 if (flags
& ROFFNUM_WHITE
)
2483 while (isspace((unsigned char)v
[p
]))
2486 for (*res
= 0; isdigit((unsigned char)v
[p
]); p
++)
2487 *res
= 10 * *res
+ v
[p
] - '0';
2494 /* Each number may be followed by one optional scaling unit. */
2498 scaled
= *res
* 65536;
2501 scaled
= *res
* 240;
2504 scaled
= *res
* 240 / 2.54;
2515 scaled
= *res
* 10 / 3;
2521 scaled
= *res
* 6 / 25;
2528 if (flags
& ROFFNUM_SCALE
)
2536 * Evaluate a string comparison condition.
2537 * The first character is the delimiter.
2538 * Succeed if the string up to its second occurrence
2539 * matches the string up to its third occurence.
2540 * Advance the cursor after the third occurrence
2541 * or lacking that, to the end of the line.
2544 roff_evalstrcond(const char *v
, int *pos
)
2546 const char *s1
, *s2
, *s3
;
2550 s1
= v
+ *pos
; /* initial delimiter */
2551 s2
= s1
+ 1; /* for scanning the first string */
2552 s3
= strchr(s2
, *s1
); /* for scanning the second string */
2554 if (NULL
== s3
) /* found no middle delimiter */
2557 while ('\0' != *++s3
) {
2558 if (*s2
!= *s3
) { /* mismatch */
2559 s3
= strchr(s3
, *s1
);
2562 if (*s3
== *s1
) { /* found the final delimiter */
2571 s3
= strchr(s2
, '\0');
2572 else if (*s3
!= '\0')
2579 * Evaluate an optionally negated single character, numerical,
2580 * or string condition.
2583 roff_evalcond(struct roff
*r
, int ln
, char *v
, int *pos
)
2585 const char *start
, *end
;
2588 int deftype
, len
, number
, savepos
, istrue
, wanttrue
;
2590 if ('!' == v
[*pos
]) {
2611 } while (v
[*pos
] == ' ');
2614 * Quirk for groff compatibility:
2615 * The horizontal tab is neither available nor unavailable.
2618 if (v
[*pos
] == '\t') {
2623 /* Printable ASCII characters are available. */
2625 if (v
[*pos
] != '\\') {
2631 switch (mandoc_escape(&end
, &start
, &len
)) {
2632 case ESCAPE_SPECIAL
:
2633 istrue
= mchars_spec2cp(start
, len
) != -1;
2635 case ESCAPE_UNICODE
:
2638 case ESCAPE_NUMBERED
:
2639 istrue
= mchars_num2char(start
, len
) != -1;
2646 return istrue
== wanttrue
;
2653 sz
= roff_getname(r
, &cp
, ln
, cp
- v
);
2656 else if (v
[*pos
] == 'r')
2657 istrue
= roff_hasregn(r
, name
, sz
);
2659 deftype
= ROFFDEF_ANY
;
2660 roff_getstrn(r
, name
, sz
, &deftype
);
2663 *pos
= (name
+ sz
) - v
;
2664 return istrue
== wanttrue
;
2670 if (roff_evalnum(r
, ln
, v
, pos
, &number
, ROFFNUM_SCALE
))
2671 return (number
> 0) == wanttrue
;
2672 else if (*pos
== savepos
)
2673 return roff_evalstrcond(v
, pos
) == wanttrue
;
2679 roff_line_ignore(ROFF_ARGS
)
2686 roff_insec(ROFF_ARGS
)
2689 mandoc_msg(MANDOCERR_REQ_INSEC
, ln
, ppos
, "%s", roff_name
[tok
]);
2694 roff_unsupp(ROFF_ARGS
)
2697 mandoc_msg(MANDOCERR_REQ_UNSUPP
, ln
, ppos
, "%s", roff_name
[tok
]);
2702 roff_cond(ROFF_ARGS
)
2706 roffnode_push(r
, tok
, NULL
, ln
, ppos
);
2709 * An `.el' has no conditional body: it will consume the value
2710 * of the current rstack entry set in prior `ie' calls or
2713 * If we're not an `el', however, then evaluate the conditional.
2716 r
->last
->rule
= tok
== ROFF_el
?
2717 (r
->rstackpos
< 0 ? 0 : r
->rstack
[r
->rstackpos
--]) :
2718 roff_evalcond(r
, ln
, buf
->buf
, &pos
);
2721 * An if-else will put the NEGATION of the current evaluated
2722 * conditional into the stack of rules.
2725 if (tok
== ROFF_ie
) {
2726 if (r
->rstackpos
+ 1 == r
->rstacksz
) {
2728 r
->rstack
= mandoc_reallocarray(r
->rstack
,
2729 r
->rstacksz
, sizeof(int));
2731 r
->rstack
[++r
->rstackpos
] = !r
->last
->rule
;
2734 /* If the parent has false as its rule, then so do we. */
2736 if (r
->last
->parent
&& !r
->last
->parent
->rule
)
2741 * If there is nothing on the line after the conditional,
2742 * not even whitespace, use next-line scope.
2743 * Except that .while does not support next-line scope.
2746 if (buf
->buf
[pos
] == '\0' && tok
!= ROFF_while
) {
2747 r
->last
->endspan
= 2;
2751 while (buf
->buf
[pos
] == ' ')
2754 /* An opening brace requests multiline scope. */
2756 if (buf
->buf
[pos
] == '\\' && buf
->buf
[pos
+ 1] == '{') {
2757 r
->last
->endspan
= -1;
2759 while (buf
->buf
[pos
] == ' ')
2765 * Anything else following the conditional causes
2766 * single-line scope. Warn if the scope contains
2767 * nothing but trailing whitespace.
2770 if (buf
->buf
[pos
] == '\0')
2771 mandoc_msg(MANDOCERR_COND_EMPTY
,
2772 ln
, ppos
, "%s", roff_name
[tok
]);
2774 r
->last
->endspan
= 1;
2779 if (tok
== ROFF_while
)
2791 /* Ignore groff compatibility mode for now. */
2793 if (tok
== ROFF_ds1
)
2795 else if (tok
== ROFF_as1
)
2799 * The first word is the name of the string.
2800 * If it is empty or terminated by an escape sequence,
2801 * abort the `ds' request without defining anything.
2804 name
= string
= buf
->buf
+ pos
;
2808 namesz
= roff_getname(r
, &string
, ln
, pos
);
2809 switch (name
[namesz
]) {
2813 string
= buf
->buf
+ pos
+ namesz
;
2819 /* Read past the initial double-quote, if any. */
2823 /* The rest is the value. */
2824 roff_setstrn(&r
->strtab
, name
, namesz
, string
, strlen(string
),
2826 roff_setstrn(&r
->rentab
, name
, namesz
, NULL
, 0, 0);
2831 * Parse a single operator, one or two characters long.
2832 * If the operator is recognized, return success and advance the
2833 * parse point, else return failure and let the parse point unchanged.
2836 roff_getop(const char *v
, int *pos
, char *res
)
2851 switch (v
[*pos
+ 1]) {
2869 switch (v
[*pos
+ 1]) {
2883 if ('=' == v
[*pos
+ 1])
2895 * Evaluate either a parenthesized numeric expression
2896 * or a single signed integer number.
2899 roff_evalpar(struct roff
*r
, int ln
,
2900 const char *v
, int *pos
, int *res
, int flags
)
2904 return roff_getnum(v
, pos
, res
, flags
);
2907 if ( ! roff_evalnum(r
, ln
, v
, pos
, res
, flags
| ROFFNUM_WHITE
))
2911 * Omission of the closing parenthesis
2912 * is an error in validation mode,
2913 * but ignored in evaluation mode.
2918 else if (NULL
== res
)
2925 * Evaluate a complete numeric expression.
2926 * Proceed left to right, there is no concept of precedence.
2929 roff_evalnum(struct roff
*r
, int ln
, const char *v
,
2930 int *pos
, int *res
, int flags
)
2932 int mypos
, operand2
;
2940 if (flags
& ROFFNUM_WHITE
)
2941 while (isspace((unsigned char)v
[*pos
]))
2944 if ( ! roff_evalpar(r
, ln
, v
, pos
, res
, flags
))
2948 if (flags
& ROFFNUM_WHITE
)
2949 while (isspace((unsigned char)v
[*pos
]))
2952 if ( ! roff_getop(v
, pos
, &operator))
2955 if (flags
& ROFFNUM_WHITE
)
2956 while (isspace((unsigned char)v
[*pos
]))
2959 if ( ! roff_evalpar(r
, ln
, v
, pos
, &operand2
, flags
))
2962 if (flags
& ROFFNUM_WHITE
)
2963 while (isspace((unsigned char)v
[*pos
]))
2980 if (operand2
== 0) {
2981 mandoc_msg(MANDOCERR_DIVZERO
,
2989 if (operand2
== 0) {
2990 mandoc_msg(MANDOCERR_DIVZERO
,
2998 *res
= *res
< operand2
;
3001 *res
= *res
> operand2
;
3004 *res
= *res
<= operand2
;
3007 *res
= *res
>= operand2
;
3010 *res
= *res
== operand2
;
3013 *res
= *res
!= operand2
;
3016 *res
= *res
&& operand2
;
3019 *res
= *res
|| operand2
;
3022 if (operand2
< *res
)
3026 if (operand2
> *res
)
3036 /* --- register management ------------------------------------------------ */
3039 roff_setreg(struct roff
*r
, const char *name
, int val
, char sign
)
3041 roff_setregn(r
, name
, strlen(name
), val
, sign
, INT_MIN
);
3045 roff_setregn(struct roff
*r
, const char *name
, size_t len
,
3046 int val
, char sign
, int step
)
3048 struct roffreg
*reg
;
3050 /* Search for an existing register with the same name. */
3053 while (reg
!= NULL
&& (reg
->key
.sz
!= len
||
3054 strncmp(reg
->key
.p
, name
, len
) != 0))
3058 /* Create a new register. */
3059 reg
= mandoc_malloc(sizeof(struct roffreg
));
3060 reg
->key
.p
= mandoc_strndup(name
, len
);
3064 reg
->next
= r
->regtab
;
3070 else if ('-' == sign
)
3074 if (step
!= INT_MIN
)
3079 * Handle some predefined read-only number registers.
3080 * For now, return -1 if the requested register is not predefined;
3081 * in case a predefined read-only register having the value -1
3082 * were to turn up, another special value would have to be chosen.
3085 roff_getregro(const struct roff
*r
, const char *name
)
3089 case '$': /* Number of arguments of the last macro evaluated. */
3090 return r
->mstackpos
< 0 ? 0 : r
->mstack
[r
->mstackpos
].argc
;
3091 case 'A': /* ASCII approximation mode is always off. */
3093 case 'g': /* Groff compatibility mode is always on. */
3095 case 'H': /* Fixed horizontal resolution. */
3097 case 'j': /* Always adjust left margin only. */
3099 case 'T': /* Some output device is always defined. */
3101 case 'V': /* Fixed vertical resolution. */
3109 roff_getreg(struct roff
*r
, const char *name
)
3111 return roff_getregn(r
, name
, strlen(name
), '\0');
3115 roff_getregn(struct roff
*r
, const char *name
, size_t len
, char sign
)
3117 struct roffreg
*reg
;
3120 if ('.' == name
[0] && 2 == len
) {
3121 val
= roff_getregro(r
, name
+ 1);
3126 for (reg
= r
->regtab
; reg
; reg
= reg
->next
) {
3127 if (len
== reg
->key
.sz
&&
3128 0 == strncmp(name
, reg
->key
.p
, len
)) {
3131 reg
->val
+= reg
->step
;
3134 reg
->val
-= reg
->step
;
3143 roff_setregn(r
, name
, len
, 0, '\0', INT_MIN
);
3148 roff_hasregn(const struct roff
*r
, const char *name
, size_t len
)
3150 struct roffreg
*reg
;
3153 if ('.' == name
[0] && 2 == len
) {
3154 val
= roff_getregro(r
, name
+ 1);
3159 for (reg
= r
->regtab
; reg
; reg
= reg
->next
)
3160 if (len
== reg
->key
.sz
&&
3161 0 == strncmp(name
, reg
->key
.p
, len
))
3168 roff_freereg(struct roffreg
*reg
)
3170 struct roffreg
*old_reg
;
3172 while (NULL
!= reg
) {
3183 char *key
, *val
, *step
;
3188 key
= val
= buf
->buf
+ pos
;
3192 keysz
= roff_getname(r
, &val
, ln
, pos
);
3193 if (key
[keysz
] == '\\' || key
[keysz
] == '\t')
3197 if (sign
== '+' || sign
== '-')
3201 if (roff_evalnum(r
, ln
, val
, &len
, &iv
, ROFFNUM_SCALE
) == 0)
3205 while (isspace((unsigned char)*step
))
3207 if (roff_evalnum(r
, ln
, step
, NULL
, &is
, 0) == 0)
3210 roff_setregn(r
, key
, keysz
, iv
, sign
, is
);
3217 struct roffreg
*reg
, **prev
;
3221 name
= cp
= buf
->buf
+ pos
;
3224 namesz
= roff_getname(r
, &cp
, ln
, pos
);
3225 name
[namesz
] = '\0';
3230 if (reg
== NULL
|| !strcmp(name
, reg
->key
.p
))
3242 /* --- handler functions for roff requests -------------------------------- */
3251 cp
= buf
->buf
+ pos
;
3252 while (*cp
!= '\0') {
3254 namesz
= roff_getname(r
, &cp
, ln
, (int)(cp
- buf
->buf
));
3255 roff_setstrn(&r
->strtab
, name
, namesz
, NULL
, 0, 0);
3256 roff_setstrn(&r
->rentab
, name
, namesz
, NULL
, 0, 0);
3257 if (name
[namesz
] == '\\' || name
[namesz
] == '\t')
3268 /* Parse the number of lines. */
3270 if ( ! roff_evalnum(r
, ln
, buf
->buf
, &pos
, &iv
, 0)) {
3271 mandoc_msg(MANDOCERR_IT_NONUM
,
3272 ln
, ppos
, "%s", buf
->buf
+ 1);
3276 while (isspace((unsigned char)buf
->buf
[pos
]))
3280 * Arm the input line trap.
3281 * Special-casing "an-trap" is an ugly workaround to cope
3282 * with DocBook stupidly fiddling with man(7) internals.
3286 roffit_macro
= mandoc_strdup(iv
!= 1 ||
3287 strcmp(buf
->buf
+ pos
, "an-trap") ?
3288 buf
->buf
+ pos
: "br");
3296 enum roff_tok t
, te
;
3303 r
->format
= MPARSE_MDOC
;
3304 mask
= MPARSE_MDOC
| MPARSE_QUICK
;
3310 r
->format
= MPARSE_MAN
;
3311 mask
= MPARSE_QUICK
;
3316 if ((r
->options
& mask
) == 0)
3317 for (t
= tok
; t
< te
; t
++)
3318 roff_setstr(r
, roff_name
[t
], NULL
, 0);
3325 r
->man
->flags
&= ~ROFF_NONOFILL
;
3326 if (r
->tbl
== NULL
) {
3327 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, ln
, ppos
, "TE");
3330 if (tbl_end(r
->tbl
, 0) == 0) {
3333 buf
->buf
= mandoc_strdup(".sp");
3336 return ROFF_REPARSE
;
3347 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, ln
, ppos
, "T&");
3349 tbl_restart(ln
, ppos
, r
->tbl
);
3355 * Handle in-line equation delimiters.
3358 roff_eqndelim(struct roff
*r
, struct buf
*buf
, int pos
)
3361 const char *bef_pr
, *bef_nl
, *mac
, *aft_nl
, *aft_pr
;
3364 * Outside equations, look for an opening delimiter.
3365 * If we are inside an equation, we already know it is
3366 * in-line, or this function wouldn't have been called;
3367 * so look for a closing delimiter.
3370 cp1
= buf
->buf
+ pos
;
3371 cp2
= strchr(cp1
, r
->eqn
== NULL
?
3372 r
->last_eqn
->odelim
: r
->last_eqn
->cdelim
);
3377 bef_pr
= bef_nl
= aft_nl
= aft_pr
= "";
3379 /* Handle preceding text, protecting whitespace. */
3381 if (*buf
->buf
!= '\0') {
3388 * Prepare replacing the delimiter with an equation macro
3389 * and drop leading white space from the equation.
3392 if (r
->eqn
== NULL
) {
3399 /* Handle following text, protecting whitespace. */
3407 /* Do the actual replacement. */
3409 buf
->sz
= mandoc_asprintf(&cp1
, "%s%s%s%s%s%s%s", buf
->buf
,
3410 bef_pr
, bef_nl
, mac
, aft_nl
, aft_pr
, cp2
) + 1;
3414 /* Toggle the in-line state of the eqn subsystem. */
3416 r
->eqn_inline
= r
->eqn
== NULL
;
3417 return ROFF_REPARSE
;
3423 struct roff_node
*n
;
3425 if (r
->man
->meta
.macroset
== MACROSET_MAN
)
3426 man_breakscope(r
->man
, ROFF_EQ
);
3427 n
= roff_node_alloc(r
->man
, ln
, ppos
, ROFFT_EQN
, TOKEN_NONE
);
3428 if (ln
> r
->man
->last
->line
)
3429 n
->flags
|= NODE_LINE
;
3430 n
->eqn
= eqn_box_new();
3431 roff_node_append(r
->man
, n
);
3432 r
->man
->next
= ROFF_NEXT_SIBLING
;
3434 assert(r
->eqn
== NULL
);
3435 if (r
->last_eqn
== NULL
)
3436 r
->last_eqn
= eqn_alloc();
3438 eqn_reset(r
->last_eqn
);
3439 r
->eqn
= r
->last_eqn
;
3442 if (buf
->buf
[pos
] != '\0')
3443 mandoc_msg(MANDOCERR_ARG_SKIP
, ln
, pos
,
3444 ".EQ %s", buf
->buf
+ pos
);
3452 if (r
->eqn
!= NULL
) {
3456 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, ln
, ppos
, "EN");
3457 if (buf
->buf
[pos
] != '\0')
3458 mandoc_msg(MANDOCERR_ARG_SKIP
, ln
, pos
,
3459 "EN %s", buf
->buf
+ pos
);
3466 if (r
->tbl
!= NULL
) {
3467 mandoc_msg(MANDOCERR_BLK_BROKEN
, ln
, ppos
, "TS breaks TS");
3470 r
->man
->flags
|= ROFF_NONOFILL
;
3471 r
->tbl
= tbl_alloc(ppos
, ln
, r
->last_tbl
);
3472 if (r
->last_tbl
== NULL
)
3473 r
->first_tbl
= r
->tbl
;
3474 r
->last_tbl
= r
->tbl
;
3479 roff_noarg(ROFF_ARGS
)
3481 if (r
->man
->flags
& (MAN_BLINE
| MAN_ELINE
))
3482 man_breakscope(r
->man
, tok
);
3483 if (tok
== ROFF_brp
)
3485 roff_elem_alloc(r
->man
, ln
, ppos
, tok
);
3486 if (buf
->buf
[pos
] != '\0')
3487 mandoc_msg(MANDOCERR_ARG_SKIP
, ln
, pos
,
3488 "%s %s", roff_name
[tok
], buf
->buf
+ pos
);
3490 r
->man
->flags
|= ROFF_NOFILL
;
3491 else if (tok
== ROFF_fi
)
3492 r
->man
->flags
&= ~ROFF_NOFILL
;
3493 r
->man
->last
->flags
|= NODE_LINE
| NODE_VALID
| NODE_ENDED
;
3494 r
->man
->next
= ROFF_NEXT_SIBLING
;
3499 roff_onearg(ROFF_ARGS
)
3501 struct roff_node
*n
;
3505 if (r
->man
->flags
& (MAN_BLINE
| MAN_ELINE
) &&
3506 (tok
== ROFF_ce
|| tok
== ROFF_rj
|| tok
== ROFF_sp
||
3508 man_breakscope(r
->man
, tok
);
3510 if (roffce_node
!= NULL
&& (tok
== ROFF_ce
|| tok
== ROFF_rj
)) {
3511 r
->man
->last
= roffce_node
;
3512 r
->man
->next
= ROFF_NEXT_SIBLING
;
3515 roff_elem_alloc(r
->man
, ln
, ppos
, tok
);
3518 cp
= buf
->buf
+ pos
;
3520 while (*cp
!= '\0' && *cp
!= ' ')
3525 mandoc_msg(MANDOCERR_ARG_EXCESS
,
3526 ln
, (int)(cp
- buf
->buf
),
3527 "%s ... %s", roff_name
[tok
], cp
);
3528 roff_word_alloc(r
->man
, ln
, pos
, buf
->buf
+ pos
);
3531 if (tok
== ROFF_ce
|| tok
== ROFF_rj
) {
3532 if (r
->man
->last
->type
== ROFFT_ELEM
) {
3533 roff_word_alloc(r
->man
, ln
, pos
, "1");
3534 r
->man
->last
->flags
|= NODE_NOSRC
;
3537 if (roff_evalnum(r
, ln
, r
->man
->last
->string
, &npos
,
3538 &roffce_lines
, 0) == 0) {
3539 mandoc_msg(MANDOCERR_CE_NONUM
,
3540 ln
, pos
, "ce %s", buf
->buf
+ pos
);
3543 if (roffce_lines
< 1) {
3544 r
->man
->last
= r
->man
->last
->parent
;
3548 roffce_node
= r
->man
->last
->parent
;
3550 n
->flags
|= NODE_VALID
| NODE_ENDED
;
3553 n
->flags
|= NODE_LINE
;
3554 r
->man
->next
= ROFF_NEXT_SIBLING
;
3559 roff_manyarg(ROFF_ARGS
)
3561 struct roff_node
*n
;
3564 roff_elem_alloc(r
->man
, ln
, ppos
, tok
);
3567 for (sp
= ep
= buf
->buf
+ pos
; *sp
!= '\0'; sp
= ep
) {
3568 while (*ep
!= '\0' && *ep
!= ' ')
3572 roff_word_alloc(r
->man
, ln
, sp
- buf
->buf
, sp
);
3575 n
->flags
|= NODE_LINE
| NODE_VALID
| NODE_ENDED
;
3577 r
->man
->next
= ROFF_NEXT_SIBLING
;
3584 char *oldn
, *newn
, *end
, *value
;
3585 size_t oldsz
, newsz
, valsz
;
3587 newn
= oldn
= buf
->buf
+ pos
;
3591 newsz
= roff_getname(r
, &oldn
, ln
, pos
);
3592 if (newn
[newsz
] == '\\' || newn
[newsz
] == '\t' || *oldn
== '\0')
3596 oldsz
= roff_getname(r
, &end
, ln
, oldn
- buf
->buf
);
3600 valsz
= mandoc_asprintf(&value
, ".%.*s \\$@\\\"\n",
3602 roff_setstrn(&r
->strtab
, newn
, newsz
, value
, valsz
, 0);
3603 roff_setstrn(&r
->rentab
, newn
, newsz
, NULL
, 0, 0);
3609 * The .break request only makes sense inside conditionals,
3610 * and that case is already handled in roff_cond_sub().
3613 roff_break(ROFF_ARGS
)
3615 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, ln
, pos
, "break");
3626 if (*p
== '\0' || (r
->control
= *p
++) == '.')
3630 mandoc_msg(MANDOCERR_ARG_EXCESS
,
3631 ln
, p
- buf
->buf
, "cc ... %s", p
);
3637 roff_char(ROFF_ARGS
)
3639 const char *p
, *kp
, *vp
;
3643 /* Parse the character to be replaced. */
3645 kp
= buf
->buf
+ pos
;
3647 if (*kp
== '\0' || (*kp
== '\\' &&
3648 mandoc_escape(&p
, NULL
, NULL
) != ESCAPE_SPECIAL
) ||
3649 (*p
!= ' ' && *p
!= '\0')) {
3650 mandoc_msg(MANDOCERR_CHAR_ARG
, ln
, pos
, "char %s", kp
);
3658 * If the replacement string contains a font escape sequence,
3659 * we have to restore the font at the end.
3665 while (*p
!= '\0') {
3668 switch (mandoc_escape(&p
, NULL
, NULL
)) {
3670 case ESCAPE_FONTROMAN
:
3671 case ESCAPE_FONTITALIC
:
3672 case ESCAPE_FONTBOLD
:
3677 case ESCAPE_FONTPREV
:
3685 mandoc_msg(MANDOCERR_CHAR_FONT
,
3686 ln
, (int)(vp
- buf
->buf
), "%s", vp
);
3689 * Approximate the effect of .char using the .tr tables.
3690 * XXX In groff, .char and .tr interact differently.
3694 if (r
->xtab
== NULL
)
3695 r
->xtab
= mandoc_calloc(128, sizeof(*r
->xtab
));
3696 assert((unsigned int)*kp
< 128);
3697 free(r
->xtab
[(int)*kp
].p
);
3698 r
->xtab
[(int)*kp
].sz
= mandoc_asprintf(&r
->xtab
[(int)*kp
].p
,
3699 "%s%s", vp
, font
? "\fP" : "");
3701 roff_setstrn(&r
->xmbtab
, kp
, ksz
, vp
, vsz
, 0);
3703 roff_setstrn(&r
->xmbtab
, kp
, ksz
, "\\fP", 3, 1);
3719 mandoc_msg(MANDOCERR_ARG_EXCESS
, ln
,
3720 (int)(p
- buf
->buf
), "ec ... %s", p
);
3729 if (buf
->buf
[pos
] != '\0')
3730 mandoc_msg(MANDOCERR_ARG_SKIP
,
3731 ln
, pos
, "eo %s", buf
->buf
+ pos
);
3738 struct roff_node
*n
;
3741 /* Parse the first argument. */
3743 cp
= buf
->buf
+ pos
;
3746 if (buf
->buf
[pos
] == '\\') {
3747 switch (mandoc_escape((const char **)&cp
, NULL
, NULL
)) {
3748 case ESCAPE_SPECIAL
:
3749 case ESCAPE_UNICODE
:
3750 case ESCAPE_NUMBERED
:
3754 mandoc_msg(MANDOCERR_MC_ESC
, ln
, pos
,
3755 "mc %s", buf
->buf
+ pos
);
3756 buf
->buf
[pos
] = '\0';
3761 /* Ignore additional arguments. */
3766 mandoc_msg(MANDOCERR_MC_DIST
, ln
, (int)(cp
- buf
->buf
),
3771 /* Create the .mc node. */
3773 roff_elem_alloc(r
->man
, ln
, ppos
, tok
);
3775 if (buf
->buf
[pos
] != '\0')
3776 roff_word_alloc(r
->man
, ln
, pos
, buf
->buf
+ pos
);
3777 n
->flags
|= NODE_LINE
| NODE_VALID
| NODE_ENDED
;
3779 r
->man
->next
= ROFF_NEXT_SIBLING
;
3786 while (buf
->buf
[pos
] == ' ')
3795 const char *p
, *first
, *second
;
3797 enum mandoc_esc esc
;
3802 mandoc_msg(MANDOCERR_REQ_EMPTY
, ln
, ppos
, "tr");
3806 while (*p
!= '\0') {
3810 if (*first
== '\\') {
3811 esc
= mandoc_escape(&p
, NULL
, NULL
);
3812 if (esc
== ESCAPE_ERROR
) {
3813 mandoc_msg(MANDOCERR_ESC_BAD
, ln
,
3814 (int)(p
- buf
->buf
), "%s", first
);
3817 fsz
= (size_t)(p
- first
);
3821 if (*second
== '\\') {
3822 esc
= mandoc_escape(&p
, NULL
, NULL
);
3823 if (esc
== ESCAPE_ERROR
) {
3824 mandoc_msg(MANDOCERR_ESC_BAD
, ln
,
3825 (int)(p
- buf
->buf
), "%s", second
);
3828 ssz
= (size_t)(p
- second
);
3829 } else if (*second
== '\0') {
3830 mandoc_msg(MANDOCERR_TR_ODD
, ln
,
3831 (int)(first
- buf
->buf
), "tr %s", first
);
3837 roff_setstrn(&r
->xmbtab
, first
, fsz
,
3842 if (r
->xtab
== NULL
)
3843 r
->xtab
= mandoc_calloc(128,
3844 sizeof(struct roffstr
));
3846 free(r
->xtab
[(int)*first
].p
);
3847 r
->xtab
[(int)*first
].p
= mandoc_strndup(second
, ssz
);
3848 r
->xtab
[(int)*first
].sz
= ssz
;
3855 * Implementation of the .return request.
3856 * There is no need to call roff_userret() from here.
3857 * The read module will call that after rewinding the reader stack
3858 * to the place from where the current macro was called.
3861 roff_return(ROFF_ARGS
)
3863 if (r
->mstackpos
>= 0)
3864 return ROFF_IGN
| ROFF_USERRET
;
3866 mandoc_msg(MANDOCERR_REQ_NOMAC
, ln
, ppos
, "return");
3874 char *oldn
, *newn
, *end
;
3875 size_t oldsz
, newsz
;
3878 oldn
= newn
= buf
->buf
+ pos
;
3882 oldsz
= roff_getname(r
, &newn
, ln
, pos
);
3883 if (oldn
[oldsz
] == '\\' || oldn
[oldsz
] == '\t' || *newn
== '\0')
3887 newsz
= roff_getname(r
, &end
, ln
, newn
- buf
->buf
);
3891 deftype
= ROFFDEF_ANY
;
3892 value
= roff_getstrn(r
, oldn
, oldsz
, &deftype
);
3895 roff_setstrn(&r
->strtab
, newn
, newsz
, value
, strlen(value
), 0);
3896 roff_setstrn(&r
->strtab
, oldn
, oldsz
, NULL
, 0, 0);
3897 roff_setstrn(&r
->rentab
, newn
, newsz
, NULL
, 0, 0);
3900 roff_setstrn(&r
->strtab
, newn
, newsz
, value
, strlen(value
), 0);
3901 roff_setstrn(&r
->rentab
, newn
, newsz
, NULL
, 0, 0);
3904 roff_setstrn(&r
->rentab
, newn
, newsz
, value
, strlen(value
), 0);
3905 roff_setstrn(&r
->rentab
, oldn
, oldsz
, NULL
, 0, 0);
3906 roff_setstrn(&r
->strtab
, newn
, newsz
, NULL
, 0, 0);
3909 roff_setstrn(&r
->rentab
, newn
, newsz
, oldn
, oldsz
, 0);
3910 roff_setstrn(&r
->strtab
, newn
, newsz
, NULL
, 0, 0);
3913 roff_setstrn(&r
->strtab
, newn
, newsz
, NULL
, 0, 0);
3914 roff_setstrn(&r
->rentab
, newn
, newsz
, NULL
, 0, 0);
3921 roff_shift(ROFF_ARGS
)
3924 int argpos
, levels
, i
;
3928 if (buf
->buf
[pos
] != '\0' &&
3929 roff_evalnum(r
, ln
, buf
->buf
, &pos
, &levels
, 0) == 0) {
3930 mandoc_msg(MANDOCERR_CE_NONUM
,
3931 ln
, pos
, "shift %s", buf
->buf
+ pos
);
3934 if (r
->mstackpos
< 0) {
3935 mandoc_msg(MANDOCERR_REQ_NOMAC
, ln
, ppos
, "shift");
3938 ctx
= r
->mstack
+ r
->mstackpos
;
3939 if (levels
> ctx
->argc
) {
3940 mandoc_msg(MANDOCERR_SHIFT
,
3941 ln
, argpos
, "%d, but max is %d", levels
, ctx
->argc
);
3945 mandoc_msg(MANDOCERR_ARG_NEG
, ln
, argpos
, "shift %d", levels
);
3950 for (i
= 0; i
< levels
; i
++)
3952 ctx
->argc
-= levels
;
3953 for (i
= 0; i
< ctx
->argc
; i
++)
3954 ctx
->argv
[i
] = ctx
->argv
[i
+ levels
];
3963 name
= buf
->buf
+ pos
;
3964 mandoc_msg(MANDOCERR_SO
, ln
, ppos
, "so %s", name
);
3967 * Handle `so'. Be EXTREMELY careful, as we shouldn't be
3968 * opening anything that's not in our cwd or anything beneath
3969 * it. Thus, explicitly disallow traversing up the file-system
3970 * or using absolute paths.
3973 if (*name
== '/' || strstr(name
, "../") || strstr(name
, "/..")) {
3974 mandoc_msg(MANDOCERR_SO_PATH
, ln
, ppos
, ".so %s", name
);
3975 buf
->sz
= mandoc_asprintf(&cp
,
3976 ".sp\nSee the file %s.\n.sp", name
) + 1;
3980 return ROFF_REPARSE
;
3987 /* --- user defined strings and macros ------------------------------------ */
3990 roff_userdef(ROFF_ARGS
)
3993 char *arg
, *ap
, *dst
, *src
;
3996 /* If the macro is empty, ignore it altogether. */
3998 if (*r
->current_string
== '\0')
4001 /* Initialize a new macro stack context. */
4003 if (++r
->mstackpos
== r
->mstacksz
) {
4004 r
->mstack
= mandoc_recallocarray(r
->mstack
,
4005 r
->mstacksz
, r
->mstacksz
+ 8, sizeof(*r
->mstack
));
4008 ctx
= r
->mstack
+ r
->mstackpos
;
4012 * Collect pointers to macro argument strings,
4013 * NUL-terminating them and escaping quotes.
4016 src
= buf
->buf
+ pos
;
4017 while (*src
!= '\0') {
4018 if (ctx
->argc
== ctx
->argsz
) {
4020 ctx
->argv
= mandoc_reallocarray(ctx
->argv
,
4021 ctx
->argsz
, sizeof(*ctx
->argv
));
4023 arg
= roff_getarg(r
, &src
, ln
, &pos
);
4024 sz
= 1; /* For the terminating NUL. */
4025 for (ap
= arg
; *ap
!= '\0'; ap
++)
4026 sz
+= *ap
== '"' ? 4 : 1;
4027 ctx
->argv
[ctx
->argc
++] = dst
= mandoc_malloc(sz
);
4028 for (ap
= arg
; *ap
!= '\0'; ap
++) {
4030 memcpy(dst
, "\\(dq", 4);
4039 /* Replace the macro invocation by the macro definition. */
4042 buf
->buf
= mandoc_strdup(r
->current_string
);
4043 buf
->sz
= strlen(buf
->buf
) + 1;
4046 return buf
->buf
[buf
->sz
- 2] == '\n' ?
4047 ROFF_REPARSE
| ROFF_USERCALL
: ROFF_IGN
| ROFF_APPEND
;
4051 * Calling a high-level macro that was renamed with .rn.
4052 * r->current_string has already been set up by roff_parse().
4055 roff_renamed(ROFF_ARGS
)
4059 buf
->sz
= mandoc_asprintf(&nbuf
, ".%s%s%s", r
->current_string
,
4060 buf
->buf
[pos
] == '\0' ? "" : " ", buf
->buf
+ pos
) + 1;
4068 * Measure the length in bytes of the roff identifier at *cpp
4069 * and advance the pointer to the next word.
4072 roff_getname(struct roff
*r
, char **cpp
, int ln
, int pos
)
4081 /* Advance cp to the byte after the end of the name. */
4083 for (cp
= name
; 1; cp
++) {
4087 if (*cp
== ' ' || *cp
== '\t') {
4093 if (cp
[1] == '{' || cp
[1] == '}')
4097 mandoc_msg(MANDOCERR_NAMESC
, ln
, pos
,
4098 "%.*s", (int)(cp
- name
+ 1), name
);
4099 mandoc_escape((const char **)&cp
, NULL
, NULL
);
4103 /* Read past spaces. */
4113 * Store *string into the user-defined string called *name.
4114 * To clear an existing entry, call with (*r, *name, NULL, 0).
4115 * append == 0: replace mode
4116 * append == 1: single-line append mode
4117 * append == 2: multiline append mode, append '\n' after each call
4120 roff_setstr(struct roff
*r
, const char *name
, const char *string
,
4125 namesz
= strlen(name
);
4126 roff_setstrn(&r
->strtab
, name
, namesz
, string
,
4127 string
? strlen(string
) : 0, append
);
4128 roff_setstrn(&r
->rentab
, name
, namesz
, NULL
, 0, 0);
4132 roff_setstrn(struct roffkv
**r
, const char *name
, size_t namesz
,
4133 const char *string
, size_t stringsz
, int append
)
4138 size_t oldch
, newch
;
4140 /* Search for an existing string with the same name. */
4143 while (n
&& (namesz
!= n
->key
.sz
||
4144 strncmp(n
->key
.p
, name
, namesz
)))
4148 /* Create a new string table entry. */
4149 n
= mandoc_malloc(sizeof(struct roffkv
));
4150 n
->key
.p
= mandoc_strndup(name
, namesz
);
4156 } else if (0 == append
) {
4166 * One additional byte for the '\n' in multiline mode,
4167 * and one for the terminating '\0'.
4169 newch
= stringsz
+ (1 < append
? 2u : 1u);
4171 if (NULL
== n
->val
.p
) {
4172 n
->val
.p
= mandoc_malloc(newch
);
4177 n
->val
.p
= mandoc_realloc(n
->val
.p
, oldch
+ newch
);
4180 /* Skip existing content in the destination buffer. */
4181 c
= n
->val
.p
+ (int)oldch
;
4183 /* Append new content to the destination buffer. */
4185 while (i
< (int)stringsz
) {
4187 * Rudimentary roff copy mode:
4188 * Handle escaped backslashes.
4190 if ('\\' == string
[i
] && '\\' == string
[i
+ 1])
4195 /* Append terminating bytes. */
4200 n
->val
.sz
= (int)(c
- n
->val
.p
);
4204 roff_getstrn(struct roff
*r
, const char *name
, size_t len
,
4207 const struct roffkv
*n
;
4212 for (n
= r
->strtab
; n
!= NULL
; n
= n
->next
) {
4213 if (strncmp(name
, n
->key
.p
, len
) != 0 ||
4214 n
->key
.p
[len
] != '\0' || n
->val
.p
== NULL
)
4216 if (*deftype
& ROFFDEF_USER
) {
4217 *deftype
= ROFFDEF_USER
;
4224 for (n
= r
->rentab
; n
!= NULL
; n
= n
->next
) {
4225 if (strncmp(name
, n
->key
.p
, len
) != 0 ||
4226 n
->key
.p
[len
] != '\0' || n
->val
.p
== NULL
)
4228 if (*deftype
& ROFFDEF_REN
) {
4229 *deftype
= ROFFDEF_REN
;
4236 for (i
= 0; i
< PREDEFS_MAX
; i
++) {
4237 if (strncmp(name
, predefs
[i
].name
, len
) != 0 ||
4238 predefs
[i
].name
[len
] != '\0')
4240 if (*deftype
& ROFFDEF_PRE
) {
4241 *deftype
= ROFFDEF_PRE
;
4242 return predefs
[i
].str
;
4248 if (r
->man
->meta
.macroset
!= MACROSET_MAN
) {
4249 for (tok
= MDOC_Dd
; tok
< MDOC_MAX
; tok
++) {
4250 if (strncmp(name
, roff_name
[tok
], len
) != 0 ||
4251 roff_name
[tok
][len
] != '\0')
4253 if (*deftype
& ROFFDEF_STD
) {
4254 *deftype
= ROFFDEF_STD
;
4262 if (r
->man
->meta
.macroset
!= MACROSET_MDOC
) {
4263 for (tok
= MAN_TH
; tok
< MAN_MAX
; tok
++) {
4264 if (strncmp(name
, roff_name
[tok
], len
) != 0 ||
4265 roff_name
[tok
][len
] != '\0')
4267 if (*deftype
& ROFFDEF_STD
) {
4268 *deftype
= ROFFDEF_STD
;
4277 if (found
== 0 && *deftype
!= ROFFDEF_ANY
) {
4278 if (*deftype
& ROFFDEF_REN
) {
4280 * This might still be a request,
4281 * so do not treat it as undefined yet.
4283 *deftype
= ROFFDEF_UNDEF
;
4287 /* Using an undefined string defines it to be empty. */
4289 roff_setstrn(&r
->strtab
, name
, len
, "", 0, 0);
4290 roff_setstrn(&r
->rentab
, name
, len
, NULL
, 0, 0);
4298 roff_freestr(struct roffkv
*r
)
4300 struct roffkv
*n
, *nn
;
4302 for (n
= r
; n
; n
= nn
) {
4310 /* --- accessors and utility functions ------------------------------------ */
4313 * Duplicate an input string, making the appropriate character
4314 * conversations (as stipulated by `tr') along the way.
4315 * Returns a heap-allocated string with all the replacements made.
4318 roff_strdup(const struct roff
*r
, const char *p
)
4320 const struct roffkv
*cp
;
4324 enum mandoc_esc esc
;
4326 if (NULL
== r
->xmbtab
&& NULL
== r
->xtab
)
4327 return mandoc_strdup(p
);
4328 else if ('\0' == *p
)
4329 return mandoc_strdup("");
4332 * Step through each character looking for term matches
4333 * (remember that a `tr' can be invoked with an escape, which is
4334 * a glyph but the escape is multi-character).
4335 * We only do this if the character hash has been initialised
4336 * and the string is >0 length.
4342 while ('\0' != *p
) {
4343 assert((unsigned int)*p
< 128);
4344 if ('\\' != *p
&& r
->xtab
&& r
->xtab
[(unsigned int)*p
].p
) {
4345 sz
= r
->xtab
[(int)*p
].sz
;
4346 res
= mandoc_realloc(res
, ssz
+ sz
+ 1);
4347 memcpy(res
+ ssz
, r
->xtab
[(int)*p
].p
, sz
);
4351 } else if ('\\' != *p
) {
4352 res
= mandoc_realloc(res
, ssz
+ 2);
4357 /* Search for term matches. */
4358 for (cp
= r
->xmbtab
; cp
; cp
= cp
->next
)
4359 if (0 == strncmp(p
, cp
->key
.p
, cp
->key
.sz
))
4364 * A match has been found.
4365 * Append the match to the array and move
4366 * forward by its keysize.
4368 res
= mandoc_realloc(res
,
4369 ssz
+ cp
->val
.sz
+ 1);
4370 memcpy(res
+ ssz
, cp
->val
.p
, cp
->val
.sz
);
4372 p
+= (int)cp
->key
.sz
;
4377 * Handle escapes carefully: we need to copy
4378 * over just the escape itself, or else we might
4379 * do replacements within the escape itself.
4380 * Make sure to pass along the bogus string.
4383 esc
= mandoc_escape(&p
, NULL
, NULL
);
4384 if (ESCAPE_ERROR
== esc
) {
4386 res
= mandoc_realloc(res
, ssz
+ sz
+ 1);
4387 memcpy(res
+ ssz
, pp
, sz
);
4391 * We bail out on bad escapes.
4392 * No need to warn: we already did so when
4393 * roff_expand() was called.
4396 res
= mandoc_realloc(res
, ssz
+ sz
+ 1);
4397 memcpy(res
+ ssz
, pp
, sz
);
4401 res
[(int)ssz
] = '\0';
4406 roff_getformat(const struct roff
*r
)
4413 * Find out whether a line is a macro line or not.
4414 * If it is, adjust the current position and return one; if it isn't,
4415 * return zero and don't change the current position.
4416 * If the control character has been set with `.cc', then let that grain
4418 * This is slighly contrary to groff, where using the non-breaking
4419 * control character when `cc' has been invoked will cause the
4420 * non-breaking macro contents to be printed verbatim.
4423 roff_getcontrol(const struct roff
*r
, const char *cp
, int *ppos
)
4429 if (r
->control
!= '\0' && cp
[pos
] == r
->control
)
4431 else if (r
->control
!= '\0')
4433 else if ('\\' == cp
[pos
] && '.' == cp
[pos
+ 1])
4435 else if ('.' == cp
[pos
] || '\'' == cp
[pos
])
4440 while (' ' == cp
[pos
] || '\t' == cp
[pos
])