]>
git.cameronkatri.com Git - mandoc.git/blob - roff.c
1 /* $Id: roff.c,v 1.345 2018/12/12 21:54:35 schwarze Exp $ */
3 * Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2010-2015, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org>
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.
20 #include <sys/types.h>
31 #include "mandoc_aux.h"
32 #include "mandoc_ohash.h"
36 #include "libmandoc.h"
40 /* Maximum number of string expansions per line, to break infinite loops. */
41 #define EXPAND_LIMIT 1000
43 /* Types of definitions of macros and strings. */
44 #define ROFFDEF_USER (1 << 1) /* User-defined. */
45 #define ROFFDEF_PRE (1 << 2) /* Predefined. */
46 #define ROFFDEF_REN (1 << 3) /* Renamed standard macro. */
47 #define ROFFDEF_STD (1 << 4) /* mdoc(7) or man(7) macro. */
48 #define ROFFDEF_ANY (ROFFDEF_USER | ROFFDEF_PRE | \
49 ROFFDEF_REN | ROFFDEF_STD)
50 #define ROFFDEF_UNDEF (1 << 5) /* Completely undefined. */
52 /* --- data types --------------------------------------------------------- */
55 * An incredibly-simple string buffer.
58 char *p
; /* nil-terminated buffer */
59 size_t sz
; /* saved strlen(p) */
63 * A key-value roffstr pair as part of a singly-linked list.
68 struct roffkv
*next
; /* next in list */
72 * A single number register as part of a singly-linked list.
82 * Association of request and macro names with token IDs.
90 * A macro processing context.
91 * More than one is needed when macro calls are nested.
100 struct mparse
*parse
; /* parse point */
101 struct roff_man
*man
; /* mdoc or man parser */
102 struct roffnode
*last
; /* leaf of stack */
103 struct mctx
*mstack
; /* stack of macro contexts */
104 int *rstack
; /* stack of inverted `ie' values */
105 struct ohash
*reqtab
; /* request lookup table */
106 struct roffreg
*regtab
; /* number registers */
107 struct roffkv
*strtab
; /* user-defined strings & macros */
108 struct roffkv
*rentab
; /* renamed strings & macros */
109 struct roffkv
*xmbtab
; /* multi-byte trans table (`tr') */
110 struct roffstr
*xtab
; /* single-byte trans table (`tr') */
111 const char *current_string
; /* value of last called user macro */
112 struct tbl_node
*first_tbl
; /* first table parsed */
113 struct tbl_node
*last_tbl
; /* last table parsed */
114 struct tbl_node
*tbl
; /* current table being parsed */
115 struct eqn_node
*last_eqn
; /* equation parser */
116 struct eqn_node
*eqn
; /* active equation parser */
117 int eqn_inline
; /* current equation is inline */
118 int options
; /* parse options */
119 int mstacksz
; /* current size of mstack */
120 int mstackpos
; /* position in mstack */
121 int rstacksz
; /* current size limit of rstack */
122 int rstackpos
; /* position in rstack */
123 int format
; /* current file in mdoc or man format */
124 char control
; /* control character */
125 char escape
; /* escape character */
129 enum roff_tok tok
; /* type of node */
130 struct roffnode
*parent
; /* up one in stack */
131 int line
; /* parse line */
132 int col
; /* parse col */
133 char *name
; /* node name, e.g. macro name */
134 char *end
; /* end-rules: custom token */
135 int endspan
; /* end-rules: next-line or infty */
136 int rule
; /* current evaluation rule */
139 #define ROFF_ARGS struct roff *r, /* parse ctx */ \
140 enum roff_tok tok, /* tok of macro */ \
141 struct buf *buf, /* input buffer */ \
142 int ln, /* parse line */ \
143 int ppos, /* original pos in buffer */ \
144 int pos, /* current pos in buffer */ \
145 int *offs /* reset offset of buffer data */
147 typedef int (*roffproc
)(ROFF_ARGS
);
150 roffproc proc
; /* process new macro */
151 roffproc text
; /* process as child text of macro */
152 roffproc sub
; /* process as child of macro */
154 #define ROFFMAC_STRUCT (1 << 0) /* always interpret */
158 const char *name
; /* predefined input name */
159 const char *str
; /* replacement symbol */
162 #define PREDEF(__name, __str) \
163 { (__name), (__str) },
165 /* --- function prototypes ------------------------------------------------ */
167 static int roffnode_cleanscope(struct roff
*);
168 static int roffnode_pop(struct roff
*);
169 static void roffnode_push(struct roff
*, enum roff_tok
,
170 const char *, int, int);
171 static void roff_addtbl(struct roff_man
*, struct tbl_node
*);
172 static int roff_als(ROFF_ARGS
);
173 static int roff_block(ROFF_ARGS
);
174 static int roff_block_text(ROFF_ARGS
);
175 static int roff_block_sub(ROFF_ARGS
);
176 static int roff_br(ROFF_ARGS
);
177 static int roff_cblock(ROFF_ARGS
);
178 static int roff_cc(ROFF_ARGS
);
179 static int roff_ccond(struct roff
*, int, int);
180 static int roff_char(ROFF_ARGS
);
181 static int roff_cond(ROFF_ARGS
);
182 static int roff_cond_text(ROFF_ARGS
);
183 static int roff_cond_sub(ROFF_ARGS
);
184 static int roff_ds(ROFF_ARGS
);
185 static int roff_ec(ROFF_ARGS
);
186 static int roff_eo(ROFF_ARGS
);
187 static int roff_eqndelim(struct roff
*, struct buf
*, int);
188 static int roff_evalcond(struct roff
*r
, int, char *, int *);
189 static int roff_evalnum(struct roff
*, int,
190 const char *, int *, int *, int);
191 static int roff_evalpar(struct roff
*, int,
192 const char *, int *, int *, int);
193 static int roff_evalstrcond(const char *, int *);
194 static void roff_free1(struct roff
*);
195 static void roff_freereg(struct roffreg
*);
196 static void roff_freestr(struct roffkv
*);
197 static size_t roff_getname(struct roff
*, char **, int, int);
198 static int roff_getnum(const char *, int *, int *, int);
199 static int roff_getop(const char *, int *, char *);
200 static int roff_getregn(struct roff
*,
201 const char *, size_t, char);
202 static int roff_getregro(const struct roff
*,
204 static const char *roff_getstrn(struct roff
*,
205 const char *, size_t, int *);
206 static int roff_hasregn(const struct roff
*,
207 const char *, size_t);
208 static int roff_insec(ROFF_ARGS
);
209 static int roff_it(ROFF_ARGS
);
210 static int roff_line_ignore(ROFF_ARGS
);
211 static void roff_man_alloc1(struct roff_man
*);
212 static void roff_man_free1(struct roff_man
*);
213 static int roff_manyarg(ROFF_ARGS
);
214 static int roff_nop(ROFF_ARGS
);
215 static int roff_nr(ROFF_ARGS
);
216 static int roff_onearg(ROFF_ARGS
);
217 static enum roff_tok
roff_parse(struct roff
*, char *, int *,
219 static int roff_parsetext(struct roff
*, struct buf
*,
221 static int roff_renamed(ROFF_ARGS
);
222 static int roff_res(struct roff
*, struct buf
*, int, int);
223 static int roff_return(ROFF_ARGS
);
224 static int roff_rm(ROFF_ARGS
);
225 static int roff_rn(ROFF_ARGS
);
226 static int roff_rr(ROFF_ARGS
);
227 static void roff_setregn(struct roff
*, const char *,
228 size_t, int, char, int);
229 static void roff_setstr(struct roff
*,
230 const char *, const char *, int);
231 static void roff_setstrn(struct roffkv
**, const char *,
232 size_t, const char *, size_t, int);
233 static int roff_shift(ROFF_ARGS
);
234 static int roff_so(ROFF_ARGS
);
235 static int roff_tr(ROFF_ARGS
);
236 static int roff_Dd(ROFF_ARGS
);
237 static int roff_TE(ROFF_ARGS
);
238 static int roff_TS(ROFF_ARGS
);
239 static int roff_EQ(ROFF_ARGS
);
240 static int roff_EN(ROFF_ARGS
);
241 static int roff_T_(ROFF_ARGS
);
242 static int roff_unsupp(ROFF_ARGS
);
243 static int roff_userdef(ROFF_ARGS
);
245 /* --- constant data ------------------------------------------------------ */
247 #define ROFFNUM_SCALE (1 << 0) /* Honour scaling in roff_getnum(). */
248 #define ROFFNUM_WHITE (1 << 1) /* Skip whitespace in roff_evalnum(). */
250 const char *__roff_name
[MAN_MAX
+ 1] = {
251 "br", "ce", "ft", "ll",
252 "mc", "po", "rj", "sp",
254 "ab", "ad", "af", "aln",
255 "als", "am", "am1", "ami",
256 "ami1", "as", "as1", "asciify",
257 "backtrace", "bd", "bleedat", "blm",
258 "box", "boxa", "bp", "BP",
259 "break", "breakchar", "brnl", "brp",
261 "cf", "cflags", "ch", "char",
262 "chop", "class", "close", "CL",
263 "color", "composite", "continue", "cp",
264 "cropat", "cs", "cu", "da",
265 "dch", "Dd", "de", "de1",
266 "defcolor", "dei", "dei1", "device",
267 "devicem", "di", "do", "ds",
268 "ds1", "dwh", "dt", "ec",
269 "ecr", "ecs", "el", "em",
270 "EN", "eo", "EP", "EQ",
271 "errprint", "ev", "evc", "ex",
272 "fallback", "fam", "fc", "fchar",
273 "fcolor", "fdeferlig", "feature", "fkern",
274 "fl", "flig", "fp", "fps",
275 "fschar", "fspacewidth", "fspecial", "ftr",
276 "fzoom", "gcolor", "hc", "hcode",
277 "hidechar", "hla", "hlm", "hpf",
278 "hpfa", "hpfcode", "hw", "hy",
279 "hylang", "hylen", "hym", "hypp",
280 "hys", "ie", "if", "ig",
281 "index", "it", "itc", "IX",
282 "kern", "kernafter", "kernbefore", "kernpair",
283 "lc", "lc_ctype", "lds", "length",
284 "letadj", "lf", "lg", "lhang",
285 "linetabs", "lnr", "lnrf", "lpfx",
287 "mediasize", "minss", "mk", "mso",
288 "na", "ne", "nh", "nhychar",
289 "nm", "nn", "nop", "nr",
290 "nrf", "nroff", "ns", "nx",
291 "open", "opena", "os", "output",
292 "padj", "papersize", "pc", "pev",
293 "pi", "PI", "pl", "pm",
295 "psbb", "pshape", "pso", "ptr",
296 "pvs", "rchar", "rd", "recursionlimit",
297 "return", "rfschar", "rhang",
298 "rm", "rn", "rnn", "rr",
299 "rs", "rt", "schar", "sentchar",
300 "shc", "shift", "sizes", "so",
301 "spacewidth", "special", "spreadwarn", "ss",
302 "sty", "substring", "sv", "sy",
305 "tm", "tm1", "tmc", "tr",
306 "track", "transchar", "trf", "trimat",
307 "trin", "trnt", "troff", "TS",
308 "uf", "ul", "unformat", "unwatch",
309 "unwatchn", "vpt", "vs", "warn",
310 "warnscale", "watch", "watchlength", "watchn",
311 "wh", "while", "write", "writec",
312 "writem", "xflag", ".", NULL
,
314 "Dd", "Dt", "Os", "Sh",
315 "Ss", "Pp", "D1", "Dl",
316 "Bd", "Ed", "Bl", "El",
317 "It", "Ad", "An", "Ap",
318 "Ar", "Cd", "Cm", "Dv",
319 "Er", "Ev", "Ex", "Fa",
320 "Fd", "Fl", "Fn", "Ft",
321 "Ic", "In", "Li", "Nd",
322 "Nm", "Op", "Ot", "Pa",
323 "Rv", "St", "Va", "Vt",
324 "Xr", "%A", "%B", "%D",
325 "%I", "%J", "%N", "%O",
326 "%P", "%R", "%T", "%V",
327 "Ac", "Ao", "Aq", "At",
328 "Bc", "Bf", "Bo", "Bq",
329 "Bsx", "Bx", "Db", "Dc",
330 "Do", "Dq", "Ec", "Ef",
331 "Em", "Eo", "Fx", "Ms",
332 "No", "Ns", "Nx", "Ox",
333 "Pc", "Pf", "Po", "Pq",
334 "Qc", "Ql", "Qo", "Qq",
335 "Re", "Rs", "Sc", "So",
336 "Sq", "Sm", "Sx", "Sy",
337 "Tn", "Ux", "Xc", "Xo",
338 "Fo", "Fc", "Oo", "Oc",
339 "Bk", "Ek", "Bt", "Hf",
340 "Fr", "Ud", "Lb", "Lp",
341 "Lk", "Mt", "Brq", "Bro",
342 "Brc", "%C", "Es", "En",
343 "Dx", "%Q", "%U", "Ta",
345 "TH", "SH", "SS", "TP",
347 "LP", "PP", "P", "IP",
348 "HP", "SM", "SB", "BI",
349 "IB", "BR", "RB", "R",
350 "B", "I", "IR", "RI",
352 "RE", "RS", "DT", "UC",
356 "UE", "MT", "ME", NULL
358 const char *const *roff_name
= __roff_name
;
360 static struct roffmac roffs
[TOKEN_NONE
] = {
361 { roff_br
, NULL
, NULL
, 0 }, /* br */
362 { roff_onearg
, NULL
, NULL
, 0 }, /* ce */
363 { roff_onearg
, NULL
, NULL
, 0 }, /* ft */
364 { roff_onearg
, NULL
, NULL
, 0 }, /* ll */
365 { roff_onearg
, NULL
, NULL
, 0 }, /* mc */
366 { roff_onearg
, NULL
, NULL
, 0 }, /* po */
367 { roff_onearg
, NULL
, NULL
, 0 }, /* rj */
368 { roff_onearg
, NULL
, NULL
, 0 }, /* sp */
369 { roff_manyarg
, NULL
, NULL
, 0 }, /* ta */
370 { roff_onearg
, NULL
, NULL
, 0 }, /* ti */
371 { NULL
, NULL
, NULL
, 0 }, /* ROFF_MAX */
372 { roff_unsupp
, NULL
, NULL
, 0 }, /* ab */
373 { roff_line_ignore
, NULL
, NULL
, 0 }, /* ad */
374 { roff_line_ignore
, NULL
, NULL
, 0 }, /* af */
375 { roff_unsupp
, NULL
, NULL
, 0 }, /* aln */
376 { roff_als
, NULL
, NULL
, 0 }, /* als */
377 { roff_block
, roff_block_text
, roff_block_sub
, 0 }, /* am */
378 { roff_block
, roff_block_text
, roff_block_sub
, 0 }, /* am1 */
379 { roff_block
, roff_block_text
, roff_block_sub
, 0 }, /* ami */
380 { roff_block
, roff_block_text
, roff_block_sub
, 0 }, /* ami1 */
381 { roff_ds
, NULL
, NULL
, 0 }, /* as */
382 { roff_ds
, NULL
, NULL
, 0 }, /* as1 */
383 { roff_unsupp
, NULL
, NULL
, 0 }, /* asciify */
384 { roff_line_ignore
, NULL
, NULL
, 0 }, /* backtrace */
385 { roff_line_ignore
, NULL
, NULL
, 0 }, /* bd */
386 { roff_line_ignore
, NULL
, NULL
, 0 }, /* bleedat */
387 { roff_unsupp
, NULL
, NULL
, 0 }, /* blm */
388 { roff_unsupp
, NULL
, NULL
, 0 }, /* box */
389 { roff_unsupp
, NULL
, NULL
, 0 }, /* boxa */
390 { roff_line_ignore
, NULL
, NULL
, 0 }, /* bp */
391 { roff_unsupp
, NULL
, NULL
, 0 }, /* BP */
392 { roff_unsupp
, NULL
, NULL
, 0 }, /* break */
393 { roff_line_ignore
, NULL
, NULL
, 0 }, /* breakchar */
394 { roff_line_ignore
, NULL
, NULL
, 0 }, /* brnl */
395 { roff_br
, NULL
, NULL
, 0 }, /* brp */
396 { roff_line_ignore
, NULL
, NULL
, 0 }, /* brpnl */
397 { roff_unsupp
, NULL
, NULL
, 0 }, /* c2 */
398 { roff_cc
, NULL
, NULL
, 0 }, /* cc */
399 { roff_insec
, NULL
, NULL
, 0 }, /* cf */
400 { roff_line_ignore
, NULL
, NULL
, 0 }, /* cflags */
401 { roff_line_ignore
, NULL
, NULL
, 0 }, /* ch */
402 { roff_char
, NULL
, NULL
, 0 }, /* char */
403 { roff_unsupp
, NULL
, NULL
, 0 }, /* chop */
404 { roff_line_ignore
, NULL
, NULL
, 0 }, /* class */
405 { roff_insec
, NULL
, NULL
, 0 }, /* close */
406 { roff_unsupp
, NULL
, NULL
, 0 }, /* CL */
407 { roff_line_ignore
, NULL
, NULL
, 0 }, /* color */
408 { roff_unsupp
, NULL
, NULL
, 0 }, /* composite */
409 { roff_unsupp
, NULL
, NULL
, 0 }, /* continue */
410 { roff_line_ignore
, NULL
, NULL
, 0 }, /* cp */
411 { roff_line_ignore
, NULL
, NULL
, 0 }, /* cropat */
412 { roff_line_ignore
, NULL
, NULL
, 0 }, /* cs */
413 { roff_line_ignore
, NULL
, NULL
, 0 }, /* cu */
414 { roff_unsupp
, NULL
, NULL
, 0 }, /* da */
415 { roff_unsupp
, NULL
, NULL
, 0 }, /* dch */
416 { roff_Dd
, NULL
, NULL
, 0 }, /* Dd */
417 { roff_block
, roff_block_text
, roff_block_sub
, 0 }, /* de */
418 { roff_block
, roff_block_text
, roff_block_sub
, 0 }, /* de1 */
419 { roff_line_ignore
, NULL
, NULL
, 0 }, /* defcolor */
420 { roff_block
, roff_block_text
, roff_block_sub
, 0 }, /* dei */
421 { roff_block
, roff_block_text
, roff_block_sub
, 0 }, /* dei1 */
422 { roff_unsupp
, NULL
, NULL
, 0 }, /* device */
423 { roff_unsupp
, NULL
, NULL
, 0 }, /* devicem */
424 { roff_unsupp
, NULL
, NULL
, 0 }, /* di */
425 { roff_unsupp
, NULL
, NULL
, 0 }, /* do */
426 { roff_ds
, NULL
, NULL
, 0 }, /* ds */
427 { roff_ds
, NULL
, NULL
, 0 }, /* ds1 */
428 { roff_unsupp
, NULL
, NULL
, 0 }, /* dwh */
429 { roff_unsupp
, NULL
, NULL
, 0 }, /* dt */
430 { roff_ec
, NULL
, NULL
, 0 }, /* ec */
431 { roff_unsupp
, NULL
, NULL
, 0 }, /* ecr */
432 { roff_unsupp
, NULL
, NULL
, 0 }, /* ecs */
433 { roff_cond
, roff_cond_text
, roff_cond_sub
, ROFFMAC_STRUCT
}, /* el */
434 { roff_unsupp
, NULL
, NULL
, 0 }, /* em */
435 { roff_EN
, NULL
, NULL
, 0 }, /* EN */
436 { roff_eo
, NULL
, NULL
, 0 }, /* eo */
437 { roff_unsupp
, NULL
, NULL
, 0 }, /* EP */
438 { roff_EQ
, NULL
, NULL
, 0 }, /* EQ */
439 { roff_line_ignore
, NULL
, NULL
, 0 }, /* errprint */
440 { roff_unsupp
, NULL
, NULL
, 0 }, /* ev */
441 { roff_unsupp
, NULL
, NULL
, 0 }, /* evc */
442 { roff_unsupp
, NULL
, NULL
, 0 }, /* ex */
443 { roff_line_ignore
, NULL
, NULL
, 0 }, /* fallback */
444 { roff_line_ignore
, NULL
, NULL
, 0 }, /* fam */
445 { roff_unsupp
, NULL
, NULL
, 0 }, /* fc */
446 { roff_unsupp
, NULL
, NULL
, 0 }, /* fchar */
447 { roff_line_ignore
, NULL
, NULL
, 0 }, /* fcolor */
448 { roff_line_ignore
, NULL
, NULL
, 0 }, /* fdeferlig */
449 { roff_line_ignore
, NULL
, NULL
, 0 }, /* feature */
450 { roff_line_ignore
, NULL
, NULL
, 0 }, /* fkern */
451 { roff_line_ignore
, NULL
, NULL
, 0 }, /* fl */
452 { roff_line_ignore
, NULL
, NULL
, 0 }, /* flig */
453 { roff_line_ignore
, NULL
, NULL
, 0 }, /* fp */
454 { roff_line_ignore
, NULL
, NULL
, 0 }, /* fps */
455 { roff_unsupp
, NULL
, NULL
, 0 }, /* fschar */
456 { roff_line_ignore
, NULL
, NULL
, 0 }, /* fspacewidth */
457 { roff_line_ignore
, NULL
, NULL
, 0 }, /* fspecial */
458 { roff_line_ignore
, NULL
, NULL
, 0 }, /* ftr */
459 { roff_line_ignore
, NULL
, NULL
, 0 }, /* fzoom */
460 { roff_line_ignore
, NULL
, NULL
, 0 }, /* gcolor */
461 { roff_line_ignore
, NULL
, NULL
, 0 }, /* hc */
462 { roff_line_ignore
, NULL
, NULL
, 0 }, /* hcode */
463 { roff_line_ignore
, NULL
, NULL
, 0 }, /* hidechar */
464 { roff_line_ignore
, NULL
, NULL
, 0 }, /* hla */
465 { roff_line_ignore
, NULL
, NULL
, 0 }, /* hlm */
466 { roff_line_ignore
, NULL
, NULL
, 0 }, /* hpf */
467 { roff_line_ignore
, NULL
, NULL
, 0 }, /* hpfa */
468 { roff_line_ignore
, NULL
, NULL
, 0 }, /* hpfcode */
469 { roff_line_ignore
, NULL
, NULL
, 0 }, /* hw */
470 { roff_line_ignore
, NULL
, NULL
, 0 }, /* hy */
471 { roff_line_ignore
, NULL
, NULL
, 0 }, /* hylang */
472 { roff_line_ignore
, NULL
, NULL
, 0 }, /* hylen */
473 { roff_line_ignore
, NULL
, NULL
, 0 }, /* hym */
474 { roff_line_ignore
, NULL
, NULL
, 0 }, /* hypp */
475 { roff_line_ignore
, NULL
, NULL
, 0 }, /* hys */
476 { roff_cond
, roff_cond_text
, roff_cond_sub
, ROFFMAC_STRUCT
}, /* ie */
477 { roff_cond
, roff_cond_text
, roff_cond_sub
, ROFFMAC_STRUCT
}, /* if */
478 { roff_block
, roff_block_text
, roff_block_sub
, 0 }, /* ig */
479 { roff_unsupp
, NULL
, NULL
, 0 }, /* index */
480 { roff_it
, NULL
, NULL
, 0 }, /* it */
481 { roff_unsupp
, NULL
, NULL
, 0 }, /* itc */
482 { roff_line_ignore
, NULL
, NULL
, 0 }, /* IX */
483 { roff_line_ignore
, NULL
, NULL
, 0 }, /* kern */
484 { roff_line_ignore
, NULL
, NULL
, 0 }, /* kernafter */
485 { roff_line_ignore
, NULL
, NULL
, 0 }, /* kernbefore */
486 { roff_line_ignore
, NULL
, NULL
, 0 }, /* kernpair */
487 { roff_unsupp
, NULL
, NULL
, 0 }, /* lc */
488 { roff_unsupp
, NULL
, NULL
, 0 }, /* lc_ctype */
489 { roff_unsupp
, NULL
, NULL
, 0 }, /* lds */
490 { roff_unsupp
, NULL
, NULL
, 0 }, /* length */
491 { roff_line_ignore
, NULL
, NULL
, 0 }, /* letadj */
492 { roff_insec
, NULL
, NULL
, 0 }, /* lf */
493 { roff_line_ignore
, NULL
, NULL
, 0 }, /* lg */
494 { roff_line_ignore
, NULL
, NULL
, 0 }, /* lhang */
495 { roff_unsupp
, NULL
, NULL
, 0 }, /* linetabs */
496 { roff_unsupp
, NULL
, NULL
, 0 }, /* lnr */
497 { roff_unsupp
, NULL
, NULL
, 0 }, /* lnrf */
498 { roff_unsupp
, NULL
, NULL
, 0 }, /* lpfx */
499 { roff_line_ignore
, NULL
, NULL
, 0 }, /* ls */
500 { roff_unsupp
, NULL
, NULL
, 0 }, /* lsm */
501 { roff_line_ignore
, NULL
, NULL
, 0 }, /* lt */
502 { roff_line_ignore
, NULL
, NULL
, 0 }, /* mediasize */
503 { roff_line_ignore
, NULL
, NULL
, 0 }, /* minss */
504 { roff_line_ignore
, NULL
, NULL
, 0 }, /* mk */
505 { roff_insec
, NULL
, NULL
, 0 }, /* mso */
506 { roff_line_ignore
, NULL
, NULL
, 0 }, /* na */
507 { roff_line_ignore
, NULL
, NULL
, 0 }, /* ne */
508 { roff_line_ignore
, NULL
, NULL
, 0 }, /* nh */
509 { roff_line_ignore
, NULL
, NULL
, 0 }, /* nhychar */
510 { roff_unsupp
, NULL
, NULL
, 0 }, /* nm */
511 { roff_unsupp
, NULL
, NULL
, 0 }, /* nn */
512 { roff_nop
, NULL
, NULL
, 0 }, /* nop */
513 { roff_nr
, NULL
, NULL
, 0 }, /* nr */
514 { roff_unsupp
, NULL
, NULL
, 0 }, /* nrf */
515 { roff_line_ignore
, NULL
, NULL
, 0 }, /* nroff */
516 { roff_line_ignore
, NULL
, NULL
, 0 }, /* ns */
517 { roff_insec
, NULL
, NULL
, 0 }, /* nx */
518 { roff_insec
, NULL
, NULL
, 0 }, /* open */
519 { roff_insec
, NULL
, NULL
, 0 }, /* opena */
520 { roff_line_ignore
, NULL
, NULL
, 0 }, /* os */
521 { roff_unsupp
, NULL
, NULL
, 0 }, /* output */
522 { roff_line_ignore
, NULL
, NULL
, 0 }, /* padj */
523 { roff_line_ignore
, NULL
, NULL
, 0 }, /* papersize */
524 { roff_line_ignore
, NULL
, NULL
, 0 }, /* pc */
525 { roff_line_ignore
, NULL
, NULL
, 0 }, /* pev */
526 { roff_insec
, NULL
, NULL
, 0 }, /* pi */
527 { roff_unsupp
, NULL
, NULL
, 0 }, /* PI */
528 { roff_line_ignore
, NULL
, NULL
, 0 }, /* pl */
529 { roff_line_ignore
, NULL
, NULL
, 0 }, /* pm */
530 { roff_line_ignore
, NULL
, NULL
, 0 }, /* pn */
531 { roff_line_ignore
, NULL
, NULL
, 0 }, /* pnr */
532 { roff_line_ignore
, NULL
, NULL
, 0 }, /* ps */
533 { roff_unsupp
, NULL
, NULL
, 0 }, /* psbb */
534 { roff_unsupp
, NULL
, NULL
, 0 }, /* pshape */
535 { roff_insec
, NULL
, NULL
, 0 }, /* pso */
536 { roff_line_ignore
, NULL
, NULL
, 0 }, /* ptr */
537 { roff_line_ignore
, NULL
, NULL
, 0 }, /* pvs */
538 { roff_unsupp
, NULL
, NULL
, 0 }, /* rchar */
539 { roff_line_ignore
, NULL
, NULL
, 0 }, /* rd */
540 { roff_line_ignore
, NULL
, NULL
, 0 }, /* recursionlimit */
541 { roff_return
, NULL
, NULL
, 0 }, /* return */
542 { roff_unsupp
, NULL
, NULL
, 0 }, /* rfschar */
543 { roff_line_ignore
, NULL
, NULL
, 0 }, /* rhang */
544 { roff_rm
, NULL
, NULL
, 0 }, /* rm */
545 { roff_rn
, NULL
, NULL
, 0 }, /* rn */
546 { roff_unsupp
, NULL
, NULL
, 0 }, /* rnn */
547 { roff_rr
, NULL
, NULL
, 0 }, /* rr */
548 { roff_line_ignore
, NULL
, NULL
, 0 }, /* rs */
549 { roff_line_ignore
, NULL
, NULL
, 0 }, /* rt */
550 { roff_unsupp
, NULL
, NULL
, 0 }, /* schar */
551 { roff_line_ignore
, NULL
, NULL
, 0 }, /* sentchar */
552 { roff_line_ignore
, NULL
, NULL
, 0 }, /* shc */
553 { roff_shift
, NULL
, NULL
, 0 }, /* shift */
554 { roff_line_ignore
, NULL
, NULL
, 0 }, /* sizes */
555 { roff_so
, NULL
, NULL
, 0 }, /* so */
556 { roff_line_ignore
, NULL
, NULL
, 0 }, /* spacewidth */
557 { roff_line_ignore
, NULL
, NULL
, 0 }, /* special */
558 { roff_line_ignore
, NULL
, NULL
, 0 }, /* spreadwarn */
559 { roff_line_ignore
, NULL
, NULL
, 0 }, /* ss */
560 { roff_line_ignore
, NULL
, NULL
, 0 }, /* sty */
561 { roff_unsupp
, NULL
, NULL
, 0 }, /* substring */
562 { roff_line_ignore
, NULL
, NULL
, 0 }, /* sv */
563 { roff_insec
, NULL
, NULL
, 0 }, /* sy */
564 { roff_T_
, NULL
, NULL
, 0 }, /* T& */
565 { roff_unsupp
, NULL
, NULL
, 0 }, /* tc */
566 { roff_TE
, NULL
, NULL
, 0 }, /* TE */
567 { roff_Dd
, NULL
, NULL
, 0 }, /* TH */
568 { roff_line_ignore
, NULL
, NULL
, 0 }, /* tkf */
569 { roff_unsupp
, NULL
, NULL
, 0 }, /* tl */
570 { roff_line_ignore
, NULL
, NULL
, 0 }, /* tm */
571 { roff_line_ignore
, NULL
, NULL
, 0 }, /* tm1 */
572 { roff_line_ignore
, NULL
, NULL
, 0 }, /* tmc */
573 { roff_tr
, NULL
, NULL
, 0 }, /* tr */
574 { roff_line_ignore
, NULL
, NULL
, 0 }, /* track */
575 { roff_line_ignore
, NULL
, NULL
, 0 }, /* transchar */
576 { roff_insec
, NULL
, NULL
, 0 }, /* trf */
577 { roff_line_ignore
, NULL
, NULL
, 0 }, /* trimat */
578 { roff_unsupp
, NULL
, NULL
, 0 }, /* trin */
579 { roff_unsupp
, NULL
, NULL
, 0 }, /* trnt */
580 { roff_line_ignore
, NULL
, NULL
, 0 }, /* troff */
581 { roff_TS
, NULL
, NULL
, 0 }, /* TS */
582 { roff_line_ignore
, NULL
, NULL
, 0 }, /* uf */
583 { roff_line_ignore
, NULL
, NULL
, 0 }, /* ul */
584 { roff_unsupp
, NULL
, NULL
, 0 }, /* unformat */
585 { roff_line_ignore
, NULL
, NULL
, 0 }, /* unwatch */
586 { roff_line_ignore
, NULL
, NULL
, 0 }, /* unwatchn */
587 { roff_line_ignore
, NULL
, NULL
, 0 }, /* vpt */
588 { roff_line_ignore
, NULL
, NULL
, 0 }, /* vs */
589 { roff_line_ignore
, NULL
, NULL
, 0 }, /* warn */
590 { roff_line_ignore
, NULL
, NULL
, 0 }, /* warnscale */
591 { roff_line_ignore
, NULL
, NULL
, 0 }, /* watch */
592 { roff_line_ignore
, NULL
, NULL
, 0 }, /* watchlength */
593 { roff_line_ignore
, NULL
, NULL
, 0 }, /* watchn */
594 { roff_unsupp
, NULL
, NULL
, 0 }, /* wh */
595 { roff_cond
, roff_cond_text
, roff_cond_sub
, ROFFMAC_STRUCT
}, /*while*/
596 { roff_insec
, NULL
, NULL
, 0 }, /* write */
597 { roff_insec
, NULL
, NULL
, 0 }, /* writec */
598 { roff_insec
, NULL
, NULL
, 0 }, /* writem */
599 { roff_line_ignore
, NULL
, NULL
, 0 }, /* xflag */
600 { roff_cblock
, NULL
, NULL
, 0 }, /* . */
601 { roff_renamed
, NULL
, NULL
, 0 },
602 { roff_userdef
, NULL
, NULL
, 0 }
605 /* Array of injected predefined strings. */
606 #define PREDEFS_MAX 38
607 static const struct predef predefs
[PREDEFS_MAX
] = {
608 #include "predefs.in"
611 static int roffce_lines
; /* number of input lines to center */
612 static struct roff_node
*roffce_node
; /* active request */
613 static int roffit_lines
; /* number of lines to delay */
614 static char *roffit_macro
; /* nil-terminated macro line */
617 /* --- request table ------------------------------------------------------ */
620 roffhash_alloc(enum roff_tok mintok
, enum roff_tok maxtok
)
628 htab
= mandoc_malloc(sizeof(*htab
));
629 mandoc_ohash_init(htab
, 8, offsetof(struct roffreq
, name
));
631 for (tok
= mintok
; tok
< maxtok
; tok
++) {
632 if (roff_name
[tok
] == NULL
)
634 sz
= strlen(roff_name
[tok
]);
635 req
= mandoc_malloc(sizeof(*req
) + sz
+ 1);
637 memcpy(req
->name
, roff_name
[tok
], sz
+ 1);
638 slot
= ohash_qlookup(htab
, req
->name
);
639 ohash_insert(htab
, slot
, req
);
645 roffhash_free(struct ohash
*htab
)
652 for (req
= ohash_first(htab
, &slot
); req
!= NULL
;
653 req
= ohash_next(htab
, &slot
))
660 roffhash_find(struct ohash
*htab
, const char *name
, size_t sz
)
667 req
= ohash_find(htab
, ohash_qlookupi(htab
, name
, &end
));
669 req
= ohash_find(htab
, ohash_qlookup(htab
, name
));
670 return req
== NULL
? TOKEN_NONE
: req
->tok
;
673 /* --- stack of request blocks -------------------------------------------- */
676 * Pop the current node off of the stack of roff instructions currently
680 roffnode_pop(struct roff
*r
)
686 inloop
= p
->tok
== ROFF_while
;
695 * Push a roff node onto the instruction stack. This must later be
696 * removed with roffnode_pop().
699 roffnode_push(struct roff
*r
, enum roff_tok tok
, const char *name
,
704 p
= mandoc_calloc(1, sizeof(struct roffnode
));
707 p
->name
= mandoc_strdup(name
);
711 p
->rule
= p
->parent
? p
->parent
->rule
: 0;
716 /* --- roff parser state data management ---------------------------------- */
719 roff_free1(struct roff
*r
)
721 struct tbl_node
*tbl
;
724 while (NULL
!= (tbl
= r
->first_tbl
)) {
725 r
->first_tbl
= tbl
->next
;
728 r
->first_tbl
= r
->last_tbl
= r
->tbl
= NULL
;
730 if (r
->last_eqn
!= NULL
)
731 eqn_free(r
->last_eqn
);
732 r
->last_eqn
= r
->eqn
= NULL
;
734 while (r
->mstackpos
>= 0)
745 roff_freereg(r
->regtab
);
748 roff_freestr(r
->strtab
);
749 roff_freestr(r
->rentab
);
750 roff_freestr(r
->xmbtab
);
751 r
->strtab
= r
->rentab
= r
->xmbtab
= NULL
;
754 for (i
= 0; i
< 128; i
++)
761 roff_reset(struct roff
*r
)
764 r
->format
= r
->options
& (MPARSE_MDOC
| MPARSE_MAN
);
774 roff_free(struct roff
*r
)
779 for (i
= 0; i
< r
->mstacksz
; i
++)
780 free(r
->mstack
[i
].argv
);
782 roffhash_free(r
->reqtab
);
787 roff_alloc(struct mparse
*parse
, int options
)
791 r
= mandoc_calloc(1, sizeof(struct roff
));
793 r
->reqtab
= roffhash_alloc(0, ROFF_RENAMED
);
794 r
->options
= options
;
795 r
->format
= options
& (MPARSE_MDOC
| MPARSE_MAN
);
802 /* --- syntax tree state data management ---------------------------------- */
805 roff_man_free1(struct roff_man
*man
)
808 if (man
->first
!= NULL
)
809 roff_node_delete(man
, man
->first
);
810 free(man
->meta
.msec
);
813 free(man
->meta
.arch
);
814 free(man
->meta
.title
);
815 free(man
->meta
.name
);
816 free(man
->meta
.date
);
820 roff_man_alloc1(struct roff_man
*man
)
823 memset(&man
->meta
, 0, sizeof(man
->meta
));
824 man
->first
= mandoc_calloc(1, sizeof(*man
->first
));
825 man
->first
->type
= ROFFT_ROOT
;
826 man
->last
= man
->first
;
829 man
->macroset
= MACROSET_NONE
;
830 man
->lastsec
= man
->lastnamed
= SEC_NONE
;
831 man
->next
= ROFF_NEXT_CHILD
;
835 roff_man_reset(struct roff_man
*man
)
839 roff_man_alloc1(man
);
843 roff_man_free(struct roff_man
*man
)
851 roff_man_alloc(struct roff
*roff
, struct mparse
*parse
,
852 const char *os_s
, int quick
)
854 struct roff_man
*man
;
856 man
= mandoc_calloc(1, sizeof(*man
));
861 roff_man_alloc1(man
);
866 /* --- syntax tree handling ----------------------------------------------- */
869 roff_node_alloc(struct roff_man
*man
, int line
, int pos
,
870 enum roff_type type
, int tok
)
874 n
= mandoc_calloc(1, sizeof(*n
));
879 n
->sec
= man
->lastsec
;
881 if (man
->flags
& MDOC_SYNOPSIS
)
882 n
->flags
|= NODE_SYNPRETTY
;
884 n
->flags
&= ~NODE_SYNPRETTY
;
885 if (man
->flags
& MDOC_NEWLINE
)
886 n
->flags
|= NODE_LINE
;
887 man
->flags
&= ~MDOC_NEWLINE
;
893 roff_node_append(struct roff_man
*man
, struct roff_node
*n
)
897 case ROFF_NEXT_SIBLING
:
898 if (man
->last
->next
!= NULL
) {
899 n
->next
= man
->last
->next
;
900 man
->last
->next
->prev
= n
;
902 man
->last
->parent
->last
= n
;
905 n
->parent
= man
->last
->parent
;
907 case ROFF_NEXT_CHILD
:
908 if (man
->last
->child
!= NULL
) {
909 n
->next
= man
->last
->child
;
910 man
->last
->child
->prev
= n
;
913 man
->last
->child
= n
;
914 n
->parent
= man
->last
;
926 if (n
->end
!= ENDBODY_NOT
)
938 * Copy over the normalised-data pointer of our parent. Not
939 * everybody has one, but copying a null pointer is fine.
942 n
->norm
= n
->parent
->norm
;
943 assert(n
->parent
->type
== ROFFT_BLOCK
);
947 roff_word_alloc(struct roff_man
*man
, int line
, int pos
, const char *word
)
951 n
= roff_node_alloc(man
, line
, pos
, ROFFT_TEXT
, TOKEN_NONE
);
952 n
->string
= roff_strdup(man
->roff
, word
);
953 roff_node_append(man
, n
);
954 n
->flags
|= NODE_VALID
| NODE_ENDED
;
955 man
->next
= ROFF_NEXT_SIBLING
;
959 roff_word_append(struct roff_man
*man
, const char *word
)
962 char *addstr
, *newstr
;
965 addstr
= roff_strdup(man
->roff
, word
);
966 mandoc_asprintf(&newstr
, "%s %s", n
->string
, addstr
);
970 man
->next
= ROFF_NEXT_SIBLING
;
974 roff_elem_alloc(struct roff_man
*man
, int line
, int pos
, int tok
)
978 n
= roff_node_alloc(man
, line
, pos
, ROFFT_ELEM
, tok
);
979 roff_node_append(man
, n
);
980 man
->next
= ROFF_NEXT_CHILD
;
984 roff_block_alloc(struct roff_man
*man
, int line
, int pos
, int tok
)
988 n
= roff_node_alloc(man
, line
, pos
, ROFFT_BLOCK
, tok
);
989 roff_node_append(man
, n
);
990 man
->next
= ROFF_NEXT_CHILD
;
995 roff_head_alloc(struct roff_man
*man
, int line
, int pos
, int tok
)
999 n
= roff_node_alloc(man
, line
, pos
, ROFFT_HEAD
, tok
);
1000 roff_node_append(man
, n
);
1001 man
->next
= ROFF_NEXT_CHILD
;
1006 roff_body_alloc(struct roff_man
*man
, int line
, int pos
, int tok
)
1008 struct roff_node
*n
;
1010 n
= roff_node_alloc(man
, line
, pos
, ROFFT_BODY
, tok
);
1011 roff_node_append(man
, n
);
1012 man
->next
= ROFF_NEXT_CHILD
;
1017 roff_addtbl(struct roff_man
*man
, struct tbl_node
*tbl
)
1019 struct roff_node
*n
;
1020 const struct tbl_span
*span
;
1022 if (man
->macroset
== MACROSET_MAN
)
1023 man_breakscope(man
, ROFF_TS
);
1024 while ((span
= tbl_span(tbl
)) != NULL
) {
1025 n
= roff_node_alloc(man
, tbl
->line
, 0, ROFFT_TBL
, TOKEN_NONE
);
1027 roff_node_append(man
, n
);
1028 n
->flags
|= NODE_VALID
| NODE_ENDED
;
1029 man
->next
= ROFF_NEXT_SIBLING
;
1034 roff_node_unlink(struct roff_man
*man
, struct roff_node
*n
)
1037 /* Adjust siblings. */
1040 n
->prev
->next
= n
->next
;
1042 n
->next
->prev
= n
->prev
;
1044 /* Adjust parent. */
1046 if (n
->parent
!= NULL
) {
1047 if (n
->parent
->child
== n
)
1048 n
->parent
->child
= n
->next
;
1049 if (n
->parent
->last
== n
)
1050 n
->parent
->last
= n
->prev
;
1053 /* Adjust parse point. */
1057 if (man
->last
== n
) {
1058 if (n
->prev
== NULL
) {
1059 man
->last
= n
->parent
;
1060 man
->next
= ROFF_NEXT_CHILD
;
1062 man
->last
= n
->prev
;
1063 man
->next
= ROFF_NEXT_SIBLING
;
1066 if (man
->first
== n
)
1071 roff_node_relink(struct roff_man
*man
, struct roff_node
*n
)
1073 roff_node_unlink(man
, n
);
1074 n
->prev
= n
->next
= NULL
;
1075 roff_node_append(man
, n
);
1079 roff_node_free(struct roff_node
*n
)
1082 if (n
->args
!= NULL
)
1083 mdoc_argv_free(n
->args
);
1084 if (n
->type
== ROFFT_BLOCK
|| n
->type
== ROFFT_ELEM
)
1087 eqn_box_free(n
->eqn
);
1093 roff_node_delete(struct roff_man
*man
, struct roff_node
*n
)
1096 while (n
->child
!= NULL
)
1097 roff_node_delete(man
, n
->child
);
1098 roff_node_unlink(man
, n
);
1103 deroff(char **dest
, const struct roff_node
*n
)
1108 if (n
->type
!= ROFFT_TEXT
) {
1109 for (n
= n
->child
; n
!= NULL
; n
= n
->next
)
1114 /* Skip leading whitespace. */
1116 for (cp
= n
->string
; *cp
!= '\0'; cp
++) {
1117 if (cp
[0] == '\\' && cp
[1] != '\0' &&
1118 strchr(" %&0^|~", cp
[1]) != NULL
)
1120 else if ( ! isspace((unsigned char)*cp
))
1124 /* Skip trailing backslash. */
1127 if (sz
> 0 && cp
[sz
- 1] == '\\')
1130 /* Skip trailing whitespace. */
1133 if ( ! isspace((unsigned char)cp
[sz
-1]))
1136 /* Skip empty strings. */
1141 if (*dest
== NULL
) {
1142 *dest
= mandoc_strndup(cp
, sz
);
1146 mandoc_asprintf(&cp
, "%s %*s", *dest
, (int)sz
, cp
);
1151 /* --- main functions of the roff parser ---------------------------------- */
1154 * In the current line, expand escape sequences that tend to get
1155 * used in numerical expressions and conditional requests.
1156 * Also check the syntax of the remaining escape sequences.
1159 roff_res(struct roff
*r
, struct buf
*buf
, int ln
, int pos
)
1161 struct mctx
*ctx
; /* current macro call context */
1162 char ubuf
[24]; /* buffer to print the number */
1163 struct roff_node
*n
; /* used for header comments */
1164 const char *start
; /* start of the string to process */
1165 char *stesc
; /* start of an escape sequence ('\\') */
1166 char *ep
; /* end of comment string */
1167 const char *stnam
; /* start of the name, after "[(*" */
1168 const char *cp
; /* end of the name, e.g. before ']' */
1169 const char *res
; /* the string to be substituted */
1170 char *nbuf
; /* new buffer to copy buf->buf to */
1171 size_t maxl
; /* expected length of the escape name */
1172 size_t naml
; /* actual length of the escape name */
1173 size_t asz
; /* length of the replacement */
1174 size_t rsz
; /* length of the rest of the string */
1175 enum mandoc_esc esc
; /* type of the escape sequence */
1176 int inaml
; /* length returned from mandoc_escape() */
1177 int expand_count
; /* to avoid infinite loops */
1178 int npos
; /* position in numeric expression */
1179 int arg_complete
; /* argument not interrupted by eol */
1180 int quote_args
; /* true for \\$@, false for \\$* */
1181 int done
; /* no more input available */
1182 int deftype
; /* type of definition to paste */
1183 int rcsid
; /* kind of RCS id seen */
1184 char sign
; /* increment number register */
1185 char term
; /* character terminating the escape */
1187 /* Search forward for comments. */
1190 start
= buf
->buf
+ pos
;
1191 for (stesc
= buf
->buf
+ pos
; *stesc
!= '\0'; stesc
++) {
1192 if (stesc
[0] != r
->escape
|| stesc
[1] == '\0')
1195 if (*stesc
!= '"' && *stesc
!= '#')
1198 /* Comment found, look for RCS id. */
1201 if ((cp
= strstr(stesc
, "$" "OpenBSD")) != NULL
) {
1202 rcsid
= 1 << MANDOC_OS_OPENBSD
;
1204 } else if ((cp
= strstr(stesc
, "$" "NetBSD")) != NULL
) {
1205 rcsid
= 1 << MANDOC_OS_NETBSD
;
1209 isalnum((unsigned char)*cp
) == 0 &&
1210 strchr(cp
, '$') != NULL
) {
1211 if (r
->man
->meta
.rcsids
& rcsid
)
1212 mandoc_msg(MANDOCERR_RCS_REP
, r
->parse
,
1213 ln
, stesc
+ 1 - buf
->buf
, stesc
+ 1);
1214 r
->man
->meta
.rcsids
|= rcsid
;
1217 /* Handle trailing whitespace. */
1219 ep
= strchr(stesc
--, '\0') - 1;
1224 if (*ep
== ' ' || *ep
== '\t')
1225 mandoc_msg(MANDOCERR_SPACE_EOL
, r
->parse
,
1226 ln
, ep
- buf
->buf
, NULL
);
1229 * Save comments preceding the title macro
1230 * in the syntax tree.
1233 if (r
->format
== 0) {
1234 while (*ep
== ' ' || *ep
== '\t')
1237 n
= roff_node_alloc(r
->man
,
1238 ln
, stesc
+ 1 - buf
->buf
,
1239 ROFFT_COMMENT
, TOKEN_NONE
);
1240 n
->string
= mandoc_strdup(stesc
+ 2);
1241 roff_node_append(r
->man
, n
);
1242 n
->flags
|= NODE_VALID
| NODE_ENDED
;
1243 r
->man
->next
= ROFF_NEXT_SIBLING
;
1246 /* Line continuation with comment. */
1248 if (stesc
[1] == '#') {
1250 return ROFF_IGN
| ROFF_APPEND
;
1253 /* Discard normal comments. */
1255 while (stesc
> start
&& stesc
[-1] == ' ' &&
1256 (stesc
== start
+ 1 || stesc
[-2] != '\\'))
1265 /* Notice the end of the input. */
1267 if (*stesc
== '\n') {
1273 while (stesc
>= start
) {
1275 /* Search backwards for the next backslash. */
1277 if (*stesc
!= r
->escape
) {
1278 if (*stesc
== '\\') {
1280 buf
->sz
= mandoc_asprintf(&nbuf
, "%s\\e%s",
1281 buf
->buf
, stesc
+ 1) + 1;
1283 stesc
= nbuf
+ (stesc
- buf
->buf
);
1291 /* If it is escaped, skip it. */
1293 for (cp
= stesc
- 1; cp
>= start
; cp
--)
1294 if (*cp
!= r
->escape
)
1297 if ((stesc
- cp
) % 2 == 0) {
1301 } else if (stesc
[1] != '\0') {
1308 return ROFF_IGN
| ROFF_APPEND
;
1311 /* Decide whether to expand or to check only. */
1326 if (sign
== '+' || sign
== '-')
1331 esc
= mandoc_escape(&cp
, &stnam
, &inaml
);
1332 if (esc
== ESCAPE_ERROR
||
1333 (esc
== ESCAPE_SPECIAL
&&
1334 mchars_spec2cp(stnam
, inaml
) < 0))
1335 mandoc_vmsg(MANDOCERR_ESC_BAD
,
1336 r
->parse
, ln
, (int)(stesc
- buf
->buf
),
1337 "%.*s", (int)(cp
- stesc
), stesc
);
1342 if (EXPAND_LIMIT
< ++expand_count
) {
1343 mandoc_msg(MANDOCERR_ROFFLOOP
, r
->parse
,
1344 ln
, (int)(stesc
- buf
->buf
), NULL
);
1349 * The third character decides the length
1350 * of the name of the string or register.
1351 * Save a pointer to the name.
1378 /* Advance to the end of the name. */
1382 while (maxl
== 0 || naml
< maxl
) {
1384 mandoc_msg(MANDOCERR_ESC_BAD
, r
->parse
,
1385 ln
, (int)(stesc
- buf
->buf
), stesc
);
1389 if (maxl
== 0 && *cp
== term
) {
1393 if (*cp
++ != '\\' || stesc
[1] != 'w') {
1397 switch (mandoc_escape(&cp
, NULL
, NULL
)) {
1398 case ESCAPE_SPECIAL
:
1399 case ESCAPE_UNICODE
:
1400 case ESCAPE_NUMBERED
:
1401 case ESCAPE_OVERSTRIKE
:
1410 * Retrieve the replacement string; if it is
1411 * undefined, resume searching for escapes.
1417 deftype
= ROFFDEF_USER
| ROFFDEF_PRE
;
1418 res
= roff_getstrn(r
, stnam
, naml
, &deftype
);
1421 * If not overriden, let \*(.T
1422 * through to the formatters.
1425 if (res
== NULL
&& naml
== 2 &&
1426 stnam
[0] == '.' && stnam
[1] == 'T') {
1427 roff_setstrn(&r
->strtab
,
1428 ".T", 2, NULL
, 0, 0);
1435 if (r
->mstackpos
< 0) {
1436 mandoc_vmsg(MANDOCERR_ARG_UNDEF
,
1437 r
->parse
, ln
, (int)(stesc
- buf
->buf
),
1441 ctx
= r
->mstack
+ r
->mstackpos
;
1442 npos
= stesc
[2] - '1';
1443 if (npos
>= 0 && npos
<= 8) {
1444 res
= npos
< ctx
->argc
?
1445 ctx
->argv
[npos
] : "";
1448 if (stesc
[2] == '*')
1450 else if (stesc
[2] == '@')
1453 mandoc_vmsg(MANDOCERR_ARG_NONUM
,
1454 r
->parse
, ln
, (int)(stesc
- buf
->buf
),
1459 for (npos
= 0; npos
< ctx
->argc
; npos
++) {
1463 asz
+= 2; /* quotes */
1464 asz
+= strlen(ctx
->argv
[npos
]);
1467 rsz
= buf
->sz
- (stesc
- buf
->buf
) - 3;
1469 memmove(stesc
+ asz
, stesc
+ 3, rsz
);
1471 nbuf
= mandoc_realloc(buf
->buf
, buf
->sz
);
1473 stesc
= nbuf
+ (stesc
- buf
->buf
);
1476 memmove(stesc
+ asz
, stesc
+ 3, rsz
);
1478 for (npos
= 0; npos
< ctx
->argc
; npos
++) {
1483 cp
= ctx
->argv
[npos
];
1492 ubuf
[0] = arg_complete
&&
1493 roff_evalnum(r
, ln
, stnam
, &npos
,
1494 NULL
, ROFFNUM_SCALE
) &&
1495 stnam
+ npos
+ 1 == cp
? '1' : '0';
1500 (void)snprintf(ubuf
, sizeof(ubuf
), "%d",
1501 roff_getregn(r
, stnam
, naml
, sign
));
1506 /* use even incomplete args */
1507 (void)snprintf(ubuf
, sizeof(ubuf
), "%d",
1513 if (stesc
[1] == '*')
1514 mandoc_vmsg(MANDOCERR_STR_UNDEF
,
1515 r
->parse
, ln
, (int)(stesc
- buf
->buf
),
1516 "%.*s", (int)naml
, stnam
);
1518 } else if (buf
->sz
+ strlen(res
) > SHRT_MAX
) {
1519 mandoc_msg(MANDOCERR_ROFFLOOP
, r
->parse
,
1520 ln
, (int)(stesc
- buf
->buf
), NULL
);
1524 /* Replace the escape sequence by the string. */
1527 buf
->sz
= mandoc_asprintf(&nbuf
, "%s%s%s",
1528 buf
->buf
, res
, cp
) + 1;
1530 /* Prepare for the next replacement. */
1533 stesc
= nbuf
+ (stesc
- buf
->buf
) + strlen(res
);
1541 * Process text streams.
1544 roff_parsetext(struct roff
*r
, struct buf
*buf
, int pos
, int *offs
)
1550 enum mandoc_esc esc
;
1552 /* Spring the input line trap. */
1554 if (roffit_lines
== 1) {
1555 isz
= mandoc_asprintf(&p
, "%s\n.%s", buf
->buf
, roffit_macro
);
1562 return ROFF_REPARSE
;
1563 } else if (roffit_lines
> 1)
1566 if (roffce_node
!= NULL
&& buf
->buf
[pos
] != '\0') {
1567 if (roffce_lines
< 1) {
1568 r
->man
->last
= roffce_node
;
1569 r
->man
->next
= ROFF_NEXT_SIBLING
;
1576 /* Convert all breakable hyphens into ASCII_HYPH. */
1578 start
= p
= buf
->buf
+ pos
;
1580 while (*p
!= '\0') {
1581 sz
= strcspn(p
, "-\\");
1588 /* Skip over escapes. */
1590 esc
= mandoc_escape((const char **)&p
, NULL
, NULL
);
1591 if (esc
== ESCAPE_ERROR
)
1596 } else if (p
== start
) {
1601 if (isalpha((unsigned char)p
[-1]) &&
1602 isalpha((unsigned char)p
[1]))
1610 roff_parseln(struct roff
*r
, int ln
, struct buf
*buf
, int *offs
)
1614 int pos
; /* parse point */
1615 int spos
; /* saved parse point for messages */
1616 int ppos
; /* original offset in buf->buf */
1617 int ctl
; /* macro line (boolean) */
1621 /* Handle in-line equation delimiters. */
1623 if (r
->tbl
== NULL
&&
1624 r
->last_eqn
!= NULL
&& r
->last_eqn
->delim
&&
1625 (r
->eqn
== NULL
|| r
->eqn_inline
)) {
1626 e
= roff_eqndelim(r
, buf
, pos
);
1627 if (e
== ROFF_REPARSE
)
1629 assert(e
== ROFF_CONT
);
1632 /* Expand some escape sequences. */
1634 e
= roff_res(r
, buf
, ln
, pos
);
1635 if ((e
& ROFF_MASK
) == ROFF_IGN
)
1637 assert(e
== ROFF_CONT
);
1639 ctl
= roff_getcontrol(r
, buf
->buf
, &pos
);
1642 * First, if a scope is open and we're not a macro, pass the
1643 * text through the macro's filter.
1644 * Equations process all content themselves.
1645 * Tables process almost all content themselves, but we want
1646 * to warn about macros before passing it there.
1649 if (r
->last
!= NULL
&& ! ctl
) {
1651 e
= (*roffs
[t
].text
)(r
, t
, buf
, ln
, pos
, pos
, offs
);
1652 if ((e
& ROFF_MASK
) == ROFF_IGN
)
1657 if (r
->eqn
!= NULL
&& strncmp(buf
->buf
+ ppos
, ".EN", 3)) {
1658 eqn_read(r
->eqn
, buf
->buf
+ ppos
);
1661 if (r
->tbl
!= NULL
&& (ctl
== 0 || buf
->buf
[pos
] == '\0')) {
1662 tbl_read(r
->tbl
, ln
, buf
->buf
, ppos
);
1663 roff_addtbl(r
->man
, r
->tbl
);
1667 return roff_parsetext(r
, buf
, pos
, offs
) | e
;
1669 /* Skip empty request lines. */
1671 if (buf
->buf
[pos
] == '"') {
1672 mandoc_msg(MANDOCERR_COMMENT_BAD
, r
->parse
,
1675 } else if (buf
->buf
[pos
] == '\0')
1679 * If a scope is open, go to the child handler for that macro,
1680 * as it may want to preprocess before doing anything with it.
1681 * Don't do so if an equation is open.
1686 return (*roffs
[t
].sub
)(r
, t
, buf
, ln
, ppos
, pos
, offs
);
1689 /* No scope is open. This is a new request or macro. */
1692 t
= roff_parse(r
, buf
->buf
, &pos
, ln
, ppos
);
1694 /* Tables ignore most macros. */
1696 if (r
->tbl
!= NULL
&& (t
== TOKEN_NONE
|| t
== ROFF_TS
||
1697 t
== ROFF_br
|| t
== ROFF_ce
|| t
== ROFF_rj
|| t
== ROFF_sp
)) {
1698 mandoc_msg(MANDOCERR_TBLMACRO
, r
->parse
,
1699 ln
, pos
, buf
->buf
+ spos
);
1700 if (t
!= TOKEN_NONE
)
1702 while (buf
->buf
[pos
] != '\0' && buf
->buf
[pos
] != ' ')
1704 while (buf
->buf
[pos
] == ' ')
1706 tbl_read(r
->tbl
, ln
, buf
->buf
, pos
);
1707 roff_addtbl(r
->man
, r
->tbl
);
1711 /* For now, let high level macros abort .ce mode. */
1713 if (ctl
&& roffce_node
!= NULL
&&
1714 (t
== TOKEN_NONE
|| t
== ROFF_Dd
|| t
== ROFF_EQ
||
1715 t
== ROFF_TH
|| t
== ROFF_TS
)) {
1716 r
->man
->last
= roffce_node
;
1717 r
->man
->next
= ROFF_NEXT_SIBLING
;
1723 * This is neither a roff request nor a user-defined macro.
1724 * Let the standard macro set parsers handle it.
1727 if (t
== TOKEN_NONE
)
1730 /* Execute a roff request or a user defined macro. */
1732 return (*roffs
[t
].proc
)(r
, t
, buf
, ln
, spos
, pos
, offs
);
1736 * Internal interface function to tell the roff parser that execution
1737 * of the current macro ended. This is required because macro
1738 * definitions usually do not end with a .return request.
1741 roff_userret(struct roff
*r
)
1746 assert(r
->mstackpos
>= 0);
1747 ctx
= r
->mstack
+ r
->mstackpos
;
1748 for (i
= 0; i
< ctx
->argc
; i
++)
1755 roff_endparse(struct roff
*r
)
1757 if (r
->last
!= NULL
)
1758 mandoc_msg(MANDOCERR_BLK_NOEND
, r
->parse
,
1759 r
->last
->line
, r
->last
->col
,
1760 roff_name
[r
->last
->tok
]);
1762 if (r
->eqn
!= NULL
) {
1763 mandoc_msg(MANDOCERR_BLK_NOEND
, r
->parse
,
1764 r
->eqn
->node
->line
, r
->eqn
->node
->pos
, "EQ");
1769 if (r
->tbl
!= NULL
) {
1770 mandoc_msg(MANDOCERR_BLK_NOEND
, r
->parse
,
1771 r
->tbl
->line
, r
->tbl
->pos
, "TS");
1778 * Parse a roff node's type from the input buffer. This must be in the
1779 * form of ".foo xxx" in the usual way.
1781 static enum roff_tok
1782 roff_parse(struct roff
*r
, char *buf
, int *pos
, int ln
, int ppos
)
1792 if ('\0' == *cp
|| '"' == *cp
|| '\t' == *cp
|| ' ' == *cp
)
1796 maclen
= roff_getname(r
, &cp
, ln
, ppos
);
1798 deftype
= ROFFDEF_USER
| ROFFDEF_REN
;
1799 r
->current_string
= roff_getstrn(r
, mac
, maclen
, &deftype
);
1808 t
= roffhash_find(r
->reqtab
, mac
, maclen
);
1811 if (t
!= TOKEN_NONE
)
1813 else if (deftype
== ROFFDEF_UNDEF
) {
1814 /* Using an undefined macro defines it to be empty. */
1815 roff_setstrn(&r
->strtab
, mac
, maclen
, "", 0, 0);
1816 roff_setstrn(&r
->rentab
, mac
, maclen
, NULL
, 0, 0);
1821 /* --- handling of request blocks ----------------------------------------- */
1824 roff_cblock(ROFF_ARGS
)
1828 * A block-close `..' should only be invoked as a child of an
1829 * ignore macro, otherwise raise a warning and just ignore it.
1832 if (r
->last
== NULL
) {
1833 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, r
->parse
,
1838 switch (r
->last
->tok
) {
1840 /* ROFF_am1 is remapped to ROFF_am in roff_block(). */
1843 /* ROFF_de1 is remapped to ROFF_de in roff_block(). */
1848 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, r
->parse
,
1853 if (buf
->buf
[pos
] != '\0')
1854 mandoc_vmsg(MANDOCERR_ARG_SKIP
, r
->parse
, ln
, pos
,
1855 ".. %s", buf
->buf
+ pos
);
1858 roffnode_cleanscope(r
);
1864 roffnode_cleanscope(struct roff
*r
)
1869 while (r
->last
!= NULL
) {
1870 if (--r
->last
->endspan
!= 0)
1872 inloop
+= roffnode_pop(r
);
1878 roff_ccond(struct roff
*r
, int ln
, int ppos
)
1880 if (NULL
== r
->last
) {
1881 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, r
->parse
,
1886 switch (r
->last
->tok
) {
1893 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, r
->parse
,
1898 if (r
->last
->endspan
> -1) {
1899 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, r
->parse
,
1904 return roffnode_pop(r
) + roffnode_cleanscope(r
);
1908 roff_block(ROFF_ARGS
)
1910 const char *name
, *value
;
1911 char *call
, *cp
, *iname
, *rname
;
1912 size_t csz
, namesz
, rsz
;
1915 /* Ignore groff compatibility mode for now. */
1917 if (tok
== ROFF_de1
)
1919 else if (tok
== ROFF_dei1
)
1921 else if (tok
== ROFF_am1
)
1923 else if (tok
== ROFF_ami1
)
1926 /* Parse the macro name argument. */
1928 cp
= buf
->buf
+ pos
;
1929 if (tok
== ROFF_ig
) {
1934 namesz
= roff_getname(r
, &cp
, ln
, ppos
);
1935 iname
[namesz
] = '\0';
1938 /* Resolve the macro name argument if it is indirect. */
1940 if (namesz
&& (tok
== ROFF_dei
|| tok
== ROFF_ami
)) {
1941 deftype
= ROFFDEF_USER
;
1942 name
= roff_getstrn(r
, iname
, namesz
, &deftype
);
1944 mandoc_vmsg(MANDOCERR_STR_UNDEF
,
1945 r
->parse
, ln
, (int)(iname
- buf
->buf
),
1946 "%.*s", (int)namesz
, iname
);
1949 namesz
= strlen(name
);
1953 if (namesz
== 0 && tok
!= ROFF_ig
) {
1954 mandoc_msg(MANDOCERR_REQ_EMPTY
, r
->parse
,
1955 ln
, ppos
, roff_name
[tok
]);
1959 roffnode_push(r
, tok
, name
, ln
, ppos
);
1962 * At the beginning of a `de' macro, clear the existing string
1963 * with the same name, if there is one. New content will be
1964 * appended from roff_block_text() in multiline mode.
1967 if (tok
== ROFF_de
|| tok
== ROFF_dei
) {
1968 roff_setstrn(&r
->strtab
, name
, namesz
, "", 0, 0);
1969 roff_setstrn(&r
->rentab
, name
, namesz
, NULL
, 0, 0);
1970 } else if (tok
== ROFF_am
|| tok
== ROFF_ami
) {
1971 deftype
= ROFFDEF_ANY
;
1972 value
= roff_getstrn(r
, iname
, namesz
, &deftype
);
1973 switch (deftype
) { /* Before appending, ... */
1974 case ROFFDEF_PRE
: /* copy predefined to user-defined. */
1975 roff_setstrn(&r
->strtab
, name
, namesz
,
1976 value
, strlen(value
), 0);
1978 case ROFFDEF_REN
: /* call original standard macro. */
1979 csz
= mandoc_asprintf(&call
, ".%.*s \\$* \\\"\n",
1980 (int)strlen(value
), value
);
1981 roff_setstrn(&r
->strtab
, name
, namesz
, call
, csz
, 0);
1982 roff_setstrn(&r
->rentab
, name
, namesz
, NULL
, 0, 0);
1985 case ROFFDEF_STD
: /* rename and call standard macro. */
1986 rsz
= mandoc_asprintf(&rname
, "__%s_renamed", name
);
1987 roff_setstrn(&r
->rentab
, rname
, rsz
, name
, namesz
, 0);
1988 csz
= mandoc_asprintf(&call
, ".%.*s \\$* \\\"\n",
1990 roff_setstrn(&r
->strtab
, name
, namesz
, call
, csz
, 0);
2002 /* Get the custom end marker. */
2005 namesz
= roff_getname(r
, &cp
, ln
, ppos
);
2007 /* Resolve the end marker if it is indirect. */
2009 if (namesz
&& (tok
== ROFF_dei
|| tok
== ROFF_ami
)) {
2010 deftype
= ROFFDEF_USER
;
2011 name
= roff_getstrn(r
, iname
, namesz
, &deftype
);
2013 mandoc_vmsg(MANDOCERR_STR_UNDEF
,
2014 r
->parse
, ln
, (int)(iname
- buf
->buf
),
2015 "%.*s", (int)namesz
, iname
);
2018 namesz
= strlen(name
);
2023 r
->last
->end
= mandoc_strndup(name
, namesz
);
2026 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, r
->parse
,
2027 ln
, pos
, ".%s ... %s", roff_name
[tok
], cp
);
2033 roff_block_sub(ROFF_ARGS
)
2039 * First check whether a custom macro exists at this level. If
2040 * it does, then check against it. This is some of groff's
2041 * stranger behaviours. If we encountered a custom end-scope
2042 * tag and that tag also happens to be a "real" macro, then we
2043 * need to try interpreting it again as a real macro. If it's
2044 * not, then return ignore. Else continue.
2048 for (i
= pos
, j
= 0; r
->last
->end
[j
]; j
++, i
++)
2049 if (buf
->buf
[i
] != r
->last
->end
[j
])
2052 if (r
->last
->end
[j
] == '\0' &&
2053 (buf
->buf
[i
] == '\0' ||
2054 buf
->buf
[i
] == ' ' ||
2055 buf
->buf
[i
] == '\t')) {
2057 roffnode_cleanscope(r
);
2059 while (buf
->buf
[i
] == ' ' || buf
->buf
[i
] == '\t')
2063 if (roff_parse(r
, buf
->buf
, &pos
, ln
, ppos
) !=
2071 * If we have no custom end-query or lookup failed, then try
2072 * pulling it out of the hashtable.
2075 t
= roff_parse(r
, buf
->buf
, &pos
, ln
, ppos
);
2077 if (t
!= ROFF_cblock
) {
2079 roff_setstr(r
, r
->last
->name
, buf
->buf
+ ppos
, 2);
2083 return (*roffs
[t
].proc
)(r
, t
, buf
, ln
, ppos
, pos
, offs
);
2087 roff_block_text(ROFF_ARGS
)
2091 roff_setstr(r
, r
->last
->name
, buf
->buf
+ pos
, 2);
2097 roff_cond_sub(ROFF_ARGS
)
2100 int endloop
, irc
, rr
;
2105 endloop
= tok
!= ROFF_while
? ROFF_IGN
:
2106 rr
? ROFF_LOOPCONT
: ROFF_LOOPEXIT
;
2107 if (roffnode_cleanscope(r
))
2111 * If `\}' occurs on a macro line without a preceding macro,
2112 * drop the line completely.
2115 ep
= buf
->buf
+ pos
;
2116 if (ep
[0] == '\\' && ep
[1] == '}')
2120 * The closing delimiter `\}' rewinds the conditional scope
2121 * but is otherwise ignored when interpreting the line.
2124 while ((ep
= strchr(ep
, '\\')) != NULL
) {
2127 memmove(ep
, ep
+ 2, strlen(ep
+ 2) + 1);
2128 if (roff_ccond(r
, ln
, ep
- buf
->buf
))
2141 * Fully handle known macros when they are structurally
2142 * required or when the conditional evaluated to true.
2145 t
= roff_parse(r
, buf
->buf
, &pos
, ln
, ppos
);
2146 irc
|= t
!= TOKEN_NONE
&& (rr
|| roffs
[t
].flags
& ROFFMAC_STRUCT
) ?
2147 (*roffs
[t
].proc
)(r
, t
, buf
, ln
, ppos
, pos
, offs
) :
2148 rr
? ROFF_CONT
: ROFF_IGN
;
2153 roff_cond_text(ROFF_ARGS
)
2156 int endloop
, irc
, rr
;
2160 endloop
= tok
!= ROFF_while
? ROFF_IGN
:
2161 rr
? ROFF_LOOPCONT
: ROFF_LOOPEXIT
;
2162 if (roffnode_cleanscope(r
))
2166 * If `\}' occurs on a text line with neither preceding
2167 * nor following characters, drop the line completely.
2170 ep
= buf
->buf
+ pos
;
2171 if (strcmp(ep
, "\\}") == 0)
2175 * The closing delimiter `\}' rewinds the conditional scope
2176 * but is otherwise ignored when interpreting the line.
2179 while ((ep
= strchr(ep
, '\\')) != NULL
) {
2182 memmove(ep
, ep
+ 2, strlen(ep
+ 2) + 1);
2183 if (roff_ccond(r
, ln
, ep
- buf
->buf
))
2199 /* --- handling of numeric and conditional expressions -------------------- */
2202 * Parse a single signed integer number. Stop at the first non-digit.
2203 * If there is at least one digit, return success and advance the
2204 * parse point, else return failure and let the parse point unchanged.
2205 * Ignore overflows, treat them just like the C language.
2208 roff_getnum(const char *v
, int *pos
, int *res
, int flags
)
2210 int myres
, scaled
, n
, p
;
2217 if (n
|| v
[p
] == '+')
2220 if (flags
& ROFFNUM_WHITE
)
2221 while (isspace((unsigned char)v
[p
]))
2224 for (*res
= 0; isdigit((unsigned char)v
[p
]); p
++)
2225 *res
= 10 * *res
+ v
[p
] - '0';
2232 /* Each number may be followed by one optional scaling unit. */
2236 scaled
= *res
* 65536;
2239 scaled
= *res
* 240;
2242 scaled
= *res
* 240 / 2.54;
2253 scaled
= *res
* 10 / 3;
2259 scaled
= *res
* 6 / 25;
2266 if (flags
& ROFFNUM_SCALE
)
2274 * Evaluate a string comparison condition.
2275 * The first character is the delimiter.
2276 * Succeed if the string up to its second occurrence
2277 * matches the string up to its third occurence.
2278 * Advance the cursor after the third occurrence
2279 * or lacking that, to the end of the line.
2282 roff_evalstrcond(const char *v
, int *pos
)
2284 const char *s1
, *s2
, *s3
;
2288 s1
= v
+ *pos
; /* initial delimiter */
2289 s2
= s1
+ 1; /* for scanning the first string */
2290 s3
= strchr(s2
, *s1
); /* for scanning the second string */
2292 if (NULL
== s3
) /* found no middle delimiter */
2295 while ('\0' != *++s3
) {
2296 if (*s2
!= *s3
) { /* mismatch */
2297 s3
= strchr(s3
, *s1
);
2300 if (*s3
== *s1
) { /* found the final delimiter */
2309 s3
= strchr(s2
, '\0');
2310 else if (*s3
!= '\0')
2317 * Evaluate an optionally negated single character, numerical,
2318 * or string condition.
2321 roff_evalcond(struct roff
*r
, int ln
, char *v
, int *pos
)
2323 const char *start
, *end
;
2326 int deftype
, len
, number
, savepos
, istrue
, wanttrue
;
2328 if ('!' == v
[*pos
]) {
2349 } while (v
[*pos
] == ' ');
2352 * Quirk for groff compatibility:
2353 * The horizontal tab is neither available nor unavailable.
2356 if (v
[*pos
] == '\t') {
2361 /* Printable ASCII characters are available. */
2363 if (v
[*pos
] != '\\') {
2369 switch (mandoc_escape(&end
, &start
, &len
)) {
2370 case ESCAPE_SPECIAL
:
2371 istrue
= mchars_spec2cp(start
, len
) != -1;
2373 case ESCAPE_UNICODE
:
2376 case ESCAPE_NUMBERED
:
2377 istrue
= mchars_num2char(start
, len
) != -1;
2384 return istrue
== wanttrue
;
2391 sz
= roff_getname(r
, &cp
, ln
, cp
- v
);
2394 else if (v
[*pos
] == 'r')
2395 istrue
= roff_hasregn(r
, name
, sz
);
2397 deftype
= ROFFDEF_ANY
;
2398 roff_getstrn(r
, name
, sz
, &deftype
);
2402 return istrue
== wanttrue
;
2408 if (roff_evalnum(r
, ln
, v
, pos
, &number
, ROFFNUM_SCALE
))
2409 return (number
> 0) == wanttrue
;
2410 else if (*pos
== savepos
)
2411 return roff_evalstrcond(v
, pos
) == wanttrue
;
2417 roff_line_ignore(ROFF_ARGS
)
2424 roff_insec(ROFF_ARGS
)
2427 mandoc_msg(MANDOCERR_REQ_INSEC
, r
->parse
,
2428 ln
, ppos
, roff_name
[tok
]);
2433 roff_unsupp(ROFF_ARGS
)
2436 mandoc_msg(MANDOCERR_REQ_UNSUPP
, r
->parse
,
2437 ln
, ppos
, roff_name
[tok
]);
2442 roff_cond(ROFF_ARGS
)
2446 roffnode_push(r
, tok
, NULL
, ln
, ppos
);
2449 * An `.el' has no conditional body: it will consume the value
2450 * of the current rstack entry set in prior `ie' calls or
2453 * If we're not an `el', however, then evaluate the conditional.
2456 r
->last
->rule
= tok
== ROFF_el
?
2457 (r
->rstackpos
< 0 ? 0 : r
->rstack
[r
->rstackpos
--]) :
2458 roff_evalcond(r
, ln
, buf
->buf
, &pos
);
2461 * An if-else will put the NEGATION of the current evaluated
2462 * conditional into the stack of rules.
2465 if (tok
== ROFF_ie
) {
2466 if (r
->rstackpos
+ 1 == r
->rstacksz
) {
2468 r
->rstack
= mandoc_reallocarray(r
->rstack
,
2469 r
->rstacksz
, sizeof(int));
2471 r
->rstack
[++r
->rstackpos
] = !r
->last
->rule
;
2474 /* If the parent has false as its rule, then so do we. */
2476 if (r
->last
->parent
&& !r
->last
->parent
->rule
)
2481 * If there is nothing on the line after the conditional,
2482 * not even whitespace, use next-line scope.
2483 * Except that .while does not support next-line scope.
2486 if (buf
->buf
[pos
] == '\0' && tok
!= ROFF_while
) {
2487 r
->last
->endspan
= 2;
2491 while (buf
->buf
[pos
] == ' ')
2494 /* An opening brace requests multiline scope. */
2496 if (buf
->buf
[pos
] == '\\' && buf
->buf
[pos
+ 1] == '{') {
2497 r
->last
->endspan
= -1;
2499 while (buf
->buf
[pos
] == ' ')
2505 * Anything else following the conditional causes
2506 * single-line scope. Warn if the scope contains
2507 * nothing but trailing whitespace.
2510 if (buf
->buf
[pos
] == '\0')
2511 mandoc_msg(MANDOCERR_COND_EMPTY
, r
->parse
,
2512 ln
, ppos
, roff_name
[tok
]);
2514 r
->last
->endspan
= 1;
2519 if (tok
== ROFF_while
)
2531 /* Ignore groff compatibility mode for now. */
2533 if (tok
== ROFF_ds1
)
2535 else if (tok
== ROFF_as1
)
2539 * The first word is the name of the string.
2540 * If it is empty or terminated by an escape sequence,
2541 * abort the `ds' request without defining anything.
2544 name
= string
= buf
->buf
+ pos
;
2548 namesz
= roff_getname(r
, &string
, ln
, pos
);
2549 if (name
[namesz
] == '\\')
2552 /* Read past the initial double-quote, if any. */
2556 /* The rest is the value. */
2557 roff_setstrn(&r
->strtab
, name
, namesz
, string
, strlen(string
),
2559 roff_setstrn(&r
->rentab
, name
, namesz
, NULL
, 0, 0);
2564 * Parse a single operator, one or two characters long.
2565 * If the operator is recognized, return success and advance the
2566 * parse point, else return failure and let the parse point unchanged.
2569 roff_getop(const char *v
, int *pos
, char *res
)
2584 switch (v
[*pos
+ 1]) {
2602 switch (v
[*pos
+ 1]) {
2616 if ('=' == v
[*pos
+ 1])
2628 * Evaluate either a parenthesized numeric expression
2629 * or a single signed integer number.
2632 roff_evalpar(struct roff
*r
, int ln
,
2633 const char *v
, int *pos
, int *res
, int flags
)
2637 return roff_getnum(v
, pos
, res
, flags
);
2640 if ( ! roff_evalnum(r
, ln
, v
, pos
, res
, flags
| ROFFNUM_WHITE
))
2644 * Omission of the closing parenthesis
2645 * is an error in validation mode,
2646 * but ignored in evaluation mode.
2651 else if (NULL
== res
)
2658 * Evaluate a complete numeric expression.
2659 * Proceed left to right, there is no concept of precedence.
2662 roff_evalnum(struct roff
*r
, int ln
, const char *v
,
2663 int *pos
, int *res
, int flags
)
2665 int mypos
, operand2
;
2673 if (flags
& ROFFNUM_WHITE
)
2674 while (isspace((unsigned char)v
[*pos
]))
2677 if ( ! roff_evalpar(r
, ln
, v
, pos
, res
, flags
))
2681 if (flags
& ROFFNUM_WHITE
)
2682 while (isspace((unsigned char)v
[*pos
]))
2685 if ( ! roff_getop(v
, pos
, &operator))
2688 if (flags
& ROFFNUM_WHITE
)
2689 while (isspace((unsigned char)v
[*pos
]))
2692 if ( ! roff_evalpar(r
, ln
, v
, pos
, &operand2
, flags
))
2695 if (flags
& ROFFNUM_WHITE
)
2696 while (isspace((unsigned char)v
[*pos
]))
2713 if (operand2
== 0) {
2714 mandoc_msg(MANDOCERR_DIVZERO
,
2715 r
->parse
, ln
, *pos
, v
);
2722 if (operand2
== 0) {
2723 mandoc_msg(MANDOCERR_DIVZERO
,
2724 r
->parse
, ln
, *pos
, v
);
2731 *res
= *res
< operand2
;
2734 *res
= *res
> operand2
;
2737 *res
= *res
<= operand2
;
2740 *res
= *res
>= operand2
;
2743 *res
= *res
== operand2
;
2746 *res
= *res
!= operand2
;
2749 *res
= *res
&& operand2
;
2752 *res
= *res
|| operand2
;
2755 if (operand2
< *res
)
2759 if (operand2
> *res
)
2769 /* --- register management ------------------------------------------------ */
2772 roff_setreg(struct roff
*r
, const char *name
, int val
, char sign
)
2774 roff_setregn(r
, name
, strlen(name
), val
, sign
, INT_MIN
);
2778 roff_setregn(struct roff
*r
, const char *name
, size_t len
,
2779 int val
, char sign
, int step
)
2781 struct roffreg
*reg
;
2783 /* Search for an existing register with the same name. */
2786 while (reg
!= NULL
&& (reg
->key
.sz
!= len
||
2787 strncmp(reg
->key
.p
, name
, len
) != 0))
2791 /* Create a new register. */
2792 reg
= mandoc_malloc(sizeof(struct roffreg
));
2793 reg
->key
.p
= mandoc_strndup(name
, len
);
2797 reg
->next
= r
->regtab
;
2803 else if ('-' == sign
)
2807 if (step
!= INT_MIN
)
2812 * Handle some predefined read-only number registers.
2813 * For now, return -1 if the requested register is not predefined;
2814 * in case a predefined read-only register having the value -1
2815 * were to turn up, another special value would have to be chosen.
2818 roff_getregro(const struct roff
*r
, const char *name
)
2822 case '$': /* Number of arguments of the last macro evaluated. */
2823 return r
->mstackpos
< 0 ? 0 : r
->mstack
[r
->mstackpos
].argc
;
2824 case 'A': /* ASCII approximation mode is always off. */
2826 case 'g': /* Groff compatibility mode is always on. */
2828 case 'H': /* Fixed horizontal resolution. */
2830 case 'j': /* Always adjust left margin only. */
2832 case 'T': /* Some output device is always defined. */
2834 case 'V': /* Fixed vertical resolution. */
2842 roff_getreg(struct roff
*r
, const char *name
)
2844 return roff_getregn(r
, name
, strlen(name
), '\0');
2848 roff_getregn(struct roff
*r
, const char *name
, size_t len
, char sign
)
2850 struct roffreg
*reg
;
2853 if ('.' == name
[0] && 2 == len
) {
2854 val
= roff_getregro(r
, name
+ 1);
2859 for (reg
= r
->regtab
; reg
; reg
= reg
->next
) {
2860 if (len
== reg
->key
.sz
&&
2861 0 == strncmp(name
, reg
->key
.p
, len
)) {
2864 reg
->val
+= reg
->step
;
2867 reg
->val
-= reg
->step
;
2876 roff_setregn(r
, name
, len
, 0, '\0', INT_MIN
);
2881 roff_hasregn(const struct roff
*r
, const char *name
, size_t len
)
2883 struct roffreg
*reg
;
2886 if ('.' == name
[0] && 2 == len
) {
2887 val
= roff_getregro(r
, name
+ 1);
2892 for (reg
= r
->regtab
; reg
; reg
= reg
->next
)
2893 if (len
== reg
->key
.sz
&&
2894 0 == strncmp(name
, reg
->key
.p
, len
))
2901 roff_freereg(struct roffreg
*reg
)
2903 struct roffreg
*old_reg
;
2905 while (NULL
!= reg
) {
2916 char *key
, *val
, *step
;
2921 key
= val
= buf
->buf
+ pos
;
2925 keysz
= roff_getname(r
, &val
, ln
, pos
);
2926 if (key
[keysz
] == '\\')
2930 if (sign
== '+' || sign
== '-')
2934 if (roff_evalnum(r
, ln
, val
, &len
, &iv
, ROFFNUM_SCALE
) == 0)
2938 while (isspace((unsigned char)*step
))
2940 if (roff_evalnum(r
, ln
, step
, NULL
, &is
, 0) == 0)
2943 roff_setregn(r
, key
, keysz
, iv
, sign
, is
);
2950 struct roffreg
*reg
, **prev
;
2954 name
= cp
= buf
->buf
+ pos
;
2957 namesz
= roff_getname(r
, &cp
, ln
, pos
);
2958 name
[namesz
] = '\0';
2963 if (reg
== NULL
|| !strcmp(name
, reg
->key
.p
))
2975 /* --- handler functions for roff requests -------------------------------- */
2984 cp
= buf
->buf
+ pos
;
2985 while (*cp
!= '\0') {
2987 namesz
= roff_getname(r
, &cp
, ln
, (int)(cp
- buf
->buf
));
2988 roff_setstrn(&r
->strtab
, name
, namesz
, NULL
, 0, 0);
2989 roff_setstrn(&r
->rentab
, name
, namesz
, NULL
, 0, 0);
2990 if (name
[namesz
] == '\\')
3001 /* Parse the number of lines. */
3003 if ( ! roff_evalnum(r
, ln
, buf
->buf
, &pos
, &iv
, 0)) {
3004 mandoc_msg(MANDOCERR_IT_NONUM
, r
->parse
,
3005 ln
, ppos
, buf
->buf
+ 1);
3009 while (isspace((unsigned char)buf
->buf
[pos
]))
3013 * Arm the input line trap.
3014 * Special-casing "an-trap" is an ugly workaround to cope
3015 * with DocBook stupidly fiddling with man(7) internals.
3019 roffit_macro
= mandoc_strdup(iv
!= 1 ||
3020 strcmp(buf
->buf
+ pos
, "an-trap") ?
3021 buf
->buf
+ pos
: "br");
3029 enum roff_tok t
, te
;
3036 r
->format
= MPARSE_MDOC
;
3037 mask
= MPARSE_MDOC
| MPARSE_QUICK
;
3043 r
->format
= MPARSE_MAN
;
3044 mask
= MPARSE_QUICK
;
3049 if ((r
->options
& mask
) == 0)
3050 for (t
= tok
; t
< te
; t
++)
3051 roff_setstr(r
, roff_name
[t
], NULL
, 0);
3058 if (r
->tbl
== NULL
) {
3059 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, r
->parse
,
3063 if (tbl_end(r
->tbl
) == 0) {
3066 buf
->buf
= mandoc_strdup(".sp");
3069 return ROFF_REPARSE
;
3080 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, r
->parse
,
3083 tbl_restart(ln
, ppos
, r
->tbl
);
3089 * Handle in-line equation delimiters.
3092 roff_eqndelim(struct roff
*r
, struct buf
*buf
, int pos
)
3095 const char *bef_pr
, *bef_nl
, *mac
, *aft_nl
, *aft_pr
;
3098 * Outside equations, look for an opening delimiter.
3099 * If we are inside an equation, we already know it is
3100 * in-line, or this function wouldn't have been called;
3101 * so look for a closing delimiter.
3104 cp1
= buf
->buf
+ pos
;
3105 cp2
= strchr(cp1
, r
->eqn
== NULL
?
3106 r
->last_eqn
->odelim
: r
->last_eqn
->cdelim
);
3111 bef_pr
= bef_nl
= aft_nl
= aft_pr
= "";
3113 /* Handle preceding text, protecting whitespace. */
3115 if (*buf
->buf
!= '\0') {
3122 * Prepare replacing the delimiter with an equation macro
3123 * and drop leading white space from the equation.
3126 if (r
->eqn
== NULL
) {
3133 /* Handle following text, protecting whitespace. */
3141 /* Do the actual replacement. */
3143 buf
->sz
= mandoc_asprintf(&cp1
, "%s%s%s%s%s%s%s", buf
->buf
,
3144 bef_pr
, bef_nl
, mac
, aft_nl
, aft_pr
, cp2
) + 1;
3148 /* Toggle the in-line state of the eqn subsystem. */
3150 r
->eqn_inline
= r
->eqn
== NULL
;
3151 return ROFF_REPARSE
;
3157 struct roff_node
*n
;
3159 if (r
->man
->macroset
== MACROSET_MAN
)
3160 man_breakscope(r
->man
, ROFF_EQ
);
3161 n
= roff_node_alloc(r
->man
, ln
, ppos
, ROFFT_EQN
, TOKEN_NONE
);
3162 if (ln
> r
->man
->last
->line
)
3163 n
->flags
|= NODE_LINE
;
3164 n
->eqn
= mandoc_calloc(1, sizeof(*n
->eqn
));
3165 n
->eqn
->expectargs
= UINT_MAX
;
3166 roff_node_append(r
->man
, n
);
3167 r
->man
->next
= ROFF_NEXT_SIBLING
;
3169 assert(r
->eqn
== NULL
);
3170 if (r
->last_eqn
== NULL
)
3171 r
->last_eqn
= eqn_alloc(r
->parse
);
3173 eqn_reset(r
->last_eqn
);
3174 r
->eqn
= r
->last_eqn
;
3177 if (buf
->buf
[pos
] != '\0')
3178 mandoc_vmsg(MANDOCERR_ARG_SKIP
, r
->parse
, ln
, pos
,
3179 ".EQ %s", buf
->buf
+ pos
);
3187 if (r
->eqn
!= NULL
) {
3191 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, r
->parse
, ln
, ppos
, "EN");
3192 if (buf
->buf
[pos
] != '\0')
3193 mandoc_vmsg(MANDOCERR_ARG_SKIP
, r
->parse
, ln
, pos
,
3194 "EN %s", buf
->buf
+ pos
);
3201 if (r
->tbl
!= NULL
) {
3202 mandoc_msg(MANDOCERR_BLK_BROKEN
, r
->parse
,
3203 ln
, ppos
, "TS breaks TS");
3206 r
->tbl
= tbl_alloc(ppos
, ln
, r
->parse
);
3208 r
->last_tbl
->next
= r
->tbl
;
3210 r
->first_tbl
= r
->tbl
;
3211 r
->last_tbl
= r
->tbl
;
3216 roff_onearg(ROFF_ARGS
)
3218 struct roff_node
*n
;
3222 if (r
->man
->flags
& (MAN_BLINE
| MAN_ELINE
) &&
3223 (tok
== ROFF_ce
|| tok
== ROFF_rj
|| tok
== ROFF_sp
||
3225 man_breakscope(r
->man
, tok
);
3227 if (roffce_node
!= NULL
&& (tok
== ROFF_ce
|| tok
== ROFF_rj
)) {
3228 r
->man
->last
= roffce_node
;
3229 r
->man
->next
= ROFF_NEXT_SIBLING
;
3232 roff_elem_alloc(r
->man
, ln
, ppos
, tok
);
3235 cp
= buf
->buf
+ pos
;
3237 while (*cp
!= '\0' && *cp
!= ' ')
3242 mandoc_vmsg(MANDOCERR_ARG_EXCESS
,
3243 r
->parse
, ln
, cp
- buf
->buf
,
3244 "%s ... %s", roff_name
[tok
], cp
);
3245 roff_word_alloc(r
->man
, ln
, pos
, buf
->buf
+ pos
);
3248 if (tok
== ROFF_ce
|| tok
== ROFF_rj
) {
3249 if (r
->man
->last
->type
== ROFFT_ELEM
) {
3250 roff_word_alloc(r
->man
, ln
, pos
, "1");
3251 r
->man
->last
->flags
|= NODE_NOSRC
;
3254 if (roff_evalnum(r
, ln
, r
->man
->last
->string
, &npos
,
3255 &roffce_lines
, 0) == 0) {
3256 mandoc_vmsg(MANDOCERR_CE_NONUM
,
3257 r
->parse
, ln
, pos
, "ce %s", buf
->buf
+ pos
);
3260 if (roffce_lines
< 1) {
3261 r
->man
->last
= r
->man
->last
->parent
;
3265 roffce_node
= r
->man
->last
->parent
;
3267 n
->flags
|= NODE_VALID
| NODE_ENDED
;
3270 n
->flags
|= NODE_LINE
;
3271 r
->man
->next
= ROFF_NEXT_SIBLING
;
3276 roff_manyarg(ROFF_ARGS
)
3278 struct roff_node
*n
;
3281 roff_elem_alloc(r
->man
, ln
, ppos
, tok
);
3284 for (sp
= ep
= buf
->buf
+ pos
; *sp
!= '\0'; sp
= ep
) {
3285 while (*ep
!= '\0' && *ep
!= ' ')
3289 roff_word_alloc(r
->man
, ln
, sp
- buf
->buf
, sp
);
3292 n
->flags
|= NODE_LINE
| NODE_VALID
| NODE_ENDED
;
3294 r
->man
->next
= ROFF_NEXT_SIBLING
;
3301 char *oldn
, *newn
, *end
, *value
;
3302 size_t oldsz
, newsz
, valsz
;
3304 newn
= oldn
= buf
->buf
+ pos
;
3308 newsz
= roff_getname(r
, &oldn
, ln
, pos
);
3309 if (newn
[newsz
] == '\\' || *oldn
== '\0')
3313 oldsz
= roff_getname(r
, &end
, ln
, oldn
- buf
->buf
);
3317 valsz
= mandoc_asprintf(&value
, ".%.*s \\$@\\\"\n",
3319 roff_setstrn(&r
->strtab
, newn
, newsz
, value
, valsz
, 0);
3320 roff_setstrn(&r
->rentab
, newn
, newsz
, NULL
, 0, 0);
3328 if (r
->man
->flags
& (MAN_BLINE
| MAN_ELINE
))
3329 man_breakscope(r
->man
, ROFF_br
);
3330 roff_elem_alloc(r
->man
, ln
, ppos
, ROFF_br
);
3331 if (buf
->buf
[pos
] != '\0')
3332 mandoc_vmsg(MANDOCERR_ARG_SKIP
, r
->parse
, ln
, pos
,
3333 "%s %s", roff_name
[tok
], buf
->buf
+ pos
);
3334 r
->man
->last
->flags
|= NODE_LINE
| NODE_VALID
| NODE_ENDED
;
3335 r
->man
->next
= ROFF_NEXT_SIBLING
;
3346 if (*p
== '\0' || (r
->control
= *p
++) == '.')
3350 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, r
->parse
,
3351 ln
, p
- buf
->buf
, "cc ... %s", p
);
3357 roff_char(ROFF_ARGS
)
3359 const char *p
, *kp
, *vp
;
3363 /* Parse the character to be replaced. */
3365 kp
= buf
->buf
+ pos
;
3367 if (*kp
== '\0' || (*kp
== '\\' &&
3368 mandoc_escape(&p
, NULL
, NULL
) != ESCAPE_SPECIAL
) ||
3369 (*p
!= ' ' && *p
!= '\0')) {
3370 mandoc_vmsg(MANDOCERR_CHAR_ARG
, r
->parse
,
3371 ln
, pos
, "char %s", kp
);
3379 * If the replacement string contains a font escape sequence,
3380 * we have to restore the font at the end.
3386 while (*p
!= '\0') {
3389 switch (mandoc_escape(&p
, NULL
, NULL
)) {
3391 case ESCAPE_FONTROMAN
:
3392 case ESCAPE_FONTITALIC
:
3393 case ESCAPE_FONTBOLD
:
3396 case ESCAPE_FONTPREV
:
3404 mandoc_msg(MANDOCERR_CHAR_FONT
, r
->parse
,
3405 ln
, vp
- buf
->buf
, vp
);
3408 * Approximate the effect of .char using the .tr tables.
3409 * XXX In groff, .char and .tr interact differently.
3413 if (r
->xtab
== NULL
)
3414 r
->xtab
= mandoc_calloc(128, sizeof(*r
->xtab
));
3415 assert((unsigned int)*kp
< 128);
3416 free(r
->xtab
[(int)*kp
].p
);
3417 r
->xtab
[(int)*kp
].sz
= mandoc_asprintf(&r
->xtab
[(int)*kp
].p
,
3418 "%s%s", vp
, font
? "\fP" : "");
3420 roff_setstrn(&r
->xmbtab
, kp
, ksz
, vp
, vsz
, 0);
3422 roff_setstrn(&r
->xmbtab
, kp
, ksz
, "\\fP", 3, 1);
3438 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, r
->parse
,
3439 ln
, p
- buf
->buf
, "ec ... %s", p
);
3448 if (buf
->buf
[pos
] != '\0')
3449 mandoc_vmsg(MANDOCERR_ARG_SKIP
, r
->parse
,
3450 ln
, pos
, "eo %s", buf
->buf
+ pos
);
3457 while (buf
->buf
[pos
] == ' ')
3466 const char *p
, *first
, *second
;
3468 enum mandoc_esc esc
;
3473 mandoc_msg(MANDOCERR_REQ_EMPTY
, r
->parse
, ln
, ppos
, "tr");
3477 while (*p
!= '\0') {
3481 if (*first
== '\\') {
3482 esc
= mandoc_escape(&p
, NULL
, NULL
);
3483 if (esc
== ESCAPE_ERROR
) {
3484 mandoc_msg(MANDOCERR_ESC_BAD
, r
->parse
,
3485 ln
, (int)(p
- buf
->buf
), first
);
3488 fsz
= (size_t)(p
- first
);
3492 if (*second
== '\\') {
3493 esc
= mandoc_escape(&p
, NULL
, NULL
);
3494 if (esc
== ESCAPE_ERROR
) {
3495 mandoc_msg(MANDOCERR_ESC_BAD
, r
->parse
,
3496 ln
, (int)(p
- buf
->buf
), second
);
3499 ssz
= (size_t)(p
- second
);
3500 } else if (*second
== '\0') {
3501 mandoc_vmsg(MANDOCERR_TR_ODD
, r
->parse
,
3502 ln
, first
- buf
->buf
, "tr %s", first
);
3508 roff_setstrn(&r
->xmbtab
, first
, fsz
,
3513 if (r
->xtab
== NULL
)
3514 r
->xtab
= mandoc_calloc(128,
3515 sizeof(struct roffstr
));
3517 free(r
->xtab
[(int)*first
].p
);
3518 r
->xtab
[(int)*first
].p
= mandoc_strndup(second
, ssz
);
3519 r
->xtab
[(int)*first
].sz
= ssz
;
3526 * Implementation of the .return request.
3527 * There is no need to call roff_userret() from here.
3528 * The read module will call that after rewinding the reader stack
3529 * to the place from where the current macro was called.
3532 roff_return(ROFF_ARGS
)
3534 if (r
->mstackpos
>= 0)
3535 return ROFF_IGN
| ROFF_USERRET
;
3537 mandoc_msg(MANDOCERR_REQ_NOMAC
, r
->parse
, ln
, ppos
, "return");
3545 char *oldn
, *newn
, *end
;
3546 size_t oldsz
, newsz
;
3549 oldn
= newn
= buf
->buf
+ pos
;
3553 oldsz
= roff_getname(r
, &newn
, ln
, pos
);
3554 if (oldn
[oldsz
] == '\\' || *newn
== '\0')
3558 newsz
= roff_getname(r
, &end
, ln
, newn
- buf
->buf
);
3562 deftype
= ROFFDEF_ANY
;
3563 value
= roff_getstrn(r
, oldn
, oldsz
, &deftype
);
3566 roff_setstrn(&r
->strtab
, newn
, newsz
, value
, strlen(value
), 0);
3567 roff_setstrn(&r
->strtab
, oldn
, oldsz
, NULL
, 0, 0);
3568 roff_setstrn(&r
->rentab
, newn
, newsz
, NULL
, 0, 0);
3571 roff_setstrn(&r
->strtab
, newn
, newsz
, value
, strlen(value
), 0);
3572 roff_setstrn(&r
->rentab
, newn
, newsz
, NULL
, 0, 0);
3575 roff_setstrn(&r
->rentab
, newn
, newsz
, value
, strlen(value
), 0);
3576 roff_setstrn(&r
->rentab
, oldn
, oldsz
, NULL
, 0, 0);
3577 roff_setstrn(&r
->strtab
, newn
, newsz
, NULL
, 0, 0);
3580 roff_setstrn(&r
->rentab
, newn
, newsz
, oldn
, oldsz
, 0);
3581 roff_setstrn(&r
->strtab
, newn
, newsz
, NULL
, 0, 0);
3584 roff_setstrn(&r
->strtab
, newn
, newsz
, NULL
, 0, 0);
3585 roff_setstrn(&r
->rentab
, newn
, newsz
, NULL
, 0, 0);
3592 roff_shift(ROFF_ARGS
)
3598 if (buf
->buf
[pos
] != '\0' &&
3599 roff_evalnum(r
, ln
, buf
->buf
, &pos
, &levels
, 0) == 0) {
3600 mandoc_vmsg(MANDOCERR_CE_NONUM
, r
->parse
,
3601 ln
, pos
, "shift %s", buf
->buf
+ pos
);
3604 if (r
->mstackpos
< 0) {
3605 mandoc_msg(MANDOCERR_REQ_NOMAC
, r
->parse
, ln
, ppos
, "shift");
3608 ctx
= r
->mstack
+ r
->mstackpos
;
3609 if (levels
> ctx
->argc
) {
3610 mandoc_vmsg(MANDOCERR_SHIFT
, r
->parse
,
3611 ln
, pos
, "%d, but max is %d", levels
, ctx
->argc
);
3616 for (i
= 0; i
< levels
; i
++)
3618 ctx
->argc
-= levels
;
3619 for (i
= 0; i
< ctx
->argc
; i
++)
3620 ctx
->argv
[i
] = ctx
->argv
[i
+ levels
];
3629 name
= buf
->buf
+ pos
;
3630 mandoc_vmsg(MANDOCERR_SO
, r
->parse
, ln
, ppos
, "so %s", name
);
3633 * Handle `so'. Be EXTREMELY careful, as we shouldn't be
3634 * opening anything that's not in our cwd or anything beneath
3635 * it. Thus, explicitly disallow traversing up the file-system
3636 * or using absolute paths.
3639 if (*name
== '/' || strstr(name
, "../") || strstr(name
, "/..")) {
3640 mandoc_vmsg(MANDOCERR_SO_PATH
, r
->parse
, ln
, ppos
,
3642 buf
->sz
= mandoc_asprintf(&cp
,
3643 ".sp\nSee the file %s.\n.sp", name
) + 1;
3647 return ROFF_REPARSE
;
3654 /* --- user defined strings and macros ------------------------------------ */
3657 roff_userdef(ROFF_ARGS
)
3660 char *arg
, *ap
, *dst
, *src
;
3663 /* Initialize a new macro stack context. */
3665 if (++r
->mstackpos
== r
->mstacksz
) {
3666 r
->mstack
= mandoc_recallocarray(r
->mstack
,
3667 r
->mstacksz
, r
->mstacksz
+ 8, sizeof(*r
->mstack
));
3670 ctx
= r
->mstack
+ r
->mstackpos
;
3676 * Collect pointers to macro argument strings,
3677 * NUL-terminating them and escaping quotes.
3680 src
= buf
->buf
+ pos
;
3681 while (*src
!= '\0') {
3682 if (ctx
->argc
== ctx
->argsz
) {
3684 ctx
->argv
= mandoc_reallocarray(ctx
->argv
,
3685 ctx
->argsz
, sizeof(*ctx
->argv
));
3687 arg
= mandoc_getarg(r
->parse
, &src
, ln
, &pos
);
3688 sz
= 1; /* For the terminating NUL. */
3689 for (ap
= arg
; *ap
!= '\0'; ap
++)
3690 sz
+= *ap
== '"' ? 4 : 1;
3691 ctx
->argv
[ctx
->argc
++] = dst
= mandoc_malloc(sz
);
3692 for (ap
= arg
; *ap
!= '\0'; ap
++) {
3694 memcpy(dst
, "\\(dq", 4);
3702 /* Replace the macro invocation by the macro definition. */
3705 buf
->buf
= mandoc_strdup(r
->current_string
);
3706 buf
->sz
= strlen(buf
->buf
) + 1;
3709 return buf
->sz
> 1 && buf
->buf
[buf
->sz
- 2] == '\n' ?
3710 ROFF_REPARSE
| ROFF_USERCALL
: ROFF_IGN
| ROFF_APPEND
;
3714 * Calling a high-level macro that was renamed with .rn.
3715 * r->current_string has already been set up by roff_parse().
3718 roff_renamed(ROFF_ARGS
)
3722 buf
->sz
= mandoc_asprintf(&nbuf
, ".%s%s%s", r
->current_string
,
3723 buf
->buf
[pos
] == '\0' ? "" : " ", buf
->buf
+ pos
) + 1;
3731 roff_getname(struct roff
*r
, char **cpp
, int ln
, int pos
)
3740 /* Read until end of name and terminate it with NUL. */
3741 for (cp
= name
; 1; cp
++) {
3742 if ('\0' == *cp
|| ' ' == *cp
) {
3749 if ('{' == cp
[1] || '}' == cp
[1])
3754 mandoc_vmsg(MANDOCERR_NAMESC
, r
->parse
, ln
, pos
,
3755 "%.*s", (int)(cp
- name
+ 1), name
);
3756 mandoc_escape((const char **)&cp
, NULL
, NULL
);
3760 /* Read past spaces. */
3769 * Store *string into the user-defined string called *name.
3770 * To clear an existing entry, call with (*r, *name, NULL, 0).
3771 * append == 0: replace mode
3772 * append == 1: single-line append mode
3773 * append == 2: multiline append mode, append '\n' after each call
3776 roff_setstr(struct roff
*r
, const char *name
, const char *string
,
3781 namesz
= strlen(name
);
3782 roff_setstrn(&r
->strtab
, name
, namesz
, string
,
3783 string
? strlen(string
) : 0, append
);
3784 roff_setstrn(&r
->rentab
, name
, namesz
, NULL
, 0, 0);
3788 roff_setstrn(struct roffkv
**r
, const char *name
, size_t namesz
,
3789 const char *string
, size_t stringsz
, int append
)
3794 size_t oldch
, newch
;
3796 /* Search for an existing string with the same name. */
3799 while (n
&& (namesz
!= n
->key
.sz
||
3800 strncmp(n
->key
.p
, name
, namesz
)))
3804 /* Create a new string table entry. */
3805 n
= mandoc_malloc(sizeof(struct roffkv
));
3806 n
->key
.p
= mandoc_strndup(name
, namesz
);
3812 } else if (0 == append
) {
3822 * One additional byte for the '\n' in multiline mode,
3823 * and one for the terminating '\0'.
3825 newch
= stringsz
+ (1 < append
? 2u : 1u);
3827 if (NULL
== n
->val
.p
) {
3828 n
->val
.p
= mandoc_malloc(newch
);
3833 n
->val
.p
= mandoc_realloc(n
->val
.p
, oldch
+ newch
);
3836 /* Skip existing content in the destination buffer. */
3837 c
= n
->val
.p
+ (int)oldch
;
3839 /* Append new content to the destination buffer. */
3841 while (i
< (int)stringsz
) {
3843 * Rudimentary roff copy mode:
3844 * Handle escaped backslashes.
3846 if ('\\' == string
[i
] && '\\' == string
[i
+ 1])
3851 /* Append terminating bytes. */
3856 n
->val
.sz
= (int)(c
- n
->val
.p
);
3860 roff_getstrn(struct roff
*r
, const char *name
, size_t len
,
3863 const struct roffkv
*n
;
3868 for (n
= r
->strtab
; n
!= NULL
; n
= n
->next
) {
3869 if (strncmp(name
, n
->key
.p
, len
) != 0 ||
3870 n
->key
.p
[len
] != '\0' || n
->val
.p
== NULL
)
3872 if (*deftype
& ROFFDEF_USER
) {
3873 *deftype
= ROFFDEF_USER
;
3880 for (n
= r
->rentab
; n
!= NULL
; n
= n
->next
) {
3881 if (strncmp(name
, n
->key
.p
, len
) != 0 ||
3882 n
->key
.p
[len
] != '\0' || n
->val
.p
== NULL
)
3884 if (*deftype
& ROFFDEF_REN
) {
3885 *deftype
= ROFFDEF_REN
;
3892 for (i
= 0; i
< PREDEFS_MAX
; i
++) {
3893 if (strncmp(name
, predefs
[i
].name
, len
) != 0 ||
3894 predefs
[i
].name
[len
] != '\0')
3896 if (*deftype
& ROFFDEF_PRE
) {
3897 *deftype
= ROFFDEF_PRE
;
3898 return predefs
[i
].str
;
3904 if (r
->man
->macroset
!= MACROSET_MAN
) {
3905 for (tok
= MDOC_Dd
; tok
< MDOC_MAX
; tok
++) {
3906 if (strncmp(name
, roff_name
[tok
], len
) != 0 ||
3907 roff_name
[tok
][len
] != '\0')
3909 if (*deftype
& ROFFDEF_STD
) {
3910 *deftype
= ROFFDEF_STD
;
3918 if (r
->man
->macroset
!= MACROSET_MDOC
) {
3919 for (tok
= MAN_TH
; tok
< MAN_MAX
; tok
++) {
3920 if (strncmp(name
, roff_name
[tok
], len
) != 0 ||
3921 roff_name
[tok
][len
] != '\0')
3923 if (*deftype
& ROFFDEF_STD
) {
3924 *deftype
= ROFFDEF_STD
;
3933 if (found
== 0 && *deftype
!= ROFFDEF_ANY
) {
3934 if (*deftype
& ROFFDEF_REN
) {
3936 * This might still be a request,
3937 * so do not treat it as undefined yet.
3939 *deftype
= ROFFDEF_UNDEF
;
3943 /* Using an undefined string defines it to be empty. */
3945 roff_setstrn(&r
->strtab
, name
, len
, "", 0, 0);
3946 roff_setstrn(&r
->rentab
, name
, len
, NULL
, 0, 0);
3954 roff_freestr(struct roffkv
*r
)
3956 struct roffkv
*n
, *nn
;
3958 for (n
= r
; n
; n
= nn
) {
3966 /* --- accessors and utility functions ------------------------------------ */
3969 * Duplicate an input string, making the appropriate character
3970 * conversations (as stipulated by `tr') along the way.
3971 * Returns a heap-allocated string with all the replacements made.
3974 roff_strdup(const struct roff
*r
, const char *p
)
3976 const struct roffkv
*cp
;
3980 enum mandoc_esc esc
;
3982 if (NULL
== r
->xmbtab
&& NULL
== r
->xtab
)
3983 return mandoc_strdup(p
);
3984 else if ('\0' == *p
)
3985 return mandoc_strdup("");
3988 * Step through each character looking for term matches
3989 * (remember that a `tr' can be invoked with an escape, which is
3990 * a glyph but the escape is multi-character).
3991 * We only do this if the character hash has been initialised
3992 * and the string is >0 length.
3998 while ('\0' != *p
) {
3999 assert((unsigned int)*p
< 128);
4000 if ('\\' != *p
&& r
->xtab
&& r
->xtab
[(unsigned int)*p
].p
) {
4001 sz
= r
->xtab
[(int)*p
].sz
;
4002 res
= mandoc_realloc(res
, ssz
+ sz
+ 1);
4003 memcpy(res
+ ssz
, r
->xtab
[(int)*p
].p
, sz
);
4007 } else if ('\\' != *p
) {
4008 res
= mandoc_realloc(res
, ssz
+ 2);
4013 /* Search for term matches. */
4014 for (cp
= r
->xmbtab
; cp
; cp
= cp
->next
)
4015 if (0 == strncmp(p
, cp
->key
.p
, cp
->key
.sz
))
4020 * A match has been found.
4021 * Append the match to the array and move
4022 * forward by its keysize.
4024 res
= mandoc_realloc(res
,
4025 ssz
+ cp
->val
.sz
+ 1);
4026 memcpy(res
+ ssz
, cp
->val
.p
, cp
->val
.sz
);
4028 p
+= (int)cp
->key
.sz
;
4033 * Handle escapes carefully: we need to copy
4034 * over just the escape itself, or else we might
4035 * do replacements within the escape itself.
4036 * Make sure to pass along the bogus string.
4039 esc
= mandoc_escape(&p
, NULL
, NULL
);
4040 if (ESCAPE_ERROR
== esc
) {
4042 res
= mandoc_realloc(res
, ssz
+ sz
+ 1);
4043 memcpy(res
+ ssz
, pp
, sz
);
4047 * We bail out on bad escapes.
4048 * No need to warn: we already did so when
4049 * roff_res() was called.
4052 res
= mandoc_realloc(res
, ssz
+ sz
+ 1);
4053 memcpy(res
+ ssz
, pp
, sz
);
4057 res
[(int)ssz
] = '\0';
4062 roff_getformat(const struct roff
*r
)
4069 * Find out whether a line is a macro line or not.
4070 * If it is, adjust the current position and return one; if it isn't,
4071 * return zero and don't change the current position.
4072 * If the control character has been set with `.cc', then let that grain
4074 * This is slighly contrary to groff, where using the non-breaking
4075 * control character when `cc' has been invoked will cause the
4076 * non-breaking macro contents to be printed verbatim.
4079 roff_getcontrol(const struct roff
*r
, const char *cp
, int *ppos
)
4085 if (r
->control
!= '\0' && cp
[pos
] == r
->control
)
4087 else if (r
->control
!= '\0')
4089 else if ('\\' == cp
[pos
] && '.' == cp
[pos
+ 1])
4091 else if ('.' == cp
[pos
] || '\'' == cp
[pos
])
4096 while (' ' == cp
[pos
] || '\t' == cp
[pos
])