1 |
|
|
/* $OpenBSD: roff.c,v 1.156 2016/01/08 17:48:04 schwarze Exp $ */ |
2 |
|
|
/* |
3 |
|
|
* Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv> |
4 |
|
|
* Copyright (c) 2010-2015 Ingo Schwarze <schwarze@openbsd.org> |
5 |
|
|
* |
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. |
9 |
|
|
* |
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. |
17 |
|
|
*/ |
18 |
|
|
#include <sys/types.h> |
19 |
|
|
|
20 |
|
|
#include <assert.h> |
21 |
|
|
#include <ctype.h> |
22 |
|
|
#include <limits.h> |
23 |
|
|
#include <stdio.h> |
24 |
|
|
#include <stdlib.h> |
25 |
|
|
#include <string.h> |
26 |
|
|
|
27 |
|
|
#include "mandoc.h" |
28 |
|
|
#include "mandoc_aux.h" |
29 |
|
|
#include "roff.h" |
30 |
|
|
#include "libmandoc.h" |
31 |
|
|
#include "roff_int.h" |
32 |
|
|
#include "libroff.h" |
33 |
|
|
|
34 |
|
|
/* Maximum number of string expansions per line, to break infinite loops. */ |
35 |
|
|
#define EXPAND_LIMIT 1000 |
36 |
|
|
|
37 |
|
|
/* --- data types --------------------------------------------------------- */ |
38 |
|
|
|
39 |
|
|
enum rofft { |
40 |
|
|
ROFF_ab, |
41 |
|
|
ROFF_ad, |
42 |
|
|
ROFF_af, |
43 |
|
|
ROFF_aln, |
44 |
|
|
ROFF_als, |
45 |
|
|
ROFF_am, |
46 |
|
|
ROFF_am1, |
47 |
|
|
ROFF_ami, |
48 |
|
|
ROFF_ami1, |
49 |
|
|
ROFF_as, |
50 |
|
|
ROFF_as1, |
51 |
|
|
ROFF_asciify, |
52 |
|
|
ROFF_backtrace, |
53 |
|
|
ROFF_bd, |
54 |
|
|
ROFF_bleedat, |
55 |
|
|
ROFF_blm, |
56 |
|
|
ROFF_box, |
57 |
|
|
ROFF_boxa, |
58 |
|
|
ROFF_bp, |
59 |
|
|
ROFF_BP, |
60 |
|
|
/* MAN_br, MDOC_br */ |
61 |
|
|
ROFF_break, |
62 |
|
|
ROFF_breakchar, |
63 |
|
|
ROFF_brnl, |
64 |
|
|
ROFF_brp, |
65 |
|
|
ROFF_brpnl, |
66 |
|
|
ROFF_c2, |
67 |
|
|
ROFF_cc, |
68 |
|
|
ROFF_ce, |
69 |
|
|
ROFF_cf, |
70 |
|
|
ROFF_cflags, |
71 |
|
|
ROFF_ch, |
72 |
|
|
ROFF_char, |
73 |
|
|
ROFF_chop, |
74 |
|
|
ROFF_class, |
75 |
|
|
ROFF_close, |
76 |
|
|
ROFF_CL, |
77 |
|
|
ROFF_color, |
78 |
|
|
ROFF_composite, |
79 |
|
|
ROFF_continue, |
80 |
|
|
ROFF_cp, |
81 |
|
|
ROFF_cropat, |
82 |
|
|
ROFF_cs, |
83 |
|
|
ROFF_cu, |
84 |
|
|
ROFF_da, |
85 |
|
|
ROFF_dch, |
86 |
|
|
ROFF_Dd, |
87 |
|
|
ROFF_de, |
88 |
|
|
ROFF_de1, |
89 |
|
|
ROFF_defcolor, |
90 |
|
|
ROFF_dei, |
91 |
|
|
ROFF_dei1, |
92 |
|
|
ROFF_device, |
93 |
|
|
ROFF_devicem, |
94 |
|
|
ROFF_di, |
95 |
|
|
ROFF_do, |
96 |
|
|
ROFF_ds, |
97 |
|
|
ROFF_ds1, |
98 |
|
|
ROFF_dwh, |
99 |
|
|
ROFF_dt, |
100 |
|
|
ROFF_ec, |
101 |
|
|
ROFF_ecr, |
102 |
|
|
ROFF_ecs, |
103 |
|
|
ROFF_el, |
104 |
|
|
ROFF_em, |
105 |
|
|
ROFF_EN, |
106 |
|
|
ROFF_eo, |
107 |
|
|
ROFF_EP, |
108 |
|
|
ROFF_EQ, |
109 |
|
|
ROFF_errprint, |
110 |
|
|
ROFF_ev, |
111 |
|
|
ROFF_evc, |
112 |
|
|
ROFF_ex, |
113 |
|
|
ROFF_fallback, |
114 |
|
|
ROFF_fam, |
115 |
|
|
ROFF_fc, |
116 |
|
|
ROFF_fchar, |
117 |
|
|
ROFF_fcolor, |
118 |
|
|
ROFF_fdeferlig, |
119 |
|
|
ROFF_feature, |
120 |
|
|
/* MAN_fi; ignored in mdoc(7) */ |
121 |
|
|
ROFF_fkern, |
122 |
|
|
ROFF_fl, |
123 |
|
|
ROFF_flig, |
124 |
|
|
ROFF_fp, |
125 |
|
|
ROFF_fps, |
126 |
|
|
ROFF_fschar, |
127 |
|
|
ROFF_fspacewidth, |
128 |
|
|
ROFF_fspecial, |
129 |
|
|
/* MAN_ft; ignored in mdoc(7) */ |
130 |
|
|
ROFF_ftr, |
131 |
|
|
ROFF_fzoom, |
132 |
|
|
ROFF_gcolor, |
133 |
|
|
ROFF_hc, |
134 |
|
|
ROFF_hcode, |
135 |
|
|
ROFF_hidechar, |
136 |
|
|
ROFF_hla, |
137 |
|
|
ROFF_hlm, |
138 |
|
|
ROFF_hpf, |
139 |
|
|
ROFF_hpfa, |
140 |
|
|
ROFF_hpfcode, |
141 |
|
|
ROFF_hw, |
142 |
|
|
ROFF_hy, |
143 |
|
|
ROFF_hylang, |
144 |
|
|
ROFF_hylen, |
145 |
|
|
ROFF_hym, |
146 |
|
|
ROFF_hypp, |
147 |
|
|
ROFF_hys, |
148 |
|
|
ROFF_ie, |
149 |
|
|
ROFF_if, |
150 |
|
|
ROFF_ig, |
151 |
|
|
/* MAN_in; ignored in mdoc(7) */ |
152 |
|
|
ROFF_index, |
153 |
|
|
ROFF_it, |
154 |
|
|
ROFF_itc, |
155 |
|
|
ROFF_IX, |
156 |
|
|
ROFF_kern, |
157 |
|
|
ROFF_kernafter, |
158 |
|
|
ROFF_kernbefore, |
159 |
|
|
ROFF_kernpair, |
160 |
|
|
ROFF_lc, |
161 |
|
|
ROFF_lc_ctype, |
162 |
|
|
ROFF_lds, |
163 |
|
|
ROFF_length, |
164 |
|
|
ROFF_letadj, |
165 |
|
|
ROFF_lf, |
166 |
|
|
ROFF_lg, |
167 |
|
|
ROFF_lhang, |
168 |
|
|
ROFF_linetabs, |
169 |
|
|
/* MAN_ll, MDOC_ll */ |
170 |
|
|
ROFF_lnr, |
171 |
|
|
ROFF_lnrf, |
172 |
|
|
ROFF_lpfx, |
173 |
|
|
ROFF_ls, |
174 |
|
|
ROFF_lsm, |
175 |
|
|
ROFF_lt, |
176 |
|
|
ROFF_mc, |
177 |
|
|
ROFF_mediasize, |
178 |
|
|
ROFF_minss, |
179 |
|
|
ROFF_mk, |
180 |
|
|
ROFF_mso, |
181 |
|
|
ROFF_na, |
182 |
|
|
ROFF_ne, |
183 |
|
|
/* MAN_nf; ignored in mdoc(7) */ |
184 |
|
|
ROFF_nh, |
185 |
|
|
ROFF_nhychar, |
186 |
|
|
ROFF_nm, |
187 |
|
|
ROFF_nn, |
188 |
|
|
ROFF_nop, |
189 |
|
|
ROFF_nr, |
190 |
|
|
ROFF_nrf, |
191 |
|
|
ROFF_nroff, |
192 |
|
|
ROFF_ns, |
193 |
|
|
ROFF_nx, |
194 |
|
|
ROFF_open, |
195 |
|
|
ROFF_opena, |
196 |
|
|
ROFF_os, |
197 |
|
|
ROFF_output, |
198 |
|
|
ROFF_padj, |
199 |
|
|
ROFF_papersize, |
200 |
|
|
ROFF_pc, |
201 |
|
|
ROFF_pev, |
202 |
|
|
ROFF_pi, |
203 |
|
|
ROFF_PI, |
204 |
|
|
ROFF_pl, |
205 |
|
|
ROFF_pm, |
206 |
|
|
ROFF_pn, |
207 |
|
|
ROFF_pnr, |
208 |
|
|
ROFF_po, |
209 |
|
|
ROFF_ps, |
210 |
|
|
ROFF_psbb, |
211 |
|
|
ROFF_pshape, |
212 |
|
|
ROFF_pso, |
213 |
|
|
ROFF_ptr, |
214 |
|
|
ROFF_pvs, |
215 |
|
|
ROFF_rchar, |
216 |
|
|
ROFF_rd, |
217 |
|
|
ROFF_recursionlimit, |
218 |
|
|
ROFF_return, |
219 |
|
|
ROFF_rfschar, |
220 |
|
|
ROFF_rhang, |
221 |
|
|
ROFF_rj, |
222 |
|
|
ROFF_rm, |
223 |
|
|
ROFF_rn, |
224 |
|
|
ROFF_rnn, |
225 |
|
|
ROFF_rr, |
226 |
|
|
ROFF_rs, |
227 |
|
|
ROFF_rt, |
228 |
|
|
ROFF_schar, |
229 |
|
|
ROFF_sentchar, |
230 |
|
|
ROFF_shc, |
231 |
|
|
ROFF_shift, |
232 |
|
|
ROFF_sizes, |
233 |
|
|
ROFF_so, |
234 |
|
|
/* MAN_sp, MDOC_sp */ |
235 |
|
|
ROFF_spacewidth, |
236 |
|
|
ROFF_special, |
237 |
|
|
ROFF_spreadwarn, |
238 |
|
|
ROFF_ss, |
239 |
|
|
ROFF_sty, |
240 |
|
|
ROFF_substring, |
241 |
|
|
ROFF_sv, |
242 |
|
|
ROFF_sy, |
243 |
|
|
ROFF_T_, |
244 |
|
|
ROFF_ta, |
245 |
|
|
ROFF_tc, |
246 |
|
|
ROFF_TE, |
247 |
|
|
ROFF_TH, |
248 |
|
|
ROFF_ti, |
249 |
|
|
ROFF_tkf, |
250 |
|
|
ROFF_tl, |
251 |
|
|
ROFF_tm, |
252 |
|
|
ROFF_tm1, |
253 |
|
|
ROFF_tmc, |
254 |
|
|
ROFF_tr, |
255 |
|
|
ROFF_track, |
256 |
|
|
ROFF_transchar, |
257 |
|
|
ROFF_trf, |
258 |
|
|
ROFF_trimat, |
259 |
|
|
ROFF_trin, |
260 |
|
|
ROFF_trnt, |
261 |
|
|
ROFF_troff, |
262 |
|
|
ROFF_TS, |
263 |
|
|
ROFF_uf, |
264 |
|
|
ROFF_ul, |
265 |
|
|
ROFF_unformat, |
266 |
|
|
ROFF_unwatch, |
267 |
|
|
ROFF_unwatchn, |
268 |
|
|
ROFF_vpt, |
269 |
|
|
ROFF_vs, |
270 |
|
|
ROFF_warn, |
271 |
|
|
ROFF_warnscale, |
272 |
|
|
ROFF_watch, |
273 |
|
|
ROFF_watchlength, |
274 |
|
|
ROFF_watchn, |
275 |
|
|
ROFF_wh, |
276 |
|
|
ROFF_while, |
277 |
|
|
ROFF_write, |
278 |
|
|
ROFF_writec, |
279 |
|
|
ROFF_writem, |
280 |
|
|
ROFF_xflag, |
281 |
|
|
ROFF_cblock, |
282 |
|
|
ROFF_USERDEF, |
283 |
|
|
ROFF_MAX |
284 |
|
|
}; |
285 |
|
|
|
286 |
|
|
/* |
287 |
|
|
* An incredibly-simple string buffer. |
288 |
|
|
*/ |
289 |
|
|
struct roffstr { |
290 |
|
|
char *p; /* nil-terminated buffer */ |
291 |
|
|
size_t sz; /* saved strlen(p) */ |
292 |
|
|
}; |
293 |
|
|
|
294 |
|
|
/* |
295 |
|
|
* A key-value roffstr pair as part of a singly-linked list. |
296 |
|
|
*/ |
297 |
|
|
struct roffkv { |
298 |
|
|
struct roffstr key; |
299 |
|
|
struct roffstr val; |
300 |
|
|
struct roffkv *next; /* next in list */ |
301 |
|
|
}; |
302 |
|
|
|
303 |
|
|
/* |
304 |
|
|
* A single number register as part of a singly-linked list. |
305 |
|
|
*/ |
306 |
|
|
struct roffreg { |
307 |
|
|
struct roffstr key; |
308 |
|
|
int val; |
309 |
|
|
struct roffreg *next; |
310 |
|
|
}; |
311 |
|
|
|
312 |
|
|
struct roff { |
313 |
|
|
struct mparse *parse; /* parse point */ |
314 |
|
|
struct roffnode *last; /* leaf of stack */ |
315 |
|
|
int *rstack; /* stack of inverted `ie' values */ |
316 |
|
|
struct roffreg *regtab; /* number registers */ |
317 |
|
|
struct roffkv *strtab; /* user-defined strings & macros */ |
318 |
|
|
struct roffkv *xmbtab; /* multi-byte trans table (`tr') */ |
319 |
|
|
struct roffstr *xtab; /* single-byte trans table (`tr') */ |
320 |
|
|
const char *current_string; /* value of last called user macro */ |
321 |
|
|
struct tbl_node *first_tbl; /* first table parsed */ |
322 |
|
|
struct tbl_node *last_tbl; /* last table parsed */ |
323 |
|
|
struct tbl_node *tbl; /* current table being parsed */ |
324 |
|
|
struct eqn_node *last_eqn; /* last equation parsed */ |
325 |
|
|
struct eqn_node *first_eqn; /* first equation parsed */ |
326 |
|
|
struct eqn_node *eqn; /* current equation being parsed */ |
327 |
|
|
int eqn_inline; /* current equation is inline */ |
328 |
|
|
int options; /* parse options */ |
329 |
|
|
int rstacksz; /* current size limit of rstack */ |
330 |
|
|
int rstackpos; /* position in rstack */ |
331 |
|
|
int format; /* current file in mdoc or man format */ |
332 |
|
|
int argc; /* number of args of the last macro */ |
333 |
|
|
char control; /* control character */ |
334 |
|
|
}; |
335 |
|
|
|
336 |
|
|
struct roffnode { |
337 |
|
|
enum rofft tok; /* type of node */ |
338 |
|
|
struct roffnode *parent; /* up one in stack */ |
339 |
|
|
int line; /* parse line */ |
340 |
|
|
int col; /* parse col */ |
341 |
|
|
char *name; /* node name, e.g. macro name */ |
342 |
|
|
char *end; /* end-rules: custom token */ |
343 |
|
|
int endspan; /* end-rules: next-line or infty */ |
344 |
|
|
int rule; /* current evaluation rule */ |
345 |
|
|
}; |
346 |
|
|
|
347 |
|
|
#define ROFF_ARGS struct roff *r, /* parse ctx */ \ |
348 |
|
|
enum rofft tok, /* tok of macro */ \ |
349 |
|
|
struct buf *buf, /* input buffer */ \ |
350 |
|
|
int ln, /* parse line */ \ |
351 |
|
|
int ppos, /* original pos in buffer */ \ |
352 |
|
|
int pos, /* current pos in buffer */ \ |
353 |
|
|
int *offs /* reset offset of buffer data */ |
354 |
|
|
|
355 |
|
|
typedef enum rofferr (*roffproc)(ROFF_ARGS); |
356 |
|
|
|
357 |
|
|
struct roffmac { |
358 |
|
|
const char *name; /* macro name */ |
359 |
|
|
roffproc proc; /* process new macro */ |
360 |
|
|
roffproc text; /* process as child text of macro */ |
361 |
|
|
roffproc sub; /* process as child of macro */ |
362 |
|
|
int flags; |
363 |
|
|
#define ROFFMAC_STRUCT (1 << 0) /* always interpret */ |
364 |
|
|
struct roffmac *next; |
365 |
|
|
}; |
366 |
|
|
|
367 |
|
|
struct predef { |
368 |
|
|
const char *name; /* predefined input name */ |
369 |
|
|
const char *str; /* replacement symbol */ |
370 |
|
|
}; |
371 |
|
|
|
372 |
|
|
#define PREDEF(__name, __str) \ |
373 |
|
|
{ (__name), (__str) }, |
374 |
|
|
|
375 |
|
|
/* --- function prototypes ------------------------------------------------ */ |
376 |
|
|
|
377 |
|
|
static enum rofft roffhash_find(const char *, size_t); |
378 |
|
|
static void roffhash_init(void); |
379 |
|
|
static void roffnode_cleanscope(struct roff *); |
380 |
|
|
static void roffnode_pop(struct roff *); |
381 |
|
|
static void roffnode_push(struct roff *, enum rofft, |
382 |
|
|
const char *, int, int); |
383 |
|
|
static enum rofferr roff_block(ROFF_ARGS); |
384 |
|
|
static enum rofferr roff_block_text(ROFF_ARGS); |
385 |
|
|
static enum rofferr roff_block_sub(ROFF_ARGS); |
386 |
|
|
static enum rofferr roff_brp(ROFF_ARGS); |
387 |
|
|
static enum rofferr roff_cblock(ROFF_ARGS); |
388 |
|
|
static enum rofferr roff_cc(ROFF_ARGS); |
389 |
|
|
static void roff_ccond(struct roff *, int, int); |
390 |
|
|
static enum rofferr roff_cond(ROFF_ARGS); |
391 |
|
|
static enum rofferr roff_cond_text(ROFF_ARGS); |
392 |
|
|
static enum rofferr roff_cond_sub(ROFF_ARGS); |
393 |
|
|
static enum rofferr roff_ds(ROFF_ARGS); |
394 |
|
|
static enum rofferr roff_eqndelim(struct roff *, struct buf *, int); |
395 |
|
|
static int roff_evalcond(struct roff *r, int, char *, int *); |
396 |
|
|
static int roff_evalnum(struct roff *, int, |
397 |
|
|
const char *, int *, int *, int); |
398 |
|
|
static int roff_evalpar(struct roff *, int, |
399 |
|
|
const char *, int *, int *, int); |
400 |
|
|
static int roff_evalstrcond(const char *, int *); |
401 |
|
|
static void roff_free1(struct roff *); |
402 |
|
|
static void roff_freereg(struct roffreg *); |
403 |
|
|
static void roff_freestr(struct roffkv *); |
404 |
|
|
static size_t roff_getname(struct roff *, char **, int, int); |
405 |
|
|
static int roff_getnum(const char *, int *, int *, int); |
406 |
|
|
static int roff_getop(const char *, int *, char *); |
407 |
|
|
static int roff_getregn(const struct roff *, |
408 |
|
|
const char *, size_t); |
409 |
|
|
static int roff_getregro(const struct roff *, |
410 |
|
|
const char *name); |
411 |
|
|
static const char *roff_getstrn(const struct roff *, |
412 |
|
|
const char *, size_t); |
413 |
|
|
static int roff_hasregn(const struct roff *, |
414 |
|
|
const char *, size_t); |
415 |
|
|
static enum rofferr roff_insec(ROFF_ARGS); |
416 |
|
|
static enum rofferr roff_it(ROFF_ARGS); |
417 |
|
|
static enum rofferr roff_line_ignore(ROFF_ARGS); |
418 |
|
|
static void roff_man_alloc1(struct roff_man *); |
419 |
|
|
static void roff_man_free1(struct roff_man *); |
420 |
|
|
static enum rofferr roff_nr(ROFF_ARGS); |
421 |
|
|
static enum rofft roff_parse(struct roff *, char *, int *, |
422 |
|
|
int, int); |
423 |
|
|
static enum rofferr roff_parsetext(struct buf *, int, int *); |
424 |
|
|
static enum rofferr roff_res(struct roff *, struct buf *, int, int); |
425 |
|
|
static enum rofferr roff_rm(ROFF_ARGS); |
426 |
|
|
static enum rofferr roff_rr(ROFF_ARGS); |
427 |
|
|
static void roff_setstr(struct roff *, |
428 |
|
|
const char *, const char *, int); |
429 |
|
|
static void roff_setstrn(struct roffkv **, const char *, |
430 |
|
|
size_t, const char *, size_t, int); |
431 |
|
|
static enum rofferr roff_so(ROFF_ARGS); |
432 |
|
|
static enum rofferr roff_tr(ROFF_ARGS); |
433 |
|
|
static enum rofferr roff_Dd(ROFF_ARGS); |
434 |
|
|
static enum rofferr roff_TH(ROFF_ARGS); |
435 |
|
|
static enum rofferr roff_TE(ROFF_ARGS); |
436 |
|
|
static enum rofferr roff_TS(ROFF_ARGS); |
437 |
|
|
static enum rofferr roff_EQ(ROFF_ARGS); |
438 |
|
|
static enum rofferr roff_EN(ROFF_ARGS); |
439 |
|
|
static enum rofferr roff_T_(ROFF_ARGS); |
440 |
|
|
static enum rofferr roff_unsupp(ROFF_ARGS); |
441 |
|
|
static enum rofferr roff_userdef(ROFF_ARGS); |
442 |
|
|
|
443 |
|
|
/* --- constant data ------------------------------------------------------ */ |
444 |
|
|
|
445 |
|
|
/* See roffhash_find() */ |
446 |
|
|
|
447 |
|
|
#define ASCII_HI 126 |
448 |
|
|
#define ASCII_LO 33 |
449 |
|
|
#define HASHWIDTH (ASCII_HI - ASCII_LO + 1) |
450 |
|
|
|
451 |
|
|
#define ROFFNUM_SCALE (1 << 0) /* Honour scaling in roff_getnum(). */ |
452 |
|
|
#define ROFFNUM_WHITE (1 << 1) /* Skip whitespace in roff_evalnum(). */ |
453 |
|
|
|
454 |
|
|
static struct roffmac *hash[HASHWIDTH]; |
455 |
|
|
|
456 |
|
|
static struct roffmac roffs[ROFF_MAX] = { |
457 |
|
|
{ "ab", roff_unsupp, NULL, NULL, 0, NULL }, |
458 |
|
|
{ "ad", roff_line_ignore, NULL, NULL, 0, NULL }, |
459 |
|
|
{ "af", roff_line_ignore, NULL, NULL, 0, NULL }, |
460 |
|
|
{ "aln", roff_unsupp, NULL, NULL, 0, NULL }, |
461 |
|
|
{ "als", roff_unsupp, NULL, NULL, 0, NULL }, |
462 |
|
|
{ "am", roff_block, roff_block_text, roff_block_sub, 0, NULL }, |
463 |
|
|
{ "am1", roff_block, roff_block_text, roff_block_sub, 0, NULL }, |
464 |
|
|
{ "ami", roff_block, roff_block_text, roff_block_sub, 0, NULL }, |
465 |
|
|
{ "ami1", roff_block, roff_block_text, roff_block_sub, 0, NULL }, |
466 |
|
|
{ "as", roff_ds, NULL, NULL, 0, NULL }, |
467 |
|
|
{ "as1", roff_ds, NULL, NULL, 0, NULL }, |
468 |
|
|
{ "asciify", roff_unsupp, NULL, NULL, 0, NULL }, |
469 |
|
|
{ "backtrace", roff_line_ignore, NULL, NULL, 0, NULL }, |
470 |
|
|
{ "bd", roff_line_ignore, NULL, NULL, 0, NULL }, |
471 |
|
|
{ "bleedat", roff_line_ignore, NULL, NULL, 0, NULL }, |
472 |
|
|
{ "blm", roff_unsupp, NULL, NULL, 0, NULL }, |
473 |
|
|
{ "box", roff_unsupp, NULL, NULL, 0, NULL }, |
474 |
|
|
{ "boxa", roff_unsupp, NULL, NULL, 0, NULL }, |
475 |
|
|
{ "bp", roff_line_ignore, NULL, NULL, 0, NULL }, |
476 |
|
|
{ "BP", roff_unsupp, NULL, NULL, 0, NULL }, |
477 |
|
|
{ "break", roff_unsupp, NULL, NULL, 0, NULL }, |
478 |
|
|
{ "breakchar", roff_line_ignore, NULL, NULL, 0, NULL }, |
479 |
|
|
{ "brnl", roff_line_ignore, NULL, NULL, 0, NULL }, |
480 |
|
|
{ "brp", roff_brp, NULL, NULL, 0, NULL }, |
481 |
|
|
{ "brpnl", roff_line_ignore, NULL, NULL, 0, NULL }, |
482 |
|
|
{ "c2", roff_unsupp, NULL, NULL, 0, NULL }, |
483 |
|
|
{ "cc", roff_cc, NULL, NULL, 0, NULL }, |
484 |
|
|
{ "ce", roff_line_ignore, NULL, NULL, 0, NULL }, |
485 |
|
|
{ "cf", roff_insec, NULL, NULL, 0, NULL }, |
486 |
|
|
{ "cflags", roff_line_ignore, NULL, NULL, 0, NULL }, |
487 |
|
|
{ "ch", roff_line_ignore, NULL, NULL, 0, NULL }, |
488 |
|
|
{ "char", roff_unsupp, NULL, NULL, 0, NULL }, |
489 |
|
|
{ "chop", roff_unsupp, NULL, NULL, 0, NULL }, |
490 |
|
|
{ "class", roff_line_ignore, NULL, NULL, 0, NULL }, |
491 |
|
|
{ "close", roff_insec, NULL, NULL, 0, NULL }, |
492 |
|
|
{ "CL", roff_unsupp, NULL, NULL, 0, NULL }, |
493 |
|
|
{ "color", roff_line_ignore, NULL, NULL, 0, NULL }, |
494 |
|
|
{ "composite", roff_unsupp, NULL, NULL, 0, NULL }, |
495 |
|
|
{ "continue", roff_unsupp, NULL, NULL, 0, NULL }, |
496 |
|
|
{ "cp", roff_line_ignore, NULL, NULL, 0, NULL }, |
497 |
|
|
{ "cropat", roff_line_ignore, NULL, NULL, 0, NULL }, |
498 |
|
|
{ "cs", roff_line_ignore, NULL, NULL, 0, NULL }, |
499 |
|
|
{ "cu", roff_line_ignore, NULL, NULL, 0, NULL }, |
500 |
|
|
{ "da", roff_unsupp, NULL, NULL, 0, NULL }, |
501 |
|
|
{ "dch", roff_unsupp, NULL, NULL, 0, NULL }, |
502 |
|
|
{ "Dd", roff_Dd, NULL, NULL, 0, NULL }, |
503 |
|
|
{ "de", roff_block, roff_block_text, roff_block_sub, 0, NULL }, |
504 |
|
|
{ "de1", roff_block, roff_block_text, roff_block_sub, 0, NULL }, |
505 |
|
|
{ "defcolor", roff_line_ignore, NULL, NULL, 0, NULL }, |
506 |
|
|
{ "dei", roff_block, roff_block_text, roff_block_sub, 0, NULL }, |
507 |
|
|
{ "dei1", roff_block, roff_block_text, roff_block_sub, 0, NULL }, |
508 |
|
|
{ "device", roff_unsupp, NULL, NULL, 0, NULL }, |
509 |
|
|
{ "devicem", roff_unsupp, NULL, NULL, 0, NULL }, |
510 |
|
|
{ "di", roff_unsupp, NULL, NULL, 0, NULL }, |
511 |
|
|
{ "do", roff_unsupp, NULL, NULL, 0, NULL }, |
512 |
|
|
{ "ds", roff_ds, NULL, NULL, 0, NULL }, |
513 |
|
|
{ "ds1", roff_ds, NULL, NULL, 0, NULL }, |
514 |
|
|
{ "dwh", roff_unsupp, NULL, NULL, 0, NULL }, |
515 |
|
|
{ "dt", roff_unsupp, NULL, NULL, 0, NULL }, |
516 |
|
|
{ "ec", roff_unsupp, NULL, NULL, 0, NULL }, |
517 |
|
|
{ "ecr", roff_unsupp, NULL, NULL, 0, NULL }, |
518 |
|
|
{ "ecs", roff_unsupp, NULL, NULL, 0, NULL }, |
519 |
|
|
{ "el", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL }, |
520 |
|
|
{ "em", roff_unsupp, NULL, NULL, 0, NULL }, |
521 |
|
|
{ "EN", roff_EN, NULL, NULL, 0, NULL }, |
522 |
|
|
{ "eo", roff_unsupp, NULL, NULL, 0, NULL }, |
523 |
|
|
{ "EP", roff_unsupp, NULL, NULL, 0, NULL }, |
524 |
|
|
{ "EQ", roff_EQ, NULL, NULL, 0, NULL }, |
525 |
|
|
{ "errprint", roff_line_ignore, NULL, NULL, 0, NULL }, |
526 |
|
|
{ "ev", roff_unsupp, NULL, NULL, 0, NULL }, |
527 |
|
|
{ "evc", roff_unsupp, NULL, NULL, 0, NULL }, |
528 |
|
|
{ "ex", roff_unsupp, NULL, NULL, 0, NULL }, |
529 |
|
|
{ "fallback", roff_line_ignore, NULL, NULL, 0, NULL }, |
530 |
|
|
{ "fam", roff_line_ignore, NULL, NULL, 0, NULL }, |
531 |
|
|
{ "fc", roff_unsupp, NULL, NULL, 0, NULL }, |
532 |
|
|
{ "fchar", roff_unsupp, NULL, NULL, 0, NULL }, |
533 |
|
|
{ "fcolor", roff_line_ignore, NULL, NULL, 0, NULL }, |
534 |
|
|
{ "fdeferlig", roff_line_ignore, NULL, NULL, 0, NULL }, |
535 |
|
|
{ "feature", roff_line_ignore, NULL, NULL, 0, NULL }, |
536 |
|
|
{ "fkern", roff_line_ignore, NULL, NULL, 0, NULL }, |
537 |
|
|
{ "fl", roff_line_ignore, NULL, NULL, 0, NULL }, |
538 |
|
|
{ "flig", roff_line_ignore, NULL, NULL, 0, NULL }, |
539 |
|
|
{ "fp", roff_line_ignore, NULL, NULL, 0, NULL }, |
540 |
|
|
{ "fps", roff_line_ignore, NULL, NULL, 0, NULL }, |
541 |
|
|
{ "fschar", roff_unsupp, NULL, NULL, 0, NULL }, |
542 |
|
|
{ "fspacewidth", roff_line_ignore, NULL, NULL, 0, NULL }, |
543 |
|
|
{ "fspecial", roff_line_ignore, NULL, NULL, 0, NULL }, |
544 |
|
|
{ "ftr", roff_line_ignore, NULL, NULL, 0, NULL }, |
545 |
|
|
{ "fzoom", roff_line_ignore, NULL, NULL, 0, NULL }, |
546 |
|
|
{ "gcolor", roff_line_ignore, NULL, NULL, 0, NULL }, |
547 |
|
|
{ "hc", roff_line_ignore, NULL, NULL, 0, NULL }, |
548 |
|
|
{ "hcode", roff_line_ignore, NULL, NULL, 0, NULL }, |
549 |
|
|
{ "hidechar", roff_line_ignore, NULL, NULL, 0, NULL }, |
550 |
|
|
{ "hla", roff_line_ignore, NULL, NULL, 0, NULL }, |
551 |
|
|
{ "hlm", roff_line_ignore, NULL, NULL, 0, NULL }, |
552 |
|
|
{ "hpf", roff_line_ignore, NULL, NULL, 0, NULL }, |
553 |
|
|
{ "hpfa", roff_line_ignore, NULL, NULL, 0, NULL }, |
554 |
|
|
{ "hpfcode", roff_line_ignore, NULL, NULL, 0, NULL }, |
555 |
|
|
{ "hw", roff_line_ignore, NULL, NULL, 0, NULL }, |
556 |
|
|
{ "hy", roff_line_ignore, NULL, NULL, 0, NULL }, |
557 |
|
|
{ "hylang", roff_line_ignore, NULL, NULL, 0, NULL }, |
558 |
|
|
{ "hylen", roff_line_ignore, NULL, NULL, 0, NULL }, |
559 |
|
|
{ "hym", roff_line_ignore, NULL, NULL, 0, NULL }, |
560 |
|
|
{ "hypp", roff_line_ignore, NULL, NULL, 0, NULL }, |
561 |
|
|
{ "hys", roff_line_ignore, NULL, NULL, 0, NULL }, |
562 |
|
|
{ "ie", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL }, |
563 |
|
|
{ "if", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL }, |
564 |
|
|
{ "ig", roff_block, roff_block_text, roff_block_sub, 0, NULL }, |
565 |
|
|
{ "index", roff_unsupp, NULL, NULL, 0, NULL }, |
566 |
|
|
{ "it", roff_it, NULL, NULL, 0, NULL }, |
567 |
|
|
{ "itc", roff_unsupp, NULL, NULL, 0, NULL }, |
568 |
|
|
{ "IX", roff_line_ignore, NULL, NULL, 0, NULL }, |
569 |
|
|
{ "kern", roff_line_ignore, NULL, NULL, 0, NULL }, |
570 |
|
|
{ "kernafter", roff_line_ignore, NULL, NULL, 0, NULL }, |
571 |
|
|
{ "kernbefore", roff_line_ignore, NULL, NULL, 0, NULL }, |
572 |
|
|
{ "kernpair", roff_line_ignore, NULL, NULL, 0, NULL }, |
573 |
|
|
{ "lc", roff_unsupp, NULL, NULL, 0, NULL }, |
574 |
|
|
{ "lc_ctype", roff_unsupp, NULL, NULL, 0, NULL }, |
575 |
|
|
{ "lds", roff_unsupp, NULL, NULL, 0, NULL }, |
576 |
|
|
{ "length", roff_unsupp, NULL, NULL, 0, NULL }, |
577 |
|
|
{ "letadj", roff_line_ignore, NULL, NULL, 0, NULL }, |
578 |
|
|
{ "lf", roff_insec, NULL, NULL, 0, NULL }, |
579 |
|
|
{ "lg", roff_line_ignore, NULL, NULL, 0, NULL }, |
580 |
|
|
{ "lhang", roff_line_ignore, NULL, NULL, 0, NULL }, |
581 |
|
|
{ "linetabs", roff_unsupp, NULL, NULL, 0, NULL }, |
582 |
|
|
{ "lnr", roff_unsupp, NULL, NULL, 0, NULL }, |
583 |
|
|
{ "lnrf", roff_unsupp, NULL, NULL, 0, NULL }, |
584 |
|
|
{ "lpfx", roff_unsupp, NULL, NULL, 0, NULL }, |
585 |
|
|
{ "ls", roff_line_ignore, NULL, NULL, 0, NULL }, |
586 |
|
|
{ "lsm", roff_unsupp, NULL, NULL, 0, NULL }, |
587 |
|
|
{ "lt", roff_line_ignore, NULL, NULL, 0, NULL }, |
588 |
|
|
{ "mc", roff_line_ignore, NULL, NULL, 0, NULL }, |
589 |
|
|
{ "mediasize", roff_line_ignore, NULL, NULL, 0, NULL }, |
590 |
|
|
{ "minss", roff_line_ignore, NULL, NULL, 0, NULL }, |
591 |
|
|
{ "mk", roff_line_ignore, NULL, NULL, 0, NULL }, |
592 |
|
|
{ "mso", roff_insec, NULL, NULL, 0, NULL }, |
593 |
|
|
{ "na", roff_line_ignore, NULL, NULL, 0, NULL }, |
594 |
|
|
{ "ne", roff_line_ignore, NULL, NULL, 0, NULL }, |
595 |
|
|
{ "nh", roff_line_ignore, NULL, NULL, 0, NULL }, |
596 |
|
|
{ "nhychar", roff_line_ignore, NULL, NULL, 0, NULL }, |
597 |
|
|
{ "nm", roff_unsupp, NULL, NULL, 0, NULL }, |
598 |
|
|
{ "nn", roff_unsupp, NULL, NULL, 0, NULL }, |
599 |
|
|
{ "nop", roff_unsupp, NULL, NULL, 0, NULL }, |
600 |
|
|
{ "nr", roff_nr, NULL, NULL, 0, NULL }, |
601 |
|
|
{ "nrf", roff_unsupp, NULL, NULL, 0, NULL }, |
602 |
|
|
{ "nroff", roff_line_ignore, NULL, NULL, 0, NULL }, |
603 |
|
|
{ "ns", roff_line_ignore, NULL, NULL, 0, NULL }, |
604 |
|
|
{ "nx", roff_insec, NULL, NULL, 0, NULL }, |
605 |
|
|
{ "open", roff_insec, NULL, NULL, 0, NULL }, |
606 |
|
|
{ "opena", roff_insec, NULL, NULL, 0, NULL }, |
607 |
|
|
{ "os", roff_line_ignore, NULL, NULL, 0, NULL }, |
608 |
|
|
{ "output", roff_unsupp, NULL, NULL, 0, NULL }, |
609 |
|
|
{ "padj", roff_line_ignore, NULL, NULL, 0, NULL }, |
610 |
|
|
{ "papersize", roff_line_ignore, NULL, NULL, 0, NULL }, |
611 |
|
|
{ "pc", roff_line_ignore, NULL, NULL, 0, NULL }, |
612 |
|
|
{ "pev", roff_line_ignore, NULL, NULL, 0, NULL }, |
613 |
|
|
{ "pi", roff_insec, NULL, NULL, 0, NULL }, |
614 |
|
|
{ "PI", roff_unsupp, NULL, NULL, 0, NULL }, |
615 |
|
|
{ "pl", roff_line_ignore, NULL, NULL, 0, NULL }, |
616 |
|
|
{ "pm", roff_line_ignore, NULL, NULL, 0, NULL }, |
617 |
|
|
{ "pn", roff_line_ignore, NULL, NULL, 0, NULL }, |
618 |
|
|
{ "pnr", roff_line_ignore, NULL, NULL, 0, NULL }, |
619 |
|
|
{ "po", roff_line_ignore, NULL, NULL, 0, NULL }, |
620 |
|
|
{ "ps", roff_line_ignore, NULL, NULL, 0, NULL }, |
621 |
|
|
{ "psbb", roff_unsupp, NULL, NULL, 0, NULL }, |
622 |
|
|
{ "pshape", roff_unsupp, NULL, NULL, 0, NULL }, |
623 |
|
|
{ "pso", roff_insec, NULL, NULL, 0, NULL }, |
624 |
|
|
{ "ptr", roff_line_ignore, NULL, NULL, 0, NULL }, |
625 |
|
|
{ "pvs", roff_line_ignore, NULL, NULL, 0, NULL }, |
626 |
|
|
{ "rchar", roff_unsupp, NULL, NULL, 0, NULL }, |
627 |
|
|
{ "rd", roff_line_ignore, NULL, NULL, 0, NULL }, |
628 |
|
|
{ "recursionlimit", roff_line_ignore, NULL, NULL, 0, NULL }, |
629 |
|
|
{ "return", roff_unsupp, NULL, NULL, 0, NULL }, |
630 |
|
|
{ "rfschar", roff_unsupp, NULL, NULL, 0, NULL }, |
631 |
|
|
{ "rhang", roff_line_ignore, NULL, NULL, 0, NULL }, |
632 |
|
|
{ "rj", roff_line_ignore, NULL, NULL, 0, NULL }, |
633 |
|
|
{ "rm", roff_rm, NULL, NULL, 0, NULL }, |
634 |
|
|
{ "rn", roff_unsupp, NULL, NULL, 0, NULL }, |
635 |
|
|
{ "rnn", roff_unsupp, NULL, NULL, 0, NULL }, |
636 |
|
|
{ "rr", roff_rr, NULL, NULL, 0, NULL }, |
637 |
|
|
{ "rs", roff_line_ignore, NULL, NULL, 0, NULL }, |
638 |
|
|
{ "rt", roff_line_ignore, NULL, NULL, 0, NULL }, |
639 |
|
|
{ "schar", roff_unsupp, NULL, NULL, 0, NULL }, |
640 |
|
|
{ "sentchar", roff_line_ignore, NULL, NULL, 0, NULL }, |
641 |
|
|
{ "shc", roff_line_ignore, NULL, NULL, 0, NULL }, |
642 |
|
|
{ "shift", roff_unsupp, NULL, NULL, 0, NULL }, |
643 |
|
|
{ "sizes", roff_line_ignore, NULL, NULL, 0, NULL }, |
644 |
|
|
{ "so", roff_so, NULL, NULL, 0, NULL }, |
645 |
|
|
{ "spacewidth", roff_line_ignore, NULL, NULL, 0, NULL }, |
646 |
|
|
{ "special", roff_line_ignore, NULL, NULL, 0, NULL }, |
647 |
|
|
{ "spreadwarn", roff_line_ignore, NULL, NULL, 0, NULL }, |
648 |
|
|
{ "ss", roff_line_ignore, NULL, NULL, 0, NULL }, |
649 |
|
|
{ "sty", roff_line_ignore, NULL, NULL, 0, NULL }, |
650 |
|
|
{ "substring", roff_unsupp, NULL, NULL, 0, NULL }, |
651 |
|
|
{ "sv", roff_line_ignore, NULL, NULL, 0, NULL }, |
652 |
|
|
{ "sy", roff_insec, NULL, NULL, 0, NULL }, |
653 |
|
|
{ "T&", roff_T_, NULL, NULL, 0, NULL }, |
654 |
|
|
{ "ta", roff_unsupp, NULL, NULL, 0, NULL }, |
655 |
|
|
{ "tc", roff_unsupp, NULL, NULL, 0, NULL }, |
656 |
|
|
{ "TE", roff_TE, NULL, NULL, 0, NULL }, |
657 |
|
|
{ "TH", roff_TH, NULL, NULL, 0, NULL }, |
658 |
|
|
{ "ti", roff_unsupp, NULL, NULL, 0, NULL }, |
659 |
|
|
{ "tkf", roff_line_ignore, NULL, NULL, 0, NULL }, |
660 |
|
|
{ "tl", roff_unsupp, NULL, NULL, 0, NULL }, |
661 |
|
|
{ "tm", roff_line_ignore, NULL, NULL, 0, NULL }, |
662 |
|
|
{ "tm1", roff_line_ignore, NULL, NULL, 0, NULL }, |
663 |
|
|
{ "tmc", roff_line_ignore, NULL, NULL, 0, NULL }, |
664 |
|
|
{ "tr", roff_tr, NULL, NULL, 0, NULL }, |
665 |
|
|
{ "track", roff_line_ignore, NULL, NULL, 0, NULL }, |
666 |
|
|
{ "transchar", roff_line_ignore, NULL, NULL, 0, NULL }, |
667 |
|
|
{ "trf", roff_insec, NULL, NULL, 0, NULL }, |
668 |
|
|
{ "trimat", roff_line_ignore, NULL, NULL, 0, NULL }, |
669 |
|
|
{ "trin", roff_unsupp, NULL, NULL, 0, NULL }, |
670 |
|
|
{ "trnt", roff_unsupp, NULL, NULL, 0, NULL }, |
671 |
|
|
{ "troff", roff_line_ignore, NULL, NULL, 0, NULL }, |
672 |
|
|
{ "TS", roff_TS, NULL, NULL, 0, NULL }, |
673 |
|
|
{ "uf", roff_line_ignore, NULL, NULL, 0, NULL }, |
674 |
|
|
{ "ul", roff_line_ignore, NULL, NULL, 0, NULL }, |
675 |
|
|
{ "unformat", roff_unsupp, NULL, NULL, 0, NULL }, |
676 |
|
|
{ "unwatch", roff_line_ignore, NULL, NULL, 0, NULL }, |
677 |
|
|
{ "unwatchn", roff_line_ignore, NULL, NULL, 0, NULL }, |
678 |
|
|
{ "vpt", roff_line_ignore, NULL, NULL, 0, NULL }, |
679 |
|
|
{ "vs", roff_line_ignore, NULL, NULL, 0, NULL }, |
680 |
|
|
{ "warn", roff_line_ignore, NULL, NULL, 0, NULL }, |
681 |
|
|
{ "warnscale", roff_line_ignore, NULL, NULL, 0, NULL }, |
682 |
|
|
{ "watch", roff_line_ignore, NULL, NULL, 0, NULL }, |
683 |
|
|
{ "watchlength", roff_line_ignore, NULL, NULL, 0, NULL }, |
684 |
|
|
{ "watchn", roff_line_ignore, NULL, NULL, 0, NULL }, |
685 |
|
|
{ "wh", roff_unsupp, NULL, NULL, 0, NULL }, |
686 |
|
|
{ "while", roff_unsupp, NULL, NULL, 0, NULL }, |
687 |
|
|
{ "write", roff_insec, NULL, NULL, 0, NULL }, |
688 |
|
|
{ "writec", roff_insec, NULL, NULL, 0, NULL }, |
689 |
|
|
{ "writem", roff_insec, NULL, NULL, 0, NULL }, |
690 |
|
|
{ "xflag", roff_line_ignore, NULL, NULL, 0, NULL }, |
691 |
|
|
{ ".", roff_cblock, NULL, NULL, 0, NULL }, |
692 |
|
|
{ NULL, roff_userdef, NULL, NULL, 0, NULL }, |
693 |
|
|
}; |
694 |
|
|
|
695 |
|
|
/* not currently implemented: Ds em Eq LP Me PP pp Or Rd Sf SH */ |
696 |
|
|
const char *const __mdoc_reserved[] = { |
697 |
|
|
"Ac", "Ad", "An", "Ao", "Ap", "Aq", "Ar", "At", |
698 |
|
|
"Bc", "Bd", "Bf", "Bk", "Bl", "Bo", "Bq", |
699 |
|
|
"Brc", "Bro", "Brq", "Bsx", "Bt", "Bx", |
700 |
|
|
"Cd", "Cm", "Db", "Dc", "Dd", "Dl", "Do", "Dq", |
701 |
|
|
"Dt", "Dv", "Dx", "D1", |
702 |
|
|
"Ec", "Ed", "Ef", "Ek", "El", "Em", |
703 |
|
|
"En", "Eo", "Er", "Es", "Ev", "Ex", |
704 |
|
|
"Fa", "Fc", "Fd", "Fl", "Fn", "Fo", "Fr", "Ft", "Fx", |
705 |
|
|
"Hf", "Ic", "In", "It", "Lb", "Li", "Lk", "Lp", |
706 |
|
|
"Ms", "Mt", "Nd", "Nm", "No", "Ns", "Nx", |
707 |
|
|
"Oc", "Oo", "Op", "Os", "Ot", "Ox", |
708 |
|
|
"Pa", "Pc", "Pf", "Po", "Pp", "Pq", |
709 |
|
|
"Qc", "Ql", "Qo", "Qq", "Re", "Rs", "Rv", |
710 |
|
|
"Sc", "Sh", "Sm", "So", "Sq", |
711 |
|
|
"Ss", "St", "Sx", "Sy", |
712 |
|
|
"Ta", "Tn", "Ud", "Ux", "Va", "Vt", "Xc", "Xo", "Xr", |
713 |
|
|
"%A", "%B", "%C", "%D", "%I", "%J", "%N", "%O", |
714 |
|
|
"%P", "%Q", "%R", "%T", "%U", "%V", |
715 |
|
|
NULL |
716 |
|
|
}; |
717 |
|
|
|
718 |
|
|
/* not currently implemented: BT DE DS ME MT PT SY TQ YS */ |
719 |
|
|
const char *const __man_reserved[] = { |
720 |
|
|
"AT", "B", "BI", "BR", "DT", |
721 |
|
|
"EE", "EN", "EQ", "EX", "HP", "I", "IB", "IP", "IR", |
722 |
|
|
"LP", "OP", "P", "PD", "PP", |
723 |
|
|
"R", "RB", "RE", "RI", "RS", "SB", "SH", "SM", "SS", |
724 |
|
|
"TE", "TH", "TP", "TS", "T&", "UC", "UE", "UR", |
725 |
|
|
NULL |
726 |
|
|
}; |
727 |
|
|
|
728 |
|
|
/* Array of injected predefined strings. */ |
729 |
|
|
#define PREDEFS_MAX 38 |
730 |
|
|
static const struct predef predefs[PREDEFS_MAX] = { |
731 |
|
|
#include "predefs.in" |
732 |
|
|
}; |
733 |
|
|
|
734 |
|
|
/* See roffhash_find() */ |
735 |
|
|
#define ROFF_HASH(p) (p[0] - ASCII_LO) |
736 |
|
|
|
737 |
|
|
static int roffit_lines; /* number of lines to delay */ |
738 |
|
|
static char *roffit_macro; /* nil-terminated macro line */ |
739 |
|
|
|
740 |
|
|
|
741 |
|
|
/* --- request table ------------------------------------------------------ */ |
742 |
|
|
|
743 |
|
|
static void |
744 |
|
|
roffhash_init(void) |
745 |
|
|
{ |
746 |
|
|
struct roffmac *n; |
747 |
|
|
int buc, i; |
748 |
|
|
|
749 |
|
|
for (i = 0; i < (int)ROFF_USERDEF; i++) { |
750 |
|
|
assert(roffs[i].name[0] >= ASCII_LO); |
751 |
|
|
assert(roffs[i].name[0] <= ASCII_HI); |
752 |
|
|
|
753 |
|
|
buc = ROFF_HASH(roffs[i].name); |
754 |
|
|
|
755 |
|
|
if (NULL != (n = hash[buc])) { |
756 |
|
|
for ( ; n->next; n = n->next) |
757 |
|
|
/* Do nothing. */ ; |
758 |
|
|
n->next = &roffs[i]; |
759 |
|
|
} else |
760 |
|
|
hash[buc] = &roffs[i]; |
761 |
|
|
} |
762 |
|
|
} |
763 |
|
|
|
764 |
|
|
/* |
765 |
|
|
* Look up a roff token by its name. Returns ROFF_MAX if no macro by |
766 |
|
|
* the nil-terminated string name could be found. |
767 |
|
|
*/ |
768 |
|
|
static enum rofft |
769 |
|
|
roffhash_find(const char *p, size_t s) |
770 |
|
|
{ |
771 |
|
|
int buc; |
772 |
|
|
struct roffmac *n; |
773 |
|
|
|
774 |
|
|
/* |
775 |
|
|
* libroff has an extremely simple hashtable, for the time |
776 |
|
|
* being, which simply keys on the first character, which must |
777 |
|
|
* be printable, then walks a chain. It works well enough until |
778 |
|
|
* optimised. |
779 |
|
|
*/ |
780 |
|
|
|
781 |
|
|
if (p[0] < ASCII_LO || p[0] > ASCII_HI) |
782 |
|
|
return ROFF_MAX; |
783 |
|
|
|
784 |
|
|
buc = ROFF_HASH(p); |
785 |
|
|
|
786 |
|
|
if (NULL == (n = hash[buc])) |
787 |
|
|
return ROFF_MAX; |
788 |
|
|
for ( ; n; n = n->next) |
789 |
|
|
if (0 == strncmp(n->name, p, s) && '\0' == n->name[(int)s]) |
790 |
|
|
return (enum rofft)(n - roffs); |
791 |
|
|
|
792 |
|
|
return ROFF_MAX; |
793 |
|
|
} |
794 |
|
|
|
795 |
|
|
/* --- stack of request blocks -------------------------------------------- */ |
796 |
|
|
|
797 |
|
|
/* |
798 |
|
|
* Pop the current node off of the stack of roff instructions currently |
799 |
|
|
* pending. |
800 |
|
|
*/ |
801 |
|
|
static void |
802 |
|
|
roffnode_pop(struct roff *r) |
803 |
|
|
{ |
804 |
|
|
struct roffnode *p; |
805 |
|
|
|
806 |
|
|
assert(r->last); |
807 |
|
|
p = r->last; |
808 |
|
|
|
809 |
|
|
r->last = r->last->parent; |
810 |
|
|
free(p->name); |
811 |
|
|
free(p->end); |
812 |
|
|
free(p); |
813 |
|
|
} |
814 |
|
|
|
815 |
|
|
/* |
816 |
|
|
* Push a roff node onto the instruction stack. This must later be |
817 |
|
|
* removed with roffnode_pop(). |
818 |
|
|
*/ |
819 |
|
|
static void |
820 |
|
|
roffnode_push(struct roff *r, enum rofft tok, const char *name, |
821 |
|
|
int line, int col) |
822 |
|
|
{ |
823 |
|
|
struct roffnode *p; |
824 |
|
|
|
825 |
|
|
p = mandoc_calloc(1, sizeof(struct roffnode)); |
826 |
|
|
p->tok = tok; |
827 |
|
|
if (name) |
828 |
|
|
p->name = mandoc_strdup(name); |
829 |
|
|
p->parent = r->last; |
830 |
|
|
p->line = line; |
831 |
|
|
p->col = col; |
832 |
|
|
p->rule = p->parent ? p->parent->rule : 0; |
833 |
|
|
|
834 |
|
|
r->last = p; |
835 |
|
|
} |
836 |
|
|
|
837 |
|
|
/* --- roff parser state data management ---------------------------------- */ |
838 |
|
|
|
839 |
|
|
static void |
840 |
|
|
roff_free1(struct roff *r) |
841 |
|
|
{ |
842 |
|
|
struct tbl_node *tbl; |
843 |
|
|
struct eqn_node *e; |
844 |
|
|
int i; |
845 |
|
|
|
846 |
|
|
while (NULL != (tbl = r->first_tbl)) { |
847 |
|
|
r->first_tbl = tbl->next; |
848 |
|
|
tbl_free(tbl); |
849 |
|
|
} |
850 |
|
|
r->first_tbl = r->last_tbl = r->tbl = NULL; |
851 |
|
|
|
852 |
|
|
while (NULL != (e = r->first_eqn)) { |
853 |
|
|
r->first_eqn = e->next; |
854 |
|
|
eqn_free(e); |
855 |
|
|
} |
856 |
|
|
r->first_eqn = r->last_eqn = r->eqn = NULL; |
857 |
|
|
|
858 |
|
|
while (r->last) |
859 |
|
|
roffnode_pop(r); |
860 |
|
|
|
861 |
|
|
free (r->rstack); |
862 |
|
|
r->rstack = NULL; |
863 |
|
|
r->rstacksz = 0; |
864 |
|
|
r->rstackpos = -1; |
865 |
|
|
|
866 |
|
|
roff_freereg(r->regtab); |
867 |
|
|
r->regtab = NULL; |
868 |
|
|
|
869 |
|
|
roff_freestr(r->strtab); |
870 |
|
|
roff_freestr(r->xmbtab); |
871 |
|
|
r->strtab = r->xmbtab = NULL; |
872 |
|
|
|
873 |
|
|
if (r->xtab) |
874 |
|
|
for (i = 0; i < 128; i++) |
875 |
|
|
free(r->xtab[i].p); |
876 |
|
|
free(r->xtab); |
877 |
|
|
r->xtab = NULL; |
878 |
|
|
} |
879 |
|
|
|
880 |
|
|
void |
881 |
|
|
roff_reset(struct roff *r) |
882 |
|
|
{ |
883 |
|
|
|
884 |
|
|
roff_free1(r); |
885 |
|
|
r->format = r->options & (MPARSE_MDOC | MPARSE_MAN); |
886 |
|
|
r->control = 0; |
887 |
|
|
} |
888 |
|
|
|
889 |
|
|
void |
890 |
|
|
roff_free(struct roff *r) |
891 |
|
|
{ |
892 |
|
|
|
893 |
|
|
roff_free1(r); |
894 |
|
|
free(r); |
895 |
|
|
} |
896 |
|
|
|
897 |
|
|
struct roff * |
898 |
|
|
roff_alloc(struct mparse *parse, int options) |
899 |
|
|
{ |
900 |
|
|
struct roff *r; |
901 |
|
|
|
902 |
|
|
r = mandoc_calloc(1, sizeof(struct roff)); |
903 |
|
|
r->parse = parse; |
904 |
|
|
r->options = options; |
905 |
|
|
r->format = options & (MPARSE_MDOC | MPARSE_MAN); |
906 |
|
|
r->rstackpos = -1; |
907 |
|
|
|
908 |
|
|
roffhash_init(); |
909 |
|
|
|
910 |
|
|
return r; |
911 |
|
|
} |
912 |
|
|
|
913 |
|
|
/* --- syntax tree state data management ---------------------------------- */ |
914 |
|
|
|
915 |
|
|
static void |
916 |
|
|
roff_man_free1(struct roff_man *man) |
917 |
|
|
{ |
918 |
|
|
|
919 |
|
|
if (man->first != NULL) |
920 |
|
|
roff_node_delete(man, man->first); |
921 |
|
|
free(man->meta.msec); |
922 |
|
|
free(man->meta.vol); |
923 |
|
|
free(man->meta.os); |
924 |
|
|
free(man->meta.arch); |
925 |
|
|
free(man->meta.title); |
926 |
|
|
free(man->meta.name); |
927 |
|
|
free(man->meta.date); |
928 |
|
|
} |
929 |
|
|
|
930 |
|
|
static void |
931 |
|
|
roff_man_alloc1(struct roff_man *man) |
932 |
|
|
{ |
933 |
|
|
|
934 |
|
|
memset(&man->meta, 0, sizeof(man->meta)); |
935 |
|
|
man->first = mandoc_calloc(1, sizeof(*man->first)); |
936 |
|
|
man->first->type = ROFFT_ROOT; |
937 |
|
|
man->last = man->first; |
938 |
|
|
man->last_es = NULL; |
939 |
|
|
man->flags = 0; |
940 |
|
|
man->macroset = MACROSET_NONE; |
941 |
|
|
man->lastsec = man->lastnamed = SEC_NONE; |
942 |
|
|
man->next = ROFF_NEXT_CHILD; |
943 |
|
|
} |
944 |
|
|
|
945 |
|
|
void |
946 |
|
|
roff_man_reset(struct roff_man *man) |
947 |
|
|
{ |
948 |
|
|
|
949 |
|
|
roff_man_free1(man); |
950 |
|
|
roff_man_alloc1(man); |
951 |
|
|
} |
952 |
|
|
|
953 |
|
|
void |
954 |
|
|
roff_man_free(struct roff_man *man) |
955 |
|
|
{ |
956 |
|
|
|
957 |
|
|
roff_man_free1(man); |
958 |
|
|
free(man); |
959 |
|
|
} |
960 |
|
|
|
961 |
|
|
struct roff_man * |
962 |
|
|
roff_man_alloc(struct roff *roff, struct mparse *parse, |
963 |
|
|
const char *defos, int quick) |
964 |
|
|
{ |
965 |
|
|
struct roff_man *man; |
966 |
|
|
|
967 |
|
|
man = mandoc_calloc(1, sizeof(*man)); |
968 |
|
|
man->parse = parse; |
969 |
|
|
man->roff = roff; |
970 |
|
|
man->defos = defos; |
971 |
|
|
man->quick = quick; |
972 |
|
|
roff_man_alloc1(man); |
973 |
|
|
return man; |
974 |
|
|
} |
975 |
|
|
|
976 |
|
|
/* --- syntax tree handling ----------------------------------------------- */ |
977 |
|
|
|
978 |
|
|
struct roff_node * |
979 |
|
|
roff_node_alloc(struct roff_man *man, int line, int pos, |
980 |
|
|
enum roff_type type, int tok) |
981 |
|
|
{ |
982 |
|
|
struct roff_node *n; |
983 |
|
|
|
984 |
|
|
n = mandoc_calloc(1, sizeof(*n)); |
985 |
|
|
n->line = line; |
986 |
|
|
n->pos = pos; |
987 |
|
|
n->tok = tok; |
988 |
|
|
n->type = type; |
989 |
|
|
n->sec = man->lastsec; |
990 |
|
|
|
991 |
|
|
if (man->flags & MDOC_SYNOPSIS) |
992 |
|
|
n->flags |= MDOC_SYNPRETTY; |
993 |
|
|
else |
994 |
|
|
n->flags &= ~MDOC_SYNPRETTY; |
995 |
|
|
if (man->flags & MDOC_NEWLINE) |
996 |
|
|
n->flags |= MDOC_LINE; |
997 |
|
|
man->flags &= ~MDOC_NEWLINE; |
998 |
|
|
|
999 |
|
|
return n; |
1000 |
|
|
} |
1001 |
|
|
|
1002 |
|
|
void |
1003 |
|
|
roff_node_append(struct roff_man *man, struct roff_node *n) |
1004 |
|
|
{ |
1005 |
|
|
|
1006 |
|
|
switch (man->next) { |
1007 |
|
|
case ROFF_NEXT_SIBLING: |
1008 |
|
|
if (man->last->next != NULL) { |
1009 |
|
|
n->next = man->last->next; |
1010 |
|
|
man->last->next->prev = n; |
1011 |
|
|
} else |
1012 |
|
|
man->last->parent->last = n; |
1013 |
|
|
man->last->next = n; |
1014 |
|
|
n->prev = man->last; |
1015 |
|
|
n->parent = man->last->parent; |
1016 |
|
|
break; |
1017 |
|
|
case ROFF_NEXT_CHILD: |
1018 |
|
|
man->last->child = n; |
1019 |
|
|
n->parent = man->last; |
1020 |
|
|
n->parent->last = n; |
1021 |
|
|
break; |
1022 |
|
|
default: |
1023 |
|
|
abort(); |
1024 |
|
|
} |
1025 |
|
|
man->last = n; |
1026 |
|
|
|
1027 |
|
|
switch (n->type) { |
1028 |
|
|
case ROFFT_HEAD: |
1029 |
|
|
n->parent->head = n; |
1030 |
|
|
break; |
1031 |
|
|
case ROFFT_BODY: |
1032 |
|
|
if (n->end != ENDBODY_NOT) |
1033 |
|
|
return; |
1034 |
|
|
n->parent->body = n; |
1035 |
|
|
break; |
1036 |
|
|
case ROFFT_TAIL: |
1037 |
|
|
n->parent->tail = n; |
1038 |
|
|
break; |
1039 |
|
|
default: |
1040 |
|
|
return; |
1041 |
|
|
} |
1042 |
|
|
|
1043 |
|
|
/* |
1044 |
|
|
* Copy over the normalised-data pointer of our parent. Not |
1045 |
|
|
* everybody has one, but copying a null pointer is fine. |
1046 |
|
|
*/ |
1047 |
|
|
|
1048 |
|
|
n->norm = n->parent->norm; |
1049 |
|
|
assert(n->parent->type == ROFFT_BLOCK); |
1050 |
|
|
} |
1051 |
|
|
|
1052 |
|
|
void |
1053 |
|
|
roff_word_alloc(struct roff_man *man, int line, int pos, const char *word) |
1054 |
|
|
{ |
1055 |
|
|
struct roff_node *n; |
1056 |
|
|
|
1057 |
|
|
n = roff_node_alloc(man, line, pos, ROFFT_TEXT, TOKEN_NONE); |
1058 |
|
|
n->string = roff_strdup(man->roff, word); |
1059 |
|
|
roff_node_append(man, n); |
1060 |
|
|
if (man->macroset == MACROSET_MDOC) |
1061 |
|
|
n->flags |= MDOC_VALID | MDOC_ENDED; |
1062 |
|
|
else |
1063 |
|
|
n->flags |= MAN_VALID; |
1064 |
|
|
man->next = ROFF_NEXT_SIBLING; |
1065 |
|
|
} |
1066 |
|
|
|
1067 |
|
|
void |
1068 |
|
|
roff_word_append(struct roff_man *man, const char *word) |
1069 |
|
|
{ |
1070 |
|
|
struct roff_node *n; |
1071 |
|
|
char *addstr, *newstr; |
1072 |
|
|
|
1073 |
|
|
n = man->last; |
1074 |
|
|
addstr = roff_strdup(man->roff, word); |
1075 |
|
|
mandoc_asprintf(&newstr, "%s %s", n->string, addstr); |
1076 |
|
|
free(addstr); |
1077 |
|
|
free(n->string); |
1078 |
|
|
n->string = newstr; |
1079 |
|
|
man->next = ROFF_NEXT_SIBLING; |
1080 |
|
|
} |
1081 |
|
|
|
1082 |
|
|
void |
1083 |
|
|
roff_elem_alloc(struct roff_man *man, int line, int pos, int tok) |
1084 |
|
|
{ |
1085 |
|
|
struct roff_node *n; |
1086 |
|
|
|
1087 |
|
|
n = roff_node_alloc(man, line, pos, ROFFT_ELEM, tok); |
1088 |
|
|
roff_node_append(man, n); |
1089 |
|
|
man->next = ROFF_NEXT_CHILD; |
1090 |
|
|
} |
1091 |
|
|
|
1092 |
|
|
struct roff_node * |
1093 |
|
|
roff_block_alloc(struct roff_man *man, int line, int pos, int tok) |
1094 |
|
|
{ |
1095 |
|
|
struct roff_node *n; |
1096 |
|
|
|
1097 |
|
|
n = roff_node_alloc(man, line, pos, ROFFT_BLOCK, tok); |
1098 |
|
|
roff_node_append(man, n); |
1099 |
|
|
man->next = ROFF_NEXT_CHILD; |
1100 |
|
|
return n; |
1101 |
|
|
} |
1102 |
|
|
|
1103 |
|
|
struct roff_node * |
1104 |
|
|
roff_head_alloc(struct roff_man *man, int line, int pos, int tok) |
1105 |
|
|
{ |
1106 |
|
|
struct roff_node *n; |
1107 |
|
|
|
1108 |
|
|
n = roff_node_alloc(man, line, pos, ROFFT_HEAD, tok); |
1109 |
|
|
roff_node_append(man, n); |
1110 |
|
|
man->next = ROFF_NEXT_CHILD; |
1111 |
|
|
return n; |
1112 |
|
|
} |
1113 |
|
|
|
1114 |
|
|
struct roff_node * |
1115 |
|
|
roff_body_alloc(struct roff_man *man, int line, int pos, int tok) |
1116 |
|
|
{ |
1117 |
|
|
struct roff_node *n; |
1118 |
|
|
|
1119 |
|
|
n = roff_node_alloc(man, line, pos, ROFFT_BODY, tok); |
1120 |
|
|
roff_node_append(man, n); |
1121 |
|
|
man->next = ROFF_NEXT_CHILD; |
1122 |
|
|
return n; |
1123 |
|
|
} |
1124 |
|
|
|
1125 |
|
|
void |
1126 |
|
|
roff_addeqn(struct roff_man *man, const struct eqn *eqn) |
1127 |
|
|
{ |
1128 |
|
|
struct roff_node *n; |
1129 |
|
|
|
1130 |
|
|
n = roff_node_alloc(man, eqn->ln, eqn->pos, ROFFT_EQN, TOKEN_NONE); |
1131 |
|
|
n->eqn = eqn; |
1132 |
|
|
if (eqn->ln > man->last->line) |
1133 |
|
|
n->flags |= MDOC_LINE; |
1134 |
|
|
roff_node_append(man, n); |
1135 |
|
|
man->next = ROFF_NEXT_SIBLING; |
1136 |
|
|
} |
1137 |
|
|
|
1138 |
|
|
void |
1139 |
|
|
roff_addtbl(struct roff_man *man, const struct tbl_span *tbl) |
1140 |
|
|
{ |
1141 |
|
|
struct roff_node *n; |
1142 |
|
|
|
1143 |
|
|
if (man->macroset == MACROSET_MAN) |
1144 |
|
|
man_breakscope(man, TOKEN_NONE); |
1145 |
|
|
n = roff_node_alloc(man, tbl->line, 0, ROFFT_TBL, TOKEN_NONE); |
1146 |
|
|
n->span = tbl; |
1147 |
|
|
roff_node_append(man, n); |
1148 |
|
|
if (man->macroset == MACROSET_MDOC) |
1149 |
|
|
n->flags |= MDOC_VALID | MDOC_ENDED; |
1150 |
|
|
else |
1151 |
|
|
n->flags |= MAN_VALID; |
1152 |
|
|
man->next = ROFF_NEXT_SIBLING; |
1153 |
|
|
} |
1154 |
|
|
|
1155 |
|
|
void |
1156 |
|
|
roff_node_unlink(struct roff_man *man, struct roff_node *n) |
1157 |
|
|
{ |
1158 |
|
|
|
1159 |
|
|
/* Adjust siblings. */ |
1160 |
|
|
|
1161 |
|
|
if (n->prev) |
1162 |
|
|
n->prev->next = n->next; |
1163 |
|
|
if (n->next) |
1164 |
|
|
n->next->prev = n->prev; |
1165 |
|
|
|
1166 |
|
|
/* Adjust parent. */ |
1167 |
|
|
|
1168 |
|
|
if (n->parent != NULL) { |
1169 |
|
|
if (n->parent->child == n) |
1170 |
|
|
n->parent->child = n->next; |
1171 |
|
|
if (n->parent->last == n) |
1172 |
|
|
n->parent->last = n->prev; |
1173 |
|
|
} |
1174 |
|
|
|
1175 |
|
|
/* Adjust parse point. */ |
1176 |
|
|
|
1177 |
|
|
if (man == NULL) |
1178 |
|
|
return; |
1179 |
|
|
if (man->last == n) { |
1180 |
|
|
if (n->prev == NULL) { |
1181 |
|
|
man->last = n->parent; |
1182 |
|
|
man->next = ROFF_NEXT_CHILD; |
1183 |
|
|
} else { |
1184 |
|
|
man->last = n->prev; |
1185 |
|
|
man->next = ROFF_NEXT_SIBLING; |
1186 |
|
|
} |
1187 |
|
|
} |
1188 |
|
|
if (man->first == n) |
1189 |
|
|
man->first = NULL; |
1190 |
|
|
} |
1191 |
|
|
|
1192 |
|
|
void |
1193 |
|
|
roff_node_free(struct roff_node *n) |
1194 |
|
|
{ |
1195 |
|
|
|
1196 |
|
|
if (n->args != NULL) |
1197 |
|
|
mdoc_argv_free(n->args); |
1198 |
|
|
if (n->type == ROFFT_BLOCK || n->type == ROFFT_ELEM) |
1199 |
|
|
free(n->norm); |
1200 |
|
|
free(n->string); |
1201 |
|
|
free(n); |
1202 |
|
|
} |
1203 |
|
|
|
1204 |
|
|
void |
1205 |
|
|
roff_node_delete(struct roff_man *man, struct roff_node *n) |
1206 |
|
|
{ |
1207 |
|
|
|
1208 |
|
|
while (n->child != NULL) |
1209 |
|
|
roff_node_delete(man, n->child); |
1210 |
|
|
roff_node_unlink(man, n); |
1211 |
|
|
roff_node_free(n); |
1212 |
|
|
} |
1213 |
|
|
|
1214 |
|
|
void |
1215 |
|
|
deroff(char **dest, const struct roff_node *n) |
1216 |
|
|
{ |
1217 |
|
|
char *cp; |
1218 |
|
|
size_t sz; |
1219 |
|
|
|
1220 |
|
|
if (n->type != ROFFT_TEXT) { |
1221 |
|
|
for (n = n->child; n != NULL; n = n->next) |
1222 |
|
|
deroff(dest, n); |
1223 |
|
|
return; |
1224 |
|
|
} |
1225 |
|
|
|
1226 |
|
|
/* Skip leading whitespace and escape sequences. */ |
1227 |
|
|
|
1228 |
|
|
cp = n->string; |
1229 |
|
|
while (*cp != '\0') { |
1230 |
|
|
if ('\\' == *cp) { |
1231 |
|
|
cp++; |
1232 |
|
|
mandoc_escape((const char **)&cp, NULL, NULL); |
1233 |
|
|
} else if (isspace((unsigned char)*cp)) |
1234 |
|
|
cp++; |
1235 |
|
|
else |
1236 |
|
|
break; |
1237 |
|
|
} |
1238 |
|
|
|
1239 |
|
|
/* Skip trailing whitespace. */ |
1240 |
|
|
|
1241 |
|
|
for (sz = strlen(cp); sz; sz--) |
1242 |
|
|
if ( ! isspace((unsigned char)cp[sz-1])) |
1243 |
|
|
break; |
1244 |
|
|
|
1245 |
|
|
/* Skip empty strings. */ |
1246 |
|
|
|
1247 |
|
|
if (sz == 0) |
1248 |
|
|
return; |
1249 |
|
|
|
1250 |
|
|
if (*dest == NULL) { |
1251 |
|
|
*dest = mandoc_strndup(cp, sz); |
1252 |
|
|
return; |
1253 |
|
|
} |
1254 |
|
|
|
1255 |
|
|
mandoc_asprintf(&cp, "%s %*s", *dest, (int)sz, cp); |
1256 |
|
|
free(*dest); |
1257 |
|
|
*dest = cp; |
1258 |
|
|
} |
1259 |
|
|
|
1260 |
|
|
/* --- main functions of the roff parser ---------------------------------- */ |
1261 |
|
|
|
1262 |
|
|
/* |
1263 |
|
|
* In the current line, expand escape sequences that tend to get |
1264 |
|
|
* used in numerical expressions and conditional requests. |
1265 |
|
|
* Also check the syntax of the remaining escape sequences. |
1266 |
|
|
*/ |
1267 |
|
|
static enum rofferr |
1268 |
|
|
roff_res(struct roff *r, struct buf *buf, int ln, int pos) |
1269 |
|
|
{ |
1270 |
|
|
char ubuf[24]; /* buffer to print the number */ |
1271 |
|
|
const char *start; /* start of the string to process */ |
1272 |
|
|
char *stesc; /* start of an escape sequence ('\\') */ |
1273 |
|
|
const char *stnam; /* start of the name, after "[(*" */ |
1274 |
|
|
const char *cp; /* end of the name, e.g. before ']' */ |
1275 |
|
|
const char *res; /* the string to be substituted */ |
1276 |
|
|
char *nbuf; /* new buffer to copy buf->buf to */ |
1277 |
|
|
size_t maxl; /* expected length of the escape name */ |
1278 |
|
|
size_t naml; /* actual length of the escape name */ |
1279 |
|
|
enum mandoc_esc esc; /* type of the escape sequence */ |
1280 |
|
|
int inaml; /* length returned from mandoc_escape() */ |
1281 |
|
|
int expand_count; /* to avoid infinite loops */ |
1282 |
|
|
int npos; /* position in numeric expression */ |
1283 |
|
|
int arg_complete; /* argument not interrupted by eol */ |
1284 |
|
|
char term; /* character terminating the escape */ |
1285 |
|
|
|
1286 |
|
|
expand_count = 0; |
1287 |
|
|
start = buf->buf + pos; |
1288 |
|
|
stesc = strchr(start, '\0') - 1; |
1289 |
|
|
while (stesc-- > start) { |
1290 |
|
|
|
1291 |
|
|
/* Search backwards for the next backslash. */ |
1292 |
|
|
|
1293 |
|
|
if (*stesc != '\\') |
1294 |
|
|
continue; |
1295 |
|
|
|
1296 |
|
|
/* If it is escaped, skip it. */ |
1297 |
|
|
|
1298 |
|
|
for (cp = stesc - 1; cp >= start; cp--) |
1299 |
|
|
if (*cp != '\\') |
1300 |
|
|
break; |
1301 |
|
|
|
1302 |
|
|
if ((stesc - cp) % 2 == 0) { |
1303 |
|
|
stesc = (char *)cp; |
1304 |
|
|
continue; |
1305 |
|
|
} |
1306 |
|
|
|
1307 |
|
|
/* Decide whether to expand or to check only. */ |
1308 |
|
|
|
1309 |
|
|
term = '\0'; |
1310 |
|
|
cp = stesc + 1; |
1311 |
|
|
switch (*cp) { |
1312 |
|
|
case '*': |
1313 |
|
|
res = NULL; |
1314 |
|
|
break; |
1315 |
|
|
case 'B': |
1316 |
|
|
case 'w': |
1317 |
|
|
term = cp[1]; |
1318 |
|
|
/* FALLTHROUGH */ |
1319 |
|
|
case 'n': |
1320 |
|
|
res = ubuf; |
1321 |
|
|
break; |
1322 |
|
|
default: |
1323 |
|
|
esc = mandoc_escape(&cp, &stnam, &inaml); |
1324 |
|
|
if (esc == ESCAPE_ERROR || |
1325 |
|
|
(esc == ESCAPE_SPECIAL && |
1326 |
|
|
mchars_spec2cp(stnam, inaml) < 0)) |
1327 |
|
|
mandoc_vmsg(MANDOCERR_ESC_BAD, |
1328 |
|
|
r->parse, ln, (int)(stesc - buf->buf), |
1329 |
|
|
"%.*s", (int)(cp - stesc), stesc); |
1330 |
|
|
continue; |
1331 |
|
|
} |
1332 |
|
|
|
1333 |
|
|
if (EXPAND_LIMIT < ++expand_count) { |
1334 |
|
|
mandoc_msg(MANDOCERR_ROFFLOOP, r->parse, |
1335 |
|
|
ln, (int)(stesc - buf->buf), NULL); |
1336 |
|
|
return ROFF_IGN; |
1337 |
|
|
} |
1338 |
|
|
|
1339 |
|
|
/* |
1340 |
|
|
* The third character decides the length |
1341 |
|
|
* of the name of the string or register. |
1342 |
|
|
* Save a pointer to the name. |
1343 |
|
|
*/ |
1344 |
|
|
|
1345 |
|
|
if (term == '\0') { |
1346 |
|
|
switch (*++cp) { |
1347 |
|
|
case '\0': |
1348 |
|
|
maxl = 0; |
1349 |
|
|
break; |
1350 |
|
|
case '(': |
1351 |
|
|
cp++; |
1352 |
|
|
maxl = 2; |
1353 |
|
|
break; |
1354 |
|
|
case '[': |
1355 |
|
|
cp++; |
1356 |
|
|
term = ']'; |
1357 |
|
|
maxl = 0; |
1358 |
|
|
break; |
1359 |
|
|
default: |
1360 |
|
|
maxl = 1; |
1361 |
|
|
break; |
1362 |
|
|
} |
1363 |
|
|
} else { |
1364 |
|
|
cp += 2; |
1365 |
|
|
maxl = 0; |
1366 |
|
|
} |
1367 |
|
|
stnam = cp; |
1368 |
|
|
|
1369 |
|
|
/* Advance to the end of the name. */ |
1370 |
|
|
|
1371 |
|
|
naml = 0; |
1372 |
|
|
arg_complete = 1; |
1373 |
|
|
while (maxl == 0 || naml < maxl) { |
1374 |
|
|
if (*cp == '\0') { |
1375 |
|
|
mandoc_msg(MANDOCERR_ESC_BAD, r->parse, |
1376 |
|
|
ln, (int)(stesc - buf->buf), stesc); |
1377 |
|
|
arg_complete = 0; |
1378 |
|
|
break; |
1379 |
|
|
} |
1380 |
|
|
if (maxl == 0 && *cp == term) { |
1381 |
|
|
cp++; |
1382 |
|
|
break; |
1383 |
|
|
} |
1384 |
|
|
if (*cp++ != '\\' || stesc[1] != 'w') { |
1385 |
|
|
naml++; |
1386 |
|
|
continue; |
1387 |
|
|
} |
1388 |
|
|
switch (mandoc_escape(&cp, NULL, NULL)) { |
1389 |
|
|
case ESCAPE_SPECIAL: |
1390 |
|
|
case ESCAPE_UNICODE: |
1391 |
|
|
case ESCAPE_NUMBERED: |
1392 |
|
|
case ESCAPE_OVERSTRIKE: |
1393 |
|
|
naml++; |
1394 |
|
|
break; |
1395 |
|
|
default: |
1396 |
|
|
break; |
1397 |
|
|
} |
1398 |
|
|
} |
1399 |
|
|
|
1400 |
|
|
/* |
1401 |
|
|
* Retrieve the replacement string; if it is |
1402 |
|
|
* undefined, resume searching for escapes. |
1403 |
|
|
*/ |
1404 |
|
|
|
1405 |
|
|
switch (stesc[1]) { |
1406 |
|
|
case '*': |
1407 |
|
|
if (arg_complete) |
1408 |
|
|
res = roff_getstrn(r, stnam, naml); |
1409 |
|
|
break; |
1410 |
|
|
case 'B': |
1411 |
|
|
npos = 0; |
1412 |
|
|
ubuf[0] = arg_complete && |
1413 |
|
|
roff_evalnum(r, ln, stnam, &npos, |
1414 |
|
|
NULL, ROFFNUM_SCALE) && |
1415 |
|
|
stnam + npos + 1 == cp ? '1' : '0'; |
1416 |
|
|
ubuf[1] = '\0'; |
1417 |
|
|
break; |
1418 |
|
|
case 'n': |
1419 |
|
|
if (arg_complete) |
1420 |
|
|
(void)snprintf(ubuf, sizeof(ubuf), "%d", |
1421 |
|
|
roff_getregn(r, stnam, naml)); |
1422 |
|
|
else |
1423 |
|
|
ubuf[0] = '\0'; |
1424 |
|
|
break; |
1425 |
|
|
case 'w': |
1426 |
|
|
/* use even incomplete args */ |
1427 |
|
|
(void)snprintf(ubuf, sizeof(ubuf), "%d", |
1428 |
|
|
24 * (int)naml); |
1429 |
|
|
break; |
1430 |
|
|
} |
1431 |
|
|
|
1432 |
|
|
if (res == NULL) { |
1433 |
|
|
mandoc_vmsg(MANDOCERR_STR_UNDEF, |
1434 |
|
|
r->parse, ln, (int)(stesc - buf->buf), |
1435 |
|
|
"%.*s", (int)naml, stnam); |
1436 |
|
|
res = ""; |
1437 |
|
|
} else if (buf->sz + strlen(res) > SHRT_MAX) { |
1438 |
|
|
mandoc_msg(MANDOCERR_ROFFLOOP, r->parse, |
1439 |
|
|
ln, (int)(stesc - buf->buf), NULL); |
1440 |
|
|
return ROFF_IGN; |
1441 |
|
|
} |
1442 |
|
|
|
1443 |
|
|
/* Replace the escape sequence by the string. */ |
1444 |
|
|
|
1445 |
|
|
*stesc = '\0'; |
1446 |
|
|
buf->sz = mandoc_asprintf(&nbuf, "%s%s%s", |
1447 |
|
|
buf->buf, res, cp) + 1; |
1448 |
|
|
|
1449 |
|
|
/* Prepare for the next replacement. */ |
1450 |
|
|
|
1451 |
|
|
start = nbuf + pos; |
1452 |
|
|
stesc = nbuf + (stesc - buf->buf) + strlen(res); |
1453 |
|
|
free(buf->buf); |
1454 |
|
|
buf->buf = nbuf; |
1455 |
|
|
} |
1456 |
|
|
return ROFF_CONT; |
1457 |
|
|
} |
1458 |
|
|
|
1459 |
|
|
/* |
1460 |
|
|
* Process text streams. |
1461 |
|
|
*/ |
1462 |
|
|
static enum rofferr |
1463 |
|
|
roff_parsetext(struct buf *buf, int pos, int *offs) |
1464 |
|
|
{ |
1465 |
|
|
size_t sz; |
1466 |
|
|
const char *start; |
1467 |
|
|
char *p; |
1468 |
|
|
int isz; |
1469 |
|
|
enum mandoc_esc esc; |
1470 |
|
|
|
1471 |
|
|
/* Spring the input line trap. */ |
1472 |
|
|
|
1473 |
|
|
if (roffit_lines == 1) { |
1474 |
|
|
isz = mandoc_asprintf(&p, "%s\n.%s", buf->buf, roffit_macro); |
1475 |
|
|
free(buf->buf); |
1476 |
|
|
buf->buf = p; |
1477 |
|
|
buf->sz = isz + 1; |
1478 |
|
|
*offs = 0; |
1479 |
|
|
free(roffit_macro); |
1480 |
|
|
roffit_lines = 0; |
1481 |
|
|
return ROFF_REPARSE; |
1482 |
|
|
} else if (roffit_lines > 1) |
1483 |
|
|
--roffit_lines; |
1484 |
|
|
|
1485 |
|
|
/* Convert all breakable hyphens into ASCII_HYPH. */ |
1486 |
|
|
|
1487 |
|
|
start = p = buf->buf + pos; |
1488 |
|
|
|
1489 |
|
|
while (*p != '\0') { |
1490 |
|
|
sz = strcspn(p, "-\\"); |
1491 |
|
|
p += sz; |
1492 |
|
|
|
1493 |
|
|
if (*p == '\0') |
1494 |
|
|
break; |
1495 |
|
|
|
1496 |
|
|
if (*p == '\\') { |
1497 |
|
|
/* Skip over escapes. */ |
1498 |
|
|
p++; |
1499 |
|
|
esc = mandoc_escape((const char **)&p, NULL, NULL); |
1500 |
|
|
if (esc == ESCAPE_ERROR) |
1501 |
|
|
break; |
1502 |
|
|
while (*p == '-') |
1503 |
|
|
p++; |
1504 |
|
|
continue; |
1505 |
|
|
} else if (p == start) { |
1506 |
|
|
p++; |
1507 |
|
|
continue; |
1508 |
|
|
} |
1509 |
|
|
|
1510 |
|
|
if (isalpha((unsigned char)p[-1]) && |
1511 |
|
|
isalpha((unsigned char)p[1])) |
1512 |
|
|
*p = ASCII_HYPH; |
1513 |
|
|
p++; |
1514 |
|
|
} |
1515 |
|
|
return ROFF_CONT; |
1516 |
|
|
} |
1517 |
|
|
|
1518 |
|
|
enum rofferr |
1519 |
|
|
roff_parseln(struct roff *r, int ln, struct buf *buf, int *offs) |
1520 |
|
|
{ |
1521 |
|
|
enum rofft t; |
1522 |
|
|
enum rofferr e; |
1523 |
|
|
int pos; /* parse point */ |
1524 |
|
|
int spos; /* saved parse point for messages */ |
1525 |
|
|
int ppos; /* original offset in buf->buf */ |
1526 |
|
|
int ctl; /* macro line (boolean) */ |
1527 |
|
|
|
1528 |
|
|
ppos = pos = *offs; |
1529 |
|
|
|
1530 |
|
|
/* Handle in-line equation delimiters. */ |
1531 |
|
|
|
1532 |
|
|
if (r->tbl == NULL && |
1533 |
|
|
r->last_eqn != NULL && r->last_eqn->delim && |
1534 |
|
|
(r->eqn == NULL || r->eqn_inline)) { |
1535 |
|
|
e = roff_eqndelim(r, buf, pos); |
1536 |
|
|
if (e == ROFF_REPARSE) |
1537 |
|
|
return e; |
1538 |
|
|
assert(e == ROFF_CONT); |
1539 |
|
|
} |
1540 |
|
|
|
1541 |
|
|
/* Expand some escape sequences. */ |
1542 |
|
|
|
1543 |
|
|
e = roff_res(r, buf, ln, pos); |
1544 |
|
|
if (e == ROFF_IGN) |
1545 |
|
|
return e; |
1546 |
|
|
assert(e == ROFF_CONT); |
1547 |
|
|
|
1548 |
|
|
ctl = roff_getcontrol(r, buf->buf, &pos); |
1549 |
|
|
|
1550 |
|
|
/* |
1551 |
|
|
* First, if a scope is open and we're not a macro, pass the |
1552 |
|
|
* text through the macro's filter. |
1553 |
|
|
* Equations process all content themselves. |
1554 |
|
|
* Tables process almost all content themselves, but we want |
1555 |
|
|
* to warn about macros before passing it there. |
1556 |
|
|
*/ |
1557 |
|
|
|
1558 |
|
|
if (r->last != NULL && ! ctl) { |
1559 |
|
|
t = r->last->tok; |
1560 |
|
|
assert(roffs[t].text); |
1561 |
|
|
e = (*roffs[t].text)(r, t, buf, ln, pos, pos, offs); |
1562 |
|
|
assert(e == ROFF_IGN || e == ROFF_CONT); |
1563 |
|
|
if (e != ROFF_CONT) |
1564 |
|
|
return e; |
1565 |
|
|
} |
1566 |
|
|
if (r->eqn != NULL) |
1567 |
|
|
return eqn_read(&r->eqn, ln, buf->buf, ppos, offs); |
1568 |
|
|
if (r->tbl != NULL && ( ! ctl || buf->buf[pos] == '\0')) |
1569 |
|
|
return tbl_read(r->tbl, ln, buf->buf, ppos); |
1570 |
|
|
if ( ! ctl) |
1571 |
|
|
return roff_parsetext(buf, pos, offs); |
1572 |
|
|
|
1573 |
|
|
/* Skip empty request lines. */ |
1574 |
|
|
|
1575 |
|
|
if (buf->buf[pos] == '"') { |
1576 |
|
|
mandoc_msg(MANDOCERR_COMMENT_BAD, r->parse, |
1577 |
|
|
ln, pos, NULL); |
1578 |
|
|
return ROFF_IGN; |
1579 |
|
|
} else if (buf->buf[pos] == '\0') |
1580 |
|
|
return ROFF_IGN; |
1581 |
|
|
|
1582 |
|
|
/* |
1583 |
|
|
* If a scope is open, go to the child handler for that macro, |
1584 |
|
|
* as it may want to preprocess before doing anything with it. |
1585 |
|
|
* Don't do so if an equation is open. |
1586 |
|
|
*/ |
1587 |
|
|
|
1588 |
|
|
if (r->last) { |
1589 |
|
|
t = r->last->tok; |
1590 |
|
|
assert(roffs[t].sub); |
1591 |
|
|
return (*roffs[t].sub)(r, t, buf, ln, ppos, pos, offs); |
1592 |
|
|
} |
1593 |
|
|
|
1594 |
|
|
/* No scope is open. This is a new request or macro. */ |
1595 |
|
|
|
1596 |
|
|
spos = pos; |
1597 |
|
|
t = roff_parse(r, buf->buf, &pos, ln, ppos); |
1598 |
|
|
|
1599 |
|
|
/* Tables ignore most macros. */ |
1600 |
|
|
|
1601 |
|
|
if (r->tbl != NULL && (t == ROFF_MAX || t == ROFF_TS)) { |
1602 |
|
|
mandoc_msg(MANDOCERR_TBLMACRO, r->parse, |
1603 |
|
|
ln, pos, buf->buf + spos); |
1604 |
|
|
if (t == ROFF_TS) |
1605 |
|
|
return ROFF_IGN; |
1606 |
|
|
while (buf->buf[pos] != '\0' && buf->buf[pos] != ' ') |
1607 |
|
|
pos++; |
1608 |
|
|
while (buf->buf[pos] != '\0' && buf->buf[pos] == ' ') |
1609 |
|
|
pos++; |
1610 |
|
|
return tbl_read(r->tbl, ln, buf->buf, pos); |
1611 |
|
|
} |
1612 |
|
|
|
1613 |
|
|
/* |
1614 |
|
|
* This is neither a roff request nor a user-defined macro. |
1615 |
|
|
* Let the standard macro set parsers handle it. |
1616 |
|
|
*/ |
1617 |
|
|
|
1618 |
|
|
if (t == ROFF_MAX) |
1619 |
|
|
return ROFF_CONT; |
1620 |
|
|
|
1621 |
|
|
/* Execute a roff request or a user defined macro. */ |
1622 |
|
|
|
1623 |
|
|
assert(roffs[t].proc); |
1624 |
|
|
return (*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs); |
1625 |
|
|
} |
1626 |
|
|
|
1627 |
|
|
void |
1628 |
|
|
roff_endparse(struct roff *r) |
1629 |
|
|
{ |
1630 |
|
|
|
1631 |
|
|
if (r->last) |
1632 |
|
|
mandoc_msg(MANDOCERR_BLK_NOEND, r->parse, |
1633 |
|
|
r->last->line, r->last->col, |
1634 |
|
|
roffs[r->last->tok].name); |
1635 |
|
|
|
1636 |
|
|
if (r->eqn) { |
1637 |
|
|
mandoc_msg(MANDOCERR_BLK_NOEND, r->parse, |
1638 |
|
|
r->eqn->eqn.ln, r->eqn->eqn.pos, "EQ"); |
1639 |
|
|
eqn_end(&r->eqn); |
1640 |
|
|
} |
1641 |
|
|
|
1642 |
|
|
if (r->tbl) { |
1643 |
|
|
mandoc_msg(MANDOCERR_BLK_NOEND, r->parse, |
1644 |
|
|
r->tbl->line, r->tbl->pos, "TS"); |
1645 |
|
|
tbl_end(&r->tbl); |
1646 |
|
|
} |
1647 |
|
|
} |
1648 |
|
|
|
1649 |
|
|
/* |
1650 |
|
|
* Parse a roff node's type from the input buffer. This must be in the |
1651 |
|
|
* form of ".foo xxx" in the usual way. |
1652 |
|
|
*/ |
1653 |
|
|
static enum rofft |
1654 |
|
|
roff_parse(struct roff *r, char *buf, int *pos, int ln, int ppos) |
1655 |
|
|
{ |
1656 |
|
|
char *cp; |
1657 |
|
|
const char *mac; |
1658 |
|
|
size_t maclen; |
1659 |
|
|
enum rofft t; |
1660 |
|
|
|
1661 |
|
|
cp = buf + *pos; |
1662 |
|
|
|
1663 |
|
|
if ('\0' == *cp || '"' == *cp || '\t' == *cp || ' ' == *cp) |
1664 |
|
|
return ROFF_MAX; |
1665 |
|
|
|
1666 |
|
|
mac = cp; |
1667 |
|
|
maclen = roff_getname(r, &cp, ln, ppos); |
1668 |
|
|
|
1669 |
|
|
t = (r->current_string = roff_getstrn(r, mac, maclen)) |
1670 |
|
|
? ROFF_USERDEF : roffhash_find(mac, maclen); |
1671 |
|
|
|
1672 |
|
|
if (ROFF_MAX != t) |
1673 |
|
|
*pos = cp - buf; |
1674 |
|
|
|
1675 |
|
|
return t; |
1676 |
|
|
} |
1677 |
|
|
|
1678 |
|
|
/* --- handling of request blocks ----------------------------------------- */ |
1679 |
|
|
|
1680 |
|
|
static enum rofferr |
1681 |
|
|
roff_cblock(ROFF_ARGS) |
1682 |
|
|
{ |
1683 |
|
|
|
1684 |
|
|
/* |
1685 |
|
|
* A block-close `..' should only be invoked as a child of an |
1686 |
|
|
* ignore macro, otherwise raise a warning and just ignore it. |
1687 |
|
|
*/ |
1688 |
|
|
|
1689 |
|
|
if (r->last == NULL) { |
1690 |
|
|
mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, |
1691 |
|
|
ln, ppos, ".."); |
1692 |
|
|
return ROFF_IGN; |
1693 |
|
|
} |
1694 |
|
|
|
1695 |
|
|
switch (r->last->tok) { |
1696 |
|
|
case ROFF_am: |
1697 |
|
|
/* ROFF_am1 is remapped to ROFF_am in roff_block(). */ |
1698 |
|
|
case ROFF_ami: |
1699 |
|
|
case ROFF_de: |
1700 |
|
|
/* ROFF_de1 is remapped to ROFF_de in roff_block(). */ |
1701 |
|
|
case ROFF_dei: |
1702 |
|
|
case ROFF_ig: |
1703 |
|
|
break; |
1704 |
|
|
default: |
1705 |
|
|
mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, |
1706 |
|
|
ln, ppos, ".."); |
1707 |
|
|
return ROFF_IGN; |
1708 |
|
|
} |
1709 |
|
|
|
1710 |
|
|
if (buf->buf[pos] != '\0') |
1711 |
|
|
mandoc_vmsg(MANDOCERR_ARG_SKIP, r->parse, ln, pos, |
1712 |
|
|
".. %s", buf->buf + pos); |
1713 |
|
|
|
1714 |
|
|
roffnode_pop(r); |
1715 |
|
|
roffnode_cleanscope(r); |
1716 |
|
|
return ROFF_IGN; |
1717 |
|
|
|
1718 |
|
|
} |
1719 |
|
|
|
1720 |
|
|
static void |
1721 |
|
|
roffnode_cleanscope(struct roff *r) |
1722 |
|
|
{ |
1723 |
|
|
|
1724 |
|
|
while (r->last) { |
1725 |
|
|
if (--r->last->endspan != 0) |
1726 |
|
|
break; |
1727 |
|
|
roffnode_pop(r); |
1728 |
|
|
} |
1729 |
|
|
} |
1730 |
|
|
|
1731 |
|
|
static void |
1732 |
|
|
roff_ccond(struct roff *r, int ln, int ppos) |
1733 |
|
|
{ |
1734 |
|
|
|
1735 |
|
|
if (NULL == r->last) { |
1736 |
|
|
mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, |
1737 |
|
|
ln, ppos, "\\}"); |
1738 |
|
|
return; |
1739 |
|
|
} |
1740 |
|
|
|
1741 |
|
|
switch (r->last->tok) { |
1742 |
|
|
case ROFF_el: |
1743 |
|
|
case ROFF_ie: |
1744 |
|
|
case ROFF_if: |
1745 |
|
|
break; |
1746 |
|
|
default: |
1747 |
|
|
mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, |
1748 |
|
|
ln, ppos, "\\}"); |
1749 |
|
|
return; |
1750 |
|
|
} |
1751 |
|
|
|
1752 |
|
|
if (r->last->endspan > -1) { |
1753 |
|
|
mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, |
1754 |
|
|
ln, ppos, "\\}"); |
1755 |
|
|
return; |
1756 |
|
|
} |
1757 |
|
|
|
1758 |
|
|
roffnode_pop(r); |
1759 |
|
|
roffnode_cleanscope(r); |
1760 |
|
|
return; |
1761 |
|
|
} |
1762 |
|
|
|
1763 |
|
|
static enum rofferr |
1764 |
|
|
roff_block(ROFF_ARGS) |
1765 |
|
|
{ |
1766 |
|
|
const char *name; |
1767 |
|
|
char *iname, *cp; |
1768 |
|
|
size_t namesz; |
1769 |
|
|
|
1770 |
|
|
/* Ignore groff compatibility mode for now. */ |
1771 |
|
|
|
1772 |
|
|
if (tok == ROFF_de1) |
1773 |
|
|
tok = ROFF_de; |
1774 |
|
|
else if (tok == ROFF_dei1) |
1775 |
|
|
tok = ROFF_dei; |
1776 |
|
|
else if (tok == ROFF_am1) |
1777 |
|
|
tok = ROFF_am; |
1778 |
|
|
else if (tok == ROFF_ami1) |
1779 |
|
|
tok = ROFF_ami; |
1780 |
|
|
|
1781 |
|
|
/* Parse the macro name argument. */ |
1782 |
|
|
|
1783 |
|
|
cp = buf->buf + pos; |
1784 |
|
|
if (tok == ROFF_ig) { |
1785 |
|
|
iname = NULL; |
1786 |
|
|
namesz = 0; |
1787 |
|
|
} else { |
1788 |
|
|
iname = cp; |
1789 |
|
|
namesz = roff_getname(r, &cp, ln, ppos); |
1790 |
|
|
iname[namesz] = '\0'; |
1791 |
|
|
} |
1792 |
|
|
|
1793 |
|
|
/* Resolve the macro name argument if it is indirect. */ |
1794 |
|
|
|
1795 |
|
|
if (namesz && (tok == ROFF_dei || tok == ROFF_ami)) { |
1796 |
|
|
if ((name = roff_getstrn(r, iname, namesz)) == NULL) { |
1797 |
|
|
mandoc_vmsg(MANDOCERR_STR_UNDEF, |
1798 |
|
|
r->parse, ln, (int)(iname - buf->buf), |
1799 |
|
|
"%.*s", (int)namesz, iname); |
1800 |
|
|
namesz = 0; |
1801 |
|
|
} else |
1802 |
|
|
namesz = strlen(name); |
1803 |
|
|
} else |
1804 |
|
|
name = iname; |
1805 |
|
|
|
1806 |
|
|
if (namesz == 0 && tok != ROFF_ig) { |
1807 |
|
|
mandoc_msg(MANDOCERR_REQ_EMPTY, r->parse, |
1808 |
|
|
ln, ppos, roffs[tok].name); |
1809 |
|
|
return ROFF_IGN; |
1810 |
|
|
} |
1811 |
|
|
|
1812 |
|
|
roffnode_push(r, tok, name, ln, ppos); |
1813 |
|
|
|
1814 |
|
|
/* |
1815 |
|
|
* At the beginning of a `de' macro, clear the existing string |
1816 |
|
|
* with the same name, if there is one. New content will be |
1817 |
|
|
* appended from roff_block_text() in multiline mode. |
1818 |
|
|
*/ |
1819 |
|
|
|
1820 |
|
|
if (tok == ROFF_de || tok == ROFF_dei) |
1821 |
|
|
roff_setstrn(&r->strtab, name, namesz, "", 0, 0); |
1822 |
|
|
|
1823 |
|
|
if (*cp == '\0') |
1824 |
|
|
return ROFF_IGN; |
1825 |
|
|
|
1826 |
|
|
/* Get the custom end marker. */ |
1827 |
|
|
|
1828 |
|
|
iname = cp; |
1829 |
|
|
namesz = roff_getname(r, &cp, ln, ppos); |
1830 |
|
|
|
1831 |
|
|
/* Resolve the end marker if it is indirect. */ |
1832 |
|
|
|
1833 |
|
|
if (namesz && (tok == ROFF_dei || tok == ROFF_ami)) { |
1834 |
|
|
if ((name = roff_getstrn(r, iname, namesz)) == NULL) { |
1835 |
|
|
mandoc_vmsg(MANDOCERR_STR_UNDEF, |
1836 |
|
|
r->parse, ln, (int)(iname - buf->buf), |
1837 |
|
|
"%.*s", (int)namesz, iname); |
1838 |
|
|
namesz = 0; |
1839 |
|
|
} else |
1840 |
|
|
namesz = strlen(name); |
1841 |
|
|
} else |
1842 |
|
|
name = iname; |
1843 |
|
|
|
1844 |
|
|
if (namesz) |
1845 |
|
|
r->last->end = mandoc_strndup(name, namesz); |
1846 |
|
|
|
1847 |
|
|
if (*cp != '\0') |
1848 |
|
|
mandoc_vmsg(MANDOCERR_ARG_EXCESS, r->parse, |
1849 |
|
|
ln, pos, ".%s ... %s", roffs[tok].name, cp); |
1850 |
|
|
|
1851 |
|
|
return ROFF_IGN; |
1852 |
|
|
} |
1853 |
|
|
|
1854 |
|
|
static enum rofferr |
1855 |
|
|
roff_block_sub(ROFF_ARGS) |
1856 |
|
|
{ |
1857 |
|
|
enum rofft t; |
1858 |
|
|
int i, j; |
1859 |
|
|
|
1860 |
|
|
/* |
1861 |
|
|
* First check whether a custom macro exists at this level. If |
1862 |
|
|
* it does, then check against it. This is some of groff's |
1863 |
|
|
* stranger behaviours. If we encountered a custom end-scope |
1864 |
|
|
* tag and that tag also happens to be a "real" macro, then we |
1865 |
|
|
* need to try interpreting it again as a real macro. If it's |
1866 |
|
|
* not, then return ignore. Else continue. |
1867 |
|
|
*/ |
1868 |
|
|
|
1869 |
|
|
if (r->last->end) { |
1870 |
|
|
for (i = pos, j = 0; r->last->end[j]; j++, i++) |
1871 |
|
|
if (buf->buf[i] != r->last->end[j]) |
1872 |
|
|
break; |
1873 |
|
|
|
1874 |
|
|
if (r->last->end[j] == '\0' && |
1875 |
|
|
(buf->buf[i] == '\0' || |
1876 |
|
|
buf->buf[i] == ' ' || |
1877 |
|
|
buf->buf[i] == '\t')) { |
1878 |
|
|
roffnode_pop(r); |
1879 |
|
|
roffnode_cleanscope(r); |
1880 |
|
|
|
1881 |
|
|
while (buf->buf[i] == ' ' || buf->buf[i] == '\t') |
1882 |
|
|
i++; |
1883 |
|
|
|
1884 |
|
|
pos = i; |
1885 |
|
|
if (roff_parse(r, buf->buf, &pos, ln, ppos) != |
1886 |
|
|
ROFF_MAX) |
1887 |
|
|
return ROFF_RERUN; |
1888 |
|
|
return ROFF_IGN; |
1889 |
|
|
} |
1890 |
|
|
} |
1891 |
|
|
|
1892 |
|
|
/* |
1893 |
|
|
* If we have no custom end-query or lookup failed, then try |
1894 |
|
|
* pulling it out of the hashtable. |
1895 |
|
|
*/ |
1896 |
|
|
|
1897 |
|
|
t = roff_parse(r, buf->buf, &pos, ln, ppos); |
1898 |
|
|
|
1899 |
|
|
if (t != ROFF_cblock) { |
1900 |
|
|
if (tok != ROFF_ig) |
1901 |
|
|
roff_setstr(r, r->last->name, buf->buf + ppos, 2); |
1902 |
|
|
return ROFF_IGN; |
1903 |
|
|
} |
1904 |
|
|
|
1905 |
|
|
assert(roffs[t].proc); |
1906 |
|
|
return (*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs); |
1907 |
|
|
} |
1908 |
|
|
|
1909 |
|
|
static enum rofferr |
1910 |
|
|
roff_block_text(ROFF_ARGS) |
1911 |
|
|
{ |
1912 |
|
|
|
1913 |
|
|
if (tok != ROFF_ig) |
1914 |
|
|
roff_setstr(r, r->last->name, buf->buf + pos, 2); |
1915 |
|
|
|
1916 |
|
|
return ROFF_IGN; |
1917 |
|
|
} |
1918 |
|
|
|
1919 |
|
|
static enum rofferr |
1920 |
|
|
roff_cond_sub(ROFF_ARGS) |
1921 |
|
|
{ |
1922 |
|
|
enum rofft t; |
1923 |
|
|
char *ep; |
1924 |
|
|
int rr; |
1925 |
|
|
|
1926 |
|
|
rr = r->last->rule; |
1927 |
|
|
roffnode_cleanscope(r); |
1928 |
|
|
t = roff_parse(r, buf->buf, &pos, ln, ppos); |
1929 |
|
|
|
1930 |
|
|
/* |
1931 |
|
|
* Fully handle known macros when they are structurally |
1932 |
|
|
* required or when the conditional evaluated to true. |
1933 |
|
|
*/ |
1934 |
|
|
|
1935 |
|
|
if ((t != ROFF_MAX) && |
1936 |
|
|
(rr || roffs[t].flags & ROFFMAC_STRUCT)) { |
1937 |
|
|
assert(roffs[t].proc); |
1938 |
|
|
return (*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs); |
1939 |
|
|
} |
1940 |
|
|
|
1941 |
|
|
/* |
1942 |
|
|
* If `\}' occurs on a macro line without a preceding macro, |
1943 |
|
|
* drop the line completely. |
1944 |
|
|
*/ |
1945 |
|
|
|
1946 |
|
|
ep = buf->buf + pos; |
1947 |
|
|
if (ep[0] == '\\' && ep[1] == '}') |
1948 |
|
|
rr = 0; |
1949 |
|
|
|
1950 |
|
|
/* Always check for the closing delimiter `\}'. */ |
1951 |
|
|
|
1952 |
|
|
while ((ep = strchr(ep, '\\')) != NULL) { |
1953 |
|
|
if (*(++ep) == '}') { |
1954 |
|
|
*ep = '&'; |
1955 |
|
|
roff_ccond(r, ln, ep - buf->buf - 1); |
1956 |
|
|
} |
1957 |
|
|
if (*ep != '\0') |
1958 |
|
|
++ep; |
1959 |
|
|
} |
1960 |
|
|
return rr ? ROFF_CONT : ROFF_IGN; |
1961 |
|
|
} |
1962 |
|
|
|
1963 |
|
|
static enum rofferr |
1964 |
|
|
roff_cond_text(ROFF_ARGS) |
1965 |
|
|
{ |
1966 |
|
|
char *ep; |
1967 |
|
|
int rr; |
1968 |
|
|
|
1969 |
|
|
rr = r->last->rule; |
1970 |
|
|
roffnode_cleanscope(r); |
1971 |
|
|
|
1972 |
|
|
ep = buf->buf + pos; |
1973 |
|
|
while ((ep = strchr(ep, '\\')) != NULL) { |
1974 |
|
|
if (*(++ep) == '}') { |
1975 |
|
|
*ep = '&'; |
1976 |
|
|
roff_ccond(r, ln, ep - buf->buf - 1); |
1977 |
|
|
} |
1978 |
|
|
if (*ep != '\0') |
1979 |
|
|
++ep; |
1980 |
|
|
} |
1981 |
|
|
return rr ? ROFF_CONT : ROFF_IGN; |
1982 |
|
|
} |
1983 |
|
|
|
1984 |
|
|
/* --- handling of numeric and conditional expressions -------------------- */ |
1985 |
|
|
|
1986 |
|
|
/* |
1987 |
|
|
* Parse a single signed integer number. Stop at the first non-digit. |
1988 |
|
|
* If there is at least one digit, return success and advance the |
1989 |
|
|
* parse point, else return failure and let the parse point unchanged. |
1990 |
|
|
* Ignore overflows, treat them just like the C language. |
1991 |
|
|
*/ |
1992 |
|
|
static int |
1993 |
|
|
roff_getnum(const char *v, int *pos, int *res, int flags) |
1994 |
|
|
{ |
1995 |
|
|
int myres, scaled, n, p; |
1996 |
|
|
|
1997 |
|
|
if (NULL == res) |
1998 |
|
|
res = &myres; |
1999 |
|
|
|
2000 |
|
|
p = *pos; |
2001 |
|
|
n = v[p] == '-'; |
2002 |
|
|
if (n || v[p] == '+') |
2003 |
|
|
p++; |
2004 |
|
|
|
2005 |
|
|
if (flags & ROFFNUM_WHITE) |
2006 |
|
|
while (isspace((unsigned char)v[p])) |
2007 |
|
|
p++; |
2008 |
|
|
|
2009 |
|
|
for (*res = 0; isdigit((unsigned char)v[p]); p++) |
2010 |
|
|
*res = 10 * *res + v[p] - '0'; |
2011 |
|
|
if (p == *pos + n) |
2012 |
|
|
return 0; |
2013 |
|
|
|
2014 |
|
|
if (n) |
2015 |
|
|
*res = -*res; |
2016 |
|
|
|
2017 |
|
|
/* Each number may be followed by one optional scaling unit. */ |
2018 |
|
|
|
2019 |
|
|
switch (v[p]) { |
2020 |
|
|
case 'f': |
2021 |
|
|
scaled = *res * 65536; |
2022 |
|
|
break; |
2023 |
|
|
case 'i': |
2024 |
|
|
scaled = *res * 240; |
2025 |
|
|
break; |
2026 |
|
|
case 'c': |
2027 |
|
|
scaled = *res * 240 / 2.54; |
2028 |
|
|
break; |
2029 |
|
|
case 'v': |
2030 |
|
|
case 'P': |
2031 |
|
|
scaled = *res * 40; |
2032 |
|
|
break; |
2033 |
|
|
case 'm': |
2034 |
|
|
case 'n': |
2035 |
|
|
scaled = *res * 24; |
2036 |
|
|
break; |
2037 |
|
|
case 'p': |
2038 |
|
|
scaled = *res * 10 / 3; |
2039 |
|
|
break; |
2040 |
|
|
case 'u': |
2041 |
|
|
scaled = *res; |
2042 |
|
|
break; |
2043 |
|
|
case 'M': |
2044 |
|
|
scaled = *res * 6 / 25; |
2045 |
|
|
break; |
2046 |
|
|
default: |
2047 |
|
|
scaled = *res; |
2048 |
|
|
p--; |
2049 |
|
|
break; |
2050 |
|
|
} |
2051 |
|
|
if (flags & ROFFNUM_SCALE) |
2052 |
|
|
*res = scaled; |
2053 |
|
|
|
2054 |
|
|
*pos = p + 1; |
2055 |
|
|
return 1; |
2056 |
|
|
} |
2057 |
|
|
|
2058 |
|
|
/* |
2059 |
|
|
* Evaluate a string comparison condition. |
2060 |
|
|
* The first character is the delimiter. |
2061 |
|
|
* Succeed if the string up to its second occurrence |
2062 |
|
|
* matches the string up to its third occurence. |
2063 |
|
|
* Advance the cursor after the third occurrence |
2064 |
|
|
* or lacking that, to the end of the line. |
2065 |
|
|
*/ |
2066 |
|
|
static int |
2067 |
|
|
roff_evalstrcond(const char *v, int *pos) |
2068 |
|
|
{ |
2069 |
|
|
const char *s1, *s2, *s3; |
2070 |
|
|
int match; |
2071 |
|
|
|
2072 |
|
|
match = 0; |
2073 |
|
|
s1 = v + *pos; /* initial delimiter */ |
2074 |
|
|
s2 = s1 + 1; /* for scanning the first string */ |
2075 |
|
|
s3 = strchr(s2, *s1); /* for scanning the second string */ |
2076 |
|
|
|
2077 |
|
|
if (NULL == s3) /* found no middle delimiter */ |
2078 |
|
|
goto out; |
2079 |
|
|
|
2080 |
|
|
while ('\0' != *++s3) { |
2081 |
|
|
if (*s2 != *s3) { /* mismatch */ |
2082 |
|
|
s3 = strchr(s3, *s1); |
2083 |
|
|
break; |
2084 |
|
|
} |
2085 |
|
|
if (*s3 == *s1) { /* found the final delimiter */ |
2086 |
|
|
match = 1; |
2087 |
|
|
break; |
2088 |
|
|
} |
2089 |
|
|
s2++; |
2090 |
|
|
} |
2091 |
|
|
|
2092 |
|
|
out: |
2093 |
|
|
if (NULL == s3) |
2094 |
|
|
s3 = strchr(s2, '\0'); |
2095 |
|
|
else if (*s3 != '\0') |
2096 |
|
|
s3++; |
2097 |
|
|
*pos = s3 - v; |
2098 |
|
|
return match; |
2099 |
|
|
} |
2100 |
|
|
|
2101 |
|
|
/* |
2102 |
|
|
* Evaluate an optionally negated single character, numerical, |
2103 |
|
|
* or string condition. |
2104 |
|
|
*/ |
2105 |
|
|
static int |
2106 |
|
|
roff_evalcond(struct roff *r, int ln, char *v, int *pos) |
2107 |
|
|
{ |
2108 |
|
|
char *cp, *name; |
2109 |
|
|
size_t sz; |
2110 |
|
|
int number, savepos, wanttrue; |
2111 |
|
|
|
2112 |
|
|
if ('!' == v[*pos]) { |
2113 |
|
|
wanttrue = 0; |
2114 |
|
|
(*pos)++; |
2115 |
|
|
} else |
2116 |
|
|
wanttrue = 1; |
2117 |
|
|
|
2118 |
|
|
switch (v[*pos]) { |
2119 |
|
|
case '\0': |
2120 |
|
|
return 0; |
2121 |
|
|
case 'n': |
2122 |
|
|
case 'o': |
2123 |
|
|
(*pos)++; |
2124 |
|
|
return wanttrue; |
2125 |
|
|
case 'c': |
2126 |
|
|
case 'd': |
2127 |
|
|
case 'e': |
2128 |
|
|
case 't': |
2129 |
|
|
case 'v': |
2130 |
|
|
(*pos)++; |
2131 |
|
|
return !wanttrue; |
2132 |
|
|
case 'r': |
2133 |
|
|
cp = name = v + ++*pos; |
2134 |
|
|
sz = roff_getname(r, &cp, ln, *pos); |
2135 |
|
|
*pos = cp - v; |
2136 |
|
|
return (sz && roff_hasregn(r, name, sz)) == wanttrue; |
2137 |
|
|
default: |
2138 |
|
|
break; |
2139 |
|
|
} |
2140 |
|
|
|
2141 |
|
|
savepos = *pos; |
2142 |
|
|
if (roff_evalnum(r, ln, v, pos, &number, ROFFNUM_SCALE)) |
2143 |
|
|
return (number > 0) == wanttrue; |
2144 |
|
|
else if (*pos == savepos) |
2145 |
|
|
return roff_evalstrcond(v, pos) == wanttrue; |
2146 |
|
|
else |
2147 |
|
|
return 0; |
2148 |
|
|
} |
2149 |
|
|
|
2150 |
|
|
static enum rofferr |
2151 |
|
|
roff_line_ignore(ROFF_ARGS) |
2152 |
|
|
{ |
2153 |
|
|
|
2154 |
|
|
return ROFF_IGN; |
2155 |
|
|
} |
2156 |
|
|
|
2157 |
|
|
static enum rofferr |
2158 |
|
|
roff_insec(ROFF_ARGS) |
2159 |
|
|
{ |
2160 |
|
|
|
2161 |
|
|
mandoc_msg(MANDOCERR_REQ_INSEC, r->parse, |
2162 |
|
|
ln, ppos, roffs[tok].name); |
2163 |
|
|
return ROFF_IGN; |
2164 |
|
|
} |
2165 |
|
|
|
2166 |
|
|
static enum rofferr |
2167 |
|
|
roff_unsupp(ROFF_ARGS) |
2168 |
|
|
{ |
2169 |
|
|
|
2170 |
|
|
mandoc_msg(MANDOCERR_REQ_UNSUPP, r->parse, |
2171 |
|
|
ln, ppos, roffs[tok].name); |
2172 |
|
|
return ROFF_IGN; |
2173 |
|
|
} |
2174 |
|
|
|
2175 |
|
|
static enum rofferr |
2176 |
|
|
roff_cond(ROFF_ARGS) |
2177 |
|
|
{ |
2178 |
|
|
|
2179 |
|
|
roffnode_push(r, tok, NULL, ln, ppos); |
2180 |
|
|
|
2181 |
|
|
/* |
2182 |
|
|
* An `.el' has no conditional body: it will consume the value |
2183 |
|
|
* of the current rstack entry set in prior `ie' calls or |
2184 |
|
|
* defaults to DENY. |
2185 |
|
|
* |
2186 |
|
|
* If we're not an `el', however, then evaluate the conditional. |
2187 |
|
|
*/ |
2188 |
|
|
|
2189 |
|
|
r->last->rule = tok == ROFF_el ? |
2190 |
|
|
(r->rstackpos < 0 ? 0 : r->rstack[r->rstackpos--]) : |
2191 |
|
|
roff_evalcond(r, ln, buf->buf, &pos); |
2192 |
|
|
|
2193 |
|
|
/* |
2194 |
|
|
* An if-else will put the NEGATION of the current evaluated |
2195 |
|
|
* conditional into the stack of rules. |
2196 |
|
|
*/ |
2197 |
|
|
|
2198 |
|
|
if (tok == ROFF_ie) { |
2199 |
|
|
if (r->rstackpos + 1 == r->rstacksz) { |
2200 |
|
|
r->rstacksz += 16; |
2201 |
|
|
r->rstack = mandoc_reallocarray(r->rstack, |
2202 |
|
|
r->rstacksz, sizeof(int)); |
2203 |
|
|
} |
2204 |
|
|
r->rstack[++r->rstackpos] = !r->last->rule; |
2205 |
|
|
} |
2206 |
|
|
|
2207 |
|
|
/* If the parent has false as its rule, then so do we. */ |
2208 |
|
|
|
2209 |
|
|
if (r->last->parent && !r->last->parent->rule) |
2210 |
|
|
r->last->rule = 0; |
2211 |
|
|
|
2212 |
|
|
/* |
2213 |
|
|
* Determine scope. |
2214 |
|
|
* If there is nothing on the line after the conditional, |
2215 |
|
|
* not even whitespace, use next-line scope. |
2216 |
|
|
*/ |
2217 |
|
|
|
2218 |
|
|
if (buf->buf[pos] == '\0') { |
2219 |
|
|
r->last->endspan = 2; |
2220 |
|
|
goto out; |
2221 |
|
|
} |
2222 |
|
|
|
2223 |
|
|
while (buf->buf[pos] == ' ') |
2224 |
|
|
pos++; |
2225 |
|
|
|
2226 |
|
|
/* An opening brace requests multiline scope. */ |
2227 |
|
|
|
2228 |
|
|
if (buf->buf[pos] == '\\' && buf->buf[pos + 1] == '{') { |
2229 |
|
|
r->last->endspan = -1; |
2230 |
|
|
pos += 2; |
2231 |
|
|
while (buf->buf[pos] == ' ') |
2232 |
|
|
pos++; |
2233 |
|
|
goto out; |
2234 |
|
|
} |
2235 |
|
|
|
2236 |
|
|
/* |
2237 |
|
|
* Anything else following the conditional causes |
2238 |
|
|
* single-line scope. Warn if the scope contains |
2239 |
|
|
* nothing but trailing whitespace. |
2240 |
|
|
*/ |
2241 |
|
|
|
2242 |
|
|
if (buf->buf[pos] == '\0') |
2243 |
|
|
mandoc_msg(MANDOCERR_COND_EMPTY, r->parse, |
2244 |
|
|
ln, ppos, roffs[tok].name); |
2245 |
|
|
|
2246 |
|
|
r->last->endspan = 1; |
2247 |
|
|
|
2248 |
|
|
out: |
2249 |
|
|
*offs = pos; |
2250 |
|
|
return ROFF_RERUN; |
2251 |
|
|
} |
2252 |
|
|
|
2253 |
|
|
static enum rofferr |
2254 |
|
|
roff_ds(ROFF_ARGS) |
2255 |
|
|
{ |
2256 |
|
|
char *string; |
2257 |
|
|
const char *name; |
2258 |
|
|
size_t namesz; |
2259 |
|
|
|
2260 |
|
|
/* Ignore groff compatibility mode for now. */ |
2261 |
|
|
|
2262 |
|
|
if (tok == ROFF_ds1) |
2263 |
|
|
tok = ROFF_ds; |
2264 |
|
|
else if (tok == ROFF_as1) |
2265 |
|
|
tok = ROFF_as; |
2266 |
|
|
|
2267 |
|
|
/* |
2268 |
|
|
* The first word is the name of the string. |
2269 |
|
|
* If it is empty or terminated by an escape sequence, |
2270 |
|
|
* abort the `ds' request without defining anything. |
2271 |
|
|
*/ |
2272 |
|
|
|
2273 |
|
|
name = string = buf->buf + pos; |
2274 |
|
|
if (*name == '\0') |
2275 |
|
|
return ROFF_IGN; |
2276 |
|
|
|
2277 |
|
|
namesz = roff_getname(r, &string, ln, pos); |
2278 |
|
|
if (name[namesz] == '\\') |
2279 |
|
|
return ROFF_IGN; |
2280 |
|
|
|
2281 |
|
|
/* Read past the initial double-quote, if any. */ |
2282 |
|
|
if (*string == '"') |
2283 |
|
|
string++; |
2284 |
|
|
|
2285 |
|
|
/* The rest is the value. */ |
2286 |
|
|
roff_setstrn(&r->strtab, name, namesz, string, strlen(string), |
2287 |
|
|
ROFF_as == tok); |
2288 |
|
|
return ROFF_IGN; |
2289 |
|
|
} |
2290 |
|
|
|
2291 |
|
|
/* |
2292 |
|
|
* Parse a single operator, one or two characters long. |
2293 |
|
|
* If the operator is recognized, return success and advance the |
2294 |
|
|
* parse point, else return failure and let the parse point unchanged. |
2295 |
|
|
*/ |
2296 |
|
|
static int |
2297 |
|
|
roff_getop(const char *v, int *pos, char *res) |
2298 |
|
|
{ |
2299 |
|
|
|
2300 |
|
|
*res = v[*pos]; |
2301 |
|
|
|
2302 |
|
|
switch (*res) { |
2303 |
|
|
case '+': |
2304 |
|
|
case '-': |
2305 |
|
|
case '*': |
2306 |
|
|
case '/': |
2307 |
|
|
case '%': |
2308 |
|
|
case '&': |
2309 |
|
|
case ':': |
2310 |
|
|
break; |
2311 |
|
|
case '<': |
2312 |
|
|
switch (v[*pos + 1]) { |
2313 |
|
|
case '=': |
2314 |
|
|
*res = 'l'; |
2315 |
|
|
(*pos)++; |
2316 |
|
|
break; |
2317 |
|
|
case '>': |
2318 |
|
|
*res = '!'; |
2319 |
|
|
(*pos)++; |
2320 |
|
|
break; |
2321 |
|
|
case '?': |
2322 |
|
|
*res = 'i'; |
2323 |
|
|
(*pos)++; |
2324 |
|
|
break; |
2325 |
|
|
default: |
2326 |
|
|
break; |
2327 |
|
|
} |
2328 |
|
|
break; |
2329 |
|
|
case '>': |
2330 |
|
|
switch (v[*pos + 1]) { |
2331 |
|
|
case '=': |
2332 |
|
|
*res = 'g'; |
2333 |
|
|
(*pos)++; |
2334 |
|
|
break; |
2335 |
|
|
case '?': |
2336 |
|
|
*res = 'a'; |
2337 |
|
|
(*pos)++; |
2338 |
|
|
break; |
2339 |
|
|
default: |
2340 |
|
|
break; |
2341 |
|
|
} |
2342 |
|
|
break; |
2343 |
|
|
case '=': |
2344 |
|
|
if ('=' == v[*pos + 1]) |
2345 |
|
|
(*pos)++; |
2346 |
|
|
break; |
2347 |
|
|
default: |
2348 |
|
|
return 0; |
2349 |
|
|
} |
2350 |
|
|
(*pos)++; |
2351 |
|
|
|
2352 |
|
|
return *res; |
2353 |
|
|
} |
2354 |
|
|
|
2355 |
|
|
/* |
2356 |
|
|
* Evaluate either a parenthesized numeric expression |
2357 |
|
|
* or a single signed integer number. |
2358 |
|
|
*/ |
2359 |
|
|
static int |
2360 |
|
|
roff_evalpar(struct roff *r, int ln, |
2361 |
|
|
const char *v, int *pos, int *res, int flags) |
2362 |
|
|
{ |
2363 |
|
|
|
2364 |
|
|
if ('(' != v[*pos]) |
2365 |
|
|
return roff_getnum(v, pos, res, flags); |
2366 |
|
|
|
2367 |
|
|
(*pos)++; |
2368 |
|
|
if ( ! roff_evalnum(r, ln, v, pos, res, flags | ROFFNUM_WHITE)) |
2369 |
|
|
return 0; |
2370 |
|
|
|
2371 |
|
|
/* |
2372 |
|
|
* Omission of the closing parenthesis |
2373 |
|
|
* is an error in validation mode, |
2374 |
|
|
* but ignored in evaluation mode. |
2375 |
|
|
*/ |
2376 |
|
|
|
2377 |
|
|
if (')' == v[*pos]) |
2378 |
|
|
(*pos)++; |
2379 |
|
|
else if (NULL == res) |
2380 |
|
|
return 0; |
2381 |
|
|
|
2382 |
|
|
return 1; |
2383 |
|
|
} |
2384 |
|
|
|
2385 |
|
|
/* |
2386 |
|
|
* Evaluate a complete numeric expression. |
2387 |
|
|
* Proceed left to right, there is no concept of precedence. |
2388 |
|
|
*/ |
2389 |
|
|
static int |
2390 |
|
|
roff_evalnum(struct roff *r, int ln, const char *v, |
2391 |
|
|
int *pos, int *res, int flags) |
2392 |
|
|
{ |
2393 |
|
|
int mypos, operand2; |
2394 |
|
|
char operator; |
2395 |
|
|
|
2396 |
|
|
if (NULL == pos) { |
2397 |
|
|
mypos = 0; |
2398 |
|
|
pos = &mypos; |
2399 |
|
|
} |
2400 |
|
|
|
2401 |
|
|
if (flags & ROFFNUM_WHITE) |
2402 |
|
|
while (isspace((unsigned char)v[*pos])) |
2403 |
|
|
(*pos)++; |
2404 |
|
|
|
2405 |
|
|
if ( ! roff_evalpar(r, ln, v, pos, res, flags)) |
2406 |
|
|
return 0; |
2407 |
|
|
|
2408 |
|
|
while (1) { |
2409 |
|
|
if (flags & ROFFNUM_WHITE) |
2410 |
|
|
while (isspace((unsigned char)v[*pos])) |
2411 |
|
|
(*pos)++; |
2412 |
|
|
|
2413 |
|
|
if ( ! roff_getop(v, pos, &operator)) |
2414 |
|
|
break; |
2415 |
|
|
|
2416 |
|
|
if (flags & ROFFNUM_WHITE) |
2417 |
|
|
while (isspace((unsigned char)v[*pos])) |
2418 |
|
|
(*pos)++; |
2419 |
|
|
|
2420 |
|
|
if ( ! roff_evalpar(r, ln, v, pos, &operand2, flags)) |
2421 |
|
|
return 0; |
2422 |
|
|
|
2423 |
|
|
if (flags & ROFFNUM_WHITE) |
2424 |
|
|
while (isspace((unsigned char)v[*pos])) |
2425 |
|
|
(*pos)++; |
2426 |
|
|
|
2427 |
|
|
if (NULL == res) |
2428 |
|
|
continue; |
2429 |
|
|
|
2430 |
|
|
switch (operator) { |
2431 |
|
|
case '+': |
2432 |
|
|
*res += operand2; |
2433 |
|
|
break; |
2434 |
|
|
case '-': |
2435 |
|
|
*res -= operand2; |
2436 |
|
|
break; |
2437 |
|
|
case '*': |
2438 |
|
|
*res *= operand2; |
2439 |
|
|
break; |
2440 |
|
|
case '/': |
2441 |
|
|
if (operand2 == 0) { |
2442 |
|
|
mandoc_msg(MANDOCERR_DIVZERO, |
2443 |
|
|
r->parse, ln, *pos, v); |
2444 |
|
|
*res = 0; |
2445 |
|
|
break; |
2446 |
|
|
} |
2447 |
|
|
*res /= operand2; |
2448 |
|
|
break; |
2449 |
|
|
case '%': |
2450 |
|
|
if (operand2 == 0) { |
2451 |
|
|
mandoc_msg(MANDOCERR_DIVZERO, |
2452 |
|
|
r->parse, ln, *pos, v); |
2453 |
|
|
*res = 0; |
2454 |
|
|
break; |
2455 |
|
|
} |
2456 |
|
|
*res %= operand2; |
2457 |
|
|
break; |
2458 |
|
|
case '<': |
2459 |
|
|
*res = *res < operand2; |
2460 |
|
|
break; |
2461 |
|
|
case '>': |
2462 |
|
|
*res = *res > operand2; |
2463 |
|
|
break; |
2464 |
|
|
case 'l': |
2465 |
|
|
*res = *res <= operand2; |
2466 |
|
|
break; |
2467 |
|
|
case 'g': |
2468 |
|
|
*res = *res >= operand2; |
2469 |
|
|
break; |
2470 |
|
|
case '=': |
2471 |
|
|
*res = *res == operand2; |
2472 |
|
|
break; |
2473 |
|
|
case '!': |
2474 |
|
|
*res = *res != operand2; |
2475 |
|
|
break; |
2476 |
|
|
case '&': |
2477 |
|
|
*res = *res && operand2; |
2478 |
|
|
break; |
2479 |
|
|
case ':': |
2480 |
|
|
*res = *res || operand2; |
2481 |
|
|
break; |
2482 |
|
|
case 'i': |
2483 |
|
|
if (operand2 < *res) |
2484 |
|
|
*res = operand2; |
2485 |
|
|
break; |
2486 |
|
|
case 'a': |
2487 |
|
|
if (operand2 > *res) |
2488 |
|
|
*res = operand2; |
2489 |
|
|
break; |
2490 |
|
|
default: |
2491 |
|
|
abort(); |
2492 |
|
|
} |
2493 |
|
|
} |
2494 |
|
|
return 1; |
2495 |
|
|
} |
2496 |
|
|
|
2497 |
|
|
/* --- register management ------------------------------------------------ */ |
2498 |
|
|
|
2499 |
|
|
void |
2500 |
|
|
roff_setreg(struct roff *r, const char *name, int val, char sign) |
2501 |
|
|
{ |
2502 |
|
|
struct roffreg *reg; |
2503 |
|
|
|
2504 |
|
|
/* Search for an existing register with the same name. */ |
2505 |
|
|
reg = r->regtab; |
2506 |
|
|
|
2507 |
|
|
while (reg && strcmp(name, reg->key.p)) |
2508 |
|
|
reg = reg->next; |
2509 |
|
|
|
2510 |
|
|
if (NULL == reg) { |
2511 |
|
|
/* Create a new register. */ |
2512 |
|
|
reg = mandoc_malloc(sizeof(struct roffreg)); |
2513 |
|
|
reg->key.p = mandoc_strdup(name); |
2514 |
|
|
reg->key.sz = strlen(name); |
2515 |
|
|
reg->val = 0; |
2516 |
|
|
reg->next = r->regtab; |
2517 |
|
|
r->regtab = reg; |
2518 |
|
|
} |
2519 |
|
|
|
2520 |
|
|
if ('+' == sign) |
2521 |
|
|
reg->val += val; |
2522 |
|
|
else if ('-' == sign) |
2523 |
|
|
reg->val -= val; |
2524 |
|
|
else |
2525 |
|
|
reg->val = val; |
2526 |
|
|
} |
2527 |
|
|
|
2528 |
|
|
/* |
2529 |
|
|
* Handle some predefined read-only number registers. |
2530 |
|
|
* For now, return -1 if the requested register is not predefined; |
2531 |
|
|
* in case a predefined read-only register having the value -1 |
2532 |
|
|
* were to turn up, another special value would have to be chosen. |
2533 |
|
|
*/ |
2534 |
|
|
static int |
2535 |
|
|
roff_getregro(const struct roff *r, const char *name) |
2536 |
|
|
{ |
2537 |
|
|
|
2538 |
|
|
switch (*name) { |
2539 |
|
|
case '$': /* Number of arguments of the last macro evaluated. */ |
2540 |
|
|
return r->argc; |
2541 |
|
|
case 'A': /* ASCII approximation mode is always off. */ |
2542 |
|
|
return 0; |
2543 |
|
|
case 'g': /* Groff compatibility mode is always on. */ |
2544 |
|
|
return 1; |
2545 |
|
|
case 'H': /* Fixed horizontal resolution. */ |
2546 |
|
|
return 24; |
2547 |
|
|
case 'j': /* Always adjust left margin only. */ |
2548 |
|
|
return 0; |
2549 |
|
|
case 'T': /* Some output device is always defined. */ |
2550 |
|
|
return 1; |
2551 |
|
|
case 'V': /* Fixed vertical resolution. */ |
2552 |
|
|
return 40; |
2553 |
|
|
default: |
2554 |
|
|
return -1; |
2555 |
|
|
} |
2556 |
|
|
} |
2557 |
|
|
|
2558 |
|
|
int |
2559 |
|
|
roff_getreg(const struct roff *r, const char *name) |
2560 |
|
|
{ |
2561 |
|
|
struct roffreg *reg; |
2562 |
|
|
int val; |
2563 |
|
|
|
2564 |
|
|
if ('.' == name[0] && '\0' != name[1] && '\0' == name[2]) { |
2565 |
|
|
val = roff_getregro(r, name + 1); |
2566 |
|
|
if (-1 != val) |
2567 |
|
|
return val; |
2568 |
|
|
} |
2569 |
|
|
|
2570 |
|
|
for (reg = r->regtab; reg; reg = reg->next) |
2571 |
|
|
if (0 == strcmp(name, reg->key.p)) |
2572 |
|
|
return reg->val; |
2573 |
|
|
|
2574 |
|
|
return 0; |
2575 |
|
|
} |
2576 |
|
|
|
2577 |
|
|
static int |
2578 |
|
|
roff_getregn(const struct roff *r, const char *name, size_t len) |
2579 |
|
|
{ |
2580 |
|
|
struct roffreg *reg; |
2581 |
|
|
int val; |
2582 |
|
|
|
2583 |
|
|
if ('.' == name[0] && 2 == len) { |
2584 |
|
|
val = roff_getregro(r, name + 1); |
2585 |
|
|
if (-1 != val) |
2586 |
|
|
return val; |
2587 |
|
|
} |
2588 |
|
|
|
2589 |
|
|
for (reg = r->regtab; reg; reg = reg->next) |
2590 |
|
|
if (len == reg->key.sz && |
2591 |
|
|
0 == strncmp(name, reg->key.p, len)) |
2592 |
|
|
return reg->val; |
2593 |
|
|
|
2594 |
|
|
return 0; |
2595 |
|
|
} |
2596 |
|
|
|
2597 |
|
|
static int |
2598 |
|
|
roff_hasregn(const struct roff *r, const char *name, size_t len) |
2599 |
|
|
{ |
2600 |
|
|
struct roffreg *reg; |
2601 |
|
|
int val; |
2602 |
|
|
|
2603 |
|
|
if ('.' == name[0] && 2 == len) { |
2604 |
|
|
val = roff_getregro(r, name + 1); |
2605 |
|
|
if (-1 != val) |
2606 |
|
|
return 1; |
2607 |
|
|
} |
2608 |
|
|
|
2609 |
|
|
for (reg = r->regtab; reg; reg = reg->next) |
2610 |
|
|
if (len == reg->key.sz && |
2611 |
|
|
0 == strncmp(name, reg->key.p, len)) |
2612 |
|
|
return 1; |
2613 |
|
|
|
2614 |
|
|
return 0; |
2615 |
|
|
} |
2616 |
|
|
|
2617 |
|
|
static void |
2618 |
|
|
roff_freereg(struct roffreg *reg) |
2619 |
|
|
{ |
2620 |
|
|
struct roffreg *old_reg; |
2621 |
|
|
|
2622 |
|
|
while (NULL != reg) { |
2623 |
|
|
free(reg->key.p); |
2624 |
|
|
old_reg = reg; |
2625 |
|
|
reg = reg->next; |
2626 |
|
|
free(old_reg); |
2627 |
|
|
} |
2628 |
|
|
} |
2629 |
|
|
|
2630 |
|
|
static enum rofferr |
2631 |
|
|
roff_nr(ROFF_ARGS) |
2632 |
|
|
{ |
2633 |
|
|
char *key, *val; |
2634 |
|
|
size_t keysz; |
2635 |
|
|
int iv; |
2636 |
|
|
char sign; |
2637 |
|
|
|
2638 |
|
|
key = val = buf->buf + pos; |
2639 |
|
|
if (*key == '\0') |
2640 |
|
|
return ROFF_IGN; |
2641 |
|
|
|
2642 |
|
|
keysz = roff_getname(r, &val, ln, pos); |
2643 |
|
|
if (key[keysz] == '\\') |
2644 |
|
|
return ROFF_IGN; |
2645 |
|
|
key[keysz] = '\0'; |
2646 |
|
|
|
2647 |
|
|
sign = *val; |
2648 |
|
|
if (sign == '+' || sign == '-') |
2649 |
|
|
val++; |
2650 |
|
|
|
2651 |
|
|
if (roff_evalnum(r, ln, val, NULL, &iv, ROFFNUM_SCALE)) |
2652 |
|
|
roff_setreg(r, key, iv, sign); |
2653 |
|
|
|
2654 |
|
|
return ROFF_IGN; |
2655 |
|
|
} |
2656 |
|
|
|
2657 |
|
|
static enum rofferr |
2658 |
|
|
roff_rr(ROFF_ARGS) |
2659 |
|
|
{ |
2660 |
|
|
struct roffreg *reg, **prev; |
2661 |
|
|
char *name, *cp; |
2662 |
|
|
size_t namesz; |
2663 |
|
|
|
2664 |
|
|
name = cp = buf->buf + pos; |
2665 |
|
|
if (*name == '\0') |
2666 |
|
|
return ROFF_IGN; |
2667 |
|
|
namesz = roff_getname(r, &cp, ln, pos); |
2668 |
|
|
name[namesz] = '\0'; |
2669 |
|
|
|
2670 |
|
|
prev = &r->regtab; |
2671 |
|
|
while (1) { |
2672 |
|
|
reg = *prev; |
2673 |
|
|
if (reg == NULL || !strcmp(name, reg->key.p)) |
2674 |
|
|
break; |
2675 |
|
|
prev = ®->next; |
2676 |
|
|
} |
2677 |
|
|
if (reg != NULL) { |
2678 |
|
|
*prev = reg->next; |
2679 |
|
|
free(reg->key.p); |
2680 |
|
|
free(reg); |
2681 |
|
|
} |
2682 |
|
|
return ROFF_IGN; |
2683 |
|
|
} |
2684 |
|
|
|
2685 |
|
|
/* --- handler functions for roff requests -------------------------------- */ |
2686 |
|
|
|
2687 |
|
|
static enum rofferr |
2688 |
|
|
roff_rm(ROFF_ARGS) |
2689 |
|
|
{ |
2690 |
|
|
const char *name; |
2691 |
|
|
char *cp; |
2692 |
|
|
size_t namesz; |
2693 |
|
|
|
2694 |
|
|
cp = buf->buf + pos; |
2695 |
|
|
while (*cp != '\0') { |
2696 |
|
|
name = cp; |
2697 |
|
|
namesz = roff_getname(r, &cp, ln, (int)(cp - buf->buf)); |
2698 |
|
|
roff_setstrn(&r->strtab, name, namesz, NULL, 0, 0); |
2699 |
|
|
if (name[namesz] == '\\') |
2700 |
|
|
break; |
2701 |
|
|
} |
2702 |
|
|
return ROFF_IGN; |
2703 |
|
|
} |
2704 |
|
|
|
2705 |
|
|
static enum rofferr |
2706 |
|
|
roff_it(ROFF_ARGS) |
2707 |
|
|
{ |
2708 |
|
|
int iv; |
2709 |
|
|
|
2710 |
|
|
/* Parse the number of lines. */ |
2711 |
|
|
|
2712 |
|
|
if ( ! roff_evalnum(r, ln, buf->buf, &pos, &iv, 0)) { |
2713 |
|
|
mandoc_msg(MANDOCERR_IT_NONUM, r->parse, |
2714 |
|
|
ln, ppos, buf->buf + 1); |
2715 |
|
|
return ROFF_IGN; |
2716 |
|
|
} |
2717 |
|
|
|
2718 |
|
|
while (isspace((unsigned char)buf->buf[pos])) |
2719 |
|
|
pos++; |
2720 |
|
|
|
2721 |
|
|
/* |
2722 |
|
|
* Arm the input line trap. |
2723 |
|
|
* Special-casing "an-trap" is an ugly workaround to cope |
2724 |
|
|
* with DocBook stupidly fiddling with man(7) internals. |
2725 |
|
|
*/ |
2726 |
|
|
|
2727 |
|
|
roffit_lines = iv; |
2728 |
|
|
roffit_macro = mandoc_strdup(iv != 1 || |
2729 |
|
|
strcmp(buf->buf + pos, "an-trap") ? |
2730 |
|
|
buf->buf + pos : "br"); |
2731 |
|
|
return ROFF_IGN; |
2732 |
|
|
} |
2733 |
|
|
|
2734 |
|
|
static enum rofferr |
2735 |
|
|
roff_Dd(ROFF_ARGS) |
2736 |
|
|
{ |
2737 |
|
|
const char *const *cp; |
2738 |
|
|
|
2739 |
|
|
if ((r->options & (MPARSE_MDOC | MPARSE_QUICK)) == 0) |
2740 |
|
|
for (cp = __mdoc_reserved; *cp; cp++) |
2741 |
|
|
roff_setstr(r, *cp, NULL, 0); |
2742 |
|
|
|
2743 |
|
|
if (r->format == 0) |
2744 |
|
|
r->format = MPARSE_MDOC; |
2745 |
|
|
|
2746 |
|
|
return ROFF_CONT; |
2747 |
|
|
} |
2748 |
|
|
|
2749 |
|
|
static enum rofferr |
2750 |
|
|
roff_TH(ROFF_ARGS) |
2751 |
|
|
{ |
2752 |
|
|
const char *const *cp; |
2753 |
|
|
|
2754 |
|
|
if ((r->options & MPARSE_QUICK) == 0) |
2755 |
|
|
for (cp = __man_reserved; *cp; cp++) |
2756 |
|
|
roff_setstr(r, *cp, NULL, 0); |
2757 |
|
|
|
2758 |
|
|
if (r->format == 0) |
2759 |
|
|
r->format = MPARSE_MAN; |
2760 |
|
|
|
2761 |
|
|
return ROFF_CONT; |
2762 |
|
|
} |
2763 |
|
|
|
2764 |
|
|
static enum rofferr |
2765 |
|
|
roff_TE(ROFF_ARGS) |
2766 |
|
|
{ |
2767 |
|
|
|
2768 |
|
|
if (NULL == r->tbl) |
2769 |
|
|
mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, |
2770 |
|
|
ln, ppos, "TE"); |
2771 |
|
|
else if ( ! tbl_end(&r->tbl)) { |
2772 |
|
|
free(buf->buf); |
2773 |
|
|
buf->buf = mandoc_strdup(".sp"); |
2774 |
|
|
buf->sz = 4; |
2775 |
|
|
return ROFF_REPARSE; |
2776 |
|
|
} |
2777 |
|
|
return ROFF_IGN; |
2778 |
|
|
} |
2779 |
|
|
|
2780 |
|
|
static enum rofferr |
2781 |
|
|
roff_T_(ROFF_ARGS) |
2782 |
|
|
{ |
2783 |
|
|
|
2784 |
|
|
if (NULL == r->tbl) |
2785 |
|
|
mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, |
2786 |
|
|
ln, ppos, "T&"); |
2787 |
|
|
else |
2788 |
|
|
tbl_restart(ppos, ln, r->tbl); |
2789 |
|
|
|
2790 |
|
|
return ROFF_IGN; |
2791 |
|
|
} |
2792 |
|
|
|
2793 |
|
|
/* |
2794 |
|
|
* Handle in-line equation delimiters. |
2795 |
|
|
*/ |
2796 |
|
|
static enum rofferr |
2797 |
|
|
roff_eqndelim(struct roff *r, struct buf *buf, int pos) |
2798 |
|
|
{ |
2799 |
|
|
char *cp1, *cp2; |
2800 |
|
|
const char *bef_pr, *bef_nl, *mac, *aft_nl, *aft_pr; |
2801 |
|
|
|
2802 |
|
|
/* |
2803 |
|
|
* Outside equations, look for an opening delimiter. |
2804 |
|
|
* If we are inside an equation, we already know it is |
2805 |
|
|
* in-line, or this function wouldn't have been called; |
2806 |
|
|
* so look for a closing delimiter. |
2807 |
|
|
*/ |
2808 |
|
|
|
2809 |
|
|
cp1 = buf->buf + pos; |
2810 |
|
|
cp2 = strchr(cp1, r->eqn == NULL ? |
2811 |
|
|
r->last_eqn->odelim : r->last_eqn->cdelim); |
2812 |
|
|
if (cp2 == NULL) |
2813 |
|
|
return ROFF_CONT; |
2814 |
|
|
|
2815 |
|
|
*cp2++ = '\0'; |
2816 |
|
|
bef_pr = bef_nl = aft_nl = aft_pr = ""; |
2817 |
|
|
|
2818 |
|
|
/* Handle preceding text, protecting whitespace. */ |
2819 |
|
|
|
2820 |
|
|
if (*buf->buf != '\0') { |
2821 |
|
|
if (r->eqn == NULL) |
2822 |
|
|
bef_pr = "\\&"; |
2823 |
|
|
bef_nl = "\n"; |
2824 |
|
|
} |
2825 |
|
|
|
2826 |
|
|
/* |
2827 |
|
|
* Prepare replacing the delimiter with an equation macro |
2828 |
|
|
* and drop leading white space from the equation. |
2829 |
|
|
*/ |
2830 |
|
|
|
2831 |
|
|
if (r->eqn == NULL) { |
2832 |
|
|
while (*cp2 == ' ') |
2833 |
|
|
cp2++; |
2834 |
|
|
mac = ".EQ"; |
2835 |
|
|
} else |
2836 |
|
|
mac = ".EN"; |
2837 |
|
|
|
2838 |
|
|
/* Handle following text, protecting whitespace. */ |
2839 |
|
|
|
2840 |
|
|
if (*cp2 != '\0') { |
2841 |
|
|
aft_nl = "\n"; |
2842 |
|
|
if (r->eqn != NULL) |
2843 |
|
|
aft_pr = "\\&"; |
2844 |
|
|
} |
2845 |
|
|
|
2846 |
|
|
/* Do the actual replacement. */ |
2847 |
|
|
|
2848 |
|
|
buf->sz = mandoc_asprintf(&cp1, "%s%s%s%s%s%s%s", buf->buf, |
2849 |
|
|
bef_pr, bef_nl, mac, aft_nl, aft_pr, cp2) + 1; |
2850 |
|
|
free(buf->buf); |
2851 |
|
|
buf->buf = cp1; |
2852 |
|
|
|
2853 |
|
|
/* Toggle the in-line state of the eqn subsystem. */ |
2854 |
|
|
|
2855 |
|
|
r->eqn_inline = r->eqn == NULL; |
2856 |
|
|
return ROFF_REPARSE; |
2857 |
|
|
} |
2858 |
|
|
|
2859 |
|
|
static enum rofferr |
2860 |
|
|
roff_EQ(ROFF_ARGS) |
2861 |
|
|
{ |
2862 |
|
|
struct eqn_node *e; |
2863 |
|
|
|
2864 |
|
|
assert(r->eqn == NULL); |
2865 |
|
|
e = eqn_alloc(ppos, ln, r->parse); |
2866 |
|
|
|
2867 |
|
|
if (r->last_eqn) { |
2868 |
|
|
r->last_eqn->next = e; |
2869 |
|
|
e->delim = r->last_eqn->delim; |
2870 |
|
|
e->odelim = r->last_eqn->odelim; |
2871 |
|
|
e->cdelim = r->last_eqn->cdelim; |
2872 |
|
|
} else |
2873 |
|
|
r->first_eqn = r->last_eqn = e; |
2874 |
|
|
|
2875 |
|
|
r->eqn = r->last_eqn = e; |
2876 |
|
|
|
2877 |
|
|
if (buf->buf[pos] != '\0') |
2878 |
|
|
mandoc_vmsg(MANDOCERR_ARG_SKIP, r->parse, ln, pos, |
2879 |
|
|
".EQ %s", buf->buf + pos); |
2880 |
|
|
|
2881 |
|
|
return ROFF_IGN; |
2882 |
|
|
} |
2883 |
|
|
|
2884 |
|
|
static enum rofferr |
2885 |
|
|
roff_EN(ROFF_ARGS) |
2886 |
|
|
{ |
2887 |
|
|
|
2888 |
|
|
mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, ln, ppos, "EN"); |
2889 |
|
|
return ROFF_IGN; |
2890 |
|
|
} |
2891 |
|
|
|
2892 |
|
|
static enum rofferr |
2893 |
|
|
roff_TS(ROFF_ARGS) |
2894 |
|
|
{ |
2895 |
|
|
struct tbl_node *tbl; |
2896 |
|
|
|
2897 |
|
|
if (r->tbl) { |
2898 |
|
|
mandoc_msg(MANDOCERR_BLK_BROKEN, r->parse, |
2899 |
|
|
ln, ppos, "TS breaks TS"); |
2900 |
|
|
tbl_end(&r->tbl); |
2901 |
|
|
} |
2902 |
|
|
|
2903 |
|
|
tbl = tbl_alloc(ppos, ln, r->parse); |
2904 |
|
|
|
2905 |
|
|
if (r->last_tbl) |
2906 |
|
|
r->last_tbl->next = tbl; |
2907 |
|
|
else |
2908 |
|
|
r->first_tbl = r->last_tbl = tbl; |
2909 |
|
|
|
2910 |
|
|
r->tbl = r->last_tbl = tbl; |
2911 |
|
|
return ROFF_IGN; |
2912 |
|
|
} |
2913 |
|
|
|
2914 |
|
|
static enum rofferr |
2915 |
|
|
roff_brp(ROFF_ARGS) |
2916 |
|
|
{ |
2917 |
|
|
|
2918 |
|
|
buf->buf[pos - 1] = '\0'; |
2919 |
|
|
return ROFF_CONT; |
2920 |
|
|
} |
2921 |
|
|
|
2922 |
|
|
static enum rofferr |
2923 |
|
|
roff_cc(ROFF_ARGS) |
2924 |
|
|
{ |
2925 |
|
|
const char *p; |
2926 |
|
|
|
2927 |
|
|
p = buf->buf + pos; |
2928 |
|
|
|
2929 |
|
|
if (*p == '\0' || (r->control = *p++) == '.') |
2930 |
|
|
r->control = 0; |
2931 |
|
|
|
2932 |
|
|
if (*p != '\0') |
2933 |
|
|
mandoc_vmsg(MANDOCERR_ARG_EXCESS, r->parse, |
2934 |
|
|
ln, p - buf->buf, "cc ... %s", p); |
2935 |
|
|
|
2936 |
|
|
return ROFF_IGN; |
2937 |
|
|
} |
2938 |
|
|
|
2939 |
|
|
static enum rofferr |
2940 |
|
|
roff_tr(ROFF_ARGS) |
2941 |
|
|
{ |
2942 |
|
|
const char *p, *first, *second; |
2943 |
|
|
size_t fsz, ssz; |
2944 |
|
|
enum mandoc_esc esc; |
2945 |
|
|
|
2946 |
|
|
p = buf->buf + pos; |
2947 |
|
|
|
2948 |
|
|
if (*p == '\0') { |
2949 |
|
|
mandoc_msg(MANDOCERR_REQ_EMPTY, r->parse, ln, ppos, "tr"); |
2950 |
|
|
return ROFF_IGN; |
2951 |
|
|
} |
2952 |
|
|
|
2953 |
|
|
while (*p != '\0') { |
2954 |
|
|
fsz = ssz = 1; |
2955 |
|
|
|
2956 |
|
|
first = p++; |
2957 |
|
|
if (*first == '\\') { |
2958 |
|
|
esc = mandoc_escape(&p, NULL, NULL); |
2959 |
|
|
if (esc == ESCAPE_ERROR) { |
2960 |
|
|
mandoc_msg(MANDOCERR_ESC_BAD, r->parse, |
2961 |
|
|
ln, (int)(p - buf->buf), first); |
2962 |
|
|
return ROFF_IGN; |
2963 |
|
|
} |
2964 |
|
|
fsz = (size_t)(p - first); |
2965 |
|
|
} |
2966 |
|
|
|
2967 |
|
|
second = p++; |
2968 |
|
|
if (*second == '\\') { |
2969 |
|
|
esc = mandoc_escape(&p, NULL, NULL); |
2970 |
|
|
if (esc == ESCAPE_ERROR) { |
2971 |
|
|
mandoc_msg(MANDOCERR_ESC_BAD, r->parse, |
2972 |
|
|
ln, (int)(p - buf->buf), second); |
2973 |
|
|
return ROFF_IGN; |
2974 |
|
|
} |
2975 |
|
|
ssz = (size_t)(p - second); |
2976 |
|
|
} else if (*second == '\0') { |
2977 |
|
|
mandoc_vmsg(MANDOCERR_TR_ODD, r->parse, |
2978 |
|
|
ln, first - buf->buf, "tr %s", first); |
2979 |
|
|
second = " "; |
2980 |
|
|
p--; |
2981 |
|
|
} |
2982 |
|
|
|
2983 |
|
|
if (fsz > 1) { |
2984 |
|
|
roff_setstrn(&r->xmbtab, first, fsz, |
2985 |
|
|
second, ssz, 0); |
2986 |
|
|
continue; |
2987 |
|
|
} |
2988 |
|
|
|
2989 |
|
|
if (r->xtab == NULL) |
2990 |
|
|
r->xtab = mandoc_calloc(128, |
2991 |
|
|
sizeof(struct roffstr)); |
2992 |
|
|
|
2993 |
|
|
free(r->xtab[(int)*first].p); |
2994 |
|
|
r->xtab[(int)*first].p = mandoc_strndup(second, ssz); |
2995 |
|
|
r->xtab[(int)*first].sz = ssz; |
2996 |
|
|
} |
2997 |
|
|
|
2998 |
|
|
return ROFF_IGN; |
2999 |
|
|
} |
3000 |
|
|
|
3001 |
|
|
static enum rofferr |
3002 |
|
|
roff_so(ROFF_ARGS) |
3003 |
|
|
{ |
3004 |
|
|
char *name, *cp; |
3005 |
|
|
|
3006 |
|
|
name = buf->buf + pos; |
3007 |
|
|
mandoc_vmsg(MANDOCERR_SO, r->parse, ln, ppos, "so %s", name); |
3008 |
|
|
|
3009 |
|
|
/* |
3010 |
|
|
* Handle `so'. Be EXTREMELY careful, as we shouldn't be |
3011 |
|
|
* opening anything that's not in our cwd or anything beneath |
3012 |
|
|
* it. Thus, explicitly disallow traversing up the file-system |
3013 |
|
|
* or using absolute paths. |
3014 |
|
|
*/ |
3015 |
|
|
|
3016 |
|
|
if (*name == '/' || strstr(name, "../") || strstr(name, "/..")) { |
3017 |
|
|
mandoc_vmsg(MANDOCERR_SO_PATH, r->parse, ln, ppos, |
3018 |
|
|
".so %s", name); |
3019 |
|
|
buf->sz = mandoc_asprintf(&cp, |
3020 |
|
|
".sp\nSee the file %s.\n.sp", name) + 1; |
3021 |
|
|
free(buf->buf); |
3022 |
|
|
buf->buf = cp; |
3023 |
|
|
*offs = 0; |
3024 |
|
|
return ROFF_REPARSE; |
3025 |
|
|
} |
3026 |
|
|
|
3027 |
|
|
*offs = pos; |
3028 |
|
|
return ROFF_SO; |
3029 |
|
|
} |
3030 |
|
|
|
3031 |
|
|
/* --- user defined strings and macros ------------------------------------ */ |
3032 |
|
|
|
3033 |
|
|
static enum rofferr |
3034 |
|
|
roff_userdef(ROFF_ARGS) |
3035 |
|
|
{ |
3036 |
|
|
const char *arg[9], *ap; |
3037 |
|
|
char *cp, *n1, *n2; |
3038 |
|
|
int i, ib, ie; |
3039 |
|
|
size_t asz, rsz; |
3040 |
|
|
|
3041 |
|
|
/* |
3042 |
|
|
* Collect pointers to macro argument strings |
3043 |
|
|
* and NUL-terminate them. |
3044 |
|
|
*/ |
3045 |
|
|
|
3046 |
|
|
r->argc = 0; |
3047 |
|
|
cp = buf->buf + pos; |
3048 |
|
|
for (i = 0; i < 9; i++) { |
3049 |
|
|
if (*cp == '\0') |
3050 |
|
|
arg[i] = ""; |
3051 |
|
|
else { |
3052 |
|
|
arg[i] = mandoc_getarg(r->parse, &cp, ln, &pos); |
3053 |
|
|
r->argc = i + 1; |
3054 |
|
|
} |
3055 |
|
|
} |
3056 |
|
|
|
3057 |
|
|
/* |
3058 |
|
|
* Expand macro arguments. |
3059 |
|
|
*/ |
3060 |
|
|
|
3061 |
|
|
buf->sz = strlen(r->current_string) + 1; |
3062 |
|
|
n1 = cp = mandoc_malloc(buf->sz); |
3063 |
|
|
memcpy(n1, r->current_string, buf->sz); |
3064 |
|
|
while (*cp != '\0') { |
3065 |
|
|
|
3066 |
|
|
/* Scan ahead for the next argument invocation. */ |
3067 |
|
|
|
3068 |
|
|
if (*cp++ != '\\') |
3069 |
|
|
continue; |
3070 |
|
|
if (*cp++ != '$') |
3071 |
|
|
continue; |
3072 |
|
|
if (*cp == '*') { /* \\$* inserts all arguments */ |
3073 |
|
|
ib = 0; |
3074 |
|
|
ie = r->argc - 1; |
3075 |
|
|
} else { /* \\$1 .. \\$9 insert one argument */ |
3076 |
|
|
ib = ie = *cp - '1'; |
3077 |
|
|
if (ib < 0 || ib > 8) |
3078 |
|
|
continue; |
3079 |
|
|
} |
3080 |
|
|
cp -= 2; |
3081 |
|
|
|
3082 |
|
|
/* |
3083 |
|
|
* Determine the size of the expanded argument, |
3084 |
|
|
* taking escaping of quotes into account. |
3085 |
|
|
*/ |
3086 |
|
|
|
3087 |
|
|
asz = ie > ib ? ie - ib : 0; /* for blanks */ |
3088 |
|
|
for (i = ib; i <= ie; i++) { |
3089 |
|
|
for (ap = arg[i]; *ap != '\0'; ap++) { |
3090 |
|
|
asz++; |
3091 |
|
|
if (*ap == '"') |
3092 |
|
|
asz += 3; |
3093 |
|
|
} |
3094 |
|
|
} |
3095 |
|
|
if (asz != 3) { |
3096 |
|
|
|
3097 |
|
|
/* |
3098 |
|
|
* Determine the size of the rest of the |
3099 |
|
|
* unexpanded macro, including the NUL. |
3100 |
|
|
*/ |
3101 |
|
|
|
3102 |
|
|
rsz = buf->sz - (cp - n1) - 3; |
3103 |
|
|
|
3104 |
|
|
/* |
3105 |
|
|
* When shrinking, move before |
3106 |
|
|
* releasing the storage. |
3107 |
|
|
*/ |
3108 |
|
|
|
3109 |
|
|
if (asz < 3) |
3110 |
|
|
memmove(cp + asz, cp + 3, rsz); |
3111 |
|
|
|
3112 |
|
|
/* |
3113 |
|
|
* Resize the storage for the macro |
3114 |
|
|
* and readjust the parse pointer. |
3115 |
|
|
*/ |
3116 |
|
|
|
3117 |
|
|
buf->sz += asz - 3; |
3118 |
|
|
n2 = mandoc_realloc(n1, buf->sz); |
3119 |
|
|
cp = n2 + (cp - n1); |
3120 |
|
|
n1 = n2; |
3121 |
|
|
|
3122 |
|
|
/* |
3123 |
|
|
* When growing, make room |
3124 |
|
|
* for the expanded argument. |
3125 |
|
|
*/ |
3126 |
|
|
|
3127 |
|
|
if (asz > 3) |
3128 |
|
|
memmove(cp + asz, cp + 3, rsz); |
3129 |
|
|
} |
3130 |
|
|
|
3131 |
|
|
/* Copy the expanded argument, escaping quotes. */ |
3132 |
|
|
|
3133 |
|
|
n2 = cp; |
3134 |
|
|
for (i = ib; i <= ie; i++) { |
3135 |
|
|
for (ap = arg[i]; *ap != '\0'; ap++) { |
3136 |
|
|
if (*ap == '"') { |
3137 |
|
|
memcpy(n2, "\\(dq", 4); |
3138 |
|
|
n2 += 4; |
3139 |
|
|
} else |
3140 |
|
|
*n2++ = *ap; |
3141 |
|
|
} |
3142 |
|
|
if (i < ie) |
3143 |
|
|
*n2++ = ' '; |
3144 |
|
|
} |
3145 |
|
|
} |
3146 |
|
|
|
3147 |
|
|
/* |
3148 |
|
|
* Replace the macro invocation |
3149 |
|
|
* by the expanded macro. |
3150 |
|
|
*/ |
3151 |
|
|
|
3152 |
|
|
free(buf->buf); |
3153 |
|
|
buf->buf = n1; |
3154 |
|
|
*offs = 0; |
3155 |
|
|
|
3156 |
|
|
return buf->sz > 1 && buf->buf[buf->sz - 2] == '\n' ? |
3157 |
|
|
ROFF_REPARSE : ROFF_APPEND; |
3158 |
|
|
} |
3159 |
|
|
|
3160 |
|
|
static size_t |
3161 |
|
|
roff_getname(struct roff *r, char **cpp, int ln, int pos) |
3162 |
|
|
{ |
3163 |
|
|
char *name, *cp; |
3164 |
|
|
size_t namesz; |
3165 |
|
|
|
3166 |
|
|
name = *cpp; |
3167 |
|
|
if ('\0' == *name) |
3168 |
|
|
return 0; |
3169 |
|
|
|
3170 |
|
|
/* Read until end of name and terminate it with NUL. */ |
3171 |
|
|
for (cp = name; 1; cp++) { |
3172 |
|
|
if ('\0' == *cp || ' ' == *cp) { |
3173 |
|
|
namesz = cp - name; |
3174 |
|
|
break; |
3175 |
|
|
} |
3176 |
|
|
if ('\\' != *cp) |
3177 |
|
|
continue; |
3178 |
|
|
namesz = cp - name; |
3179 |
|
|
if ('{' == cp[1] || '}' == cp[1]) |
3180 |
|
|
break; |
3181 |
|
|
cp++; |
3182 |
|
|
if ('\\' == *cp) |
3183 |
|
|
continue; |
3184 |
|
|
mandoc_vmsg(MANDOCERR_NAMESC, r->parse, ln, pos, |
3185 |
|
|
"%.*s", (int)(cp - name + 1), name); |
3186 |
|
|
mandoc_escape((const char **)&cp, NULL, NULL); |
3187 |
|
|
break; |
3188 |
|
|
} |
3189 |
|
|
|
3190 |
|
|
/* Read past spaces. */ |
3191 |
|
|
while (' ' == *cp) |
3192 |
|
|
cp++; |
3193 |
|
|
|
3194 |
|
|
*cpp = cp; |
3195 |
|
|
return namesz; |
3196 |
|
|
} |
3197 |
|
|
|
3198 |
|
|
/* |
3199 |
|
|
* Store *string into the user-defined string called *name. |
3200 |
|
|
* To clear an existing entry, call with (*r, *name, NULL, 0). |
3201 |
|
|
* append == 0: replace mode |
3202 |
|
|
* append == 1: single-line append mode |
3203 |
|
|
* append == 2: multiline append mode, append '\n' after each call |
3204 |
|
|
*/ |
3205 |
|
|
static void |
3206 |
|
|
roff_setstr(struct roff *r, const char *name, const char *string, |
3207 |
|
|
int append) |
3208 |
|
|
{ |
3209 |
|
|
|
3210 |
|
|
roff_setstrn(&r->strtab, name, strlen(name), string, |
3211 |
|
|
string ? strlen(string) : 0, append); |
3212 |
|
|
} |
3213 |
|
|
|
3214 |
|
|
static void |
3215 |
|
|
roff_setstrn(struct roffkv **r, const char *name, size_t namesz, |
3216 |
|
|
const char *string, size_t stringsz, int append) |
3217 |
|
|
{ |
3218 |
|
|
struct roffkv *n; |
3219 |
|
|
char *c; |
3220 |
|
|
int i; |
3221 |
|
|
size_t oldch, newch; |
3222 |
|
|
|
3223 |
|
|
/* Search for an existing string with the same name. */ |
3224 |
|
|
n = *r; |
3225 |
|
|
|
3226 |
|
|
while (n && (namesz != n->key.sz || |
3227 |
|
|
strncmp(n->key.p, name, namesz))) |
3228 |
|
|
n = n->next; |
3229 |
|
|
|
3230 |
|
|
if (NULL == n) { |
3231 |
|
|
/* Create a new string table entry. */ |
3232 |
|
|
n = mandoc_malloc(sizeof(struct roffkv)); |
3233 |
|
|
n->key.p = mandoc_strndup(name, namesz); |
3234 |
|
|
n->key.sz = namesz; |
3235 |
|
|
n->val.p = NULL; |
3236 |
|
|
n->val.sz = 0; |
3237 |
|
|
n->next = *r; |
3238 |
|
|
*r = n; |
3239 |
|
|
} else if (0 == append) { |
3240 |
|
|
free(n->val.p); |
3241 |
|
|
n->val.p = NULL; |
3242 |
|
|
n->val.sz = 0; |
3243 |
|
|
} |
3244 |
|
|
|
3245 |
|
|
if (NULL == string) |
3246 |
|
|
return; |
3247 |
|
|
|
3248 |
|
|
/* |
3249 |
|
|
* One additional byte for the '\n' in multiline mode, |
3250 |
|
|
* and one for the terminating '\0'. |
3251 |
|
|
*/ |
3252 |
|
|
newch = stringsz + (1 < append ? 2u : 1u); |
3253 |
|
|
|
3254 |
|
|
if (NULL == n->val.p) { |
3255 |
|
|
n->val.p = mandoc_malloc(newch); |
3256 |
|
|
*n->val.p = '\0'; |
3257 |
|
|
oldch = 0; |
3258 |
|
|
} else { |
3259 |
|
|
oldch = n->val.sz; |
3260 |
|
|
n->val.p = mandoc_realloc(n->val.p, oldch + newch); |
3261 |
|
|
} |
3262 |
|
|
|
3263 |
|
|
/* Skip existing content in the destination buffer. */ |
3264 |
|
|
c = n->val.p + (int)oldch; |
3265 |
|
|
|
3266 |
|
|
/* Append new content to the destination buffer. */ |
3267 |
|
|
i = 0; |
3268 |
|
|
while (i < (int)stringsz) { |
3269 |
|
|
/* |
3270 |
|
|
* Rudimentary roff copy mode: |
3271 |
|
|
* Handle escaped backslashes. |
3272 |
|
|
*/ |
3273 |
|
|
if ('\\' == string[i] && '\\' == string[i + 1]) |
3274 |
|
|
i++; |
3275 |
|
|
*c++ = string[i++]; |
3276 |
|
|
} |
3277 |
|
|
|
3278 |
|
|
/* Append terminating bytes. */ |
3279 |
|
|
if (1 < append) |
3280 |
|
|
*c++ = '\n'; |
3281 |
|
|
|
3282 |
|
|
*c = '\0'; |
3283 |
|
|
n->val.sz = (int)(c - n->val.p); |
3284 |
|
|
} |
3285 |
|
|
|
3286 |
|
|
static const char * |
3287 |
|
|
roff_getstrn(const struct roff *r, const char *name, size_t len) |
3288 |
|
|
{ |
3289 |
|
|
const struct roffkv *n; |
3290 |
|
|
int i; |
3291 |
|
|
|
3292 |
|
|
for (n = r->strtab; n; n = n->next) |
3293 |
|
|
if (0 == strncmp(name, n->key.p, len) && |
3294 |
|
|
'\0' == n->key.p[(int)len]) |
3295 |
|
|
return n->val.p; |
3296 |
|
|
|
3297 |
|
|
for (i = 0; i < PREDEFS_MAX; i++) |
3298 |
|
|
if (0 == strncmp(name, predefs[i].name, len) && |
3299 |
|
|
'\0' == predefs[i].name[(int)len]) |
3300 |
|
|
return predefs[i].str; |
3301 |
|
|
|
3302 |
|
|
return NULL; |
3303 |
|
|
} |
3304 |
|
|
|
3305 |
|
|
static void |
3306 |
|
|
roff_freestr(struct roffkv *r) |
3307 |
|
|
{ |
3308 |
|
|
struct roffkv *n, *nn; |
3309 |
|
|
|
3310 |
|
|
for (n = r; n; n = nn) { |
3311 |
|
|
free(n->key.p); |
3312 |
|
|
free(n->val.p); |
3313 |
|
|
nn = n->next; |
3314 |
|
|
free(n); |
3315 |
|
|
} |
3316 |
|
|
} |
3317 |
|
|
|
3318 |
|
|
/* --- accessors and utility functions ------------------------------------ */ |
3319 |
|
|
|
3320 |
|
|
const struct tbl_span * |
3321 |
|
|
roff_span(const struct roff *r) |
3322 |
|
|
{ |
3323 |
|
|
|
3324 |
|
|
return r->tbl ? tbl_span(r->tbl) : NULL; |
3325 |
|
|
} |
3326 |
|
|
|
3327 |
|
|
const struct eqn * |
3328 |
|
|
roff_eqn(const struct roff *r) |
3329 |
|
|
{ |
3330 |
|
|
|
3331 |
|
|
return r->last_eqn ? &r->last_eqn->eqn : NULL; |
3332 |
|
|
} |
3333 |
|
|
|
3334 |
|
|
/* |
3335 |
|
|
* Duplicate an input string, making the appropriate character |
3336 |
|
|
* conversations (as stipulated by `tr') along the way. |
3337 |
|
|
* Returns a heap-allocated string with all the replacements made. |
3338 |
|
|
*/ |
3339 |
|
|
char * |
3340 |
|
|
roff_strdup(const struct roff *r, const char *p) |
3341 |
|
|
{ |
3342 |
|
|
const struct roffkv *cp; |
3343 |
|
|
char *res; |
3344 |
|
|
const char *pp; |
3345 |
|
|
size_t ssz, sz; |
3346 |
|
|
enum mandoc_esc esc; |
3347 |
|
|
|
3348 |
|
|
if (NULL == r->xmbtab && NULL == r->xtab) |
3349 |
|
|
return mandoc_strdup(p); |
3350 |
|
|
else if ('\0' == *p) |
3351 |
|
|
return mandoc_strdup(""); |
3352 |
|
|
|
3353 |
|
|
/* |
3354 |
|
|
* Step through each character looking for term matches |
3355 |
|
|
* (remember that a `tr' can be invoked with an escape, which is |
3356 |
|
|
* a glyph but the escape is multi-character). |
3357 |
|
|
* We only do this if the character hash has been initialised |
3358 |
|
|
* and the string is >0 length. |
3359 |
|
|
*/ |
3360 |
|
|
|
3361 |
|
|
res = NULL; |
3362 |
|
|
ssz = 0; |
3363 |
|
|
|
3364 |
|
|
while ('\0' != *p) { |
3365 |
|
|
if ('\\' != *p && r->xtab && r->xtab[(int)*p].p) { |
3366 |
|
|
sz = r->xtab[(int)*p].sz; |
3367 |
|
|
res = mandoc_realloc(res, ssz + sz + 1); |
3368 |
|
|
memcpy(res + ssz, r->xtab[(int)*p].p, sz); |
3369 |
|
|
ssz += sz; |
3370 |
|
|
p++; |
3371 |
|
|
continue; |
3372 |
|
|
} else if ('\\' != *p) { |
3373 |
|
|
res = mandoc_realloc(res, ssz + 2); |
3374 |
|
|
res[ssz++] = *p++; |
3375 |
|
|
continue; |
3376 |
|
|
} |
3377 |
|
|
|
3378 |
|
|
/* Search for term matches. */ |
3379 |
|
|
for (cp = r->xmbtab; cp; cp = cp->next) |
3380 |
|
|
if (0 == strncmp(p, cp->key.p, cp->key.sz)) |
3381 |
|
|
break; |
3382 |
|
|
|
3383 |
|
|
if (NULL != cp) { |
3384 |
|
|
/* |
3385 |
|
|
* A match has been found. |
3386 |
|
|
* Append the match to the array and move |
3387 |
|
|
* forward by its keysize. |
3388 |
|
|
*/ |
3389 |
|
|
res = mandoc_realloc(res, |
3390 |
|
|
ssz + cp->val.sz + 1); |
3391 |
|
|
memcpy(res + ssz, cp->val.p, cp->val.sz); |
3392 |
|
|
ssz += cp->val.sz; |
3393 |
|
|
p += (int)cp->key.sz; |
3394 |
|
|
continue; |
3395 |
|
|
} |
3396 |
|
|
|
3397 |
|
|
/* |
3398 |
|
|
* Handle escapes carefully: we need to copy |
3399 |
|
|
* over just the escape itself, or else we might |
3400 |
|
|
* do replacements within the escape itself. |
3401 |
|
|
* Make sure to pass along the bogus string. |
3402 |
|
|
*/ |
3403 |
|
|
pp = p++; |
3404 |
|
|
esc = mandoc_escape(&p, NULL, NULL); |
3405 |
|
|
if (ESCAPE_ERROR == esc) { |
3406 |
|
|
sz = strlen(pp); |
3407 |
|
|
res = mandoc_realloc(res, ssz + sz + 1); |
3408 |
|
|
memcpy(res + ssz, pp, sz); |
3409 |
|
|
break; |
3410 |
|
|
} |
3411 |
|
|
/* |
3412 |
|
|
* We bail out on bad escapes. |
3413 |
|
|
* No need to warn: we already did so when |
3414 |
|
|
* roff_res() was called. |
3415 |
|
|
*/ |
3416 |
|
|
sz = (int)(p - pp); |
3417 |
|
|
res = mandoc_realloc(res, ssz + sz + 1); |
3418 |
|
|
memcpy(res + ssz, pp, sz); |
3419 |
|
|
ssz += sz; |
3420 |
|
|
} |
3421 |
|
|
|
3422 |
|
|
res[(int)ssz] = '\0'; |
3423 |
|
|
return res; |
3424 |
|
|
} |
3425 |
|
|
|
3426 |
|
|
int |
3427 |
|
|
roff_getformat(const struct roff *r) |
3428 |
|
|
{ |
3429 |
|
|
|
3430 |
|
|
return r->format; |
3431 |
|
|
} |
3432 |
|
|
|
3433 |
|
|
/* |
3434 |
|
|
* Find out whether a line is a macro line or not. |
3435 |
|
|
* If it is, adjust the current position and return one; if it isn't, |
3436 |
|
|
* return zero and don't change the current position. |
3437 |
|
|
* If the control character has been set with `.cc', then let that grain |
3438 |
|
|
* precedence. |
3439 |
|
|
* This is slighly contrary to groff, where using the non-breaking |
3440 |
|
|
* control character when `cc' has been invoked will cause the |
3441 |
|
|
* non-breaking macro contents to be printed verbatim. |
3442 |
|
|
*/ |
3443 |
|
|
int |
3444 |
|
|
roff_getcontrol(const struct roff *r, const char *cp, int *ppos) |
3445 |
|
|
{ |
3446 |
|
|
int pos; |
3447 |
|
|
|
3448 |
|
|
pos = *ppos; |
3449 |
|
|
|
3450 |
|
|
if (0 != r->control && cp[pos] == r->control) |
3451 |
|
|
pos++; |
3452 |
|
|
else if (0 != r->control) |
3453 |
|
|
return 0; |
3454 |
|
|
else if ('\\' == cp[pos] && '.' == cp[pos + 1]) |
3455 |
|
|
pos += 2; |
3456 |
|
|
else if ('.' == cp[pos] || '\'' == cp[pos]) |
3457 |
|
|
pos++; |
3458 |
|
|
else |
3459 |
|
|
return 0; |
3460 |
|
|
|
3461 |
|
|
while (' ' == cp[pos] || '\t' == cp[pos]) |
3462 |
|
|
pos++; |
3463 |
|
|
|
3464 |
|
|
*ppos = pos; |
3465 |
|
|
return 1; |
3466 |
|
|
} |