]>
git.cameronkatri.com Git - mandoc.git/blob - roff.c
bd8c8250a99b33678efabc6b2d18ad1611c8c735
1 /* $Id: roff.c,v 1.334 2018/08/18 21:37:01 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 /* Line continuation with comment. */
1210 if (stesc
[1] == '#') {
1215 /* Discard normal comments. */
1217 while (stesc
> start
&& stesc
[-1] == ' ')
1226 /* Notice the end of the input. */
1228 if (*stesc
== '\n') {
1234 while (stesc
>= start
) {
1236 /* Search backwards for the next backslash. */
1238 if (*stesc
!= r
->escape
) {
1239 if (*stesc
== '\\') {
1241 buf
->sz
= mandoc_asprintf(&nbuf
, "%s\\e%s",
1242 buf
->buf
, stesc
+ 1) + 1;
1244 stesc
= nbuf
+ (stesc
- buf
->buf
);
1252 /* If it is escaped, skip it. */
1254 for (cp
= stesc
- 1; cp
>= start
; cp
--)
1255 if (*cp
!= r
->escape
)
1258 if ((stesc
- cp
) % 2 == 0) {
1262 } else if (stesc
[1] != '\0') {
1272 /* Decide whether to expand or to check only. */
1286 if (sign
== '+' || sign
== '-')
1291 esc
= mandoc_escape(&cp
, &stnam
, &inaml
);
1292 if (esc
== ESCAPE_ERROR
||
1293 (esc
== ESCAPE_SPECIAL
&&
1294 mchars_spec2cp(stnam
, inaml
) < 0))
1295 mandoc_vmsg(MANDOCERR_ESC_BAD
,
1296 r
->parse
, ln
, (int)(stesc
- buf
->buf
),
1297 "%.*s", (int)(cp
- stesc
), stesc
);
1302 if (EXPAND_LIMIT
< ++expand_count
) {
1303 mandoc_msg(MANDOCERR_ROFFLOOP
, r
->parse
,
1304 ln
, (int)(stesc
- buf
->buf
), NULL
);
1309 * The third character decides the length
1310 * of the name of the string or register.
1311 * Save a pointer to the name.
1338 /* Advance to the end of the name. */
1342 while (maxl
== 0 || naml
< maxl
) {
1344 mandoc_msg(MANDOCERR_ESC_BAD
, r
->parse
,
1345 ln
, (int)(stesc
- buf
->buf
), stesc
);
1349 if (maxl
== 0 && *cp
== term
) {
1353 if (*cp
++ != '\\' || stesc
[1] != 'w') {
1357 switch (mandoc_escape(&cp
, NULL
, NULL
)) {
1358 case ESCAPE_SPECIAL
:
1359 case ESCAPE_UNICODE
:
1360 case ESCAPE_NUMBERED
:
1361 case ESCAPE_OVERSTRIKE
:
1370 * Retrieve the replacement string; if it is
1371 * undefined, resume searching for escapes.
1377 deftype
= ROFFDEF_USER
| ROFFDEF_PRE
;
1378 res
= roff_getstrn(r
, stnam
, naml
, &deftype
);
1381 * If not overriden, let \*(.T
1382 * through to the formatters.
1385 if (res
== NULL
&& naml
== 2 &&
1386 stnam
[0] == '.' && stnam
[1] == 'T') {
1387 roff_setstrn(&r
->strtab
,
1388 ".T", 2, NULL
, 0, 0);
1396 ubuf
[0] = arg_complete
&&
1397 roff_evalnum(r
, ln
, stnam
, &npos
,
1398 NULL
, ROFFNUM_SCALE
) &&
1399 stnam
+ npos
+ 1 == cp
? '1' : '0';
1404 (void)snprintf(ubuf
, sizeof(ubuf
), "%d",
1405 roff_getregn(r
, stnam
, naml
, sign
));
1410 /* use even incomplete args */
1411 (void)snprintf(ubuf
, sizeof(ubuf
), "%d",
1417 mandoc_vmsg(MANDOCERR_STR_UNDEF
,
1418 r
->parse
, ln
, (int)(stesc
- buf
->buf
),
1419 "%.*s", (int)naml
, stnam
);
1421 } else if (buf
->sz
+ strlen(res
) > SHRT_MAX
) {
1422 mandoc_msg(MANDOCERR_ROFFLOOP
, r
->parse
,
1423 ln
, (int)(stesc
- buf
->buf
), NULL
);
1427 /* Replace the escape sequence by the string. */
1430 buf
->sz
= mandoc_asprintf(&nbuf
, "%s%s%s",
1431 buf
->buf
, res
, cp
) + 1;
1433 /* Prepare for the next replacement. */
1436 stesc
= nbuf
+ (stesc
- buf
->buf
) + strlen(res
);
1444 * Process text streams.
1447 roff_parsetext(struct roff
*r
, struct buf
*buf
, int pos
, int *offs
)
1453 enum mandoc_esc esc
;
1455 /* Spring the input line trap. */
1457 if (roffit_lines
== 1) {
1458 isz
= mandoc_asprintf(&p
, "%s\n.%s", buf
->buf
, roffit_macro
);
1465 return ROFF_REPARSE
;
1466 } else if (roffit_lines
> 1)
1469 if (roffce_node
!= NULL
&& buf
->buf
[pos
] != '\0') {
1470 if (roffce_lines
< 1) {
1471 r
->man
->last
= roffce_node
;
1472 r
->man
->next
= ROFF_NEXT_SIBLING
;
1479 /* Convert all breakable hyphens into ASCII_HYPH. */
1481 start
= p
= buf
->buf
+ pos
;
1483 while (*p
!= '\0') {
1484 sz
= strcspn(p
, "-\\");
1491 /* Skip over escapes. */
1493 esc
= mandoc_escape((const char **)&p
, NULL
, NULL
);
1494 if (esc
== ESCAPE_ERROR
)
1499 } else if (p
== start
) {
1504 if (isalpha((unsigned char)p
[-1]) &&
1505 isalpha((unsigned char)p
[1]))
1513 roff_parseln(struct roff
*r
, int ln
, struct buf
*buf
, int *offs
)
1517 int pos
; /* parse point */
1518 int spos
; /* saved parse point for messages */
1519 int ppos
; /* original offset in buf->buf */
1520 int ctl
; /* macro line (boolean) */
1524 /* Handle in-line equation delimiters. */
1526 if (r
->tbl
== NULL
&&
1527 r
->last_eqn
!= NULL
&& r
->last_eqn
->delim
&&
1528 (r
->eqn
== NULL
|| r
->eqn_inline
)) {
1529 e
= roff_eqndelim(r
, buf
, pos
);
1530 if (e
== ROFF_REPARSE
)
1532 assert(e
== ROFF_CONT
);
1535 /* Expand some escape sequences. */
1537 e
= roff_res(r
, buf
, ln
, pos
);
1538 if (e
== ROFF_IGN
|| e
== ROFF_APPEND
)
1540 assert(e
== ROFF_CONT
);
1542 ctl
= roff_getcontrol(r
, buf
->buf
, &pos
);
1545 * First, if a scope is open and we're not a macro, pass the
1546 * text through the macro's filter.
1547 * Equations process all content themselves.
1548 * Tables process almost all content themselves, but we want
1549 * to warn about macros before passing it there.
1552 if (r
->last
!= NULL
&& ! ctl
) {
1554 e
= (*roffs
[t
].text
)(r
, t
, buf
, ln
, pos
, pos
, offs
);
1557 assert(e
== ROFF_CONT
);
1559 if (r
->eqn
!= NULL
&& strncmp(buf
->buf
+ ppos
, ".EN", 3)) {
1560 eqn_read(r
->eqn
, buf
->buf
+ ppos
);
1563 if (r
->tbl
!= NULL
&& (ctl
== 0 || buf
->buf
[pos
] == '\0')) {
1564 tbl_read(r
->tbl
, ln
, buf
->buf
, ppos
);
1565 roff_addtbl(r
->man
, r
->tbl
);
1569 return roff_parsetext(r
, buf
, pos
, offs
);
1571 /* Skip empty request lines. */
1573 if (buf
->buf
[pos
] == '"') {
1574 mandoc_msg(MANDOCERR_COMMENT_BAD
, r
->parse
,
1577 } else if (buf
->buf
[pos
] == '\0')
1581 * If a scope is open, go to the child handler for that macro,
1582 * as it may want to preprocess before doing anything with it.
1583 * Don't do so if an equation is open.
1588 return (*roffs
[t
].sub
)(r
, t
, buf
, ln
, ppos
, pos
, offs
);
1591 /* No scope is open. This is a new request or macro. */
1594 t
= roff_parse(r
, buf
->buf
, &pos
, ln
, ppos
);
1596 /* Tables ignore most macros. */
1598 if (r
->tbl
!= NULL
&& (t
== TOKEN_NONE
|| t
== ROFF_TS
||
1599 t
== ROFF_br
|| t
== ROFF_ce
|| t
== ROFF_rj
|| t
== ROFF_sp
)) {
1600 mandoc_msg(MANDOCERR_TBLMACRO
, r
->parse
,
1601 ln
, pos
, buf
->buf
+ spos
);
1602 if (t
!= TOKEN_NONE
)
1604 while (buf
->buf
[pos
] != '\0' && buf
->buf
[pos
] != ' ')
1606 while (buf
->buf
[pos
] == ' ')
1608 tbl_read(r
->tbl
, ln
, buf
->buf
, pos
);
1609 roff_addtbl(r
->man
, r
->tbl
);
1613 /* For now, let high level macros abort .ce mode. */
1615 if (ctl
&& roffce_node
!= NULL
&&
1616 (t
== TOKEN_NONE
|| t
== ROFF_Dd
|| t
== ROFF_EQ
||
1617 t
== ROFF_TH
|| t
== ROFF_TS
)) {
1618 r
->man
->last
= roffce_node
;
1619 r
->man
->next
= ROFF_NEXT_SIBLING
;
1625 * This is neither a roff request nor a user-defined macro.
1626 * Let the standard macro set parsers handle it.
1629 if (t
== TOKEN_NONE
)
1632 /* Execute a roff request or a user defined macro. */
1634 return (*roffs
[t
].proc
)(r
, t
, buf
, ln
, spos
, pos
, offs
);
1638 roff_endparse(struct roff
*r
)
1640 if (r
->last
!= NULL
)
1641 mandoc_msg(MANDOCERR_BLK_NOEND
, r
->parse
,
1642 r
->last
->line
, r
->last
->col
,
1643 roff_name
[r
->last
->tok
]);
1645 if (r
->eqn
!= NULL
) {
1646 mandoc_msg(MANDOCERR_BLK_NOEND
, r
->parse
,
1647 r
->eqn
->node
->line
, r
->eqn
->node
->pos
, "EQ");
1652 if (r
->tbl
!= NULL
) {
1653 mandoc_msg(MANDOCERR_BLK_NOEND
, r
->parse
,
1654 r
->tbl
->line
, r
->tbl
->pos
, "TS");
1661 * Parse a roff node's type from the input buffer. This must be in the
1662 * form of ".foo xxx" in the usual way.
1664 static enum roff_tok
1665 roff_parse(struct roff
*r
, char *buf
, int *pos
, int ln
, int ppos
)
1675 if ('\0' == *cp
|| '"' == *cp
|| '\t' == *cp
|| ' ' == *cp
)
1679 maclen
= roff_getname(r
, &cp
, ln
, ppos
);
1681 deftype
= ROFFDEF_USER
| ROFFDEF_REN
;
1682 r
->current_string
= roff_getstrn(r
, mac
, maclen
, &deftype
);
1691 t
= roffhash_find(r
->reqtab
, mac
, maclen
);
1694 if (t
!= TOKEN_NONE
)
1696 else if (deftype
== ROFFDEF_UNDEF
) {
1697 /* Using an undefined macro defines it to be empty. */
1698 roff_setstrn(&r
->strtab
, mac
, maclen
, "", 0, 0);
1699 roff_setstrn(&r
->rentab
, mac
, maclen
, NULL
, 0, 0);
1704 /* --- handling of request blocks ----------------------------------------- */
1707 roff_cblock(ROFF_ARGS
)
1711 * A block-close `..' should only be invoked as a child of an
1712 * ignore macro, otherwise raise a warning and just ignore it.
1715 if (r
->last
== NULL
) {
1716 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, r
->parse
,
1721 switch (r
->last
->tok
) {
1723 /* ROFF_am1 is remapped to ROFF_am in roff_block(). */
1726 /* ROFF_de1 is remapped to ROFF_de in roff_block(). */
1731 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, r
->parse
,
1736 if (buf
->buf
[pos
] != '\0')
1737 mandoc_vmsg(MANDOCERR_ARG_SKIP
, r
->parse
, ln
, pos
,
1738 ".. %s", buf
->buf
+ pos
);
1741 roffnode_cleanscope(r
);
1747 roffnode_cleanscope(struct roff
*r
)
1751 if (--r
->last
->endspan
!= 0)
1758 roff_ccond(struct roff
*r
, int ln
, int ppos
)
1761 if (NULL
== r
->last
) {
1762 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, r
->parse
,
1767 switch (r
->last
->tok
) {
1773 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, r
->parse
,
1778 if (r
->last
->endspan
> -1) {
1779 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, r
->parse
,
1785 roffnode_cleanscope(r
);
1790 roff_block(ROFF_ARGS
)
1792 const char *name
, *value
;
1793 char *call
, *cp
, *iname
, *rname
;
1794 size_t csz
, namesz
, rsz
;
1797 /* Ignore groff compatibility mode for now. */
1799 if (tok
== ROFF_de1
)
1801 else if (tok
== ROFF_dei1
)
1803 else if (tok
== ROFF_am1
)
1805 else if (tok
== ROFF_ami1
)
1808 /* Parse the macro name argument. */
1810 cp
= buf
->buf
+ pos
;
1811 if (tok
== ROFF_ig
) {
1816 namesz
= roff_getname(r
, &cp
, ln
, ppos
);
1817 iname
[namesz
] = '\0';
1820 /* Resolve the macro name argument if it is indirect. */
1822 if (namesz
&& (tok
== ROFF_dei
|| tok
== ROFF_ami
)) {
1823 deftype
= ROFFDEF_USER
;
1824 name
= roff_getstrn(r
, iname
, namesz
, &deftype
);
1826 mandoc_vmsg(MANDOCERR_STR_UNDEF
,
1827 r
->parse
, ln
, (int)(iname
- buf
->buf
),
1828 "%.*s", (int)namesz
, iname
);
1831 namesz
= strlen(name
);
1835 if (namesz
== 0 && tok
!= ROFF_ig
) {
1836 mandoc_msg(MANDOCERR_REQ_EMPTY
, r
->parse
,
1837 ln
, ppos
, roff_name
[tok
]);
1841 roffnode_push(r
, tok
, name
, ln
, ppos
);
1844 * At the beginning of a `de' macro, clear the existing string
1845 * with the same name, if there is one. New content will be
1846 * appended from roff_block_text() in multiline mode.
1849 if (tok
== ROFF_de
|| tok
== ROFF_dei
) {
1850 roff_setstrn(&r
->strtab
, name
, namesz
, "", 0, 0);
1851 roff_setstrn(&r
->rentab
, name
, namesz
, NULL
, 0, 0);
1852 } else if (tok
== ROFF_am
|| tok
== ROFF_ami
) {
1853 deftype
= ROFFDEF_ANY
;
1854 value
= roff_getstrn(r
, iname
, namesz
, &deftype
);
1855 switch (deftype
) { /* Before appending, ... */
1856 case ROFFDEF_PRE
: /* copy predefined to user-defined. */
1857 roff_setstrn(&r
->strtab
, name
, namesz
,
1858 value
, strlen(value
), 0);
1860 case ROFFDEF_REN
: /* call original standard macro. */
1861 csz
= mandoc_asprintf(&call
, ".%.*s \\$* \\\"\n",
1862 (int)strlen(value
), value
);
1863 roff_setstrn(&r
->strtab
, name
, namesz
, call
, csz
, 0);
1864 roff_setstrn(&r
->rentab
, name
, namesz
, NULL
, 0, 0);
1867 case ROFFDEF_STD
: /* rename and call standard macro. */
1868 rsz
= mandoc_asprintf(&rname
, "__%s_renamed", name
);
1869 roff_setstrn(&r
->rentab
, rname
, rsz
, name
, namesz
, 0);
1870 csz
= mandoc_asprintf(&call
, ".%.*s \\$* \\\"\n",
1872 roff_setstrn(&r
->strtab
, name
, namesz
, call
, csz
, 0);
1884 /* Get the custom end marker. */
1887 namesz
= roff_getname(r
, &cp
, ln
, ppos
);
1889 /* Resolve the end marker if it is indirect. */
1891 if (namesz
&& (tok
== ROFF_dei
|| tok
== ROFF_ami
)) {
1892 deftype
= ROFFDEF_USER
;
1893 name
= roff_getstrn(r
, iname
, namesz
, &deftype
);
1895 mandoc_vmsg(MANDOCERR_STR_UNDEF
,
1896 r
->parse
, ln
, (int)(iname
- buf
->buf
),
1897 "%.*s", (int)namesz
, iname
);
1900 namesz
= strlen(name
);
1905 r
->last
->end
= mandoc_strndup(name
, namesz
);
1908 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, r
->parse
,
1909 ln
, pos
, ".%s ... %s", roff_name
[tok
], cp
);
1915 roff_block_sub(ROFF_ARGS
)
1921 * First check whether a custom macro exists at this level. If
1922 * it does, then check against it. This is some of groff's
1923 * stranger behaviours. If we encountered a custom end-scope
1924 * tag and that tag also happens to be a "real" macro, then we
1925 * need to try interpreting it again as a real macro. If it's
1926 * not, then return ignore. Else continue.
1930 for (i
= pos
, j
= 0; r
->last
->end
[j
]; j
++, i
++)
1931 if (buf
->buf
[i
] != r
->last
->end
[j
])
1934 if (r
->last
->end
[j
] == '\0' &&
1935 (buf
->buf
[i
] == '\0' ||
1936 buf
->buf
[i
] == ' ' ||
1937 buf
->buf
[i
] == '\t')) {
1939 roffnode_cleanscope(r
);
1941 while (buf
->buf
[i
] == ' ' || buf
->buf
[i
] == '\t')
1945 if (roff_parse(r
, buf
->buf
, &pos
, ln
, ppos
) !=
1953 * If we have no custom end-query or lookup failed, then try
1954 * pulling it out of the hashtable.
1957 t
= roff_parse(r
, buf
->buf
, &pos
, ln
, ppos
);
1959 if (t
!= ROFF_cblock
) {
1961 roff_setstr(r
, r
->last
->name
, buf
->buf
+ ppos
, 2);
1965 return (*roffs
[t
].proc
)(r
, t
, buf
, ln
, ppos
, pos
, offs
);
1969 roff_block_text(ROFF_ARGS
)
1973 roff_setstr(r
, r
->last
->name
, buf
->buf
+ pos
, 2);
1979 roff_cond_sub(ROFF_ARGS
)
1986 roffnode_cleanscope(r
);
1989 * If `\}' occurs on a macro line without a preceding macro,
1990 * drop the line completely.
1993 ep
= buf
->buf
+ pos
;
1994 if (ep
[0] == '\\' && ep
[1] == '}')
1997 /* Always check for the closing delimiter `\}'. */
1999 while ((ep
= strchr(ep
, '\\')) != NULL
) {
2002 memmove(ep
, ep
+ 2, strlen(ep
+ 2) + 1);
2003 roff_ccond(r
, ln
, ep
- buf
->buf
);
2015 * Fully handle known macros when they are structurally
2016 * required or when the conditional evaluated to true.
2019 t
= roff_parse(r
, buf
->buf
, &pos
, ln
, ppos
);
2020 return t
!= TOKEN_NONE
&& (rr
|| roffs
[t
].flags
& ROFFMAC_STRUCT
)
2021 ? (*roffs
[t
].proc
)(r
, t
, buf
, ln
, ppos
, pos
, offs
) : rr
2022 ? ROFF_CONT
: ROFF_IGN
;
2026 roff_cond_text(ROFF_ARGS
)
2032 roffnode_cleanscope(r
);
2034 ep
= buf
->buf
+ pos
;
2035 while ((ep
= strchr(ep
, '\\')) != NULL
) {
2036 if (*(++ep
) == '}') {
2038 roff_ccond(r
, ln
, ep
- buf
->buf
- 1);
2043 return rr
? ROFF_CONT
: ROFF_IGN
;
2046 /* --- handling of numeric and conditional expressions -------------------- */
2049 * Parse a single signed integer number. Stop at the first non-digit.
2050 * If there is at least one digit, return success and advance the
2051 * parse point, else return failure and let the parse point unchanged.
2052 * Ignore overflows, treat them just like the C language.
2055 roff_getnum(const char *v
, int *pos
, int *res
, int flags
)
2057 int myres
, scaled
, n
, p
;
2064 if (n
|| v
[p
] == '+')
2067 if (flags
& ROFFNUM_WHITE
)
2068 while (isspace((unsigned char)v
[p
]))
2071 for (*res
= 0; isdigit((unsigned char)v
[p
]); p
++)
2072 *res
= 10 * *res
+ v
[p
] - '0';
2079 /* Each number may be followed by one optional scaling unit. */
2083 scaled
= *res
* 65536;
2086 scaled
= *res
* 240;
2089 scaled
= *res
* 240 / 2.54;
2100 scaled
= *res
* 10 / 3;
2106 scaled
= *res
* 6 / 25;
2113 if (flags
& ROFFNUM_SCALE
)
2121 * Evaluate a string comparison condition.
2122 * The first character is the delimiter.
2123 * Succeed if the string up to its second occurrence
2124 * matches the string up to its third occurence.
2125 * Advance the cursor after the third occurrence
2126 * or lacking that, to the end of the line.
2129 roff_evalstrcond(const char *v
, int *pos
)
2131 const char *s1
, *s2
, *s3
;
2135 s1
= v
+ *pos
; /* initial delimiter */
2136 s2
= s1
+ 1; /* for scanning the first string */
2137 s3
= strchr(s2
, *s1
); /* for scanning the second string */
2139 if (NULL
== s3
) /* found no middle delimiter */
2142 while ('\0' != *++s3
) {
2143 if (*s2
!= *s3
) { /* mismatch */
2144 s3
= strchr(s3
, *s1
);
2147 if (*s3
== *s1
) { /* found the final delimiter */
2156 s3
= strchr(s2
, '\0');
2157 else if (*s3
!= '\0')
2164 * Evaluate an optionally negated single character, numerical,
2165 * or string condition.
2168 roff_evalcond(struct roff
*r
, int ln
, char *v
, int *pos
)
2172 int deftype
, number
, savepos
, istrue
, wanttrue
;
2174 if ('!' == v
[*pos
]) {
2199 sz
= roff_getname(r
, &cp
, ln
, cp
- v
);
2202 else if (v
[*pos
] == 'r')
2203 istrue
= roff_hasregn(r
, name
, sz
);
2205 deftype
= ROFFDEF_ANY
;
2206 roff_getstrn(r
, name
, sz
, &deftype
);
2210 return istrue
== wanttrue
;
2216 if (roff_evalnum(r
, ln
, v
, pos
, &number
, ROFFNUM_SCALE
))
2217 return (number
> 0) == wanttrue
;
2218 else if (*pos
== savepos
)
2219 return roff_evalstrcond(v
, pos
) == wanttrue
;
2225 roff_line_ignore(ROFF_ARGS
)
2232 roff_insec(ROFF_ARGS
)
2235 mandoc_msg(MANDOCERR_REQ_INSEC
, r
->parse
,
2236 ln
, ppos
, roff_name
[tok
]);
2241 roff_unsupp(ROFF_ARGS
)
2244 mandoc_msg(MANDOCERR_REQ_UNSUPP
, r
->parse
,
2245 ln
, ppos
, roff_name
[tok
]);
2250 roff_cond(ROFF_ARGS
)
2253 roffnode_push(r
, tok
, NULL
, ln
, ppos
);
2256 * An `.el' has no conditional body: it will consume the value
2257 * of the current rstack entry set in prior `ie' calls or
2260 * If we're not an `el', however, then evaluate the conditional.
2263 r
->last
->rule
= tok
== ROFF_el
?
2264 (r
->rstackpos
< 0 ? 0 : r
->rstack
[r
->rstackpos
--]) :
2265 roff_evalcond(r
, ln
, buf
->buf
, &pos
);
2268 * An if-else will put the NEGATION of the current evaluated
2269 * conditional into the stack of rules.
2272 if (tok
== ROFF_ie
) {
2273 if (r
->rstackpos
+ 1 == r
->rstacksz
) {
2275 r
->rstack
= mandoc_reallocarray(r
->rstack
,
2276 r
->rstacksz
, sizeof(int));
2278 r
->rstack
[++r
->rstackpos
] = !r
->last
->rule
;
2281 /* If the parent has false as its rule, then so do we. */
2283 if (r
->last
->parent
&& !r
->last
->parent
->rule
)
2288 * If there is nothing on the line after the conditional,
2289 * not even whitespace, use next-line scope.
2292 if (buf
->buf
[pos
] == '\0') {
2293 r
->last
->endspan
= 2;
2297 while (buf
->buf
[pos
] == ' ')
2300 /* An opening brace requests multiline scope. */
2302 if (buf
->buf
[pos
] == '\\' && buf
->buf
[pos
+ 1] == '{') {
2303 r
->last
->endspan
= -1;
2305 while (buf
->buf
[pos
] == ' ')
2311 * Anything else following the conditional causes
2312 * single-line scope. Warn if the scope contains
2313 * nothing but trailing whitespace.
2316 if (buf
->buf
[pos
] == '\0')
2317 mandoc_msg(MANDOCERR_COND_EMPTY
, r
->parse
,
2318 ln
, ppos
, roff_name
[tok
]);
2320 r
->last
->endspan
= 1;
2334 /* Ignore groff compatibility mode for now. */
2336 if (tok
== ROFF_ds1
)
2338 else if (tok
== ROFF_as1
)
2342 * The first word is the name of the string.
2343 * If it is empty or terminated by an escape sequence,
2344 * abort the `ds' request without defining anything.
2347 name
= string
= buf
->buf
+ pos
;
2351 namesz
= roff_getname(r
, &string
, ln
, pos
);
2352 if (name
[namesz
] == '\\')
2355 /* Read past the initial double-quote, if any. */
2359 /* The rest is the value. */
2360 roff_setstrn(&r
->strtab
, name
, namesz
, string
, strlen(string
),
2362 roff_setstrn(&r
->rentab
, name
, namesz
, NULL
, 0, 0);
2367 * Parse a single operator, one or two characters long.
2368 * If the operator is recognized, return success and advance the
2369 * parse point, else return failure and let the parse point unchanged.
2372 roff_getop(const char *v
, int *pos
, char *res
)
2387 switch (v
[*pos
+ 1]) {
2405 switch (v
[*pos
+ 1]) {
2419 if ('=' == v
[*pos
+ 1])
2431 * Evaluate either a parenthesized numeric expression
2432 * or a single signed integer number.
2435 roff_evalpar(struct roff
*r
, int ln
,
2436 const char *v
, int *pos
, int *res
, int flags
)
2440 return roff_getnum(v
, pos
, res
, flags
);
2443 if ( ! roff_evalnum(r
, ln
, v
, pos
, res
, flags
| ROFFNUM_WHITE
))
2447 * Omission of the closing parenthesis
2448 * is an error in validation mode,
2449 * but ignored in evaluation mode.
2454 else if (NULL
== res
)
2461 * Evaluate a complete numeric expression.
2462 * Proceed left to right, there is no concept of precedence.
2465 roff_evalnum(struct roff
*r
, int ln
, const char *v
,
2466 int *pos
, int *res
, int flags
)
2468 int mypos
, operand2
;
2476 if (flags
& ROFFNUM_WHITE
)
2477 while (isspace((unsigned char)v
[*pos
]))
2480 if ( ! roff_evalpar(r
, ln
, v
, pos
, res
, flags
))
2484 if (flags
& ROFFNUM_WHITE
)
2485 while (isspace((unsigned char)v
[*pos
]))
2488 if ( ! roff_getop(v
, pos
, &operator))
2491 if (flags
& ROFFNUM_WHITE
)
2492 while (isspace((unsigned char)v
[*pos
]))
2495 if ( ! roff_evalpar(r
, ln
, v
, pos
, &operand2
, flags
))
2498 if (flags
& ROFFNUM_WHITE
)
2499 while (isspace((unsigned char)v
[*pos
]))
2516 if (operand2
== 0) {
2517 mandoc_msg(MANDOCERR_DIVZERO
,
2518 r
->parse
, ln
, *pos
, v
);
2525 if (operand2
== 0) {
2526 mandoc_msg(MANDOCERR_DIVZERO
,
2527 r
->parse
, ln
, *pos
, v
);
2534 *res
= *res
< operand2
;
2537 *res
= *res
> operand2
;
2540 *res
= *res
<= operand2
;
2543 *res
= *res
>= operand2
;
2546 *res
= *res
== operand2
;
2549 *res
= *res
!= operand2
;
2552 *res
= *res
&& operand2
;
2555 *res
= *res
|| operand2
;
2558 if (operand2
< *res
)
2562 if (operand2
> *res
)
2572 /* --- register management ------------------------------------------------ */
2575 roff_setreg(struct roff
*r
, const char *name
, int val
, char sign
)
2577 roff_setregn(r
, name
, strlen(name
), val
, sign
, INT_MIN
);
2581 roff_setregn(struct roff
*r
, const char *name
, size_t len
,
2582 int val
, char sign
, int step
)
2584 struct roffreg
*reg
;
2586 /* Search for an existing register with the same name. */
2589 while (reg
!= NULL
&& (reg
->key
.sz
!= len
||
2590 strncmp(reg
->key
.p
, name
, len
) != 0))
2594 /* Create a new register. */
2595 reg
= mandoc_malloc(sizeof(struct roffreg
));
2596 reg
->key
.p
= mandoc_strndup(name
, len
);
2600 reg
->next
= r
->regtab
;
2606 else if ('-' == sign
)
2610 if (step
!= INT_MIN
)
2615 * Handle some predefined read-only number registers.
2616 * For now, return -1 if the requested register is not predefined;
2617 * in case a predefined read-only register having the value -1
2618 * were to turn up, another special value would have to be chosen.
2621 roff_getregro(const struct roff
*r
, const char *name
)
2625 case '$': /* Number of arguments of the last macro evaluated. */
2627 case 'A': /* ASCII approximation mode is always off. */
2629 case 'g': /* Groff compatibility mode is always on. */
2631 case 'H': /* Fixed horizontal resolution. */
2633 case 'j': /* Always adjust left margin only. */
2635 case 'T': /* Some output device is always defined. */
2637 case 'V': /* Fixed vertical resolution. */
2645 roff_getreg(struct roff
*r
, const char *name
)
2647 return roff_getregn(r
, name
, strlen(name
), '\0');
2651 roff_getregn(struct roff
*r
, const char *name
, size_t len
, char sign
)
2653 struct roffreg
*reg
;
2656 if ('.' == name
[0] && 2 == len
) {
2657 val
= roff_getregro(r
, name
+ 1);
2662 for (reg
= r
->regtab
; reg
; reg
= reg
->next
) {
2663 if (len
== reg
->key
.sz
&&
2664 0 == strncmp(name
, reg
->key
.p
, len
)) {
2667 reg
->val
+= reg
->step
;
2670 reg
->val
-= reg
->step
;
2679 roff_setregn(r
, name
, len
, 0, '\0', INT_MIN
);
2684 roff_hasregn(const struct roff
*r
, const char *name
, size_t len
)
2686 struct roffreg
*reg
;
2689 if ('.' == name
[0] && 2 == len
) {
2690 val
= roff_getregro(r
, name
+ 1);
2695 for (reg
= r
->regtab
; reg
; reg
= reg
->next
)
2696 if (len
== reg
->key
.sz
&&
2697 0 == strncmp(name
, reg
->key
.p
, len
))
2704 roff_freereg(struct roffreg
*reg
)
2706 struct roffreg
*old_reg
;
2708 while (NULL
!= reg
) {
2719 char *key
, *val
, *step
;
2724 key
= val
= buf
->buf
+ pos
;
2728 keysz
= roff_getname(r
, &val
, ln
, pos
);
2729 if (key
[keysz
] == '\\')
2733 if (sign
== '+' || sign
== '-')
2737 if (roff_evalnum(r
, ln
, val
, &len
, &iv
, ROFFNUM_SCALE
) == 0)
2741 while (isspace((unsigned char)*step
))
2743 if (roff_evalnum(r
, ln
, step
, NULL
, &is
, 0) == 0)
2746 roff_setregn(r
, key
, keysz
, iv
, sign
, is
);
2753 struct roffreg
*reg
, **prev
;
2757 name
= cp
= buf
->buf
+ pos
;
2760 namesz
= roff_getname(r
, &cp
, ln
, pos
);
2761 name
[namesz
] = '\0';
2766 if (reg
== NULL
|| !strcmp(name
, reg
->key
.p
))
2778 /* --- handler functions for roff requests -------------------------------- */
2787 cp
= buf
->buf
+ pos
;
2788 while (*cp
!= '\0') {
2790 namesz
= roff_getname(r
, &cp
, ln
, (int)(cp
- buf
->buf
));
2791 roff_setstrn(&r
->strtab
, name
, namesz
, NULL
, 0, 0);
2792 roff_setstrn(&r
->rentab
, name
, namesz
, NULL
, 0, 0);
2793 if (name
[namesz
] == '\\')
2804 /* Parse the number of lines. */
2806 if ( ! roff_evalnum(r
, ln
, buf
->buf
, &pos
, &iv
, 0)) {
2807 mandoc_msg(MANDOCERR_IT_NONUM
, r
->parse
,
2808 ln
, ppos
, buf
->buf
+ 1);
2812 while (isspace((unsigned char)buf
->buf
[pos
]))
2816 * Arm the input line trap.
2817 * Special-casing "an-trap" is an ugly workaround to cope
2818 * with DocBook stupidly fiddling with man(7) internals.
2822 roffit_macro
= mandoc_strdup(iv
!= 1 ||
2823 strcmp(buf
->buf
+ pos
, "an-trap") ?
2824 buf
->buf
+ pos
: "br");
2832 enum roff_tok t
, te
;
2839 r
->format
= MPARSE_MDOC
;
2840 mask
= MPARSE_MDOC
| MPARSE_QUICK
;
2846 r
->format
= MPARSE_MAN
;
2847 mask
= MPARSE_QUICK
;
2852 if ((r
->options
& mask
) == 0)
2853 for (t
= tok
; t
< te
; t
++)
2854 roff_setstr(r
, roff_name
[t
], NULL
, 0);
2861 if (r
->tbl
== NULL
) {
2862 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, r
->parse
,
2866 if (tbl_end(r
->tbl
) == 0) {
2869 buf
->buf
= mandoc_strdup(".sp");
2872 return ROFF_REPARSE
;
2883 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, r
->parse
,
2886 tbl_restart(ln
, ppos
, r
->tbl
);
2892 * Handle in-line equation delimiters.
2895 roff_eqndelim(struct roff
*r
, struct buf
*buf
, int pos
)
2898 const char *bef_pr
, *bef_nl
, *mac
, *aft_nl
, *aft_pr
;
2901 * Outside equations, look for an opening delimiter.
2902 * If we are inside an equation, we already know it is
2903 * in-line, or this function wouldn't have been called;
2904 * so look for a closing delimiter.
2907 cp1
= buf
->buf
+ pos
;
2908 cp2
= strchr(cp1
, r
->eqn
== NULL
?
2909 r
->last_eqn
->odelim
: r
->last_eqn
->cdelim
);
2914 bef_pr
= bef_nl
= aft_nl
= aft_pr
= "";
2916 /* Handle preceding text, protecting whitespace. */
2918 if (*buf
->buf
!= '\0') {
2925 * Prepare replacing the delimiter with an equation macro
2926 * and drop leading white space from the equation.
2929 if (r
->eqn
== NULL
) {
2936 /* Handle following text, protecting whitespace. */
2944 /* Do the actual replacement. */
2946 buf
->sz
= mandoc_asprintf(&cp1
, "%s%s%s%s%s%s%s", buf
->buf
,
2947 bef_pr
, bef_nl
, mac
, aft_nl
, aft_pr
, cp2
) + 1;
2951 /* Toggle the in-line state of the eqn subsystem. */
2953 r
->eqn_inline
= r
->eqn
== NULL
;
2954 return ROFF_REPARSE
;
2960 struct roff_node
*n
;
2962 if (r
->man
->macroset
== MACROSET_MAN
)
2963 man_breakscope(r
->man
, ROFF_EQ
);
2964 n
= roff_node_alloc(r
->man
, ln
, ppos
, ROFFT_EQN
, TOKEN_NONE
);
2965 if (ln
> r
->man
->last
->line
)
2966 n
->flags
|= NODE_LINE
;
2967 n
->eqn
= mandoc_calloc(1, sizeof(*n
->eqn
));
2968 n
->eqn
->expectargs
= UINT_MAX
;
2969 roff_node_append(r
->man
, n
);
2970 r
->man
->next
= ROFF_NEXT_SIBLING
;
2972 assert(r
->eqn
== NULL
);
2973 if (r
->last_eqn
== NULL
)
2974 r
->last_eqn
= eqn_alloc(r
->parse
);
2976 eqn_reset(r
->last_eqn
);
2977 r
->eqn
= r
->last_eqn
;
2980 if (buf
->buf
[pos
] != '\0')
2981 mandoc_vmsg(MANDOCERR_ARG_SKIP
, r
->parse
, ln
, pos
,
2982 ".EQ %s", buf
->buf
+ pos
);
2990 if (r
->eqn
!= NULL
) {
2994 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, r
->parse
, ln
, ppos
, "EN");
2995 if (buf
->buf
[pos
] != '\0')
2996 mandoc_vmsg(MANDOCERR_ARG_SKIP
, r
->parse
, ln
, pos
,
2997 "EN %s", buf
->buf
+ pos
);
3004 if (r
->tbl
!= NULL
) {
3005 mandoc_msg(MANDOCERR_BLK_BROKEN
, r
->parse
,
3006 ln
, ppos
, "TS breaks TS");
3009 r
->tbl
= tbl_alloc(ppos
, ln
, r
->parse
);
3011 r
->last_tbl
->next
= r
->tbl
;
3013 r
->first_tbl
= r
->tbl
;
3014 r
->last_tbl
= r
->tbl
;
3019 roff_onearg(ROFF_ARGS
)
3021 struct roff_node
*n
;
3025 if (r
->man
->flags
& (MAN_BLINE
| MAN_ELINE
) &&
3026 (tok
== ROFF_ce
|| tok
== ROFF_rj
|| tok
== ROFF_sp
||
3028 man_breakscope(r
->man
, tok
);
3030 if (roffce_node
!= NULL
&& (tok
== ROFF_ce
|| tok
== ROFF_rj
)) {
3031 r
->man
->last
= roffce_node
;
3032 r
->man
->next
= ROFF_NEXT_SIBLING
;
3035 roff_elem_alloc(r
->man
, ln
, ppos
, tok
);
3038 cp
= buf
->buf
+ pos
;
3040 while (*cp
!= '\0' && *cp
!= ' ')
3045 mandoc_vmsg(MANDOCERR_ARG_EXCESS
,
3046 r
->parse
, ln
, cp
- buf
->buf
,
3047 "%s ... %s", roff_name
[tok
], cp
);
3048 roff_word_alloc(r
->man
, ln
, pos
, buf
->buf
+ pos
);
3051 if (tok
== ROFF_ce
|| tok
== ROFF_rj
) {
3052 if (r
->man
->last
->type
== ROFFT_ELEM
) {
3053 roff_word_alloc(r
->man
, ln
, pos
, "1");
3054 r
->man
->last
->flags
|= NODE_NOSRC
;
3057 if (roff_evalnum(r
, ln
, r
->man
->last
->string
, &npos
,
3058 &roffce_lines
, 0) == 0) {
3059 mandoc_vmsg(MANDOCERR_CE_NONUM
,
3060 r
->parse
, ln
, pos
, "ce %s", buf
->buf
+ pos
);
3063 if (roffce_lines
< 1) {
3064 r
->man
->last
= r
->man
->last
->parent
;
3068 roffce_node
= r
->man
->last
->parent
;
3070 n
->flags
|= NODE_VALID
| NODE_ENDED
;
3073 n
->flags
|= NODE_LINE
;
3074 r
->man
->next
= ROFF_NEXT_SIBLING
;
3079 roff_manyarg(ROFF_ARGS
)
3081 struct roff_node
*n
;
3084 roff_elem_alloc(r
->man
, ln
, ppos
, tok
);
3087 for (sp
= ep
= buf
->buf
+ pos
; *sp
!= '\0'; sp
= ep
) {
3088 while (*ep
!= '\0' && *ep
!= ' ')
3092 roff_word_alloc(r
->man
, ln
, sp
- buf
->buf
, sp
);
3095 n
->flags
|= NODE_LINE
| NODE_VALID
| NODE_ENDED
;
3097 r
->man
->next
= ROFF_NEXT_SIBLING
;
3104 char *oldn
, *newn
, *end
, *value
;
3105 size_t oldsz
, newsz
, valsz
;
3107 newn
= oldn
= buf
->buf
+ pos
;
3111 newsz
= roff_getname(r
, &oldn
, ln
, pos
);
3112 if (newn
[newsz
] == '\\' || *oldn
== '\0')
3116 oldsz
= roff_getname(r
, &end
, ln
, oldn
- buf
->buf
);
3120 valsz
= mandoc_asprintf(&value
, ".%.*s \\$*\\\"\n",
3122 roff_setstrn(&r
->strtab
, newn
, newsz
, value
, valsz
, 0);
3123 roff_setstrn(&r
->rentab
, newn
, newsz
, NULL
, 0, 0);
3131 if (r
->man
->flags
& (MAN_BLINE
| MAN_ELINE
))
3132 man_breakscope(r
->man
, ROFF_br
);
3133 roff_elem_alloc(r
->man
, ln
, ppos
, ROFF_br
);
3134 if (buf
->buf
[pos
] != '\0')
3135 mandoc_vmsg(MANDOCERR_ARG_SKIP
, r
->parse
, ln
, pos
,
3136 "%s %s", roff_name
[tok
], buf
->buf
+ pos
);
3137 r
->man
->last
->flags
|= NODE_LINE
| NODE_VALID
| NODE_ENDED
;
3138 r
->man
->next
= ROFF_NEXT_SIBLING
;
3149 if (*p
== '\0' || (r
->control
= *p
++) == '.')
3153 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, r
->parse
,
3154 ln
, p
- buf
->buf
, "cc ... %s", p
);
3170 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, r
->parse
,
3171 ln
, p
- buf
->buf
, "ec ... %s", p
);
3180 if (buf
->buf
[pos
] != '\0')
3181 mandoc_vmsg(MANDOCERR_ARG_SKIP
, r
->parse
,
3182 ln
, pos
, "eo %s", buf
->buf
+ pos
);
3189 while (buf
->buf
[pos
] == ' ')
3198 const char *p
, *first
, *second
;
3200 enum mandoc_esc esc
;
3205 mandoc_msg(MANDOCERR_REQ_EMPTY
, r
->parse
, ln
, ppos
, "tr");
3209 while (*p
!= '\0') {
3213 if (*first
== '\\') {
3214 esc
= mandoc_escape(&p
, NULL
, NULL
);
3215 if (esc
== ESCAPE_ERROR
) {
3216 mandoc_msg(MANDOCERR_ESC_BAD
, r
->parse
,
3217 ln
, (int)(p
- buf
->buf
), first
);
3220 fsz
= (size_t)(p
- first
);
3224 if (*second
== '\\') {
3225 esc
= mandoc_escape(&p
, NULL
, NULL
);
3226 if (esc
== ESCAPE_ERROR
) {
3227 mandoc_msg(MANDOCERR_ESC_BAD
, r
->parse
,
3228 ln
, (int)(p
- buf
->buf
), second
);
3231 ssz
= (size_t)(p
- second
);
3232 } else if (*second
== '\0') {
3233 mandoc_vmsg(MANDOCERR_TR_ODD
, r
->parse
,
3234 ln
, first
- buf
->buf
, "tr %s", first
);
3240 roff_setstrn(&r
->xmbtab
, first
, fsz
,
3245 if (r
->xtab
== NULL
)
3246 r
->xtab
= mandoc_calloc(128,
3247 sizeof(struct roffstr
));
3249 free(r
->xtab
[(int)*first
].p
);
3250 r
->xtab
[(int)*first
].p
= mandoc_strndup(second
, ssz
);
3251 r
->xtab
[(int)*first
].sz
= ssz
;
3261 char *oldn
, *newn
, *end
;
3262 size_t oldsz
, newsz
;
3265 oldn
= newn
= buf
->buf
+ pos
;
3269 oldsz
= roff_getname(r
, &newn
, ln
, pos
);
3270 if (oldn
[oldsz
] == '\\' || *newn
== '\0')
3274 newsz
= roff_getname(r
, &end
, ln
, newn
- buf
->buf
);
3278 deftype
= ROFFDEF_ANY
;
3279 value
= roff_getstrn(r
, oldn
, oldsz
, &deftype
);
3282 roff_setstrn(&r
->strtab
, newn
, newsz
, value
, strlen(value
), 0);
3283 roff_setstrn(&r
->strtab
, oldn
, oldsz
, NULL
, 0, 0);
3284 roff_setstrn(&r
->rentab
, newn
, newsz
, NULL
, 0, 0);
3287 roff_setstrn(&r
->strtab
, newn
, newsz
, value
, strlen(value
), 0);
3288 roff_setstrn(&r
->rentab
, newn
, newsz
, NULL
, 0, 0);
3291 roff_setstrn(&r
->rentab
, newn
, newsz
, value
, strlen(value
), 0);
3292 roff_setstrn(&r
->rentab
, oldn
, oldsz
, NULL
, 0, 0);
3293 roff_setstrn(&r
->strtab
, newn
, newsz
, NULL
, 0, 0);
3296 roff_setstrn(&r
->rentab
, newn
, newsz
, oldn
, oldsz
, 0);
3297 roff_setstrn(&r
->strtab
, newn
, newsz
, NULL
, 0, 0);
3300 roff_setstrn(&r
->strtab
, newn
, newsz
, NULL
, 0, 0);
3301 roff_setstrn(&r
->rentab
, newn
, newsz
, NULL
, 0, 0);
3312 name
= buf
->buf
+ pos
;
3313 mandoc_vmsg(MANDOCERR_SO
, r
->parse
, ln
, ppos
, "so %s", name
);
3316 * Handle `so'. Be EXTREMELY careful, as we shouldn't be
3317 * opening anything that's not in our cwd or anything beneath
3318 * it. Thus, explicitly disallow traversing up the file-system
3319 * or using absolute paths.
3322 if (*name
== '/' || strstr(name
, "../") || strstr(name
, "/..")) {
3323 mandoc_vmsg(MANDOCERR_SO_PATH
, r
->parse
, ln
, ppos
,
3325 buf
->sz
= mandoc_asprintf(&cp
,
3326 ".sp\nSee the file %s.\n.sp", name
) + 1;
3330 return ROFF_REPARSE
;
3337 /* --- user defined strings and macros ------------------------------------ */
3340 roff_userdef(ROFF_ARGS
)
3342 const char *arg
[16], *ap
;
3344 int expand_count
, i
, ib
, ie
;
3348 * Collect pointers to macro argument strings
3349 * and NUL-terminate them.
3353 cp
= buf
->buf
+ pos
;
3354 for (i
= 0; i
< 16; i
++) {
3358 arg
[i
] = mandoc_getarg(r
->parse
, &cp
, ln
, &pos
);
3364 * Expand macro arguments.
3367 buf
->sz
= strlen(r
->current_string
) + 1;
3368 n1
= n2
= cp
= mandoc_malloc(buf
->sz
);
3369 memcpy(n1
, r
->current_string
, buf
->sz
);
3371 while (*cp
!= '\0') {
3373 /* Scan ahead for the next argument invocation. */
3379 if (*cp
== '*') { /* \\$* inserts all arguments */
3382 } else { /* \\$1 .. \\$9 insert one argument */
3383 ib
= ie
= *cp
- '1';
3384 if (ib
< 0 || ib
> 8)
3390 * Prevent infinite recursion.
3395 else if (++expand_count
> EXPAND_LIMIT
) {
3396 mandoc_msg(MANDOCERR_ROFFLOOP
, r
->parse
,
3397 ln
, (int)(cp
- n1
), NULL
);
3405 * Determine the size of the expanded argument,
3406 * taking escaping of quotes into account.
3409 asz
= ie
> ib
? ie
- ib
: 0; /* for blanks */
3410 for (i
= ib
; i
<= ie
; i
++) {
3411 for (ap
= arg
[i
]; *ap
!= '\0'; ap
++) {
3420 * Determine the size of the rest of the
3421 * unexpanded macro, including the NUL.
3424 rsz
= buf
->sz
- (cp
- n1
) - 3;
3427 * When shrinking, move before
3428 * releasing the storage.
3432 memmove(cp
+ asz
, cp
+ 3, rsz
);
3435 * Resize the storage for the macro
3436 * and readjust the parse pointer.
3440 n2
= mandoc_realloc(n1
, buf
->sz
);
3441 cp
= n2
+ (cp
- n1
);
3445 * When growing, make room
3446 * for the expanded argument.
3450 memmove(cp
+ asz
, cp
+ 3, rsz
);
3453 /* Copy the expanded argument, escaping quotes. */
3456 for (i
= ib
; i
<= ie
; i
++) {
3457 for (ap
= arg
[i
]; *ap
!= '\0'; ap
++) {
3459 memcpy(n2
, "\\(dq", 4);
3470 * Replace the macro invocation
3471 * by the expanded macro.
3478 return buf
->sz
> 1 && buf
->buf
[buf
->sz
- 2] == '\n' ?
3479 ROFF_REPARSE
: ROFF_APPEND
;
3483 * Calling a high-level macro that was renamed with .rn.
3484 * r->current_string has already been set up by roff_parse().
3487 roff_renamed(ROFF_ARGS
)
3491 buf
->sz
= mandoc_asprintf(&nbuf
, ".%s%s%s", r
->current_string
,
3492 buf
->buf
[pos
] == '\0' ? "" : " ", buf
->buf
+ pos
) + 1;
3500 roff_getname(struct roff
*r
, char **cpp
, int ln
, int pos
)
3509 /* Read until end of name and terminate it with NUL. */
3510 for (cp
= name
; 1; cp
++) {
3511 if ('\0' == *cp
|| ' ' == *cp
) {
3518 if ('{' == cp
[1] || '}' == cp
[1])
3523 mandoc_vmsg(MANDOCERR_NAMESC
, r
->parse
, ln
, pos
,
3524 "%.*s", (int)(cp
- name
+ 1), name
);
3525 mandoc_escape((const char **)&cp
, NULL
, NULL
);
3529 /* Read past spaces. */
3538 * Store *string into the user-defined string called *name.
3539 * To clear an existing entry, call with (*r, *name, NULL, 0).
3540 * append == 0: replace mode
3541 * append == 1: single-line append mode
3542 * append == 2: multiline append mode, append '\n' after each call
3545 roff_setstr(struct roff
*r
, const char *name
, const char *string
,
3550 namesz
= strlen(name
);
3551 roff_setstrn(&r
->strtab
, name
, namesz
, string
,
3552 string
? strlen(string
) : 0, append
);
3553 roff_setstrn(&r
->rentab
, name
, namesz
, NULL
, 0, 0);
3557 roff_setstrn(struct roffkv
**r
, const char *name
, size_t namesz
,
3558 const char *string
, size_t stringsz
, int append
)
3563 size_t oldch
, newch
;
3565 /* Search for an existing string with the same name. */
3568 while (n
&& (namesz
!= n
->key
.sz
||
3569 strncmp(n
->key
.p
, name
, namesz
)))
3573 /* Create a new string table entry. */
3574 n
= mandoc_malloc(sizeof(struct roffkv
));
3575 n
->key
.p
= mandoc_strndup(name
, namesz
);
3581 } else if (0 == append
) {
3591 * One additional byte for the '\n' in multiline mode,
3592 * and one for the terminating '\0'.
3594 newch
= stringsz
+ (1 < append
? 2u : 1u);
3596 if (NULL
== n
->val
.p
) {
3597 n
->val
.p
= mandoc_malloc(newch
);
3602 n
->val
.p
= mandoc_realloc(n
->val
.p
, oldch
+ newch
);
3605 /* Skip existing content in the destination buffer. */
3606 c
= n
->val
.p
+ (int)oldch
;
3608 /* Append new content to the destination buffer. */
3610 while (i
< (int)stringsz
) {
3612 * Rudimentary roff copy mode:
3613 * Handle escaped backslashes.
3615 if ('\\' == string
[i
] && '\\' == string
[i
+ 1])
3620 /* Append terminating bytes. */
3625 n
->val
.sz
= (int)(c
- n
->val
.p
);
3629 roff_getstrn(struct roff
*r
, const char *name
, size_t len
,
3632 const struct roffkv
*n
;
3637 for (n
= r
->strtab
; n
!= NULL
; n
= n
->next
) {
3638 if (strncmp(name
, n
->key
.p
, len
) != 0 ||
3639 n
->key
.p
[len
] != '\0' || n
->val
.p
== NULL
)
3641 if (*deftype
& ROFFDEF_USER
) {
3642 *deftype
= ROFFDEF_USER
;
3649 for (n
= r
->rentab
; n
!= NULL
; n
= n
->next
) {
3650 if (strncmp(name
, n
->key
.p
, len
) != 0 ||
3651 n
->key
.p
[len
] != '\0' || n
->val
.p
== NULL
)
3653 if (*deftype
& ROFFDEF_REN
) {
3654 *deftype
= ROFFDEF_REN
;
3661 for (i
= 0; i
< PREDEFS_MAX
; i
++) {
3662 if (strncmp(name
, predefs
[i
].name
, len
) != 0 ||
3663 predefs
[i
].name
[len
] != '\0')
3665 if (*deftype
& ROFFDEF_PRE
) {
3666 *deftype
= ROFFDEF_PRE
;
3667 return predefs
[i
].str
;
3673 if (r
->man
->macroset
!= MACROSET_MAN
) {
3674 for (tok
= MDOC_Dd
; tok
< MDOC_MAX
; tok
++) {
3675 if (strncmp(name
, roff_name
[tok
], len
) != 0 ||
3676 roff_name
[tok
][len
] != '\0')
3678 if (*deftype
& ROFFDEF_STD
) {
3679 *deftype
= ROFFDEF_STD
;
3687 if (r
->man
->macroset
!= MACROSET_MDOC
) {
3688 for (tok
= MAN_TH
; tok
< MAN_MAX
; tok
++) {
3689 if (strncmp(name
, roff_name
[tok
], len
) != 0 ||
3690 roff_name
[tok
][len
] != '\0')
3692 if (*deftype
& ROFFDEF_STD
) {
3693 *deftype
= ROFFDEF_STD
;
3702 if (found
== 0 && *deftype
!= ROFFDEF_ANY
) {
3703 if (*deftype
& ROFFDEF_REN
) {
3705 * This might still be a request,
3706 * so do not treat it as undefined yet.
3708 *deftype
= ROFFDEF_UNDEF
;
3712 /* Using an undefined string defines it to be empty. */
3714 roff_setstrn(&r
->strtab
, name
, len
, "", 0, 0);
3715 roff_setstrn(&r
->rentab
, name
, len
, NULL
, 0, 0);
3723 roff_freestr(struct roffkv
*r
)
3725 struct roffkv
*n
, *nn
;
3727 for (n
= r
; n
; n
= nn
) {
3735 /* --- accessors and utility functions ------------------------------------ */
3738 * Duplicate an input string, making the appropriate character
3739 * conversations (as stipulated by `tr') along the way.
3740 * Returns a heap-allocated string with all the replacements made.
3743 roff_strdup(const struct roff
*r
, const char *p
)
3745 const struct roffkv
*cp
;
3749 enum mandoc_esc esc
;
3751 if (NULL
== r
->xmbtab
&& NULL
== r
->xtab
)
3752 return mandoc_strdup(p
);
3753 else if ('\0' == *p
)
3754 return mandoc_strdup("");
3757 * Step through each character looking for term matches
3758 * (remember that a `tr' can be invoked with an escape, which is
3759 * a glyph but the escape is multi-character).
3760 * We only do this if the character hash has been initialised
3761 * and the string is >0 length.
3767 while ('\0' != *p
) {
3768 assert((unsigned int)*p
< 128);
3769 if ('\\' != *p
&& r
->xtab
&& r
->xtab
[(unsigned int)*p
].p
) {
3770 sz
= r
->xtab
[(int)*p
].sz
;
3771 res
= mandoc_realloc(res
, ssz
+ sz
+ 1);
3772 memcpy(res
+ ssz
, r
->xtab
[(int)*p
].p
, sz
);
3776 } else if ('\\' != *p
) {
3777 res
= mandoc_realloc(res
, ssz
+ 2);
3782 /* Search for term matches. */
3783 for (cp
= r
->xmbtab
; cp
; cp
= cp
->next
)
3784 if (0 == strncmp(p
, cp
->key
.p
, cp
->key
.sz
))
3789 * A match has been found.
3790 * Append the match to the array and move
3791 * forward by its keysize.
3793 res
= mandoc_realloc(res
,
3794 ssz
+ cp
->val
.sz
+ 1);
3795 memcpy(res
+ ssz
, cp
->val
.p
, cp
->val
.sz
);
3797 p
+= (int)cp
->key
.sz
;
3802 * Handle escapes carefully: we need to copy
3803 * over just the escape itself, or else we might
3804 * do replacements within the escape itself.
3805 * Make sure to pass along the bogus string.
3808 esc
= mandoc_escape(&p
, NULL
, NULL
);
3809 if (ESCAPE_ERROR
== esc
) {
3811 res
= mandoc_realloc(res
, ssz
+ sz
+ 1);
3812 memcpy(res
+ ssz
, pp
, sz
);
3816 * We bail out on bad escapes.
3817 * No need to warn: we already did so when
3818 * roff_res() was called.
3821 res
= mandoc_realloc(res
, ssz
+ sz
+ 1);
3822 memcpy(res
+ ssz
, pp
, sz
);
3826 res
[(int)ssz
] = '\0';
3831 roff_getformat(const struct roff
*r
)
3838 * Find out whether a line is a macro line or not.
3839 * If it is, adjust the current position and return one; if it isn't,
3840 * return zero and don't change the current position.
3841 * If the control character has been set with `.cc', then let that grain
3843 * This is slighly contrary to groff, where using the non-breaking
3844 * control character when `cc' has been invoked will cause the
3845 * non-breaking macro contents to be printed verbatim.
3848 roff_getcontrol(const struct roff
*r
, const char *cp
, int *ppos
)
3854 if (r
->control
!= '\0' && cp
[pos
] == r
->control
)
3856 else if (r
->control
!= '\0')
3858 else if ('\\' == cp
[pos
] && '.' == cp
[pos
+ 1])
3860 else if ('.' == cp
[pos
] || '\'' == cp
[pos
])
3865 while (' ' == cp
[pos
] || '\t' == cp
[pos
])