]>
git.cameronkatri.com Git - mandoc.git/blob - roff.c
ca45b0f68b7a37eb21d232f2b16c548d9f4c66c0
1 /* $Id: roff.c,v 1.385 2022/04/30 11:32:42 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 * If a custom end marker is a user-defined or predefined macro
2280 * or a request, interpret it.
2284 for (i
= pos
, j
= 0; r
->last
->end
[j
]; j
++, i
++)
2285 if (buf
->buf
[i
] != r
->last
->end
[j
])
2288 if (r
->last
->end
[j
] == '\0' &&
2289 (buf
->buf
[i
] == '\0' ||
2290 buf
->buf
[i
] == ' ' ||
2291 buf
->buf
[i
] == '\t')) {
2293 roffnode_cleanscope(r
);
2295 while (buf
->buf
[i
] == ' ' || buf
->buf
[i
] == '\t')
2299 if (roff_parse(r
, buf
->buf
, &pos
, ln
, ppos
) !=
2306 /* Handle the standard end marker. */
2308 t
= roff_parse(r
, buf
->buf
, &pos
, ln
, ppos
);
2309 if (t
== ROFF_cblock
)
2310 return roff_cblock(r
, t
, buf
, ln
, ppos
, pos
, offs
);
2312 /* Not an end marker, so append the line to the block. */
2315 roff_setstr(r
, r
->last
->name
, buf
->buf
+ ppos
, 2);
2320 roff_block_text(ROFF_ARGS
)
2324 roff_setstr(r
, r
->last
->name
, buf
->buf
+ pos
, 2);
2330 * Check for a closing "\}" and handle it.
2331 * In this function, the final "int *offs" argument is used for
2332 * different purposes than elsewhere:
2333 * Input: *offs == 0: caller wants to discard arguments following \}
2334 * *offs == 1: caller wants to preserve text following \}
2335 * Output: *offs = 0: tell caller to discard input line
2336 * *offs = 1: tell caller to use input line
2339 roff_cond_checkend(ROFF_ARGS
)
2342 int endloop
, irc
, rr
;
2346 endloop
= tok
!= ROFF_while
? ROFF_IGN
:
2347 rr
? ROFF_LOOPCONT
: ROFF_LOOPEXIT
;
2348 if (roffnode_cleanscope(r
))
2352 * If "\}" occurs on a macro line without a preceding macro or
2353 * a text line contains nothing else, drop the line completely.
2356 ep
= buf
->buf
+ pos
;
2357 if (ep
[0] == '\\' && ep
[1] == '}' && (ep
[2] == '\0' || *offs
== 0))
2361 * The closing delimiter "\}" rewinds the conditional scope
2362 * but is otherwise ignored when interpreting the line.
2365 while ((ep
= strchr(ep
, '\\')) != NULL
) {
2373 memmove(ep
, ep
+ 2, strlen(ep
+ 2) + 1);
2374 if (roff_ccond(r
, ln
, ep
- buf
->buf
))
2390 * Parse and process a request or macro line in conditional scope.
2393 roff_cond_sub(ROFF_ARGS
)
2395 struct roffnode
*bl
;
2399 rr
= 0; /* If arguments follow "\}", skip them. */
2400 irc
= roff_cond_checkend(r
, tok
, buf
, ln
, ppos
, pos
, &rr
);
2401 t
= roff_parse(r
, buf
->buf
, &pos
, ln
, ppos
);
2403 /* For now, let high level macros abort .ce mode. */
2405 if (roffce_node
!= NULL
&&
2406 (t
== TOKEN_NONE
|| t
== ROFF_Dd
|| t
== ROFF_EQ
||
2407 t
== ROFF_TH
|| t
== ROFF_TS
)) {
2408 r
->man
->last
= roffce_node
;
2409 r
->man
->next
= ROFF_NEXT_SIBLING
;
2415 * Fully handle known macros when they are structurally
2416 * required or when the conditional evaluated to true.
2419 if (t
== ROFF_break
) {
2420 if (irc
& ROFF_LOOPMASK
)
2421 irc
= ROFF_IGN
| ROFF_LOOPEXIT
;
2423 for (bl
= r
->last
; bl
!= NULL
; bl
= bl
->parent
) {
2425 if (bl
->tok
== ROFF_while
)
2429 } else if (t
!= TOKEN_NONE
&&
2430 (rr
|| roffs
[t
].flags
& ROFFMAC_STRUCT
)) {
2431 irc
|= (*roffs
[t
].proc
)(r
, t
, buf
, ln
, ppos
, pos
, offs
);
2432 if (irc
& ROFF_WHILE
)
2433 irc
&= ~(ROFF_LOOPCONT
| ROFF_LOOPEXIT
);
2435 irc
|= rr
? ROFF_CONT
: ROFF_IGN
;
2440 * Parse and process a text line in conditional scope.
2443 roff_cond_text(ROFF_ARGS
)
2447 rr
= 1; /* If arguments follow "\}", preserve them. */
2448 irc
= roff_cond_checkend(r
, tok
, buf
, ln
, ppos
, pos
, &rr
);
2454 /* --- handling of numeric and conditional expressions -------------------- */
2457 * Parse a single signed integer number. Stop at the first non-digit.
2458 * If there is at least one digit, return success and advance the
2459 * parse point, else return failure and let the parse point unchanged.
2460 * Ignore overflows, treat them just like the C language.
2463 roff_getnum(const char *v
, int *pos
, int *res
, int flags
)
2465 int myres
, scaled
, n
, p
;
2472 if (n
|| v
[p
] == '+')
2475 if (flags
& ROFFNUM_WHITE
)
2476 while (isspace((unsigned char)v
[p
]))
2479 for (*res
= 0; isdigit((unsigned char)v
[p
]); p
++)
2480 *res
= 10 * *res
+ v
[p
] - '0';
2487 /* Each number may be followed by one optional scaling unit. */
2491 scaled
= *res
* 65536;
2494 scaled
= *res
* 240;
2497 scaled
= *res
* 240 / 2.54;
2508 scaled
= *res
* 10 / 3;
2514 scaled
= *res
* 6 / 25;
2521 if (flags
& ROFFNUM_SCALE
)
2529 * Evaluate a string comparison condition.
2530 * The first character is the delimiter.
2531 * Succeed if the string up to its second occurrence
2532 * matches the string up to its third occurence.
2533 * Advance the cursor after the third occurrence
2534 * or lacking that, to the end of the line.
2537 roff_evalstrcond(const char *v
, int *pos
)
2539 const char *s1
, *s2
, *s3
;
2543 s1
= v
+ *pos
; /* initial delimiter */
2544 s2
= s1
+ 1; /* for scanning the first string */
2545 s3
= strchr(s2
, *s1
); /* for scanning the second string */
2547 if (NULL
== s3
) /* found no middle delimiter */
2550 while ('\0' != *++s3
) {
2551 if (*s2
!= *s3
) { /* mismatch */
2552 s3
= strchr(s3
, *s1
);
2555 if (*s3
== *s1
) { /* found the final delimiter */
2564 s3
= strchr(s2
, '\0');
2565 else if (*s3
!= '\0')
2572 * Evaluate an optionally negated single character, numerical,
2573 * or string condition.
2576 roff_evalcond(struct roff
*r
, int ln
, char *v
, int *pos
)
2578 const char *start
, *end
;
2581 int deftype
, len
, number
, savepos
, istrue
, wanttrue
;
2583 if ('!' == v
[*pos
]) {
2604 } while (v
[*pos
] == ' ');
2607 * Quirk for groff compatibility:
2608 * The horizontal tab is neither available nor unavailable.
2611 if (v
[*pos
] == '\t') {
2616 /* Printable ASCII characters are available. */
2618 if (v
[*pos
] != '\\') {
2624 switch (mandoc_escape(&end
, &start
, &len
)) {
2625 case ESCAPE_SPECIAL
:
2626 istrue
= mchars_spec2cp(start
, len
) != -1;
2628 case ESCAPE_UNICODE
:
2631 case ESCAPE_NUMBERED
:
2632 istrue
= mchars_num2char(start
, len
) != -1;
2639 return istrue
== wanttrue
;
2646 sz
= roff_getname(r
, &cp
, ln
, cp
- v
);
2649 else if (v
[*pos
] == 'r')
2650 istrue
= roff_hasregn(r
, name
, sz
);
2652 deftype
= ROFFDEF_ANY
;
2653 roff_getstrn(r
, name
, sz
, &deftype
);
2656 *pos
= (name
+ sz
) - v
;
2657 return istrue
== wanttrue
;
2663 if (roff_evalnum(r
, ln
, v
, pos
, &number
, ROFFNUM_SCALE
))
2664 return (number
> 0) == wanttrue
;
2665 else if (*pos
== savepos
)
2666 return roff_evalstrcond(v
, pos
) == wanttrue
;
2672 roff_line_ignore(ROFF_ARGS
)
2679 roff_insec(ROFF_ARGS
)
2682 mandoc_msg(MANDOCERR_REQ_INSEC
, ln
, ppos
, "%s", roff_name
[tok
]);
2687 roff_unsupp(ROFF_ARGS
)
2690 mandoc_msg(MANDOCERR_REQ_UNSUPP
, ln
, ppos
, "%s", roff_name
[tok
]);
2695 roff_cond(ROFF_ARGS
)
2699 roffnode_push(r
, tok
, NULL
, ln
, ppos
);
2702 * An `.el' has no conditional body: it will consume the value
2703 * of the current rstack entry set in prior `ie' calls or
2706 * If we're not an `el', however, then evaluate the conditional.
2709 r
->last
->rule
= tok
== ROFF_el
?
2710 (r
->rstackpos
< 0 ? 0 : r
->rstack
[r
->rstackpos
--]) :
2711 roff_evalcond(r
, ln
, buf
->buf
, &pos
);
2714 * An if-else will put the NEGATION of the current evaluated
2715 * conditional into the stack of rules.
2718 if (tok
== ROFF_ie
) {
2719 if (r
->rstackpos
+ 1 == r
->rstacksz
) {
2721 r
->rstack
= mandoc_reallocarray(r
->rstack
,
2722 r
->rstacksz
, sizeof(int));
2724 r
->rstack
[++r
->rstackpos
] = !r
->last
->rule
;
2727 /* If the parent has false as its rule, then so do we. */
2729 if (r
->last
->parent
&& !r
->last
->parent
->rule
)
2734 * If there is nothing on the line after the conditional,
2735 * not even whitespace, use next-line scope.
2736 * Except that .while does not support next-line scope.
2739 if (buf
->buf
[pos
] == '\0' && tok
!= ROFF_while
) {
2740 r
->last
->endspan
= 2;
2744 while (buf
->buf
[pos
] == ' ')
2747 /* An opening brace requests multiline scope. */
2749 if (buf
->buf
[pos
] == '\\' && buf
->buf
[pos
+ 1] == '{') {
2750 r
->last
->endspan
= -1;
2752 while (buf
->buf
[pos
] == ' ')
2758 * Anything else following the conditional causes
2759 * single-line scope. Warn if the scope contains
2760 * nothing but trailing whitespace.
2763 if (buf
->buf
[pos
] == '\0')
2764 mandoc_msg(MANDOCERR_COND_EMPTY
,
2765 ln
, ppos
, "%s", roff_name
[tok
]);
2767 r
->last
->endspan
= 1;
2772 if (tok
== ROFF_while
)
2784 /* Ignore groff compatibility mode for now. */
2786 if (tok
== ROFF_ds1
)
2788 else if (tok
== ROFF_as1
)
2792 * The first word is the name of the string.
2793 * If it is empty or terminated by an escape sequence,
2794 * abort the `ds' request without defining anything.
2797 name
= string
= buf
->buf
+ pos
;
2801 namesz
= roff_getname(r
, &string
, ln
, pos
);
2802 switch (name
[namesz
]) {
2806 string
= buf
->buf
+ pos
+ namesz
;
2812 /* Read past the initial double-quote, if any. */
2816 /* The rest is the value. */
2817 roff_setstrn(&r
->strtab
, name
, namesz
, string
, strlen(string
),
2819 roff_setstrn(&r
->rentab
, name
, namesz
, NULL
, 0, 0);
2824 * Parse a single operator, one or two characters long.
2825 * If the operator is recognized, return success and advance the
2826 * parse point, else return failure and let the parse point unchanged.
2829 roff_getop(const char *v
, int *pos
, char *res
)
2844 switch (v
[*pos
+ 1]) {
2862 switch (v
[*pos
+ 1]) {
2876 if ('=' == v
[*pos
+ 1])
2888 * Evaluate either a parenthesized numeric expression
2889 * or a single signed integer number.
2892 roff_evalpar(struct roff
*r
, int ln
,
2893 const char *v
, int *pos
, int *res
, int flags
)
2897 return roff_getnum(v
, pos
, res
, flags
);
2900 if ( ! roff_evalnum(r
, ln
, v
, pos
, res
, flags
| ROFFNUM_WHITE
))
2904 * Omission of the closing parenthesis
2905 * is an error in validation mode,
2906 * but ignored in evaluation mode.
2911 else if (NULL
== res
)
2918 * Evaluate a complete numeric expression.
2919 * Proceed left to right, there is no concept of precedence.
2922 roff_evalnum(struct roff
*r
, int ln
, const char *v
,
2923 int *pos
, int *res
, int flags
)
2925 int mypos
, operand2
;
2933 if (flags
& ROFFNUM_WHITE
)
2934 while (isspace((unsigned char)v
[*pos
]))
2937 if ( ! roff_evalpar(r
, ln
, v
, pos
, res
, flags
))
2941 if (flags
& ROFFNUM_WHITE
)
2942 while (isspace((unsigned char)v
[*pos
]))
2945 if ( ! roff_getop(v
, pos
, &operator))
2948 if (flags
& ROFFNUM_WHITE
)
2949 while (isspace((unsigned char)v
[*pos
]))
2952 if ( ! roff_evalpar(r
, ln
, v
, pos
, &operand2
, flags
))
2955 if (flags
& ROFFNUM_WHITE
)
2956 while (isspace((unsigned char)v
[*pos
]))
2973 if (operand2
== 0) {
2974 mandoc_msg(MANDOCERR_DIVZERO
,
2982 if (operand2
== 0) {
2983 mandoc_msg(MANDOCERR_DIVZERO
,
2991 *res
= *res
< operand2
;
2994 *res
= *res
> operand2
;
2997 *res
= *res
<= operand2
;
3000 *res
= *res
>= operand2
;
3003 *res
= *res
== operand2
;
3006 *res
= *res
!= operand2
;
3009 *res
= *res
&& operand2
;
3012 *res
= *res
|| operand2
;
3015 if (operand2
< *res
)
3019 if (operand2
> *res
)
3029 /* --- register management ------------------------------------------------ */
3032 roff_setreg(struct roff
*r
, const char *name
, int val
, char sign
)
3034 roff_setregn(r
, name
, strlen(name
), val
, sign
, INT_MIN
);
3038 roff_setregn(struct roff
*r
, const char *name
, size_t len
,
3039 int val
, char sign
, int step
)
3041 struct roffreg
*reg
;
3043 /* Search for an existing register with the same name. */
3046 while (reg
!= NULL
&& (reg
->key
.sz
!= len
||
3047 strncmp(reg
->key
.p
, name
, len
) != 0))
3051 /* Create a new register. */
3052 reg
= mandoc_malloc(sizeof(struct roffreg
));
3053 reg
->key
.p
= mandoc_strndup(name
, len
);
3057 reg
->next
= r
->regtab
;
3063 else if ('-' == sign
)
3067 if (step
!= INT_MIN
)
3072 * Handle some predefined read-only number registers.
3073 * For now, return -1 if the requested register is not predefined;
3074 * in case a predefined read-only register having the value -1
3075 * were to turn up, another special value would have to be chosen.
3078 roff_getregro(const struct roff
*r
, const char *name
)
3082 case '$': /* Number of arguments of the last macro evaluated. */
3083 return r
->mstackpos
< 0 ? 0 : r
->mstack
[r
->mstackpos
].argc
;
3084 case 'A': /* ASCII approximation mode is always off. */
3086 case 'g': /* Groff compatibility mode is always on. */
3088 case 'H': /* Fixed horizontal resolution. */
3090 case 'j': /* Always adjust left margin only. */
3092 case 'T': /* Some output device is always defined. */
3094 case 'V': /* Fixed vertical resolution. */
3102 roff_getreg(struct roff
*r
, const char *name
)
3104 return roff_getregn(r
, name
, strlen(name
), '\0');
3108 roff_getregn(struct roff
*r
, const char *name
, size_t len
, char sign
)
3110 struct roffreg
*reg
;
3113 if ('.' == name
[0] && 2 == len
) {
3114 val
= roff_getregro(r
, name
+ 1);
3119 for (reg
= r
->regtab
; reg
; reg
= reg
->next
) {
3120 if (len
== reg
->key
.sz
&&
3121 0 == strncmp(name
, reg
->key
.p
, len
)) {
3124 reg
->val
+= reg
->step
;
3127 reg
->val
-= reg
->step
;
3136 roff_setregn(r
, name
, len
, 0, '\0', INT_MIN
);
3141 roff_hasregn(const struct roff
*r
, const char *name
, size_t len
)
3143 struct roffreg
*reg
;
3146 if ('.' == name
[0] && 2 == len
) {
3147 val
= roff_getregro(r
, name
+ 1);
3152 for (reg
= r
->regtab
; reg
; reg
= reg
->next
)
3153 if (len
== reg
->key
.sz
&&
3154 0 == strncmp(name
, reg
->key
.p
, len
))
3161 roff_freereg(struct roffreg
*reg
)
3163 struct roffreg
*old_reg
;
3165 while (NULL
!= reg
) {
3176 char *key
, *val
, *step
;
3181 key
= val
= buf
->buf
+ pos
;
3185 keysz
= roff_getname(r
, &val
, ln
, pos
);
3186 if (key
[keysz
] == '\\' || key
[keysz
] == '\t')
3190 if (sign
== '+' || sign
== '-')
3194 if (roff_evalnum(r
, ln
, val
, &len
, &iv
, ROFFNUM_SCALE
) == 0)
3198 while (isspace((unsigned char)*step
))
3200 if (roff_evalnum(r
, ln
, step
, NULL
, &is
, 0) == 0)
3203 roff_setregn(r
, key
, keysz
, iv
, sign
, is
);
3210 struct roffreg
*reg
, **prev
;
3214 name
= cp
= buf
->buf
+ pos
;
3217 namesz
= roff_getname(r
, &cp
, ln
, pos
);
3218 name
[namesz
] = '\0';
3223 if (reg
== NULL
|| !strcmp(name
, reg
->key
.p
))
3235 /* --- handler functions for roff requests -------------------------------- */
3244 cp
= buf
->buf
+ pos
;
3245 while (*cp
!= '\0') {
3247 namesz
= roff_getname(r
, &cp
, ln
, (int)(cp
- buf
->buf
));
3248 roff_setstrn(&r
->strtab
, name
, namesz
, NULL
, 0, 0);
3249 roff_setstrn(&r
->rentab
, name
, namesz
, NULL
, 0, 0);
3250 if (name
[namesz
] == '\\' || name
[namesz
] == '\t')
3261 /* Parse the number of lines. */
3263 if ( ! roff_evalnum(r
, ln
, buf
->buf
, &pos
, &iv
, 0)) {
3264 mandoc_msg(MANDOCERR_IT_NONUM
,
3265 ln
, ppos
, "%s", buf
->buf
+ 1);
3269 while (isspace((unsigned char)buf
->buf
[pos
]))
3273 * Arm the input line trap.
3274 * Special-casing "an-trap" is an ugly workaround to cope
3275 * with DocBook stupidly fiddling with man(7) internals.
3279 roffit_macro
= mandoc_strdup(iv
!= 1 ||
3280 strcmp(buf
->buf
+ pos
, "an-trap") ?
3281 buf
->buf
+ pos
: "br");
3289 enum roff_tok t
, te
;
3296 r
->format
= MPARSE_MDOC
;
3297 mask
= MPARSE_MDOC
| MPARSE_QUICK
;
3303 r
->format
= MPARSE_MAN
;
3304 mask
= MPARSE_QUICK
;
3309 if ((r
->options
& mask
) == 0)
3310 for (t
= tok
; t
< te
; t
++)
3311 roff_setstr(r
, roff_name
[t
], NULL
, 0);
3318 r
->man
->flags
&= ~ROFF_NONOFILL
;
3319 if (r
->tbl
== NULL
) {
3320 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, ln
, ppos
, "TE");
3323 if (tbl_end(r
->tbl
, 0) == 0) {
3326 buf
->buf
= mandoc_strdup(".sp");
3329 return ROFF_REPARSE
;
3340 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, ln
, ppos
, "T&");
3342 tbl_restart(ln
, ppos
, r
->tbl
);
3348 * Handle in-line equation delimiters.
3351 roff_eqndelim(struct roff
*r
, struct buf
*buf
, int pos
)
3354 const char *bef_pr
, *bef_nl
, *mac
, *aft_nl
, *aft_pr
;
3357 * Outside equations, look for an opening delimiter.
3358 * If we are inside an equation, we already know it is
3359 * in-line, or this function wouldn't have been called;
3360 * so look for a closing delimiter.
3363 cp1
= buf
->buf
+ pos
;
3364 cp2
= strchr(cp1
, r
->eqn
== NULL
?
3365 r
->last_eqn
->odelim
: r
->last_eqn
->cdelim
);
3370 bef_pr
= bef_nl
= aft_nl
= aft_pr
= "";
3372 /* Handle preceding text, protecting whitespace. */
3374 if (*buf
->buf
!= '\0') {
3381 * Prepare replacing the delimiter with an equation macro
3382 * and drop leading white space from the equation.
3385 if (r
->eqn
== NULL
) {
3392 /* Handle following text, protecting whitespace. */
3400 /* Do the actual replacement. */
3402 buf
->sz
= mandoc_asprintf(&cp1
, "%s%s%s%s%s%s%s", buf
->buf
,
3403 bef_pr
, bef_nl
, mac
, aft_nl
, aft_pr
, cp2
) + 1;
3407 /* Toggle the in-line state of the eqn subsystem. */
3409 r
->eqn_inline
= r
->eqn
== NULL
;
3410 return ROFF_REPARSE
;
3416 struct roff_node
*n
;
3418 if (r
->man
->meta
.macroset
== MACROSET_MAN
)
3419 man_breakscope(r
->man
, ROFF_EQ
);
3420 n
= roff_node_alloc(r
->man
, ln
, ppos
, ROFFT_EQN
, TOKEN_NONE
);
3421 if (ln
> r
->man
->last
->line
)
3422 n
->flags
|= NODE_LINE
;
3423 n
->eqn
= eqn_box_new();
3424 roff_node_append(r
->man
, n
);
3425 r
->man
->next
= ROFF_NEXT_SIBLING
;
3427 assert(r
->eqn
== NULL
);
3428 if (r
->last_eqn
== NULL
)
3429 r
->last_eqn
= eqn_alloc();
3431 eqn_reset(r
->last_eqn
);
3432 r
->eqn
= r
->last_eqn
;
3435 if (buf
->buf
[pos
] != '\0')
3436 mandoc_msg(MANDOCERR_ARG_SKIP
, ln
, pos
,
3437 ".EQ %s", buf
->buf
+ pos
);
3445 if (r
->eqn
!= NULL
) {
3449 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, ln
, ppos
, "EN");
3450 if (buf
->buf
[pos
] != '\0')
3451 mandoc_msg(MANDOCERR_ARG_SKIP
, ln
, pos
,
3452 "EN %s", buf
->buf
+ pos
);
3459 if (r
->tbl
!= NULL
) {
3460 mandoc_msg(MANDOCERR_BLK_BROKEN
, ln
, ppos
, "TS breaks TS");
3463 r
->man
->flags
|= ROFF_NONOFILL
;
3464 r
->tbl
= tbl_alloc(ppos
, ln
, r
->last_tbl
);
3465 if (r
->last_tbl
== NULL
)
3466 r
->first_tbl
= r
->tbl
;
3467 r
->last_tbl
= r
->tbl
;
3472 roff_noarg(ROFF_ARGS
)
3474 if (r
->man
->flags
& (MAN_BLINE
| MAN_ELINE
))
3475 man_breakscope(r
->man
, tok
);
3476 if (tok
== ROFF_brp
)
3478 roff_elem_alloc(r
->man
, ln
, ppos
, tok
);
3479 if (buf
->buf
[pos
] != '\0')
3480 mandoc_msg(MANDOCERR_ARG_SKIP
, ln
, pos
,
3481 "%s %s", roff_name
[tok
], buf
->buf
+ pos
);
3483 r
->man
->flags
|= ROFF_NOFILL
;
3484 else if (tok
== ROFF_fi
)
3485 r
->man
->flags
&= ~ROFF_NOFILL
;
3486 r
->man
->last
->flags
|= NODE_LINE
| NODE_VALID
| NODE_ENDED
;
3487 r
->man
->next
= ROFF_NEXT_SIBLING
;
3492 roff_onearg(ROFF_ARGS
)
3494 struct roff_node
*n
;
3498 if (r
->man
->flags
& (MAN_BLINE
| MAN_ELINE
) &&
3499 (tok
== ROFF_ce
|| tok
== ROFF_rj
|| tok
== ROFF_sp
||
3501 man_breakscope(r
->man
, tok
);
3503 if (roffce_node
!= NULL
&& (tok
== ROFF_ce
|| tok
== ROFF_rj
)) {
3504 r
->man
->last
= roffce_node
;
3505 r
->man
->next
= ROFF_NEXT_SIBLING
;
3508 roff_elem_alloc(r
->man
, ln
, ppos
, tok
);
3511 cp
= buf
->buf
+ pos
;
3513 while (*cp
!= '\0' && *cp
!= ' ')
3518 mandoc_msg(MANDOCERR_ARG_EXCESS
,
3519 ln
, (int)(cp
- buf
->buf
),
3520 "%s ... %s", roff_name
[tok
], cp
);
3521 roff_word_alloc(r
->man
, ln
, pos
, buf
->buf
+ pos
);
3524 if (tok
== ROFF_ce
|| tok
== ROFF_rj
) {
3525 if (r
->man
->last
->type
== ROFFT_ELEM
) {
3526 roff_word_alloc(r
->man
, ln
, pos
, "1");
3527 r
->man
->last
->flags
|= NODE_NOSRC
;
3530 if (roff_evalnum(r
, ln
, r
->man
->last
->string
, &npos
,
3531 &roffce_lines
, 0) == 0) {
3532 mandoc_msg(MANDOCERR_CE_NONUM
,
3533 ln
, pos
, "ce %s", buf
->buf
+ pos
);
3536 if (roffce_lines
< 1) {
3537 r
->man
->last
= r
->man
->last
->parent
;
3541 roffce_node
= r
->man
->last
->parent
;
3543 n
->flags
|= NODE_VALID
| NODE_ENDED
;
3546 n
->flags
|= NODE_LINE
;
3547 r
->man
->next
= ROFF_NEXT_SIBLING
;
3552 roff_manyarg(ROFF_ARGS
)
3554 struct roff_node
*n
;
3557 roff_elem_alloc(r
->man
, ln
, ppos
, tok
);
3560 for (sp
= ep
= buf
->buf
+ pos
; *sp
!= '\0'; sp
= ep
) {
3561 while (*ep
!= '\0' && *ep
!= ' ')
3565 roff_word_alloc(r
->man
, ln
, sp
- buf
->buf
, sp
);
3568 n
->flags
|= NODE_LINE
| NODE_VALID
| NODE_ENDED
;
3570 r
->man
->next
= ROFF_NEXT_SIBLING
;
3577 char *oldn
, *newn
, *end
, *value
;
3578 size_t oldsz
, newsz
, valsz
;
3580 newn
= oldn
= buf
->buf
+ pos
;
3584 newsz
= roff_getname(r
, &oldn
, ln
, pos
);
3585 if (newn
[newsz
] == '\\' || newn
[newsz
] == '\t' || *oldn
== '\0')
3589 oldsz
= roff_getname(r
, &end
, ln
, oldn
- buf
->buf
);
3593 valsz
= mandoc_asprintf(&value
, ".%.*s \\$@\\\"\n",
3595 roff_setstrn(&r
->strtab
, newn
, newsz
, value
, valsz
, 0);
3596 roff_setstrn(&r
->rentab
, newn
, newsz
, NULL
, 0, 0);
3602 * The .break request only makes sense inside conditionals,
3603 * and that case is already handled in roff_cond_sub().
3606 roff_break(ROFF_ARGS
)
3608 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, ln
, pos
, "break");
3619 if (*p
== '\0' || (r
->control
= *p
++) == '.')
3623 mandoc_msg(MANDOCERR_ARG_EXCESS
,
3624 ln
, p
- buf
->buf
, "cc ... %s", p
);
3630 roff_char(ROFF_ARGS
)
3632 const char *p
, *kp
, *vp
;
3636 /* Parse the character to be replaced. */
3638 kp
= buf
->buf
+ pos
;
3640 if (*kp
== '\0' || (*kp
== '\\' &&
3641 mandoc_escape(&p
, NULL
, NULL
) != ESCAPE_SPECIAL
) ||
3642 (*p
!= ' ' && *p
!= '\0')) {
3643 mandoc_msg(MANDOCERR_CHAR_ARG
, ln
, pos
, "char %s", kp
);
3651 * If the replacement string contains a font escape sequence,
3652 * we have to restore the font at the end.
3658 while (*p
!= '\0') {
3661 switch (mandoc_escape(&p
, NULL
, NULL
)) {
3663 case ESCAPE_FONTROMAN
:
3664 case ESCAPE_FONTITALIC
:
3665 case ESCAPE_FONTBOLD
:
3670 case ESCAPE_FONTPREV
:
3678 mandoc_msg(MANDOCERR_CHAR_FONT
,
3679 ln
, (int)(vp
- buf
->buf
), "%s", vp
);
3682 * Approximate the effect of .char using the .tr tables.
3683 * XXX In groff, .char and .tr interact differently.
3687 if (r
->xtab
== NULL
)
3688 r
->xtab
= mandoc_calloc(128, sizeof(*r
->xtab
));
3689 assert((unsigned int)*kp
< 128);
3690 free(r
->xtab
[(int)*kp
].p
);
3691 r
->xtab
[(int)*kp
].sz
= mandoc_asprintf(&r
->xtab
[(int)*kp
].p
,
3692 "%s%s", vp
, font
? "\fP" : "");
3694 roff_setstrn(&r
->xmbtab
, kp
, ksz
, vp
, vsz
, 0);
3696 roff_setstrn(&r
->xmbtab
, kp
, ksz
, "\\fP", 3, 1);
3712 mandoc_msg(MANDOCERR_ARG_EXCESS
, ln
,
3713 (int)(p
- buf
->buf
), "ec ... %s", p
);
3722 if (buf
->buf
[pos
] != '\0')
3723 mandoc_msg(MANDOCERR_ARG_SKIP
,
3724 ln
, pos
, "eo %s", buf
->buf
+ pos
);
3731 struct roff_node
*n
;
3734 /* Parse the first argument. */
3736 cp
= buf
->buf
+ pos
;
3739 if (buf
->buf
[pos
] == '\\') {
3740 switch (mandoc_escape((const char **)&cp
, NULL
, NULL
)) {
3741 case ESCAPE_SPECIAL
:
3742 case ESCAPE_UNICODE
:
3743 case ESCAPE_NUMBERED
:
3747 mandoc_msg(MANDOCERR_MC_ESC
, ln
, pos
,
3748 "mc %s", buf
->buf
+ pos
);
3749 buf
->buf
[pos
] = '\0';
3754 /* Ignore additional arguments. */
3759 mandoc_msg(MANDOCERR_MC_DIST
, ln
, (int)(cp
- buf
->buf
),
3764 /* Create the .mc node. */
3766 roff_elem_alloc(r
->man
, ln
, ppos
, tok
);
3768 if (buf
->buf
[pos
] != '\0')
3769 roff_word_alloc(r
->man
, ln
, pos
, buf
->buf
+ pos
);
3770 n
->flags
|= NODE_LINE
| NODE_VALID
| NODE_ENDED
;
3772 r
->man
->next
= ROFF_NEXT_SIBLING
;
3779 while (buf
->buf
[pos
] == ' ')
3788 const char *p
, *first
, *second
;
3790 enum mandoc_esc esc
;
3795 mandoc_msg(MANDOCERR_REQ_EMPTY
, ln
, ppos
, "tr");
3799 while (*p
!= '\0') {
3803 if (*first
== '\\') {
3804 esc
= mandoc_escape(&p
, NULL
, NULL
);
3805 if (esc
== ESCAPE_ERROR
) {
3806 mandoc_msg(MANDOCERR_ESC_BAD
, ln
,
3807 (int)(p
- buf
->buf
), "%s", first
);
3810 fsz
= (size_t)(p
- first
);
3814 if (*second
== '\\') {
3815 esc
= mandoc_escape(&p
, NULL
, NULL
);
3816 if (esc
== ESCAPE_ERROR
) {
3817 mandoc_msg(MANDOCERR_ESC_BAD
, ln
,
3818 (int)(p
- buf
->buf
), "%s", second
);
3821 ssz
= (size_t)(p
- second
);
3822 } else if (*second
== '\0') {
3823 mandoc_msg(MANDOCERR_TR_ODD
, ln
,
3824 (int)(first
- buf
->buf
), "tr %s", first
);
3830 roff_setstrn(&r
->xmbtab
, first
, fsz
,
3835 if (r
->xtab
== NULL
)
3836 r
->xtab
= mandoc_calloc(128,
3837 sizeof(struct roffstr
));
3839 free(r
->xtab
[(int)*first
].p
);
3840 r
->xtab
[(int)*first
].p
= mandoc_strndup(second
, ssz
);
3841 r
->xtab
[(int)*first
].sz
= ssz
;
3848 * Implementation of the .return request.
3849 * There is no need to call roff_userret() from here.
3850 * The read module will call that after rewinding the reader stack
3851 * to the place from where the current macro was called.
3854 roff_return(ROFF_ARGS
)
3856 if (r
->mstackpos
>= 0)
3857 return ROFF_IGN
| ROFF_USERRET
;
3859 mandoc_msg(MANDOCERR_REQ_NOMAC
, ln
, ppos
, "return");
3867 char *oldn
, *newn
, *end
;
3868 size_t oldsz
, newsz
;
3871 oldn
= newn
= buf
->buf
+ pos
;
3875 oldsz
= roff_getname(r
, &newn
, ln
, pos
);
3876 if (oldn
[oldsz
] == '\\' || oldn
[oldsz
] == '\t' || *newn
== '\0')
3880 newsz
= roff_getname(r
, &end
, ln
, newn
- buf
->buf
);
3884 deftype
= ROFFDEF_ANY
;
3885 value
= roff_getstrn(r
, oldn
, oldsz
, &deftype
);
3888 roff_setstrn(&r
->strtab
, newn
, newsz
, value
, strlen(value
), 0);
3889 roff_setstrn(&r
->strtab
, oldn
, oldsz
, NULL
, 0, 0);
3890 roff_setstrn(&r
->rentab
, newn
, newsz
, NULL
, 0, 0);
3893 roff_setstrn(&r
->strtab
, newn
, newsz
, value
, strlen(value
), 0);
3894 roff_setstrn(&r
->rentab
, newn
, newsz
, NULL
, 0, 0);
3897 roff_setstrn(&r
->rentab
, newn
, newsz
, value
, strlen(value
), 0);
3898 roff_setstrn(&r
->rentab
, oldn
, oldsz
, NULL
, 0, 0);
3899 roff_setstrn(&r
->strtab
, newn
, newsz
, NULL
, 0, 0);
3902 roff_setstrn(&r
->rentab
, newn
, newsz
, oldn
, oldsz
, 0);
3903 roff_setstrn(&r
->strtab
, newn
, newsz
, NULL
, 0, 0);
3906 roff_setstrn(&r
->strtab
, newn
, newsz
, NULL
, 0, 0);
3907 roff_setstrn(&r
->rentab
, newn
, newsz
, NULL
, 0, 0);
3914 roff_shift(ROFF_ARGS
)
3917 int argpos
, levels
, i
;
3921 if (buf
->buf
[pos
] != '\0' &&
3922 roff_evalnum(r
, ln
, buf
->buf
, &pos
, &levels
, 0) == 0) {
3923 mandoc_msg(MANDOCERR_CE_NONUM
,
3924 ln
, pos
, "shift %s", buf
->buf
+ pos
);
3927 if (r
->mstackpos
< 0) {
3928 mandoc_msg(MANDOCERR_REQ_NOMAC
, ln
, ppos
, "shift");
3931 ctx
= r
->mstack
+ r
->mstackpos
;
3932 if (levels
> ctx
->argc
) {
3933 mandoc_msg(MANDOCERR_SHIFT
,
3934 ln
, argpos
, "%d, but max is %d", levels
, ctx
->argc
);
3938 mandoc_msg(MANDOCERR_ARG_NEG
, ln
, argpos
, "shift %d", levels
);
3943 for (i
= 0; i
< levels
; i
++)
3945 ctx
->argc
-= levels
;
3946 for (i
= 0; i
< ctx
->argc
; i
++)
3947 ctx
->argv
[i
] = ctx
->argv
[i
+ levels
];
3956 name
= buf
->buf
+ pos
;
3957 mandoc_msg(MANDOCERR_SO
, ln
, ppos
, "so %s", name
);
3960 * Handle `so'. Be EXTREMELY careful, as we shouldn't be
3961 * opening anything that's not in our cwd or anything beneath
3962 * it. Thus, explicitly disallow traversing up the file-system
3963 * or using absolute paths.
3966 if (*name
== '/' || strstr(name
, "../") || strstr(name
, "/..")) {
3967 mandoc_msg(MANDOCERR_SO_PATH
, ln
, ppos
, ".so %s", name
);
3968 buf
->sz
= mandoc_asprintf(&cp
,
3969 ".sp\nSee the file %s.\n.sp", name
) + 1;
3973 return ROFF_REPARSE
;
3980 /* --- user defined strings and macros ------------------------------------ */
3983 roff_userdef(ROFF_ARGS
)
3986 char *arg
, *ap
, *dst
, *src
;
3989 /* If the macro is empty, ignore it altogether. */
3991 if (*r
->current_string
== '\0')
3994 /* Initialize a new macro stack context. */
3996 if (++r
->mstackpos
== r
->mstacksz
) {
3997 r
->mstack
= mandoc_recallocarray(r
->mstack
,
3998 r
->mstacksz
, r
->mstacksz
+ 8, sizeof(*r
->mstack
));
4001 ctx
= r
->mstack
+ r
->mstackpos
;
4005 * Collect pointers to macro argument strings,
4006 * NUL-terminating them and escaping quotes.
4009 src
= buf
->buf
+ pos
;
4010 while (*src
!= '\0') {
4011 if (ctx
->argc
== ctx
->argsz
) {
4013 ctx
->argv
= mandoc_reallocarray(ctx
->argv
,
4014 ctx
->argsz
, sizeof(*ctx
->argv
));
4016 arg
= roff_getarg(r
, &src
, ln
, &pos
);
4017 sz
= 1; /* For the terminating NUL. */
4018 for (ap
= arg
; *ap
!= '\0'; ap
++)
4019 sz
+= *ap
== '"' ? 4 : 1;
4020 ctx
->argv
[ctx
->argc
++] = dst
= mandoc_malloc(sz
);
4021 for (ap
= arg
; *ap
!= '\0'; ap
++) {
4023 memcpy(dst
, "\\(dq", 4);
4032 /* Replace the macro invocation by the macro definition. */
4035 buf
->buf
= mandoc_strdup(r
->current_string
);
4036 buf
->sz
= strlen(buf
->buf
) + 1;
4039 return buf
->buf
[buf
->sz
- 2] == '\n' ?
4040 ROFF_REPARSE
| ROFF_USERCALL
: ROFF_IGN
| ROFF_APPEND
;
4044 * Calling a high-level macro that was renamed with .rn.
4045 * r->current_string has already been set up by roff_parse().
4048 roff_renamed(ROFF_ARGS
)
4052 buf
->sz
= mandoc_asprintf(&nbuf
, ".%s%s%s", r
->current_string
,
4053 buf
->buf
[pos
] == '\0' ? "" : " ", buf
->buf
+ pos
) + 1;
4061 * Measure the length in bytes of the roff identifier at *cpp
4062 * and advance the pointer to the next word.
4065 roff_getname(struct roff
*r
, char **cpp
, int ln
, int pos
)
4074 /* Advance cp to the byte after the end of the name. */
4076 for (cp
= name
; 1; cp
++) {
4080 if (*cp
== ' ' || *cp
== '\t') {
4086 if (cp
[1] == '{' || cp
[1] == '}')
4090 mandoc_msg(MANDOCERR_NAMESC
, ln
, pos
,
4091 "%.*s", (int)(cp
- name
+ 1), name
);
4092 mandoc_escape((const char **)&cp
, NULL
, NULL
);
4096 /* Read past spaces. */
4106 * Store *string into the user-defined string called *name.
4107 * To clear an existing entry, call with (*r, *name, NULL, 0).
4108 * append == 0: replace mode
4109 * append == 1: single-line append mode
4110 * append == 2: multiline append mode, append '\n' after each call
4113 roff_setstr(struct roff
*r
, const char *name
, const char *string
,
4118 namesz
= strlen(name
);
4119 roff_setstrn(&r
->strtab
, name
, namesz
, string
,
4120 string
? strlen(string
) : 0, append
);
4121 roff_setstrn(&r
->rentab
, name
, namesz
, NULL
, 0, 0);
4125 roff_setstrn(struct roffkv
**r
, const char *name
, size_t namesz
,
4126 const char *string
, size_t stringsz
, int append
)
4131 size_t oldch
, newch
;
4133 /* Search for an existing string with the same name. */
4136 while (n
&& (namesz
!= n
->key
.sz
||
4137 strncmp(n
->key
.p
, name
, namesz
)))
4141 /* Create a new string table entry. */
4142 n
= mandoc_malloc(sizeof(struct roffkv
));
4143 n
->key
.p
= mandoc_strndup(name
, namesz
);
4149 } else if (0 == append
) {
4159 * One additional byte for the '\n' in multiline mode,
4160 * and one for the terminating '\0'.
4162 newch
= stringsz
+ (1 < append
? 2u : 1u);
4164 if (NULL
== n
->val
.p
) {
4165 n
->val
.p
= mandoc_malloc(newch
);
4170 n
->val
.p
= mandoc_realloc(n
->val
.p
, oldch
+ newch
);
4173 /* Skip existing content in the destination buffer. */
4174 c
= n
->val
.p
+ (int)oldch
;
4176 /* Append new content to the destination buffer. */
4178 while (i
< (int)stringsz
) {
4180 * Rudimentary roff copy mode:
4181 * Handle escaped backslashes.
4183 if ('\\' == string
[i
] && '\\' == string
[i
+ 1])
4188 /* Append terminating bytes. */
4193 n
->val
.sz
= (int)(c
- n
->val
.p
);
4197 roff_getstrn(struct roff
*r
, const char *name
, size_t len
,
4200 const struct roffkv
*n
;
4205 for (n
= r
->strtab
; n
!= NULL
; n
= n
->next
) {
4206 if (strncmp(name
, n
->key
.p
, len
) != 0 ||
4207 n
->key
.p
[len
] != '\0' || n
->val
.p
== NULL
)
4209 if (*deftype
& ROFFDEF_USER
) {
4210 *deftype
= ROFFDEF_USER
;
4217 for (n
= r
->rentab
; n
!= NULL
; n
= n
->next
) {
4218 if (strncmp(name
, n
->key
.p
, len
) != 0 ||
4219 n
->key
.p
[len
] != '\0' || n
->val
.p
== NULL
)
4221 if (*deftype
& ROFFDEF_REN
) {
4222 *deftype
= ROFFDEF_REN
;
4229 for (i
= 0; i
< PREDEFS_MAX
; i
++) {
4230 if (strncmp(name
, predefs
[i
].name
, len
) != 0 ||
4231 predefs
[i
].name
[len
] != '\0')
4233 if (*deftype
& ROFFDEF_PRE
) {
4234 *deftype
= ROFFDEF_PRE
;
4235 return predefs
[i
].str
;
4241 if (r
->man
->meta
.macroset
!= MACROSET_MAN
) {
4242 for (tok
= MDOC_Dd
; tok
< MDOC_MAX
; tok
++) {
4243 if (strncmp(name
, roff_name
[tok
], len
) != 0 ||
4244 roff_name
[tok
][len
] != '\0')
4246 if (*deftype
& ROFFDEF_STD
) {
4247 *deftype
= ROFFDEF_STD
;
4255 if (r
->man
->meta
.macroset
!= MACROSET_MDOC
) {
4256 for (tok
= MAN_TH
; tok
< MAN_MAX
; tok
++) {
4257 if (strncmp(name
, roff_name
[tok
], len
) != 0 ||
4258 roff_name
[tok
][len
] != '\0')
4260 if (*deftype
& ROFFDEF_STD
) {
4261 *deftype
= ROFFDEF_STD
;
4270 if (found
== 0 && *deftype
!= ROFFDEF_ANY
) {
4271 if (*deftype
& ROFFDEF_REN
) {
4273 * This might still be a request,
4274 * so do not treat it as undefined yet.
4276 *deftype
= ROFFDEF_UNDEF
;
4280 /* Using an undefined string defines it to be empty. */
4282 roff_setstrn(&r
->strtab
, name
, len
, "", 0, 0);
4283 roff_setstrn(&r
->rentab
, name
, len
, NULL
, 0, 0);
4291 roff_freestr(struct roffkv
*r
)
4293 struct roffkv
*n
, *nn
;
4295 for (n
= r
; n
; n
= nn
) {
4303 /* --- accessors and utility functions ------------------------------------ */
4306 * Duplicate an input string, making the appropriate character
4307 * conversations (as stipulated by `tr') along the way.
4308 * Returns a heap-allocated string with all the replacements made.
4311 roff_strdup(const struct roff
*r
, const char *p
)
4313 const struct roffkv
*cp
;
4317 enum mandoc_esc esc
;
4319 if (NULL
== r
->xmbtab
&& NULL
== r
->xtab
)
4320 return mandoc_strdup(p
);
4321 else if ('\0' == *p
)
4322 return mandoc_strdup("");
4325 * Step through each character looking for term matches
4326 * (remember that a `tr' can be invoked with an escape, which is
4327 * a glyph but the escape is multi-character).
4328 * We only do this if the character hash has been initialised
4329 * and the string is >0 length.
4335 while ('\0' != *p
) {
4336 assert((unsigned int)*p
< 128);
4337 if ('\\' != *p
&& r
->xtab
&& r
->xtab
[(unsigned int)*p
].p
) {
4338 sz
= r
->xtab
[(int)*p
].sz
;
4339 res
= mandoc_realloc(res
, ssz
+ sz
+ 1);
4340 memcpy(res
+ ssz
, r
->xtab
[(int)*p
].p
, sz
);
4344 } else if ('\\' != *p
) {
4345 res
= mandoc_realloc(res
, ssz
+ 2);
4350 /* Search for term matches. */
4351 for (cp
= r
->xmbtab
; cp
; cp
= cp
->next
)
4352 if (0 == strncmp(p
, cp
->key
.p
, cp
->key
.sz
))
4357 * A match has been found.
4358 * Append the match to the array and move
4359 * forward by its keysize.
4361 res
= mandoc_realloc(res
,
4362 ssz
+ cp
->val
.sz
+ 1);
4363 memcpy(res
+ ssz
, cp
->val
.p
, cp
->val
.sz
);
4365 p
+= (int)cp
->key
.sz
;
4370 * Handle escapes carefully: we need to copy
4371 * over just the escape itself, or else we might
4372 * do replacements within the escape itself.
4373 * Make sure to pass along the bogus string.
4376 esc
= mandoc_escape(&p
, NULL
, NULL
);
4377 if (ESCAPE_ERROR
== esc
) {
4379 res
= mandoc_realloc(res
, ssz
+ sz
+ 1);
4380 memcpy(res
+ ssz
, pp
, sz
);
4384 * We bail out on bad escapes.
4385 * No need to warn: we already did so when
4386 * roff_expand() was called.
4389 res
= mandoc_realloc(res
, ssz
+ sz
+ 1);
4390 memcpy(res
+ ssz
, pp
, sz
);
4394 res
[(int)ssz
] = '\0';
4399 roff_getformat(const struct roff
*r
)
4406 * Find out whether a line is a macro line or not.
4407 * If it is, adjust the current position and return one; if it isn't,
4408 * return zero and don't change the current position.
4409 * If the control character has been set with `.cc', then let that grain
4411 * This is slighly contrary to groff, where using the non-breaking
4412 * control character when `cc' has been invoked will cause the
4413 * non-breaking macro contents to be printed verbatim.
4416 roff_getcontrol(const struct roff
*r
, const char *cp
, int *ppos
)
4422 if (r
->control
!= '\0' && cp
[pos
] == r
->control
)
4424 else if (r
->control
!= '\0')
4426 else if ('\\' == cp
[pos
] && '.' == cp
[pos
+ 1])
4428 else if ('.' == cp
[pos
] || '\'' == cp
[pos
])
4433 while (' ' == cp
[pos
] || '\t' == cp
[pos
])