]>
git.cameronkatri.com Git - mandoc.git/blob - roff.c
1 /* $Id: roff.c,v 1.333 2018/08/18 02:08:27 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>
32 #include "mandoc_aux.h"
33 #include "mandoc_ohash.h"
35 #include "libmandoc.h"
39 /* Maximum number of string expansions per line, to break infinite loops. */
40 #define EXPAND_LIMIT 1000
42 /* Types of definitions of macros and strings. */
43 #define ROFFDEF_USER (1 << 1) /* User-defined. */
44 #define ROFFDEF_PRE (1 << 2) /* Predefined. */
45 #define ROFFDEF_REN (1 << 3) /* Renamed standard macro. */
46 #define ROFFDEF_STD (1 << 4) /* mdoc(7) or man(7) macro. */
47 #define ROFFDEF_ANY (ROFFDEF_USER | ROFFDEF_PRE | \
48 ROFFDEF_REN | ROFFDEF_STD)
49 #define ROFFDEF_UNDEF (1 << 5) /* Completely undefined. */
51 /* --- data types --------------------------------------------------------- */
54 * An incredibly-simple string buffer.
57 char *p
; /* nil-terminated buffer */
58 size_t sz
; /* saved strlen(p) */
62 * A key-value roffstr pair as part of a singly-linked list.
67 struct roffkv
*next
; /* next in list */
71 * A single number register as part of a singly-linked list.
81 * Association of request and macro names with token IDs.
89 struct mparse
*parse
; /* parse point */
90 struct roff_man
*man
; /* mdoc or man parser */
91 struct roffnode
*last
; /* leaf of stack */
92 int *rstack
; /* stack of inverted `ie' values */
93 struct ohash
*reqtab
; /* request lookup table */
94 struct roffreg
*regtab
; /* number registers */
95 struct roffkv
*strtab
; /* user-defined strings & macros */
96 struct roffkv
*rentab
; /* renamed strings & macros */
97 struct roffkv
*xmbtab
; /* multi-byte trans table (`tr') */
98 struct roffstr
*xtab
; /* single-byte trans table (`tr') */
99 const char *current_string
; /* value of last called user macro */
100 struct tbl_node
*first_tbl
; /* first table parsed */
101 struct tbl_node
*last_tbl
; /* last table parsed */
102 struct tbl_node
*tbl
; /* current table being parsed */
103 struct eqn_node
*last_eqn
; /* equation parser */
104 struct eqn_node
*eqn
; /* active equation parser */
105 int eqn_inline
; /* current equation is inline */
106 int options
; /* parse options */
107 int rstacksz
; /* current size limit of rstack */
108 int rstackpos
; /* position in rstack */
109 int format
; /* current file in mdoc or man format */
110 int argc
; /* number of args of the last macro */
111 char control
; /* control character */
112 char escape
; /* escape character */
116 enum roff_tok tok
; /* type of node */
117 struct roffnode
*parent
; /* up one in stack */
118 int line
; /* parse line */
119 int col
; /* parse col */
120 char *name
; /* node name, e.g. macro name */
121 char *end
; /* end-rules: custom token */
122 int endspan
; /* end-rules: next-line or infty */
123 int rule
; /* current evaluation rule */
126 #define ROFF_ARGS struct roff *r, /* parse ctx */ \
127 enum roff_tok tok, /* tok of macro */ \
128 struct buf *buf, /* input buffer */ \
129 int ln, /* parse line */ \
130 int ppos, /* original pos in buffer */ \
131 int pos, /* current pos in buffer */ \
132 int *offs /* reset offset of buffer data */
134 typedef enum rofferr (*roffproc
)(ROFF_ARGS
);
137 roffproc proc
; /* process new macro */
138 roffproc text
; /* process as child text of macro */
139 roffproc sub
; /* process as child of macro */
141 #define ROFFMAC_STRUCT (1 << 0) /* always interpret */
145 const char *name
; /* predefined input name */
146 const char *str
; /* replacement symbol */
149 #define PREDEF(__name, __str) \
150 { (__name), (__str) },
152 /* --- function prototypes ------------------------------------------------ */
154 static void roffnode_cleanscope(struct roff
*);
155 static void roffnode_pop(struct roff
*);
156 static void roffnode_push(struct roff
*, enum roff_tok
,
157 const char *, int, int);
158 static void roff_addtbl(struct roff_man
*, struct tbl_node
*);
159 static enum rofferr
roff_als(ROFF_ARGS
);
160 static enum rofferr
roff_block(ROFF_ARGS
);
161 static enum rofferr
roff_block_text(ROFF_ARGS
);
162 static enum rofferr
roff_block_sub(ROFF_ARGS
);
163 static enum rofferr
roff_br(ROFF_ARGS
);
164 static enum rofferr
roff_cblock(ROFF_ARGS
);
165 static enum rofferr
roff_cc(ROFF_ARGS
);
166 static void roff_ccond(struct roff
*, int, int);
167 static enum rofferr
roff_cond(ROFF_ARGS
);
168 static enum rofferr
roff_cond_text(ROFF_ARGS
);
169 static enum rofferr
roff_cond_sub(ROFF_ARGS
);
170 static enum rofferr
roff_ds(ROFF_ARGS
);
171 static enum rofferr
roff_ec(ROFF_ARGS
);
172 static enum rofferr
roff_eo(ROFF_ARGS
);
173 static enum rofferr
roff_eqndelim(struct roff
*, struct buf
*, int);
174 static int roff_evalcond(struct roff
*r
, int, char *, int *);
175 static int roff_evalnum(struct roff
*, int,
176 const char *, int *, int *, int);
177 static int roff_evalpar(struct roff
*, int,
178 const char *, int *, int *, int);
179 static int roff_evalstrcond(const char *, int *);
180 static void roff_free1(struct roff
*);
181 static void roff_freereg(struct roffreg
*);
182 static void roff_freestr(struct roffkv
*);
183 static size_t roff_getname(struct roff
*, char **, int, int);
184 static int roff_getnum(const char *, int *, int *, int);
185 static int roff_getop(const char *, int *, char *);
186 static int roff_getregn(struct roff
*,
187 const char *, size_t, char);
188 static int roff_getregro(const struct roff
*,
190 static const char *roff_getstrn(struct roff
*,
191 const char *, size_t, int *);
192 static int roff_hasregn(const struct roff
*,
193 const char *, size_t);
194 static enum rofferr
roff_insec(ROFF_ARGS
);
195 static enum rofferr
roff_it(ROFF_ARGS
);
196 static enum rofferr
roff_line_ignore(ROFF_ARGS
);
197 static void roff_man_alloc1(struct roff_man
*);
198 static void roff_man_free1(struct roff_man
*);
199 static enum rofferr
roff_manyarg(ROFF_ARGS
);
200 static enum rofferr
roff_nop(ROFF_ARGS
);
201 static enum rofferr
roff_nr(ROFF_ARGS
);
202 static enum rofferr
roff_onearg(ROFF_ARGS
);
203 static enum roff_tok
roff_parse(struct roff
*, char *, int *,
205 static enum rofferr
roff_parsetext(struct roff
*, struct buf
*,
207 static enum rofferr
roff_renamed(ROFF_ARGS
);
208 static enum rofferr
roff_res(struct roff
*, struct buf
*, int, int);
209 static enum rofferr
roff_rm(ROFF_ARGS
);
210 static enum rofferr
roff_rn(ROFF_ARGS
);
211 static enum rofferr
roff_rr(ROFF_ARGS
);
212 static void roff_setregn(struct roff
*, const char *,
213 size_t, int, char, int);
214 static void roff_setstr(struct roff
*,
215 const char *, const char *, int);
216 static void roff_setstrn(struct roffkv
**, const char *,
217 size_t, const char *, size_t, int);
218 static enum rofferr
roff_so(ROFF_ARGS
);
219 static enum rofferr
roff_tr(ROFF_ARGS
);
220 static enum rofferr
roff_Dd(ROFF_ARGS
);
221 static enum rofferr
roff_TE(ROFF_ARGS
);
222 static enum rofferr
roff_TS(ROFF_ARGS
);
223 static enum rofferr
roff_EQ(ROFF_ARGS
);
224 static enum rofferr
roff_EN(ROFF_ARGS
);
225 static enum rofferr
roff_T_(ROFF_ARGS
);
226 static enum rofferr
roff_unsupp(ROFF_ARGS
);
227 static enum rofferr
roff_userdef(ROFF_ARGS
);
229 /* --- constant data ------------------------------------------------------ */
231 #define ROFFNUM_SCALE (1 << 0) /* Honour scaling in roff_getnum(). */
232 #define ROFFNUM_WHITE (1 << 1) /* Skip whitespace in roff_evalnum(). */
234 const char *__roff_name
[MAN_MAX
+ 1] = {
235 "br", "ce", "ft", "ll",
236 "mc", "po", "rj", "sp",
238 "ab", "ad", "af", "aln",
239 "als", "am", "am1", "ami",
240 "ami1", "as", "as1", "asciify",
241 "backtrace", "bd", "bleedat", "blm",
242 "box", "boxa", "bp", "BP",
243 "break", "breakchar", "brnl", "brp",
245 "cf", "cflags", "ch", "char",
246 "chop", "class", "close", "CL",
247 "color", "composite", "continue", "cp",
248 "cropat", "cs", "cu", "da",
249 "dch", "Dd", "de", "de1",
250 "defcolor", "dei", "dei1", "device",
251 "devicem", "di", "do", "ds",
252 "ds1", "dwh", "dt", "ec",
253 "ecr", "ecs", "el", "em",
254 "EN", "eo", "EP", "EQ",
255 "errprint", "ev", "evc", "ex",
256 "fallback", "fam", "fc", "fchar",
257 "fcolor", "fdeferlig", "feature", "fkern",
258 "fl", "flig", "fp", "fps",
259 "fschar", "fspacewidth", "fspecial", "ftr",
260 "fzoom", "gcolor", "hc", "hcode",
261 "hidechar", "hla", "hlm", "hpf",
262 "hpfa", "hpfcode", "hw", "hy",
263 "hylang", "hylen", "hym", "hypp",
264 "hys", "ie", "if", "ig",
265 "index", "it", "itc", "IX",
266 "kern", "kernafter", "kernbefore", "kernpair",
267 "lc", "lc_ctype", "lds", "length",
268 "letadj", "lf", "lg", "lhang",
269 "linetabs", "lnr", "lnrf", "lpfx",
271 "mediasize", "minss", "mk", "mso",
272 "na", "ne", "nh", "nhychar",
273 "nm", "nn", "nop", "nr",
274 "nrf", "nroff", "ns", "nx",
275 "open", "opena", "os", "output",
276 "padj", "papersize", "pc", "pev",
277 "pi", "PI", "pl", "pm",
279 "psbb", "pshape", "pso", "ptr",
280 "pvs", "rchar", "rd", "recursionlimit",
281 "return", "rfschar", "rhang",
282 "rm", "rn", "rnn", "rr",
283 "rs", "rt", "schar", "sentchar",
284 "shc", "shift", "sizes", "so",
285 "spacewidth", "special", "spreadwarn", "ss",
286 "sty", "substring", "sv", "sy",
289 "tm", "tm1", "tmc", "tr",
290 "track", "transchar", "trf", "trimat",
291 "trin", "trnt", "troff", "TS",
292 "uf", "ul", "unformat", "unwatch",
293 "unwatchn", "vpt", "vs", "warn",
294 "warnscale", "watch", "watchlength", "watchn",
295 "wh", "while", "write", "writec",
296 "writem", "xflag", ".", NULL
,
298 "Dd", "Dt", "Os", "Sh",
299 "Ss", "Pp", "D1", "Dl",
300 "Bd", "Ed", "Bl", "El",
301 "It", "Ad", "An", "Ap",
302 "Ar", "Cd", "Cm", "Dv",
303 "Er", "Ev", "Ex", "Fa",
304 "Fd", "Fl", "Fn", "Ft",
305 "Ic", "In", "Li", "Nd",
306 "Nm", "Op", "Ot", "Pa",
307 "Rv", "St", "Va", "Vt",
308 "Xr", "%A", "%B", "%D",
309 "%I", "%J", "%N", "%O",
310 "%P", "%R", "%T", "%V",
311 "Ac", "Ao", "Aq", "At",
312 "Bc", "Bf", "Bo", "Bq",
313 "Bsx", "Bx", "Db", "Dc",
314 "Do", "Dq", "Ec", "Ef",
315 "Em", "Eo", "Fx", "Ms",
316 "No", "Ns", "Nx", "Ox",
317 "Pc", "Pf", "Po", "Pq",
318 "Qc", "Ql", "Qo", "Qq",
319 "Re", "Rs", "Sc", "So",
320 "Sq", "Sm", "Sx", "Sy",
321 "Tn", "Ux", "Xc", "Xo",
322 "Fo", "Fc", "Oo", "Oc",
323 "Bk", "Ek", "Bt", "Hf",
324 "Fr", "Ud", "Lb", "Lp",
325 "Lk", "Mt", "Brq", "Bro",
326 "Brc", "%C", "Es", "En",
327 "Dx", "%Q", "%U", "Ta",
329 "TH", "SH", "SS", "TP",
331 "LP", "PP", "P", "IP",
332 "HP", "SM", "SB", "BI",
333 "IB", "BR", "RB", "R",
334 "B", "I", "IR", "RI",
336 "RE", "RS", "DT", "UC",
340 "UE", "MT", "ME", NULL
342 const char *const *roff_name
= __roff_name
;
344 static struct roffmac roffs
[TOKEN_NONE
] = {
345 { roff_br
, NULL
, NULL
, 0 }, /* br */
346 { roff_onearg
, NULL
, NULL
, 0 }, /* ce */
347 { roff_onearg
, NULL
, NULL
, 0 }, /* ft */
348 { roff_onearg
, NULL
, NULL
, 0 }, /* ll */
349 { roff_onearg
, NULL
, NULL
, 0 }, /* mc */
350 { roff_onearg
, NULL
, NULL
, 0 }, /* po */
351 { roff_onearg
, NULL
, NULL
, 0 }, /* rj */
352 { roff_onearg
, NULL
, NULL
, 0 }, /* sp */
353 { roff_manyarg
, NULL
, NULL
, 0 }, /* ta */
354 { roff_onearg
, NULL
, NULL
, 0 }, /* ti */
355 { NULL
, NULL
, NULL
, 0 }, /* ROFF_MAX */
356 { roff_unsupp
, NULL
, NULL
, 0 }, /* ab */
357 { roff_line_ignore
, NULL
, NULL
, 0 }, /* ad */
358 { roff_line_ignore
, NULL
, NULL
, 0 }, /* af */
359 { roff_unsupp
, NULL
, NULL
, 0 }, /* aln */
360 { roff_als
, NULL
, NULL
, 0 }, /* als */
361 { roff_block
, roff_block_text
, roff_block_sub
, 0 }, /* am */
362 { roff_block
, roff_block_text
, roff_block_sub
, 0 }, /* am1 */
363 { roff_block
, roff_block_text
, roff_block_sub
, 0 }, /* ami */
364 { roff_block
, roff_block_text
, roff_block_sub
, 0 }, /* ami1 */
365 { roff_ds
, NULL
, NULL
, 0 }, /* as */
366 { roff_ds
, NULL
, NULL
, 0 }, /* as1 */
367 { roff_unsupp
, NULL
, NULL
, 0 }, /* asciify */
368 { roff_line_ignore
, NULL
, NULL
, 0 }, /* backtrace */
369 { roff_line_ignore
, NULL
, NULL
, 0 }, /* bd */
370 { roff_line_ignore
, NULL
, NULL
, 0 }, /* bleedat */
371 { roff_unsupp
, NULL
, NULL
, 0 }, /* blm */
372 { roff_unsupp
, NULL
, NULL
, 0 }, /* box */
373 { roff_unsupp
, NULL
, NULL
, 0 }, /* boxa */
374 { roff_line_ignore
, NULL
, NULL
, 0 }, /* bp */
375 { roff_unsupp
, NULL
, NULL
, 0 }, /* BP */
376 { roff_unsupp
, NULL
, NULL
, 0 }, /* break */
377 { roff_line_ignore
, NULL
, NULL
, 0 }, /* breakchar */
378 { roff_line_ignore
, NULL
, NULL
, 0 }, /* brnl */
379 { roff_br
, NULL
, NULL
, 0 }, /* brp */
380 { roff_line_ignore
, NULL
, NULL
, 0 }, /* brpnl */
381 { roff_unsupp
, NULL
, NULL
, 0 }, /* c2 */
382 { roff_cc
, NULL
, NULL
, 0 }, /* cc */
383 { roff_insec
, NULL
, NULL
, 0 }, /* cf */
384 { roff_line_ignore
, NULL
, NULL
, 0 }, /* cflags */
385 { roff_line_ignore
, NULL
, NULL
, 0 }, /* ch */
386 { roff_unsupp
, NULL
, NULL
, 0 }, /* char */
387 { roff_unsupp
, NULL
, NULL
, 0 }, /* chop */
388 { roff_line_ignore
, NULL
, NULL
, 0 }, /* class */
389 { roff_insec
, NULL
, NULL
, 0 }, /* close */
390 { roff_unsupp
, NULL
, NULL
, 0 }, /* CL */
391 { roff_line_ignore
, NULL
, NULL
, 0 }, /* color */
392 { roff_unsupp
, NULL
, NULL
, 0 }, /* composite */
393 { roff_unsupp
, NULL
, NULL
, 0 }, /* continue */
394 { roff_line_ignore
, NULL
, NULL
, 0 }, /* cp */
395 { roff_line_ignore
, NULL
, NULL
, 0 }, /* cropat */
396 { roff_line_ignore
, NULL
, NULL
, 0 }, /* cs */
397 { roff_line_ignore
, NULL
, NULL
, 0 }, /* cu */
398 { roff_unsupp
, NULL
, NULL
, 0 }, /* da */
399 { roff_unsupp
, NULL
, NULL
, 0 }, /* dch */
400 { roff_Dd
, NULL
, NULL
, 0 }, /* Dd */
401 { roff_block
, roff_block_text
, roff_block_sub
, 0 }, /* de */
402 { roff_block
, roff_block_text
, roff_block_sub
, 0 }, /* de1 */
403 { roff_line_ignore
, NULL
, NULL
, 0 }, /* defcolor */
404 { roff_block
, roff_block_text
, roff_block_sub
, 0 }, /* dei */
405 { roff_block
, roff_block_text
, roff_block_sub
, 0 }, /* dei1 */
406 { roff_unsupp
, NULL
, NULL
, 0 }, /* device */
407 { roff_unsupp
, NULL
, NULL
, 0 }, /* devicem */
408 { roff_unsupp
, NULL
, NULL
, 0 }, /* di */
409 { roff_unsupp
, NULL
, NULL
, 0 }, /* do */
410 { roff_ds
, NULL
, NULL
, 0 }, /* ds */
411 { roff_ds
, NULL
, NULL
, 0 }, /* ds1 */
412 { roff_unsupp
, NULL
, NULL
, 0 }, /* dwh */
413 { roff_unsupp
, NULL
, NULL
, 0 }, /* dt */
414 { roff_ec
, NULL
, NULL
, 0 }, /* ec */
415 { roff_unsupp
, NULL
, NULL
, 0 }, /* ecr */
416 { roff_unsupp
, NULL
, NULL
, 0 }, /* ecs */
417 { roff_cond
, roff_cond_text
, roff_cond_sub
, ROFFMAC_STRUCT
}, /* el */
418 { roff_unsupp
, NULL
, NULL
, 0 }, /* em */
419 { roff_EN
, NULL
, NULL
, 0 }, /* EN */
420 { roff_eo
, NULL
, NULL
, 0 }, /* eo */
421 { roff_unsupp
, NULL
, NULL
, 0 }, /* EP */
422 { roff_EQ
, NULL
, NULL
, 0 }, /* EQ */
423 { roff_line_ignore
, NULL
, NULL
, 0 }, /* errprint */
424 { roff_unsupp
, NULL
, NULL
, 0 }, /* ev */
425 { roff_unsupp
, NULL
, NULL
, 0 }, /* evc */
426 { roff_unsupp
, NULL
, NULL
, 0 }, /* ex */
427 { roff_line_ignore
, NULL
, NULL
, 0 }, /* fallback */
428 { roff_line_ignore
, NULL
, NULL
, 0 }, /* fam */
429 { roff_unsupp
, NULL
, NULL
, 0 }, /* fc */
430 { roff_unsupp
, NULL
, NULL
, 0 }, /* fchar */
431 { roff_line_ignore
, NULL
, NULL
, 0 }, /* fcolor */
432 { roff_line_ignore
, NULL
, NULL
, 0 }, /* fdeferlig */
433 { roff_line_ignore
, NULL
, NULL
, 0 }, /* feature */
434 { roff_line_ignore
, NULL
, NULL
, 0 }, /* fkern */
435 { roff_line_ignore
, NULL
, NULL
, 0 }, /* fl */
436 { roff_line_ignore
, NULL
, NULL
, 0 }, /* flig */
437 { roff_line_ignore
, NULL
, NULL
, 0 }, /* fp */
438 { roff_line_ignore
, NULL
, NULL
, 0 }, /* fps */
439 { roff_unsupp
, NULL
, NULL
, 0 }, /* fschar */
440 { roff_line_ignore
, NULL
, NULL
, 0 }, /* fspacewidth */
441 { roff_line_ignore
, NULL
, NULL
, 0 }, /* fspecial */
442 { roff_line_ignore
, NULL
, NULL
, 0 }, /* ftr */
443 { roff_line_ignore
, NULL
, NULL
, 0 }, /* fzoom */
444 { roff_line_ignore
, NULL
, NULL
, 0 }, /* gcolor */
445 { roff_line_ignore
, NULL
, NULL
, 0 }, /* hc */
446 { roff_line_ignore
, NULL
, NULL
, 0 }, /* hcode */
447 { roff_line_ignore
, NULL
, NULL
, 0 }, /* hidechar */
448 { roff_line_ignore
, NULL
, NULL
, 0 }, /* hla */
449 { roff_line_ignore
, NULL
, NULL
, 0 }, /* hlm */
450 { roff_line_ignore
, NULL
, NULL
, 0 }, /* hpf */
451 { roff_line_ignore
, NULL
, NULL
, 0 }, /* hpfa */
452 { roff_line_ignore
, NULL
, NULL
, 0 }, /* hpfcode */
453 { roff_line_ignore
, NULL
, NULL
, 0 }, /* hw */
454 { roff_line_ignore
, NULL
, NULL
, 0 }, /* hy */
455 { roff_line_ignore
, NULL
, NULL
, 0 }, /* hylang */
456 { roff_line_ignore
, NULL
, NULL
, 0 }, /* hylen */
457 { roff_line_ignore
, NULL
, NULL
, 0 }, /* hym */
458 { roff_line_ignore
, NULL
, NULL
, 0 }, /* hypp */
459 { roff_line_ignore
, NULL
, NULL
, 0 }, /* hys */
460 { roff_cond
, roff_cond_text
, roff_cond_sub
, ROFFMAC_STRUCT
}, /* ie */
461 { roff_cond
, roff_cond_text
, roff_cond_sub
, ROFFMAC_STRUCT
}, /* if */
462 { roff_block
, roff_block_text
, roff_block_sub
, 0 }, /* ig */
463 { roff_unsupp
, NULL
, NULL
, 0 }, /* index */
464 { roff_it
, NULL
, NULL
, 0 }, /* it */
465 { roff_unsupp
, NULL
, NULL
, 0 }, /* itc */
466 { roff_line_ignore
, NULL
, NULL
, 0 }, /* IX */
467 { roff_line_ignore
, NULL
, NULL
, 0 }, /* kern */
468 { roff_line_ignore
, NULL
, NULL
, 0 }, /* kernafter */
469 { roff_line_ignore
, NULL
, NULL
, 0 }, /* kernbefore */
470 { roff_line_ignore
, NULL
, NULL
, 0 }, /* kernpair */
471 { roff_unsupp
, NULL
, NULL
, 0 }, /* lc */
472 { roff_unsupp
, NULL
, NULL
, 0 }, /* lc_ctype */
473 { roff_unsupp
, NULL
, NULL
, 0 }, /* lds */
474 { roff_unsupp
, NULL
, NULL
, 0 }, /* length */
475 { roff_line_ignore
, NULL
, NULL
, 0 }, /* letadj */
476 { roff_insec
, NULL
, NULL
, 0 }, /* lf */
477 { roff_line_ignore
, NULL
, NULL
, 0 }, /* lg */
478 { roff_line_ignore
, NULL
, NULL
, 0 }, /* lhang */
479 { roff_unsupp
, NULL
, NULL
, 0 }, /* linetabs */
480 { roff_unsupp
, NULL
, NULL
, 0 }, /* lnr */
481 { roff_unsupp
, NULL
, NULL
, 0 }, /* lnrf */
482 { roff_unsupp
, NULL
, NULL
, 0 }, /* lpfx */
483 { roff_line_ignore
, NULL
, NULL
, 0 }, /* ls */
484 { roff_unsupp
, NULL
, NULL
, 0 }, /* lsm */
485 { roff_line_ignore
, NULL
, NULL
, 0 }, /* lt */
486 { roff_line_ignore
, NULL
, NULL
, 0 }, /* mediasize */
487 { roff_line_ignore
, NULL
, NULL
, 0 }, /* minss */
488 { roff_line_ignore
, NULL
, NULL
, 0 }, /* mk */
489 { roff_insec
, NULL
, NULL
, 0 }, /* mso */
490 { roff_line_ignore
, NULL
, NULL
, 0 }, /* na */
491 { roff_line_ignore
, NULL
, NULL
, 0 }, /* ne */
492 { roff_line_ignore
, NULL
, NULL
, 0 }, /* nh */
493 { roff_line_ignore
, NULL
, NULL
, 0 }, /* nhychar */
494 { roff_unsupp
, NULL
, NULL
, 0 }, /* nm */
495 { roff_unsupp
, NULL
, NULL
, 0 }, /* nn */
496 { roff_nop
, NULL
, NULL
, 0 }, /* nop */
497 { roff_nr
, NULL
, NULL
, 0 }, /* nr */
498 { roff_unsupp
, NULL
, NULL
, 0 }, /* nrf */
499 { roff_line_ignore
, NULL
, NULL
, 0 }, /* nroff */
500 { roff_line_ignore
, NULL
, NULL
, 0 }, /* ns */
501 { roff_insec
, NULL
, NULL
, 0 }, /* nx */
502 { roff_insec
, NULL
, NULL
, 0 }, /* open */
503 { roff_insec
, NULL
, NULL
, 0 }, /* opena */
504 { roff_line_ignore
, NULL
, NULL
, 0 }, /* os */
505 { roff_unsupp
, NULL
, NULL
, 0 }, /* output */
506 { roff_line_ignore
, NULL
, NULL
, 0 }, /* padj */
507 { roff_line_ignore
, NULL
, NULL
, 0 }, /* papersize */
508 { roff_line_ignore
, NULL
, NULL
, 0 }, /* pc */
509 { roff_line_ignore
, NULL
, NULL
, 0 }, /* pev */
510 { roff_insec
, NULL
, NULL
, 0 }, /* pi */
511 { roff_unsupp
, NULL
, NULL
, 0 }, /* PI */
512 { roff_line_ignore
, NULL
, NULL
, 0 }, /* pl */
513 { roff_line_ignore
, NULL
, NULL
, 0 }, /* pm */
514 { roff_line_ignore
, NULL
, NULL
, 0 }, /* pn */
515 { roff_line_ignore
, NULL
, NULL
, 0 }, /* pnr */
516 { roff_line_ignore
, NULL
, NULL
, 0 }, /* ps */
517 { roff_unsupp
, NULL
, NULL
, 0 }, /* psbb */
518 { roff_unsupp
, NULL
, NULL
, 0 }, /* pshape */
519 { roff_insec
, NULL
, NULL
, 0 }, /* pso */
520 { roff_line_ignore
, NULL
, NULL
, 0 }, /* ptr */
521 { roff_line_ignore
, NULL
, NULL
, 0 }, /* pvs */
522 { roff_unsupp
, NULL
, NULL
, 0 }, /* rchar */
523 { roff_line_ignore
, NULL
, NULL
, 0 }, /* rd */
524 { roff_line_ignore
, NULL
, NULL
, 0 }, /* recursionlimit */
525 { roff_unsupp
, NULL
, NULL
, 0 }, /* return */
526 { roff_unsupp
, NULL
, NULL
, 0 }, /* rfschar */
527 { roff_line_ignore
, NULL
, NULL
, 0 }, /* rhang */
528 { roff_rm
, NULL
, NULL
, 0 }, /* rm */
529 { roff_rn
, NULL
, NULL
, 0 }, /* rn */
530 { roff_unsupp
, NULL
, NULL
, 0 }, /* rnn */
531 { roff_rr
, NULL
, NULL
, 0 }, /* rr */
532 { roff_line_ignore
, NULL
, NULL
, 0 }, /* rs */
533 { roff_line_ignore
, NULL
, NULL
, 0 }, /* rt */
534 { roff_unsupp
, NULL
, NULL
, 0 }, /* schar */
535 { roff_line_ignore
, NULL
, NULL
, 0 }, /* sentchar */
536 { roff_line_ignore
, NULL
, NULL
, 0 }, /* shc */
537 { roff_unsupp
, NULL
, NULL
, 0 }, /* shift */
538 { roff_line_ignore
, NULL
, NULL
, 0 }, /* sizes */
539 { roff_so
, NULL
, NULL
, 0 }, /* so */
540 { roff_line_ignore
, NULL
, NULL
, 0 }, /* spacewidth */
541 { roff_line_ignore
, NULL
, NULL
, 0 }, /* special */
542 { roff_line_ignore
, NULL
, NULL
, 0 }, /* spreadwarn */
543 { roff_line_ignore
, NULL
, NULL
, 0 }, /* ss */
544 { roff_line_ignore
, NULL
, NULL
, 0 }, /* sty */
545 { roff_unsupp
, NULL
, NULL
, 0 }, /* substring */
546 { roff_line_ignore
, NULL
, NULL
, 0 }, /* sv */
547 { roff_insec
, NULL
, NULL
, 0 }, /* sy */
548 { roff_T_
, NULL
, NULL
, 0 }, /* T& */
549 { roff_unsupp
, NULL
, NULL
, 0 }, /* tc */
550 { roff_TE
, NULL
, NULL
, 0 }, /* TE */
551 { roff_Dd
, NULL
, NULL
, 0 }, /* TH */
552 { roff_line_ignore
, NULL
, NULL
, 0 }, /* tkf */
553 { roff_unsupp
, NULL
, NULL
, 0 }, /* tl */
554 { roff_line_ignore
, NULL
, NULL
, 0 }, /* tm */
555 { roff_line_ignore
, NULL
, NULL
, 0 }, /* tm1 */
556 { roff_line_ignore
, NULL
, NULL
, 0 }, /* tmc */
557 { roff_tr
, NULL
, NULL
, 0 }, /* tr */
558 { roff_line_ignore
, NULL
, NULL
, 0 }, /* track */
559 { roff_line_ignore
, NULL
, NULL
, 0 }, /* transchar */
560 { roff_insec
, NULL
, NULL
, 0 }, /* trf */
561 { roff_line_ignore
, NULL
, NULL
, 0 }, /* trimat */
562 { roff_unsupp
, NULL
, NULL
, 0 }, /* trin */
563 { roff_unsupp
, NULL
, NULL
, 0 }, /* trnt */
564 { roff_line_ignore
, NULL
, NULL
, 0 }, /* troff */
565 { roff_TS
, NULL
, NULL
, 0 }, /* TS */
566 { roff_line_ignore
, NULL
, NULL
, 0 }, /* uf */
567 { roff_line_ignore
, NULL
, NULL
, 0 }, /* ul */
568 { roff_unsupp
, NULL
, NULL
, 0 }, /* unformat */
569 { roff_line_ignore
, NULL
, NULL
, 0 }, /* unwatch */
570 { roff_line_ignore
, NULL
, NULL
, 0 }, /* unwatchn */
571 { roff_line_ignore
, NULL
, NULL
, 0 }, /* vpt */
572 { roff_line_ignore
, NULL
, NULL
, 0 }, /* vs */
573 { roff_line_ignore
, NULL
, NULL
, 0 }, /* warn */
574 { roff_line_ignore
, NULL
, NULL
, 0 }, /* warnscale */
575 { roff_line_ignore
, NULL
, NULL
, 0 }, /* watch */
576 { roff_line_ignore
, NULL
, NULL
, 0 }, /* watchlength */
577 { roff_line_ignore
, NULL
, NULL
, 0 }, /* watchn */
578 { roff_unsupp
, NULL
, NULL
, 0 }, /* wh */
579 { roff_unsupp
, NULL
, NULL
, 0 }, /* while */
580 { roff_insec
, NULL
, NULL
, 0 }, /* write */
581 { roff_insec
, NULL
, NULL
, 0 }, /* writec */
582 { roff_insec
, NULL
, NULL
, 0 }, /* writem */
583 { roff_line_ignore
, NULL
, NULL
, 0 }, /* xflag */
584 { roff_cblock
, NULL
, NULL
, 0 }, /* . */
585 { roff_renamed
, NULL
, NULL
, 0 },
586 { roff_userdef
, NULL
, NULL
, 0 }
589 /* Array of injected predefined strings. */
590 #define PREDEFS_MAX 38
591 static const struct predef predefs
[PREDEFS_MAX
] = {
592 #include "predefs.in"
595 static int roffce_lines
; /* number of input lines to center */
596 static struct roff_node
*roffce_node
; /* active request */
597 static int roffit_lines
; /* number of lines to delay */
598 static char *roffit_macro
; /* nil-terminated macro line */
601 /* --- request table ------------------------------------------------------ */
604 roffhash_alloc(enum roff_tok mintok
, enum roff_tok maxtok
)
612 htab
= mandoc_malloc(sizeof(*htab
));
613 mandoc_ohash_init(htab
, 8, offsetof(struct roffreq
, name
));
615 for (tok
= mintok
; tok
< maxtok
; tok
++) {
616 if (roff_name
[tok
] == NULL
)
618 sz
= strlen(roff_name
[tok
]);
619 req
= mandoc_malloc(sizeof(*req
) + sz
+ 1);
621 memcpy(req
->name
, roff_name
[tok
], sz
+ 1);
622 slot
= ohash_qlookup(htab
, req
->name
);
623 ohash_insert(htab
, slot
, req
);
629 roffhash_free(struct ohash
*htab
)
636 for (req
= ohash_first(htab
, &slot
); req
!= NULL
;
637 req
= ohash_next(htab
, &slot
))
644 roffhash_find(struct ohash
*htab
, const char *name
, size_t sz
)
651 req
= ohash_find(htab
, ohash_qlookupi(htab
, name
, &end
));
653 req
= ohash_find(htab
, ohash_qlookup(htab
, name
));
654 return req
== NULL
? TOKEN_NONE
: req
->tok
;
657 /* --- stack of request blocks -------------------------------------------- */
660 * Pop the current node off of the stack of roff instructions currently
664 roffnode_pop(struct roff
*r
)
671 r
->last
= r
->last
->parent
;
678 * Push a roff node onto the instruction stack. This must later be
679 * removed with roffnode_pop().
682 roffnode_push(struct roff
*r
, enum roff_tok tok
, const char *name
,
687 p
= mandoc_calloc(1, sizeof(struct roffnode
));
690 p
->name
= mandoc_strdup(name
);
694 p
->rule
= p
->parent
? p
->parent
->rule
: 0;
699 /* --- roff parser state data management ---------------------------------- */
702 roff_free1(struct roff
*r
)
704 struct tbl_node
*tbl
;
707 while (NULL
!= (tbl
= r
->first_tbl
)) {
708 r
->first_tbl
= tbl
->next
;
711 r
->first_tbl
= r
->last_tbl
= r
->tbl
= NULL
;
713 if (r
->last_eqn
!= NULL
)
714 eqn_free(r
->last_eqn
);
715 r
->last_eqn
= r
->eqn
= NULL
;
725 roff_freereg(r
->regtab
);
728 roff_freestr(r
->strtab
);
729 roff_freestr(r
->rentab
);
730 roff_freestr(r
->xmbtab
);
731 r
->strtab
= r
->rentab
= r
->xmbtab
= NULL
;
734 for (i
= 0; i
< 128; i
++)
741 roff_reset(struct roff
*r
)
744 r
->format
= r
->options
& (MPARSE_MDOC
| MPARSE_MAN
);
754 roff_free(struct roff
*r
)
757 roffhash_free(r
->reqtab
);
762 roff_alloc(struct mparse
*parse
, int options
)
766 r
= mandoc_calloc(1, sizeof(struct roff
));
768 r
->reqtab
= roffhash_alloc(0, ROFF_RENAMED
);
769 r
->options
= options
;
770 r
->format
= options
& (MPARSE_MDOC
| MPARSE_MAN
);
776 /* --- syntax tree state data management ---------------------------------- */
779 roff_man_free1(struct roff_man
*man
)
782 if (man
->first
!= NULL
)
783 roff_node_delete(man
, man
->first
);
784 free(man
->meta
.msec
);
787 free(man
->meta
.arch
);
788 free(man
->meta
.title
);
789 free(man
->meta
.name
);
790 free(man
->meta
.date
);
794 roff_man_alloc1(struct roff_man
*man
)
797 memset(&man
->meta
, 0, sizeof(man
->meta
));
798 man
->first
= mandoc_calloc(1, sizeof(*man
->first
));
799 man
->first
->type
= ROFFT_ROOT
;
800 man
->last
= man
->first
;
803 man
->macroset
= MACROSET_NONE
;
804 man
->lastsec
= man
->lastnamed
= SEC_NONE
;
805 man
->next
= ROFF_NEXT_CHILD
;
809 roff_man_reset(struct roff_man
*man
)
813 roff_man_alloc1(man
);
817 roff_man_free(struct roff_man
*man
)
825 roff_man_alloc(struct roff
*roff
, struct mparse
*parse
,
826 const char *os_s
, int quick
)
828 struct roff_man
*man
;
830 man
= mandoc_calloc(1, sizeof(*man
));
835 roff_man_alloc1(man
);
840 /* --- syntax tree handling ----------------------------------------------- */
843 roff_node_alloc(struct roff_man
*man
, int line
, int pos
,
844 enum roff_type type
, int tok
)
848 n
= mandoc_calloc(1, sizeof(*n
));
853 n
->sec
= man
->lastsec
;
855 if (man
->flags
& MDOC_SYNOPSIS
)
856 n
->flags
|= NODE_SYNPRETTY
;
858 n
->flags
&= ~NODE_SYNPRETTY
;
859 if (man
->flags
& MDOC_NEWLINE
)
860 n
->flags
|= NODE_LINE
;
861 man
->flags
&= ~MDOC_NEWLINE
;
867 roff_node_append(struct roff_man
*man
, struct roff_node
*n
)
871 case ROFF_NEXT_SIBLING
:
872 if (man
->last
->next
!= NULL
) {
873 n
->next
= man
->last
->next
;
874 man
->last
->next
->prev
= n
;
876 man
->last
->parent
->last
= n
;
879 n
->parent
= man
->last
->parent
;
881 case ROFF_NEXT_CHILD
:
882 if (man
->last
->child
!= NULL
) {
883 n
->next
= man
->last
->child
;
884 man
->last
->child
->prev
= n
;
887 man
->last
->child
= n
;
888 n
->parent
= man
->last
;
900 if (n
->end
!= ENDBODY_NOT
)
912 * Copy over the normalised-data pointer of our parent. Not
913 * everybody has one, but copying a null pointer is fine.
916 n
->norm
= n
->parent
->norm
;
917 assert(n
->parent
->type
== ROFFT_BLOCK
);
921 roff_word_alloc(struct roff_man
*man
, int line
, int pos
, const char *word
)
925 n
= roff_node_alloc(man
, line
, pos
, ROFFT_TEXT
, TOKEN_NONE
);
926 n
->string
= roff_strdup(man
->roff
, word
);
927 roff_node_append(man
, n
);
928 n
->flags
|= NODE_VALID
| NODE_ENDED
;
929 man
->next
= ROFF_NEXT_SIBLING
;
933 roff_word_append(struct roff_man
*man
, const char *word
)
936 char *addstr
, *newstr
;
939 addstr
= roff_strdup(man
->roff
, word
);
940 mandoc_asprintf(&newstr
, "%s %s", n
->string
, addstr
);
944 man
->next
= ROFF_NEXT_SIBLING
;
948 roff_elem_alloc(struct roff_man
*man
, int line
, int pos
, int tok
)
952 n
= roff_node_alloc(man
, line
, pos
, ROFFT_ELEM
, tok
);
953 roff_node_append(man
, n
);
954 man
->next
= ROFF_NEXT_CHILD
;
958 roff_block_alloc(struct roff_man
*man
, int line
, int pos
, int tok
)
962 n
= roff_node_alloc(man
, line
, pos
, ROFFT_BLOCK
, tok
);
963 roff_node_append(man
, n
);
964 man
->next
= ROFF_NEXT_CHILD
;
969 roff_head_alloc(struct roff_man
*man
, int line
, int pos
, int tok
)
973 n
= roff_node_alloc(man
, line
, pos
, ROFFT_HEAD
, tok
);
974 roff_node_append(man
, n
);
975 man
->next
= ROFF_NEXT_CHILD
;
980 roff_body_alloc(struct roff_man
*man
, int line
, int pos
, int tok
)
984 n
= roff_node_alloc(man
, line
, pos
, ROFFT_BODY
, tok
);
985 roff_node_append(man
, n
);
986 man
->next
= ROFF_NEXT_CHILD
;
991 roff_addtbl(struct roff_man
*man
, struct tbl_node
*tbl
)
994 const struct tbl_span
*span
;
996 if (man
->macroset
== MACROSET_MAN
)
997 man_breakscope(man
, ROFF_TS
);
998 while ((span
= tbl_span(tbl
)) != NULL
) {
999 n
= roff_node_alloc(man
, tbl
->line
, 0, ROFFT_TBL
, TOKEN_NONE
);
1001 roff_node_append(man
, n
);
1002 n
->flags
|= NODE_VALID
| NODE_ENDED
;
1003 man
->next
= ROFF_NEXT_SIBLING
;
1008 roff_node_unlink(struct roff_man
*man
, struct roff_node
*n
)
1011 /* Adjust siblings. */
1014 n
->prev
->next
= n
->next
;
1016 n
->next
->prev
= n
->prev
;
1018 /* Adjust parent. */
1020 if (n
->parent
!= NULL
) {
1021 if (n
->parent
->child
== n
)
1022 n
->parent
->child
= n
->next
;
1023 if (n
->parent
->last
== n
)
1024 n
->parent
->last
= n
->prev
;
1027 /* Adjust parse point. */
1031 if (man
->last
== n
) {
1032 if (n
->prev
== NULL
) {
1033 man
->last
= n
->parent
;
1034 man
->next
= ROFF_NEXT_CHILD
;
1036 man
->last
= n
->prev
;
1037 man
->next
= ROFF_NEXT_SIBLING
;
1040 if (man
->first
== n
)
1045 roff_node_free(struct roff_node
*n
)
1048 if (n
->args
!= NULL
)
1049 mdoc_argv_free(n
->args
);
1050 if (n
->type
== ROFFT_BLOCK
|| n
->type
== ROFFT_ELEM
)
1053 eqn_box_free(n
->eqn
);
1059 roff_node_delete(struct roff_man
*man
, struct roff_node
*n
)
1062 while (n
->child
!= NULL
)
1063 roff_node_delete(man
, n
->child
);
1064 roff_node_unlink(man
, n
);
1069 deroff(char **dest
, const struct roff_node
*n
)
1074 if (n
->type
!= ROFFT_TEXT
) {
1075 for (n
= n
->child
; n
!= NULL
; n
= n
->next
)
1080 /* Skip leading whitespace. */
1082 for (cp
= n
->string
; *cp
!= '\0'; cp
++) {
1083 if (cp
[0] == '\\' && cp
[1] != '\0' &&
1084 strchr(" %&0^|~", cp
[1]) != NULL
)
1086 else if ( ! isspace((unsigned char)*cp
))
1090 /* Skip trailing backslash. */
1093 if (sz
> 0 && cp
[sz
- 1] == '\\')
1096 /* Skip trailing whitespace. */
1099 if ( ! isspace((unsigned char)cp
[sz
-1]))
1102 /* Skip empty strings. */
1107 if (*dest
== NULL
) {
1108 *dest
= mandoc_strndup(cp
, sz
);
1112 mandoc_asprintf(&cp
, "%s %*s", *dest
, (int)sz
, cp
);
1117 /* --- main functions of the roff parser ---------------------------------- */
1120 * In the current line, expand escape sequences that tend to get
1121 * used in numerical expressions and conditional requests.
1122 * Also check the syntax of the remaining escape sequences.
1125 roff_res(struct roff
*r
, struct buf
*buf
, int ln
, int pos
)
1127 char ubuf
[24]; /* buffer to print the number */
1128 struct roff_node
*n
; /* used for header comments */
1129 const char *start
; /* start of the string to process */
1130 char *stesc
; /* start of an escape sequence ('\\') */
1131 char *ep
; /* end of comment string */
1132 const char *stnam
; /* start of the name, after "[(*" */
1133 const char *cp
; /* end of the name, e.g. before ']' */
1134 const char *res
; /* the string to be substituted */
1135 char *nbuf
; /* new buffer to copy buf->buf to */
1136 size_t maxl
; /* expected length of the escape name */
1137 size_t naml
; /* actual length of the escape name */
1138 enum mandoc_esc esc
; /* type of the escape sequence */
1139 int inaml
; /* length returned from mandoc_escape() */
1140 int expand_count
; /* to avoid infinite loops */
1141 int npos
; /* position in numeric expression */
1142 int arg_complete
; /* argument not interrupted by eol */
1143 int done
; /* no more input available */
1144 int deftype
; /* type of definition to paste */
1145 int rcsid
; /* kind of RCS id seen */
1146 char sign
; /* increment number register */
1147 char term
; /* character terminating the escape */
1149 /* Search forward for comments. */
1152 start
= buf
->buf
+ pos
;
1153 for (stesc
= buf
->buf
+ pos
; *stesc
!= '\0'; stesc
++) {
1154 if (stesc
[0] != r
->escape
|| stesc
[1] == '\0')
1157 if (*stesc
!= '"' && *stesc
!= '#')
1160 /* Comment found, look for RCS id. */
1163 if ((cp
= strstr(stesc
, "$" "OpenBSD")) != NULL
) {
1164 rcsid
= 1 << MANDOC_OS_OPENBSD
;
1166 } else if ((cp
= strstr(stesc
, "$" "NetBSD")) != NULL
) {
1167 rcsid
= 1 << MANDOC_OS_NETBSD
;
1171 isalnum((unsigned char)*cp
) == 0 &&
1172 strchr(cp
, '$') != NULL
) {
1173 if (r
->man
->meta
.rcsids
& rcsid
)
1174 mandoc_msg(MANDOCERR_RCS_REP
, r
->parse
,
1175 ln
, stesc
+ 1 - buf
->buf
, stesc
+ 1);
1176 r
->man
->meta
.rcsids
|= rcsid
;
1179 /* Handle trailing whitespace. */
1181 ep
= strchr(stesc
--, '\0') - 1;
1186 if (*ep
== ' ' || *ep
== '\t')
1187 mandoc_msg(MANDOCERR_SPACE_EOL
, r
->parse
,
1188 ln
, ep
- buf
->buf
, NULL
);
1191 * Save comments preceding the title macro
1192 * in the syntax tree.
1195 if (r
->format
== 0) {
1196 while (*ep
== ' ' || *ep
== '\t')
1199 n
= roff_node_alloc(r
->man
,
1200 ln
, stesc
+ 1 - buf
->buf
,
1201 ROFFT_COMMENT
, TOKEN_NONE
);
1202 n
->string
= mandoc_strdup(stesc
+ 2);
1203 roff_node_append(r
->man
, n
);
1204 n
->flags
|= NODE_VALID
| NODE_ENDED
;
1205 r
->man
->next
= ROFF_NEXT_SIBLING
;
1208 /* Discard comments. */
1210 while (stesc
> start
&& stesc
[-1] == ' ')
1219 /* Notice the end of the input. */
1221 if (*stesc
== '\n') {
1227 while (stesc
>= start
) {
1229 /* Search backwards for the next backslash. */
1231 if (*stesc
!= r
->escape
) {
1232 if (*stesc
== '\\') {
1234 buf
->sz
= mandoc_asprintf(&nbuf
, "%s\\e%s",
1235 buf
->buf
, stesc
+ 1) + 1;
1237 stesc
= nbuf
+ (stesc
- buf
->buf
);
1245 /* If it is escaped, skip it. */
1247 for (cp
= stesc
- 1; cp
>= start
; cp
--)
1248 if (*cp
!= r
->escape
)
1251 if ((stesc
- cp
) % 2 == 0) {
1255 } else if (stesc
[1] != '\0') {
1265 /* Decide whether to expand or to check only. */
1279 if (sign
== '+' || sign
== '-')
1284 esc
= mandoc_escape(&cp
, &stnam
, &inaml
);
1285 if (esc
== ESCAPE_ERROR
||
1286 (esc
== ESCAPE_SPECIAL
&&
1287 mchars_spec2cp(stnam
, inaml
) < 0))
1288 mandoc_vmsg(MANDOCERR_ESC_BAD
,
1289 r
->parse
, ln
, (int)(stesc
- buf
->buf
),
1290 "%.*s", (int)(cp
- stesc
), stesc
);
1295 if (EXPAND_LIMIT
< ++expand_count
) {
1296 mandoc_msg(MANDOCERR_ROFFLOOP
, r
->parse
,
1297 ln
, (int)(stesc
- buf
->buf
), NULL
);
1302 * The third character decides the length
1303 * of the name of the string or register.
1304 * Save a pointer to the name.
1331 /* Advance to the end of the name. */
1335 while (maxl
== 0 || naml
< maxl
) {
1337 mandoc_msg(MANDOCERR_ESC_BAD
, r
->parse
,
1338 ln
, (int)(stesc
- buf
->buf
), stesc
);
1342 if (maxl
== 0 && *cp
== term
) {
1346 if (*cp
++ != '\\' || stesc
[1] != 'w') {
1350 switch (mandoc_escape(&cp
, NULL
, NULL
)) {
1351 case ESCAPE_SPECIAL
:
1352 case ESCAPE_UNICODE
:
1353 case ESCAPE_NUMBERED
:
1354 case ESCAPE_OVERSTRIKE
:
1363 * Retrieve the replacement string; if it is
1364 * undefined, resume searching for escapes.
1370 deftype
= ROFFDEF_USER
| ROFFDEF_PRE
;
1371 res
= roff_getstrn(r
, stnam
, naml
, &deftype
);
1374 * If not overriden, let \*(.T
1375 * through to the formatters.
1378 if (res
== NULL
&& naml
== 2 &&
1379 stnam
[0] == '.' && stnam
[1] == 'T') {
1380 roff_setstrn(&r
->strtab
,
1381 ".T", 2, NULL
, 0, 0);
1389 ubuf
[0] = arg_complete
&&
1390 roff_evalnum(r
, ln
, stnam
, &npos
,
1391 NULL
, ROFFNUM_SCALE
) &&
1392 stnam
+ npos
+ 1 == cp
? '1' : '0';
1397 (void)snprintf(ubuf
, sizeof(ubuf
), "%d",
1398 roff_getregn(r
, stnam
, naml
, sign
));
1403 /* use even incomplete args */
1404 (void)snprintf(ubuf
, sizeof(ubuf
), "%d",
1410 mandoc_vmsg(MANDOCERR_STR_UNDEF
,
1411 r
->parse
, ln
, (int)(stesc
- buf
->buf
),
1412 "%.*s", (int)naml
, stnam
);
1414 } else if (buf
->sz
+ strlen(res
) > SHRT_MAX
) {
1415 mandoc_msg(MANDOCERR_ROFFLOOP
, r
->parse
,
1416 ln
, (int)(stesc
- buf
->buf
), NULL
);
1420 /* Replace the escape sequence by the string. */
1423 buf
->sz
= mandoc_asprintf(&nbuf
, "%s%s%s",
1424 buf
->buf
, res
, cp
) + 1;
1426 /* Prepare for the next replacement. */
1429 stesc
= nbuf
+ (stesc
- buf
->buf
) + strlen(res
);
1437 * Process text streams.
1440 roff_parsetext(struct roff
*r
, struct buf
*buf
, int pos
, int *offs
)
1446 enum mandoc_esc esc
;
1448 /* Spring the input line trap. */
1450 if (roffit_lines
== 1) {
1451 isz
= mandoc_asprintf(&p
, "%s\n.%s", buf
->buf
, roffit_macro
);
1458 return ROFF_REPARSE
;
1459 } else if (roffit_lines
> 1)
1462 if (roffce_node
!= NULL
&& buf
->buf
[pos
] != '\0') {
1463 if (roffce_lines
< 1) {
1464 r
->man
->last
= roffce_node
;
1465 r
->man
->next
= ROFF_NEXT_SIBLING
;
1472 /* Convert all breakable hyphens into ASCII_HYPH. */
1474 start
= p
= buf
->buf
+ pos
;
1476 while (*p
!= '\0') {
1477 sz
= strcspn(p
, "-\\");
1484 /* Skip over escapes. */
1486 esc
= mandoc_escape((const char **)&p
, NULL
, NULL
);
1487 if (esc
== ESCAPE_ERROR
)
1492 } else if (p
== start
) {
1497 if (isalpha((unsigned char)p
[-1]) &&
1498 isalpha((unsigned char)p
[1]))
1506 roff_parseln(struct roff
*r
, int ln
, struct buf
*buf
, int *offs
)
1510 int pos
; /* parse point */
1511 int spos
; /* saved parse point for messages */
1512 int ppos
; /* original offset in buf->buf */
1513 int ctl
; /* macro line (boolean) */
1517 /* Handle in-line equation delimiters. */
1519 if (r
->tbl
== NULL
&&
1520 r
->last_eqn
!= NULL
&& r
->last_eqn
->delim
&&
1521 (r
->eqn
== NULL
|| r
->eqn_inline
)) {
1522 e
= roff_eqndelim(r
, buf
, pos
);
1523 if (e
== ROFF_REPARSE
)
1525 assert(e
== ROFF_CONT
);
1528 /* Expand some escape sequences. */
1530 e
= roff_res(r
, buf
, ln
, pos
);
1531 if (e
== ROFF_IGN
|| e
== ROFF_APPEND
)
1533 assert(e
== ROFF_CONT
);
1535 ctl
= roff_getcontrol(r
, buf
->buf
, &pos
);
1538 * First, if a scope is open and we're not a macro, pass the
1539 * text through the macro's filter.
1540 * Equations process all content themselves.
1541 * Tables process almost all content themselves, but we want
1542 * to warn about macros before passing it there.
1545 if (r
->last
!= NULL
&& ! ctl
) {
1547 e
= (*roffs
[t
].text
)(r
, t
, buf
, ln
, pos
, pos
, offs
);
1550 assert(e
== ROFF_CONT
);
1552 if (r
->eqn
!= NULL
&& strncmp(buf
->buf
+ ppos
, ".EN", 3)) {
1553 eqn_read(r
->eqn
, buf
->buf
+ ppos
);
1556 if (r
->tbl
!= NULL
&& (ctl
== 0 || buf
->buf
[pos
] == '\0')) {
1557 tbl_read(r
->tbl
, ln
, buf
->buf
, ppos
);
1558 roff_addtbl(r
->man
, r
->tbl
);
1562 return roff_parsetext(r
, buf
, pos
, offs
);
1564 /* Skip empty request lines. */
1566 if (buf
->buf
[pos
] == '"') {
1567 mandoc_msg(MANDOCERR_COMMENT_BAD
, r
->parse
,
1570 } else if (buf
->buf
[pos
] == '\0')
1574 * If a scope is open, go to the child handler for that macro,
1575 * as it may want to preprocess before doing anything with it.
1576 * Don't do so if an equation is open.
1581 return (*roffs
[t
].sub
)(r
, t
, buf
, ln
, ppos
, pos
, offs
);
1584 /* No scope is open. This is a new request or macro. */
1587 t
= roff_parse(r
, buf
->buf
, &pos
, ln
, ppos
);
1589 /* Tables ignore most macros. */
1591 if (r
->tbl
!= NULL
&& (t
== TOKEN_NONE
|| t
== ROFF_TS
||
1592 t
== ROFF_br
|| t
== ROFF_ce
|| t
== ROFF_rj
|| t
== ROFF_sp
)) {
1593 mandoc_msg(MANDOCERR_TBLMACRO
, r
->parse
,
1594 ln
, pos
, buf
->buf
+ spos
);
1595 if (t
!= TOKEN_NONE
)
1597 while (buf
->buf
[pos
] != '\0' && buf
->buf
[pos
] != ' ')
1599 while (buf
->buf
[pos
] == ' ')
1601 tbl_read(r
->tbl
, ln
, buf
->buf
, pos
);
1602 roff_addtbl(r
->man
, r
->tbl
);
1606 /* For now, let high level macros abort .ce mode. */
1608 if (ctl
&& roffce_node
!= NULL
&&
1609 (t
== TOKEN_NONE
|| t
== ROFF_Dd
|| t
== ROFF_EQ
||
1610 t
== ROFF_TH
|| t
== ROFF_TS
)) {
1611 r
->man
->last
= roffce_node
;
1612 r
->man
->next
= ROFF_NEXT_SIBLING
;
1618 * This is neither a roff request nor a user-defined macro.
1619 * Let the standard macro set parsers handle it.
1622 if (t
== TOKEN_NONE
)
1625 /* Execute a roff request or a user defined macro. */
1627 return (*roffs
[t
].proc
)(r
, t
, buf
, ln
, spos
, pos
, offs
);
1631 roff_endparse(struct roff
*r
)
1633 if (r
->last
!= NULL
)
1634 mandoc_msg(MANDOCERR_BLK_NOEND
, r
->parse
,
1635 r
->last
->line
, r
->last
->col
,
1636 roff_name
[r
->last
->tok
]);
1638 if (r
->eqn
!= NULL
) {
1639 mandoc_msg(MANDOCERR_BLK_NOEND
, r
->parse
,
1640 r
->eqn
->node
->line
, r
->eqn
->node
->pos
, "EQ");
1645 if (r
->tbl
!= NULL
) {
1646 mandoc_msg(MANDOCERR_BLK_NOEND
, r
->parse
,
1647 r
->tbl
->line
, r
->tbl
->pos
, "TS");
1654 * Parse a roff node's type from the input buffer. This must be in the
1655 * form of ".foo xxx" in the usual way.
1657 static enum roff_tok
1658 roff_parse(struct roff
*r
, char *buf
, int *pos
, int ln
, int ppos
)
1668 if ('\0' == *cp
|| '"' == *cp
|| '\t' == *cp
|| ' ' == *cp
)
1672 maclen
= roff_getname(r
, &cp
, ln
, ppos
);
1674 deftype
= ROFFDEF_USER
| ROFFDEF_REN
;
1675 r
->current_string
= roff_getstrn(r
, mac
, maclen
, &deftype
);
1684 t
= roffhash_find(r
->reqtab
, mac
, maclen
);
1687 if (t
!= TOKEN_NONE
)
1689 else if (deftype
== ROFFDEF_UNDEF
) {
1690 /* Using an undefined macro defines it to be empty. */
1691 roff_setstrn(&r
->strtab
, mac
, maclen
, "", 0, 0);
1692 roff_setstrn(&r
->rentab
, mac
, maclen
, NULL
, 0, 0);
1697 /* --- handling of request blocks ----------------------------------------- */
1700 roff_cblock(ROFF_ARGS
)
1704 * A block-close `..' should only be invoked as a child of an
1705 * ignore macro, otherwise raise a warning and just ignore it.
1708 if (r
->last
== NULL
) {
1709 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, r
->parse
,
1714 switch (r
->last
->tok
) {
1716 /* ROFF_am1 is remapped to ROFF_am in roff_block(). */
1719 /* ROFF_de1 is remapped to ROFF_de in roff_block(). */
1724 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, r
->parse
,
1729 if (buf
->buf
[pos
] != '\0')
1730 mandoc_vmsg(MANDOCERR_ARG_SKIP
, r
->parse
, ln
, pos
,
1731 ".. %s", buf
->buf
+ pos
);
1734 roffnode_cleanscope(r
);
1740 roffnode_cleanscope(struct roff
*r
)
1744 if (--r
->last
->endspan
!= 0)
1751 roff_ccond(struct roff
*r
, int ln
, int ppos
)
1754 if (NULL
== r
->last
) {
1755 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, r
->parse
,
1760 switch (r
->last
->tok
) {
1766 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, r
->parse
,
1771 if (r
->last
->endspan
> -1) {
1772 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, r
->parse
,
1778 roffnode_cleanscope(r
);
1783 roff_block(ROFF_ARGS
)
1785 const char *name
, *value
;
1786 char *call
, *cp
, *iname
, *rname
;
1787 size_t csz
, namesz
, rsz
;
1790 /* Ignore groff compatibility mode for now. */
1792 if (tok
== ROFF_de1
)
1794 else if (tok
== ROFF_dei1
)
1796 else if (tok
== ROFF_am1
)
1798 else if (tok
== ROFF_ami1
)
1801 /* Parse the macro name argument. */
1803 cp
= buf
->buf
+ pos
;
1804 if (tok
== ROFF_ig
) {
1809 namesz
= roff_getname(r
, &cp
, ln
, ppos
);
1810 iname
[namesz
] = '\0';
1813 /* Resolve the macro name argument if it is indirect. */
1815 if (namesz
&& (tok
== ROFF_dei
|| tok
== ROFF_ami
)) {
1816 deftype
= ROFFDEF_USER
;
1817 name
= roff_getstrn(r
, iname
, namesz
, &deftype
);
1819 mandoc_vmsg(MANDOCERR_STR_UNDEF
,
1820 r
->parse
, ln
, (int)(iname
- buf
->buf
),
1821 "%.*s", (int)namesz
, iname
);
1824 namesz
= strlen(name
);
1828 if (namesz
== 0 && tok
!= ROFF_ig
) {
1829 mandoc_msg(MANDOCERR_REQ_EMPTY
, r
->parse
,
1830 ln
, ppos
, roff_name
[tok
]);
1834 roffnode_push(r
, tok
, name
, ln
, ppos
);
1837 * At the beginning of a `de' macro, clear the existing string
1838 * with the same name, if there is one. New content will be
1839 * appended from roff_block_text() in multiline mode.
1842 if (tok
== ROFF_de
|| tok
== ROFF_dei
) {
1843 roff_setstrn(&r
->strtab
, name
, namesz
, "", 0, 0);
1844 roff_setstrn(&r
->rentab
, name
, namesz
, NULL
, 0, 0);
1845 } else if (tok
== ROFF_am
|| tok
== ROFF_ami
) {
1846 deftype
= ROFFDEF_ANY
;
1847 value
= roff_getstrn(r
, iname
, namesz
, &deftype
);
1848 switch (deftype
) { /* Before appending, ... */
1849 case ROFFDEF_PRE
: /* copy predefined to user-defined. */
1850 roff_setstrn(&r
->strtab
, name
, namesz
,
1851 value
, strlen(value
), 0);
1853 case ROFFDEF_REN
: /* call original standard macro. */
1854 csz
= mandoc_asprintf(&call
, ".%.*s \\$* \\\"\n",
1855 (int)strlen(value
), value
);
1856 roff_setstrn(&r
->strtab
, name
, namesz
, call
, csz
, 0);
1857 roff_setstrn(&r
->rentab
, name
, namesz
, NULL
, 0, 0);
1860 case ROFFDEF_STD
: /* rename and call standard macro. */
1861 rsz
= mandoc_asprintf(&rname
, "__%s_renamed", name
);
1862 roff_setstrn(&r
->rentab
, rname
, rsz
, name
, namesz
, 0);
1863 csz
= mandoc_asprintf(&call
, ".%.*s \\$* \\\"\n",
1865 roff_setstrn(&r
->strtab
, name
, namesz
, call
, csz
, 0);
1877 /* Get the custom end marker. */
1880 namesz
= roff_getname(r
, &cp
, ln
, ppos
);
1882 /* Resolve the end marker if it is indirect. */
1884 if (namesz
&& (tok
== ROFF_dei
|| tok
== ROFF_ami
)) {
1885 deftype
= ROFFDEF_USER
;
1886 name
= roff_getstrn(r
, iname
, namesz
, &deftype
);
1888 mandoc_vmsg(MANDOCERR_STR_UNDEF
,
1889 r
->parse
, ln
, (int)(iname
- buf
->buf
),
1890 "%.*s", (int)namesz
, iname
);
1893 namesz
= strlen(name
);
1898 r
->last
->end
= mandoc_strndup(name
, namesz
);
1901 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, r
->parse
,
1902 ln
, pos
, ".%s ... %s", roff_name
[tok
], cp
);
1908 roff_block_sub(ROFF_ARGS
)
1914 * First check whether a custom macro exists at this level. If
1915 * it does, then check against it. This is some of groff's
1916 * stranger behaviours. If we encountered a custom end-scope
1917 * tag and that tag also happens to be a "real" macro, then we
1918 * need to try interpreting it again as a real macro. If it's
1919 * not, then return ignore. Else continue.
1923 for (i
= pos
, j
= 0; r
->last
->end
[j
]; j
++, i
++)
1924 if (buf
->buf
[i
] != r
->last
->end
[j
])
1927 if (r
->last
->end
[j
] == '\0' &&
1928 (buf
->buf
[i
] == '\0' ||
1929 buf
->buf
[i
] == ' ' ||
1930 buf
->buf
[i
] == '\t')) {
1932 roffnode_cleanscope(r
);
1934 while (buf
->buf
[i
] == ' ' || buf
->buf
[i
] == '\t')
1938 if (roff_parse(r
, buf
->buf
, &pos
, ln
, ppos
) !=
1946 * If we have no custom end-query or lookup failed, then try
1947 * pulling it out of the hashtable.
1950 t
= roff_parse(r
, buf
->buf
, &pos
, ln
, ppos
);
1952 if (t
!= ROFF_cblock
) {
1954 roff_setstr(r
, r
->last
->name
, buf
->buf
+ ppos
, 2);
1958 return (*roffs
[t
].proc
)(r
, t
, buf
, ln
, ppos
, pos
, offs
);
1962 roff_block_text(ROFF_ARGS
)
1966 roff_setstr(r
, r
->last
->name
, buf
->buf
+ pos
, 2);
1972 roff_cond_sub(ROFF_ARGS
)
1979 roffnode_cleanscope(r
);
1982 * If `\}' occurs on a macro line without a preceding macro,
1983 * drop the line completely.
1986 ep
= buf
->buf
+ pos
;
1987 if (ep
[0] == '\\' && ep
[1] == '}')
1990 /* Always check for the closing delimiter `\}'. */
1992 while ((ep
= strchr(ep
, '\\')) != NULL
) {
1995 memmove(ep
, ep
+ 2, strlen(ep
+ 2) + 1);
1996 roff_ccond(r
, ln
, ep
- buf
->buf
);
2008 * Fully handle known macros when they are structurally
2009 * required or when the conditional evaluated to true.
2012 t
= roff_parse(r
, buf
->buf
, &pos
, ln
, ppos
);
2013 return t
!= TOKEN_NONE
&& (rr
|| roffs
[t
].flags
& ROFFMAC_STRUCT
)
2014 ? (*roffs
[t
].proc
)(r
, t
, buf
, ln
, ppos
, pos
, offs
) : rr
2015 ? ROFF_CONT
: ROFF_IGN
;
2019 roff_cond_text(ROFF_ARGS
)
2025 roffnode_cleanscope(r
);
2027 ep
= buf
->buf
+ pos
;
2028 while ((ep
= strchr(ep
, '\\')) != NULL
) {
2029 if (*(++ep
) == '}') {
2031 roff_ccond(r
, ln
, ep
- buf
->buf
- 1);
2036 return rr
? ROFF_CONT
: ROFF_IGN
;
2039 /* --- handling of numeric and conditional expressions -------------------- */
2042 * Parse a single signed integer number. Stop at the first non-digit.
2043 * If there is at least one digit, return success and advance the
2044 * parse point, else return failure and let the parse point unchanged.
2045 * Ignore overflows, treat them just like the C language.
2048 roff_getnum(const char *v
, int *pos
, int *res
, int flags
)
2050 int myres
, scaled
, n
, p
;
2057 if (n
|| v
[p
] == '+')
2060 if (flags
& ROFFNUM_WHITE
)
2061 while (isspace((unsigned char)v
[p
]))
2064 for (*res
= 0; isdigit((unsigned char)v
[p
]); p
++)
2065 *res
= 10 * *res
+ v
[p
] - '0';
2072 /* Each number may be followed by one optional scaling unit. */
2076 scaled
= *res
* 65536;
2079 scaled
= *res
* 240;
2082 scaled
= *res
* 240 / 2.54;
2093 scaled
= *res
* 10 / 3;
2099 scaled
= *res
* 6 / 25;
2106 if (flags
& ROFFNUM_SCALE
)
2114 * Evaluate a string comparison condition.
2115 * The first character is the delimiter.
2116 * Succeed if the string up to its second occurrence
2117 * matches the string up to its third occurence.
2118 * Advance the cursor after the third occurrence
2119 * or lacking that, to the end of the line.
2122 roff_evalstrcond(const char *v
, int *pos
)
2124 const char *s1
, *s2
, *s3
;
2128 s1
= v
+ *pos
; /* initial delimiter */
2129 s2
= s1
+ 1; /* for scanning the first string */
2130 s3
= strchr(s2
, *s1
); /* for scanning the second string */
2132 if (NULL
== s3
) /* found no middle delimiter */
2135 while ('\0' != *++s3
) {
2136 if (*s2
!= *s3
) { /* mismatch */
2137 s3
= strchr(s3
, *s1
);
2140 if (*s3
== *s1
) { /* found the final delimiter */
2149 s3
= strchr(s2
, '\0');
2150 else if (*s3
!= '\0')
2157 * Evaluate an optionally negated single character, numerical,
2158 * or string condition.
2161 roff_evalcond(struct roff
*r
, int ln
, char *v
, int *pos
)
2165 int deftype
, number
, savepos
, istrue
, wanttrue
;
2167 if ('!' == v
[*pos
]) {
2192 sz
= roff_getname(r
, &cp
, ln
, cp
- v
);
2195 else if (v
[*pos
] == 'r')
2196 istrue
= roff_hasregn(r
, name
, sz
);
2198 deftype
= ROFFDEF_ANY
;
2199 roff_getstrn(r
, name
, sz
, &deftype
);
2203 return istrue
== wanttrue
;
2209 if (roff_evalnum(r
, ln
, v
, pos
, &number
, ROFFNUM_SCALE
))
2210 return (number
> 0) == wanttrue
;
2211 else if (*pos
== savepos
)
2212 return roff_evalstrcond(v
, pos
) == wanttrue
;
2218 roff_line_ignore(ROFF_ARGS
)
2225 roff_insec(ROFF_ARGS
)
2228 mandoc_msg(MANDOCERR_REQ_INSEC
, r
->parse
,
2229 ln
, ppos
, roff_name
[tok
]);
2234 roff_unsupp(ROFF_ARGS
)
2237 mandoc_msg(MANDOCERR_REQ_UNSUPP
, r
->parse
,
2238 ln
, ppos
, roff_name
[tok
]);
2243 roff_cond(ROFF_ARGS
)
2246 roffnode_push(r
, tok
, NULL
, ln
, ppos
);
2249 * An `.el' has no conditional body: it will consume the value
2250 * of the current rstack entry set in prior `ie' calls or
2253 * If we're not an `el', however, then evaluate the conditional.
2256 r
->last
->rule
= tok
== ROFF_el
?
2257 (r
->rstackpos
< 0 ? 0 : r
->rstack
[r
->rstackpos
--]) :
2258 roff_evalcond(r
, ln
, buf
->buf
, &pos
);
2261 * An if-else will put the NEGATION of the current evaluated
2262 * conditional into the stack of rules.
2265 if (tok
== ROFF_ie
) {
2266 if (r
->rstackpos
+ 1 == r
->rstacksz
) {
2268 r
->rstack
= mandoc_reallocarray(r
->rstack
,
2269 r
->rstacksz
, sizeof(int));
2271 r
->rstack
[++r
->rstackpos
] = !r
->last
->rule
;
2274 /* If the parent has false as its rule, then so do we. */
2276 if (r
->last
->parent
&& !r
->last
->parent
->rule
)
2281 * If there is nothing on the line after the conditional,
2282 * not even whitespace, use next-line scope.
2285 if (buf
->buf
[pos
] == '\0') {
2286 r
->last
->endspan
= 2;
2290 while (buf
->buf
[pos
] == ' ')
2293 /* An opening brace requests multiline scope. */
2295 if (buf
->buf
[pos
] == '\\' && buf
->buf
[pos
+ 1] == '{') {
2296 r
->last
->endspan
= -1;
2298 while (buf
->buf
[pos
] == ' ')
2304 * Anything else following the conditional causes
2305 * single-line scope. Warn if the scope contains
2306 * nothing but trailing whitespace.
2309 if (buf
->buf
[pos
] == '\0')
2310 mandoc_msg(MANDOCERR_COND_EMPTY
, r
->parse
,
2311 ln
, ppos
, roff_name
[tok
]);
2313 r
->last
->endspan
= 1;
2327 /* Ignore groff compatibility mode for now. */
2329 if (tok
== ROFF_ds1
)
2331 else if (tok
== ROFF_as1
)
2335 * The first word is the name of the string.
2336 * If it is empty or terminated by an escape sequence,
2337 * abort the `ds' request without defining anything.
2340 name
= string
= buf
->buf
+ pos
;
2344 namesz
= roff_getname(r
, &string
, ln
, pos
);
2345 if (name
[namesz
] == '\\')
2348 /* Read past the initial double-quote, if any. */
2352 /* The rest is the value. */
2353 roff_setstrn(&r
->strtab
, name
, namesz
, string
, strlen(string
),
2355 roff_setstrn(&r
->rentab
, name
, namesz
, NULL
, 0, 0);
2360 * Parse a single operator, one or two characters long.
2361 * If the operator is recognized, return success and advance the
2362 * parse point, else return failure and let the parse point unchanged.
2365 roff_getop(const char *v
, int *pos
, char *res
)
2380 switch (v
[*pos
+ 1]) {
2398 switch (v
[*pos
+ 1]) {
2412 if ('=' == v
[*pos
+ 1])
2424 * Evaluate either a parenthesized numeric expression
2425 * or a single signed integer number.
2428 roff_evalpar(struct roff
*r
, int ln
,
2429 const char *v
, int *pos
, int *res
, int flags
)
2433 return roff_getnum(v
, pos
, res
, flags
);
2436 if ( ! roff_evalnum(r
, ln
, v
, pos
, res
, flags
| ROFFNUM_WHITE
))
2440 * Omission of the closing parenthesis
2441 * is an error in validation mode,
2442 * but ignored in evaluation mode.
2447 else if (NULL
== res
)
2454 * Evaluate a complete numeric expression.
2455 * Proceed left to right, there is no concept of precedence.
2458 roff_evalnum(struct roff
*r
, int ln
, const char *v
,
2459 int *pos
, int *res
, int flags
)
2461 int mypos
, operand2
;
2469 if (flags
& ROFFNUM_WHITE
)
2470 while (isspace((unsigned char)v
[*pos
]))
2473 if ( ! roff_evalpar(r
, ln
, v
, pos
, res
, flags
))
2477 if (flags
& ROFFNUM_WHITE
)
2478 while (isspace((unsigned char)v
[*pos
]))
2481 if ( ! roff_getop(v
, pos
, &operator))
2484 if (flags
& ROFFNUM_WHITE
)
2485 while (isspace((unsigned char)v
[*pos
]))
2488 if ( ! roff_evalpar(r
, ln
, v
, pos
, &operand2
, flags
))
2491 if (flags
& ROFFNUM_WHITE
)
2492 while (isspace((unsigned char)v
[*pos
]))
2509 if (operand2
== 0) {
2510 mandoc_msg(MANDOCERR_DIVZERO
,
2511 r
->parse
, ln
, *pos
, v
);
2518 if (operand2
== 0) {
2519 mandoc_msg(MANDOCERR_DIVZERO
,
2520 r
->parse
, ln
, *pos
, v
);
2527 *res
= *res
< operand2
;
2530 *res
= *res
> operand2
;
2533 *res
= *res
<= operand2
;
2536 *res
= *res
>= operand2
;
2539 *res
= *res
== operand2
;
2542 *res
= *res
!= operand2
;
2545 *res
= *res
&& operand2
;
2548 *res
= *res
|| operand2
;
2551 if (operand2
< *res
)
2555 if (operand2
> *res
)
2565 /* --- register management ------------------------------------------------ */
2568 roff_setreg(struct roff
*r
, const char *name
, int val
, char sign
)
2570 roff_setregn(r
, name
, strlen(name
), val
, sign
, INT_MIN
);
2574 roff_setregn(struct roff
*r
, const char *name
, size_t len
,
2575 int val
, char sign
, int step
)
2577 struct roffreg
*reg
;
2579 /* Search for an existing register with the same name. */
2582 while (reg
!= NULL
&& (reg
->key
.sz
!= len
||
2583 strncmp(reg
->key
.p
, name
, len
) != 0))
2587 /* Create a new register. */
2588 reg
= mandoc_malloc(sizeof(struct roffreg
));
2589 reg
->key
.p
= mandoc_strndup(name
, len
);
2593 reg
->next
= r
->regtab
;
2599 else if ('-' == sign
)
2603 if (step
!= INT_MIN
)
2608 * Handle some predefined read-only number registers.
2609 * For now, return -1 if the requested register is not predefined;
2610 * in case a predefined read-only register having the value -1
2611 * were to turn up, another special value would have to be chosen.
2614 roff_getregro(const struct roff
*r
, const char *name
)
2618 case '$': /* Number of arguments of the last macro evaluated. */
2620 case 'A': /* ASCII approximation mode is always off. */
2622 case 'g': /* Groff compatibility mode is always on. */
2624 case 'H': /* Fixed horizontal resolution. */
2626 case 'j': /* Always adjust left margin only. */
2628 case 'T': /* Some output device is always defined. */
2630 case 'V': /* Fixed vertical resolution. */
2638 roff_getreg(struct roff
*r
, const char *name
)
2640 return roff_getregn(r
, name
, strlen(name
), '\0');
2644 roff_getregn(struct roff
*r
, const char *name
, size_t len
, char sign
)
2646 struct roffreg
*reg
;
2649 if ('.' == name
[0] && 2 == len
) {
2650 val
= roff_getregro(r
, name
+ 1);
2655 for (reg
= r
->regtab
; reg
; reg
= reg
->next
) {
2656 if (len
== reg
->key
.sz
&&
2657 0 == strncmp(name
, reg
->key
.p
, len
)) {
2660 reg
->val
+= reg
->step
;
2663 reg
->val
-= reg
->step
;
2672 roff_setregn(r
, name
, len
, 0, '\0', INT_MIN
);
2677 roff_hasregn(const struct roff
*r
, const char *name
, size_t len
)
2679 struct roffreg
*reg
;
2682 if ('.' == name
[0] && 2 == len
) {
2683 val
= roff_getregro(r
, name
+ 1);
2688 for (reg
= r
->regtab
; reg
; reg
= reg
->next
)
2689 if (len
== reg
->key
.sz
&&
2690 0 == strncmp(name
, reg
->key
.p
, len
))
2697 roff_freereg(struct roffreg
*reg
)
2699 struct roffreg
*old_reg
;
2701 while (NULL
!= reg
) {
2712 char *key
, *val
, *step
;
2717 key
= val
= buf
->buf
+ pos
;
2721 keysz
= roff_getname(r
, &val
, ln
, pos
);
2722 if (key
[keysz
] == '\\')
2726 if (sign
== '+' || sign
== '-')
2730 if (roff_evalnum(r
, ln
, val
, &len
, &iv
, ROFFNUM_SCALE
) == 0)
2734 while (isspace((unsigned char)*step
))
2736 if (roff_evalnum(r
, ln
, step
, NULL
, &is
, 0) == 0)
2739 roff_setregn(r
, key
, keysz
, iv
, sign
, is
);
2746 struct roffreg
*reg
, **prev
;
2750 name
= cp
= buf
->buf
+ pos
;
2753 namesz
= roff_getname(r
, &cp
, ln
, pos
);
2754 name
[namesz
] = '\0';
2759 if (reg
== NULL
|| !strcmp(name
, reg
->key
.p
))
2771 /* --- handler functions for roff requests -------------------------------- */
2780 cp
= buf
->buf
+ pos
;
2781 while (*cp
!= '\0') {
2783 namesz
= roff_getname(r
, &cp
, ln
, (int)(cp
- buf
->buf
));
2784 roff_setstrn(&r
->strtab
, name
, namesz
, NULL
, 0, 0);
2785 roff_setstrn(&r
->rentab
, name
, namesz
, NULL
, 0, 0);
2786 if (name
[namesz
] == '\\')
2797 /* Parse the number of lines. */
2799 if ( ! roff_evalnum(r
, ln
, buf
->buf
, &pos
, &iv
, 0)) {
2800 mandoc_msg(MANDOCERR_IT_NONUM
, r
->parse
,
2801 ln
, ppos
, buf
->buf
+ 1);
2805 while (isspace((unsigned char)buf
->buf
[pos
]))
2809 * Arm the input line trap.
2810 * Special-casing "an-trap" is an ugly workaround to cope
2811 * with DocBook stupidly fiddling with man(7) internals.
2815 roffit_macro
= mandoc_strdup(iv
!= 1 ||
2816 strcmp(buf
->buf
+ pos
, "an-trap") ?
2817 buf
->buf
+ pos
: "br");
2825 enum roff_tok t
, te
;
2832 r
->format
= MPARSE_MDOC
;
2833 mask
= MPARSE_MDOC
| MPARSE_QUICK
;
2839 r
->format
= MPARSE_MAN
;
2840 mask
= MPARSE_QUICK
;
2845 if ((r
->options
& mask
) == 0)
2846 for (t
= tok
; t
< te
; t
++)
2847 roff_setstr(r
, roff_name
[t
], NULL
, 0);
2854 if (r
->tbl
== NULL
) {
2855 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, r
->parse
,
2859 if (tbl_end(r
->tbl
) == 0) {
2862 buf
->buf
= mandoc_strdup(".sp");
2865 return ROFF_REPARSE
;
2876 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, r
->parse
,
2879 tbl_restart(ln
, ppos
, r
->tbl
);
2885 * Handle in-line equation delimiters.
2888 roff_eqndelim(struct roff
*r
, struct buf
*buf
, int pos
)
2891 const char *bef_pr
, *bef_nl
, *mac
, *aft_nl
, *aft_pr
;
2894 * Outside equations, look for an opening delimiter.
2895 * If we are inside an equation, we already know it is
2896 * in-line, or this function wouldn't have been called;
2897 * so look for a closing delimiter.
2900 cp1
= buf
->buf
+ pos
;
2901 cp2
= strchr(cp1
, r
->eqn
== NULL
?
2902 r
->last_eqn
->odelim
: r
->last_eqn
->cdelim
);
2907 bef_pr
= bef_nl
= aft_nl
= aft_pr
= "";
2909 /* Handle preceding text, protecting whitespace. */
2911 if (*buf
->buf
!= '\0') {
2918 * Prepare replacing the delimiter with an equation macro
2919 * and drop leading white space from the equation.
2922 if (r
->eqn
== NULL
) {
2929 /* Handle following text, protecting whitespace. */
2937 /* Do the actual replacement. */
2939 buf
->sz
= mandoc_asprintf(&cp1
, "%s%s%s%s%s%s%s", buf
->buf
,
2940 bef_pr
, bef_nl
, mac
, aft_nl
, aft_pr
, cp2
) + 1;
2944 /* Toggle the in-line state of the eqn subsystem. */
2946 r
->eqn_inline
= r
->eqn
== NULL
;
2947 return ROFF_REPARSE
;
2953 struct roff_node
*n
;
2955 if (r
->man
->macroset
== MACROSET_MAN
)
2956 man_breakscope(r
->man
, ROFF_EQ
);
2957 n
= roff_node_alloc(r
->man
, ln
, ppos
, ROFFT_EQN
, TOKEN_NONE
);
2958 if (ln
> r
->man
->last
->line
)
2959 n
->flags
|= NODE_LINE
;
2960 n
->eqn
= mandoc_calloc(1, sizeof(*n
->eqn
));
2961 n
->eqn
->expectargs
= UINT_MAX
;
2962 roff_node_append(r
->man
, n
);
2963 r
->man
->next
= ROFF_NEXT_SIBLING
;
2965 assert(r
->eqn
== NULL
);
2966 if (r
->last_eqn
== NULL
)
2967 r
->last_eqn
= eqn_alloc(r
->parse
);
2969 eqn_reset(r
->last_eqn
);
2970 r
->eqn
= r
->last_eqn
;
2973 if (buf
->buf
[pos
] != '\0')
2974 mandoc_vmsg(MANDOCERR_ARG_SKIP
, r
->parse
, ln
, pos
,
2975 ".EQ %s", buf
->buf
+ pos
);
2983 if (r
->eqn
!= NULL
) {
2987 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, r
->parse
, ln
, ppos
, "EN");
2988 if (buf
->buf
[pos
] != '\0')
2989 mandoc_vmsg(MANDOCERR_ARG_SKIP
, r
->parse
, ln
, pos
,
2990 "EN %s", buf
->buf
+ pos
);
2997 if (r
->tbl
!= NULL
) {
2998 mandoc_msg(MANDOCERR_BLK_BROKEN
, r
->parse
,
2999 ln
, ppos
, "TS breaks TS");
3002 r
->tbl
= tbl_alloc(ppos
, ln
, r
->parse
);
3004 r
->last_tbl
->next
= r
->tbl
;
3006 r
->first_tbl
= r
->tbl
;
3007 r
->last_tbl
= r
->tbl
;
3012 roff_onearg(ROFF_ARGS
)
3014 struct roff_node
*n
;
3018 if (r
->man
->flags
& (MAN_BLINE
| MAN_ELINE
) &&
3019 (tok
== ROFF_ce
|| tok
== ROFF_rj
|| tok
== ROFF_sp
||
3021 man_breakscope(r
->man
, tok
);
3023 if (roffce_node
!= NULL
&& (tok
== ROFF_ce
|| tok
== ROFF_rj
)) {
3024 r
->man
->last
= roffce_node
;
3025 r
->man
->next
= ROFF_NEXT_SIBLING
;
3028 roff_elem_alloc(r
->man
, ln
, ppos
, tok
);
3031 cp
= buf
->buf
+ pos
;
3033 while (*cp
!= '\0' && *cp
!= ' ')
3038 mandoc_vmsg(MANDOCERR_ARG_EXCESS
,
3039 r
->parse
, ln
, cp
- buf
->buf
,
3040 "%s ... %s", roff_name
[tok
], cp
);
3041 roff_word_alloc(r
->man
, ln
, pos
, buf
->buf
+ pos
);
3044 if (tok
== ROFF_ce
|| tok
== ROFF_rj
) {
3045 if (r
->man
->last
->type
== ROFFT_ELEM
) {
3046 roff_word_alloc(r
->man
, ln
, pos
, "1");
3047 r
->man
->last
->flags
|= NODE_NOSRC
;
3050 if (roff_evalnum(r
, ln
, r
->man
->last
->string
, &npos
,
3051 &roffce_lines
, 0) == 0) {
3052 mandoc_vmsg(MANDOCERR_CE_NONUM
,
3053 r
->parse
, ln
, pos
, "ce %s", buf
->buf
+ pos
);
3056 if (roffce_lines
< 1) {
3057 r
->man
->last
= r
->man
->last
->parent
;
3061 roffce_node
= r
->man
->last
->parent
;
3063 n
->flags
|= NODE_VALID
| NODE_ENDED
;
3066 n
->flags
|= NODE_LINE
;
3067 r
->man
->next
= ROFF_NEXT_SIBLING
;
3072 roff_manyarg(ROFF_ARGS
)
3074 struct roff_node
*n
;
3077 roff_elem_alloc(r
->man
, ln
, ppos
, tok
);
3080 for (sp
= ep
= buf
->buf
+ pos
; *sp
!= '\0'; sp
= ep
) {
3081 while (*ep
!= '\0' && *ep
!= ' ')
3085 roff_word_alloc(r
->man
, ln
, sp
- buf
->buf
, sp
);
3088 n
->flags
|= NODE_LINE
| NODE_VALID
| NODE_ENDED
;
3090 r
->man
->next
= ROFF_NEXT_SIBLING
;
3097 char *oldn
, *newn
, *end
, *value
;
3098 size_t oldsz
, newsz
, valsz
;
3100 newn
= oldn
= buf
->buf
+ pos
;
3104 newsz
= roff_getname(r
, &oldn
, ln
, pos
);
3105 if (newn
[newsz
] == '\\' || *oldn
== '\0')
3109 oldsz
= roff_getname(r
, &end
, ln
, oldn
- buf
->buf
);
3113 valsz
= mandoc_asprintf(&value
, ".%.*s \\$*\\\"\n",
3115 roff_setstrn(&r
->strtab
, newn
, newsz
, value
, valsz
, 0);
3116 roff_setstrn(&r
->rentab
, newn
, newsz
, NULL
, 0, 0);
3124 if (r
->man
->flags
& (MAN_BLINE
| MAN_ELINE
))
3125 man_breakscope(r
->man
, ROFF_br
);
3126 roff_elem_alloc(r
->man
, ln
, ppos
, ROFF_br
);
3127 if (buf
->buf
[pos
] != '\0')
3128 mandoc_vmsg(MANDOCERR_ARG_SKIP
, r
->parse
, ln
, pos
,
3129 "%s %s", roff_name
[tok
], buf
->buf
+ pos
);
3130 r
->man
->last
->flags
|= NODE_LINE
| NODE_VALID
| NODE_ENDED
;
3131 r
->man
->next
= ROFF_NEXT_SIBLING
;
3142 if (*p
== '\0' || (r
->control
= *p
++) == '.')
3146 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, r
->parse
,
3147 ln
, p
- buf
->buf
, "cc ... %s", p
);
3163 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, r
->parse
,
3164 ln
, p
- buf
->buf
, "ec ... %s", p
);
3173 if (buf
->buf
[pos
] != '\0')
3174 mandoc_vmsg(MANDOCERR_ARG_SKIP
, r
->parse
,
3175 ln
, pos
, "eo %s", buf
->buf
+ pos
);
3182 while (buf
->buf
[pos
] == ' ')
3191 const char *p
, *first
, *second
;
3193 enum mandoc_esc esc
;
3198 mandoc_msg(MANDOCERR_REQ_EMPTY
, r
->parse
, ln
, ppos
, "tr");
3202 while (*p
!= '\0') {
3206 if (*first
== '\\') {
3207 esc
= mandoc_escape(&p
, NULL
, NULL
);
3208 if (esc
== ESCAPE_ERROR
) {
3209 mandoc_msg(MANDOCERR_ESC_BAD
, r
->parse
,
3210 ln
, (int)(p
- buf
->buf
), first
);
3213 fsz
= (size_t)(p
- first
);
3217 if (*second
== '\\') {
3218 esc
= mandoc_escape(&p
, NULL
, NULL
);
3219 if (esc
== ESCAPE_ERROR
) {
3220 mandoc_msg(MANDOCERR_ESC_BAD
, r
->parse
,
3221 ln
, (int)(p
- buf
->buf
), second
);
3224 ssz
= (size_t)(p
- second
);
3225 } else if (*second
== '\0') {
3226 mandoc_vmsg(MANDOCERR_TR_ODD
, r
->parse
,
3227 ln
, first
- buf
->buf
, "tr %s", first
);
3233 roff_setstrn(&r
->xmbtab
, first
, fsz
,
3238 if (r
->xtab
== NULL
)
3239 r
->xtab
= mandoc_calloc(128,
3240 sizeof(struct roffstr
));
3242 free(r
->xtab
[(int)*first
].p
);
3243 r
->xtab
[(int)*first
].p
= mandoc_strndup(second
, ssz
);
3244 r
->xtab
[(int)*first
].sz
= ssz
;
3254 char *oldn
, *newn
, *end
;
3255 size_t oldsz
, newsz
;
3258 oldn
= newn
= buf
->buf
+ pos
;
3262 oldsz
= roff_getname(r
, &newn
, ln
, pos
);
3263 if (oldn
[oldsz
] == '\\' || *newn
== '\0')
3267 newsz
= roff_getname(r
, &end
, ln
, newn
- buf
->buf
);
3271 deftype
= ROFFDEF_ANY
;
3272 value
= roff_getstrn(r
, oldn
, oldsz
, &deftype
);
3275 roff_setstrn(&r
->strtab
, newn
, newsz
, value
, strlen(value
), 0);
3276 roff_setstrn(&r
->strtab
, oldn
, oldsz
, NULL
, 0, 0);
3277 roff_setstrn(&r
->rentab
, newn
, newsz
, NULL
, 0, 0);
3280 roff_setstrn(&r
->strtab
, newn
, newsz
, value
, strlen(value
), 0);
3281 roff_setstrn(&r
->rentab
, newn
, newsz
, NULL
, 0, 0);
3284 roff_setstrn(&r
->rentab
, newn
, newsz
, value
, strlen(value
), 0);
3285 roff_setstrn(&r
->rentab
, oldn
, oldsz
, NULL
, 0, 0);
3286 roff_setstrn(&r
->strtab
, newn
, newsz
, NULL
, 0, 0);
3289 roff_setstrn(&r
->rentab
, newn
, newsz
, oldn
, oldsz
, 0);
3290 roff_setstrn(&r
->strtab
, newn
, newsz
, NULL
, 0, 0);
3293 roff_setstrn(&r
->strtab
, newn
, newsz
, NULL
, 0, 0);
3294 roff_setstrn(&r
->rentab
, newn
, newsz
, NULL
, 0, 0);
3305 name
= buf
->buf
+ pos
;
3306 mandoc_vmsg(MANDOCERR_SO
, r
->parse
, ln
, ppos
, "so %s", name
);
3309 * Handle `so'. Be EXTREMELY careful, as we shouldn't be
3310 * opening anything that's not in our cwd or anything beneath
3311 * it. Thus, explicitly disallow traversing up the file-system
3312 * or using absolute paths.
3315 if (*name
== '/' || strstr(name
, "../") || strstr(name
, "/..")) {
3316 mandoc_vmsg(MANDOCERR_SO_PATH
, r
->parse
, ln
, ppos
,
3318 buf
->sz
= mandoc_asprintf(&cp
,
3319 ".sp\nSee the file %s.\n.sp", name
) + 1;
3323 return ROFF_REPARSE
;
3330 /* --- user defined strings and macros ------------------------------------ */
3333 roff_userdef(ROFF_ARGS
)
3335 const char *arg
[16], *ap
;
3337 int expand_count
, i
, ib
, ie
;
3341 * Collect pointers to macro argument strings
3342 * and NUL-terminate them.
3346 cp
= buf
->buf
+ pos
;
3347 for (i
= 0; i
< 16; i
++) {
3351 arg
[i
] = mandoc_getarg(r
->parse
, &cp
, ln
, &pos
);
3357 * Expand macro arguments.
3360 buf
->sz
= strlen(r
->current_string
) + 1;
3361 n1
= n2
= cp
= mandoc_malloc(buf
->sz
);
3362 memcpy(n1
, r
->current_string
, buf
->sz
);
3364 while (*cp
!= '\0') {
3366 /* Scan ahead for the next argument invocation. */
3372 if (*cp
== '*') { /* \\$* inserts all arguments */
3375 } else { /* \\$1 .. \\$9 insert one argument */
3376 ib
= ie
= *cp
- '1';
3377 if (ib
< 0 || ib
> 8)
3383 * Prevent infinite recursion.
3388 else if (++expand_count
> EXPAND_LIMIT
) {
3389 mandoc_msg(MANDOCERR_ROFFLOOP
, r
->parse
,
3390 ln
, (int)(cp
- n1
), NULL
);
3398 * Determine the size of the expanded argument,
3399 * taking escaping of quotes into account.
3402 asz
= ie
> ib
? ie
- ib
: 0; /* for blanks */
3403 for (i
= ib
; i
<= ie
; i
++) {
3404 for (ap
= arg
[i
]; *ap
!= '\0'; ap
++) {
3413 * Determine the size of the rest of the
3414 * unexpanded macro, including the NUL.
3417 rsz
= buf
->sz
- (cp
- n1
) - 3;
3420 * When shrinking, move before
3421 * releasing the storage.
3425 memmove(cp
+ asz
, cp
+ 3, rsz
);
3428 * Resize the storage for the macro
3429 * and readjust the parse pointer.
3433 n2
= mandoc_realloc(n1
, buf
->sz
);
3434 cp
= n2
+ (cp
- n1
);
3438 * When growing, make room
3439 * for the expanded argument.
3443 memmove(cp
+ asz
, cp
+ 3, rsz
);
3446 /* Copy the expanded argument, escaping quotes. */
3449 for (i
= ib
; i
<= ie
; i
++) {
3450 for (ap
= arg
[i
]; *ap
!= '\0'; ap
++) {
3452 memcpy(n2
, "\\(dq", 4);
3463 * Replace the macro invocation
3464 * by the expanded macro.
3471 return buf
->sz
> 1 && buf
->buf
[buf
->sz
- 2] == '\n' ?
3472 ROFF_REPARSE
: ROFF_APPEND
;
3476 * Calling a high-level macro that was renamed with .rn.
3477 * r->current_string has already been set up by roff_parse().
3480 roff_renamed(ROFF_ARGS
)
3484 buf
->sz
= mandoc_asprintf(&nbuf
, ".%s%s%s", r
->current_string
,
3485 buf
->buf
[pos
] == '\0' ? "" : " ", buf
->buf
+ pos
) + 1;
3493 roff_getname(struct roff
*r
, char **cpp
, int ln
, int pos
)
3502 /* Read until end of name and terminate it with NUL. */
3503 for (cp
= name
; 1; cp
++) {
3504 if ('\0' == *cp
|| ' ' == *cp
) {
3511 if ('{' == cp
[1] || '}' == cp
[1])
3516 mandoc_vmsg(MANDOCERR_NAMESC
, r
->parse
, ln
, pos
,
3517 "%.*s", (int)(cp
- name
+ 1), name
);
3518 mandoc_escape((const char **)&cp
, NULL
, NULL
);
3522 /* Read past spaces. */
3531 * Store *string into the user-defined string called *name.
3532 * To clear an existing entry, call with (*r, *name, NULL, 0).
3533 * append == 0: replace mode
3534 * append == 1: single-line append mode
3535 * append == 2: multiline append mode, append '\n' after each call
3538 roff_setstr(struct roff
*r
, const char *name
, const char *string
,
3543 namesz
= strlen(name
);
3544 roff_setstrn(&r
->strtab
, name
, namesz
, string
,
3545 string
? strlen(string
) : 0, append
);
3546 roff_setstrn(&r
->rentab
, name
, namesz
, NULL
, 0, 0);
3550 roff_setstrn(struct roffkv
**r
, const char *name
, size_t namesz
,
3551 const char *string
, size_t stringsz
, int append
)
3556 size_t oldch
, newch
;
3558 /* Search for an existing string with the same name. */
3561 while (n
&& (namesz
!= n
->key
.sz
||
3562 strncmp(n
->key
.p
, name
, namesz
)))
3566 /* Create a new string table entry. */
3567 n
= mandoc_malloc(sizeof(struct roffkv
));
3568 n
->key
.p
= mandoc_strndup(name
, namesz
);
3574 } else if (0 == append
) {
3584 * One additional byte for the '\n' in multiline mode,
3585 * and one for the terminating '\0'.
3587 newch
= stringsz
+ (1 < append
? 2u : 1u);
3589 if (NULL
== n
->val
.p
) {
3590 n
->val
.p
= mandoc_malloc(newch
);
3595 n
->val
.p
= mandoc_realloc(n
->val
.p
, oldch
+ newch
);
3598 /* Skip existing content in the destination buffer. */
3599 c
= n
->val
.p
+ (int)oldch
;
3601 /* Append new content to the destination buffer. */
3603 while (i
< (int)stringsz
) {
3605 * Rudimentary roff copy mode:
3606 * Handle escaped backslashes.
3608 if ('\\' == string
[i
] && '\\' == string
[i
+ 1])
3613 /* Append terminating bytes. */
3618 n
->val
.sz
= (int)(c
- n
->val
.p
);
3622 roff_getstrn(struct roff
*r
, const char *name
, size_t len
,
3625 const struct roffkv
*n
;
3630 for (n
= r
->strtab
; n
!= NULL
; n
= n
->next
) {
3631 if (strncmp(name
, n
->key
.p
, len
) != 0 ||
3632 n
->key
.p
[len
] != '\0' || n
->val
.p
== NULL
)
3634 if (*deftype
& ROFFDEF_USER
) {
3635 *deftype
= ROFFDEF_USER
;
3642 for (n
= r
->rentab
; n
!= NULL
; n
= n
->next
) {
3643 if (strncmp(name
, n
->key
.p
, len
) != 0 ||
3644 n
->key
.p
[len
] != '\0' || n
->val
.p
== NULL
)
3646 if (*deftype
& ROFFDEF_REN
) {
3647 *deftype
= ROFFDEF_REN
;
3654 for (i
= 0; i
< PREDEFS_MAX
; i
++) {
3655 if (strncmp(name
, predefs
[i
].name
, len
) != 0 ||
3656 predefs
[i
].name
[len
] != '\0')
3658 if (*deftype
& ROFFDEF_PRE
) {
3659 *deftype
= ROFFDEF_PRE
;
3660 return predefs
[i
].str
;
3666 if (r
->man
->macroset
!= MACROSET_MAN
) {
3667 for (tok
= MDOC_Dd
; tok
< MDOC_MAX
; tok
++) {
3668 if (strncmp(name
, roff_name
[tok
], len
) != 0 ||
3669 roff_name
[tok
][len
] != '\0')
3671 if (*deftype
& ROFFDEF_STD
) {
3672 *deftype
= ROFFDEF_STD
;
3680 if (r
->man
->macroset
!= MACROSET_MDOC
) {
3681 for (tok
= MAN_TH
; tok
< MAN_MAX
; tok
++) {
3682 if (strncmp(name
, roff_name
[tok
], len
) != 0 ||
3683 roff_name
[tok
][len
] != '\0')
3685 if (*deftype
& ROFFDEF_STD
) {
3686 *deftype
= ROFFDEF_STD
;
3695 if (found
== 0 && *deftype
!= ROFFDEF_ANY
) {
3696 if (*deftype
& ROFFDEF_REN
) {
3698 * This might still be a request,
3699 * so do not treat it as undefined yet.
3701 *deftype
= ROFFDEF_UNDEF
;
3705 /* Using an undefined string defines it to be empty. */
3707 roff_setstrn(&r
->strtab
, name
, len
, "", 0, 0);
3708 roff_setstrn(&r
->rentab
, name
, len
, NULL
, 0, 0);
3716 roff_freestr(struct roffkv
*r
)
3718 struct roffkv
*n
, *nn
;
3720 for (n
= r
; n
; n
= nn
) {
3728 /* --- accessors and utility functions ------------------------------------ */
3731 * Duplicate an input string, making the appropriate character
3732 * conversations (as stipulated by `tr') along the way.
3733 * Returns a heap-allocated string with all the replacements made.
3736 roff_strdup(const struct roff
*r
, const char *p
)
3738 const struct roffkv
*cp
;
3742 enum mandoc_esc esc
;
3744 if (NULL
== r
->xmbtab
&& NULL
== r
->xtab
)
3745 return mandoc_strdup(p
);
3746 else if ('\0' == *p
)
3747 return mandoc_strdup("");
3750 * Step through each character looking for term matches
3751 * (remember that a `tr' can be invoked with an escape, which is
3752 * a glyph but the escape is multi-character).
3753 * We only do this if the character hash has been initialised
3754 * and the string is >0 length.
3760 while ('\0' != *p
) {
3761 assert((unsigned int)*p
< 128);
3762 if ('\\' != *p
&& r
->xtab
&& r
->xtab
[(unsigned int)*p
].p
) {
3763 sz
= r
->xtab
[(int)*p
].sz
;
3764 res
= mandoc_realloc(res
, ssz
+ sz
+ 1);
3765 memcpy(res
+ ssz
, r
->xtab
[(int)*p
].p
, sz
);
3769 } else if ('\\' != *p
) {
3770 res
= mandoc_realloc(res
, ssz
+ 2);
3775 /* Search for term matches. */
3776 for (cp
= r
->xmbtab
; cp
; cp
= cp
->next
)
3777 if (0 == strncmp(p
, cp
->key
.p
, cp
->key
.sz
))
3782 * A match has been found.
3783 * Append the match to the array and move
3784 * forward by its keysize.
3786 res
= mandoc_realloc(res
,
3787 ssz
+ cp
->val
.sz
+ 1);
3788 memcpy(res
+ ssz
, cp
->val
.p
, cp
->val
.sz
);
3790 p
+= (int)cp
->key
.sz
;
3795 * Handle escapes carefully: we need to copy
3796 * over just the escape itself, or else we might
3797 * do replacements within the escape itself.
3798 * Make sure to pass along the bogus string.
3801 esc
= mandoc_escape(&p
, NULL
, NULL
);
3802 if (ESCAPE_ERROR
== esc
) {
3804 res
= mandoc_realloc(res
, ssz
+ sz
+ 1);
3805 memcpy(res
+ ssz
, pp
, sz
);
3809 * We bail out on bad escapes.
3810 * No need to warn: we already did so when
3811 * roff_res() was called.
3814 res
= mandoc_realloc(res
, ssz
+ sz
+ 1);
3815 memcpy(res
+ ssz
, pp
, sz
);
3819 res
[(int)ssz
] = '\0';
3824 roff_getformat(const struct roff
*r
)
3831 * Find out whether a line is a macro line or not.
3832 * If it is, adjust the current position and return one; if it isn't,
3833 * return zero and don't change the current position.
3834 * If the control character has been set with `.cc', then let that grain
3836 * This is slighly contrary to groff, where using the non-breaking
3837 * control character when `cc' has been invoked will cause the
3838 * non-breaking macro contents to be printed verbatim.
3841 roff_getcontrol(const struct roff
*r
, const char *cp
, int *ppos
)
3847 if (r
->control
!= '\0' && cp
[pos
] == r
->control
)
3849 else if (r
->control
!= '\0')
3851 else if ('\\' == cp
[pos
] && '.' == cp
[pos
+ 1])
3853 else if ('.' == cp
[pos
] || '\'' == cp
[pos
])
3858 while (' ' == cp
[pos
] || '\t' == cp
[pos
])