]>
git.cameronkatri.com Git - mandoc.git/blob - roff.c
1 /* $Id: roff.c,v 1.31 2008/12/02 00:10:37 kristaps Exp $ */
3 * Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the
7 * above copyright notice and this permission notice appear in all
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
19 #include <sys/param.h>
30 #include "libmdocml.h"
33 /* TODO: warn if Pp occurs before/after Sh etc. (see mdoc.samples). */
35 /* TODO: warn about "X section only" macros. */
37 /* TODO: warn about empty lists. */
39 /* TODO: (warn) some sections need specific elements. */
41 /* TODO: (warn) NAME section has particular order. */
43 /* TODO: unify empty-content tags a la <br />. */
45 /* TODO: macros with a set number of arguments? */
47 #define ROFF_MAXARG 32
61 #define ROFFCALL_ARGS \
62 int tok, struct rofftree *tree, \
63 char *argv[], enum roffd type
68 int (*cb
)(ROFFCALL_ARGS
); /* Callback. */
69 const int *args
; /* Args (or NULL). */
73 enum rofftype type
; /* Type of macro. */
75 #define ROFF_PARSED (1 << 0) /* "Parsed". */
76 #define ROFF_CALLABLE (1 << 1) /* "Callable". */
77 #define ROFF_SHALLOW (1 << 2) /* Nesting block. */
78 #define ROFF_LSCOPE (1 << 3)
83 #define ROFF_VALUE (1 << 0) /* Has a value. */
87 int tok
; /* Token id. */
88 struct roffnode
*parent
; /* Parent (or NULL). */
92 struct roffnode
*last
; /* Last parsed node. */
95 struct tm tm
; /* `Dd' results. */
96 char os
[64]; /* `Os' results. */
97 char title
[64]; /* `Dt' results. */
98 char section
[64]; /* `Dt' results. */
99 char volume
[64]; /* `Dt' results. */
102 #define ROFF_PRELUDE (1 << 1) /* In roff prelude. */
103 #define ROFF_PRELUDE_Os (1 << 2) /* `Os' is parsed. */
104 #define ROFF_PRELUDE_Dt (1 << 3) /* `Dt' is parsed. */
105 #define ROFF_PRELUDE_Dd (1 << 4) /* `Dd' is parsed. */
106 #define ROFF_BODY (1 << 5) /* In roff body. */
112 static int roff_Dd(ROFFCALL_ARGS
);
113 static int roff_Dt(ROFFCALL_ARGS
);
114 static int roff_Os(ROFFCALL_ARGS
);
115 static int roff_Ns(ROFFCALL_ARGS
);
117 static int roff_layout(ROFFCALL_ARGS
);
118 static int roff_text(ROFFCALL_ARGS
);
119 static int roff_noop(ROFFCALL_ARGS
);
120 static int roff_depr(ROFFCALL_ARGS
);
122 static struct roffnode
*roffnode_new(int, struct rofftree
*);
123 static void roffnode_free(struct rofftree
*);
125 static void roff_warn(const struct rofftree
*,
126 const char *, char *, ...);
127 static void roff_err(const struct rofftree
*,
128 const char *, char *, ...);
130 static int roffpurgepunct(struct rofftree
*, char **);
131 static int roffscan(int, const int *);
132 static int rofffindtok(const char *);
133 static int rofffindarg(const char *);
134 static int rofffindcallable(const char *);
135 static int roffargs(const struct rofftree
*,
136 int, char *, char **);
137 static int roffargok(int, int);
138 static int roffnextopt(const struct rofftree
*,
139 int, char ***, char **);
140 static int roffparseopts(struct rofftree
*, int,
141 char ***, int *, char **);
142 static int roffparse(struct rofftree
*, char *);
143 static int textparse(const struct rofftree
*, char *);
146 static const int roffarg_An
[] = { ROFF_Split
, ROFF_Nosplit
,
148 static const int roffarg_Bd
[] = { ROFF_Ragged
, ROFF_Unfilled
,
149 ROFF_Literal
, ROFF_File
, ROFF_Offset
, ROFF_Filled
,
150 ROFF_Compact
, ROFF_ARGMAX
};
151 static const int roffarg_Bk
[] = { ROFF_Words
, ROFF_ARGMAX
};
152 static const int roffarg_Ex
[] = { ROFF_Std
, ROFF_ARGMAX
};
153 static const int roffarg_Rv
[] = { ROFF_Std
, ROFF_ARGMAX
};
154 static const int roffarg_Bl
[] = { ROFF_Bullet
, ROFF_Dash
,
155 ROFF_Hyphen
, ROFF_Item
, ROFF_Enum
, ROFF_Tag
, ROFF_Diag
,
156 ROFF_Hang
, ROFF_Ohang
, ROFF_Inset
, ROFF_Column
, ROFF_Offset
,
157 ROFF_Width
, ROFF_Compact
, ROFF_ARGMAX
};
158 static const int roffarg_St
[] = {
159 ROFF_p1003_1_88
, ROFF_p1003_1_90
, ROFF_p1003_1_96
,
160 ROFF_p1003_1_2001
, ROFF_p1003_1_2004
, ROFF_p1003_1
,
161 ROFF_p1003_1b
, ROFF_p1003_1b_93
, ROFF_p1003_1c_95
,
162 ROFF_p1003_1g_2000
, ROFF_p1003_2_92
, ROFF_p1387_2_95
,
163 ROFF_p1003_2
, ROFF_p1387_2
, ROFF_isoC_90
, ROFF_isoC_amd1
,
164 ROFF_isoC_tcor1
, ROFF_isoC_tcor2
, ROFF_isoC_99
, ROFF_ansiC
,
165 ROFF_ansiC_89
, ROFF_ansiC_99
, ROFF_ieee754
, ROFF_iso8802_3
,
166 ROFF_xpg3
, ROFF_xpg4
, ROFF_xpg4_2
, ROFF_xpg4_3
, ROFF_xbd5
,
167 ROFF_xcu5
, ROFF_xsh5
, ROFF_xns5
, ROFF_xns5_2d2_0
,
168 ROFF_xcurses4_2
, ROFF_susv2
, ROFF_susv3
, ROFF_svid4
,
171 static const int roffchild_Bl
[] = { ROFF_It
, ROFF_El
, ROFF_MAX
};
172 static const int roffchild_Fo
[] = { ROFF_Fa
, ROFF_Fc
, ROFF_MAX
};
173 static const int roffchild_Rs
[] = { ROFF_Re
, ROFF__A
, ROFF__B
,
174 ROFF__D
, ROFF__I
, ROFF__J
, ROFF__N
, ROFF__O
, ROFF__P
,
175 ROFF__R
, ROFF__T
, ROFF__V
, ROFF_MAX
};
177 static const int roffparent_El
[] = { ROFF_Bl
, ROFF_It
, ROFF_MAX
};
178 static const int roffparent_Fc
[] = { ROFF_Fo
, ROFF_Fa
, ROFF_MAX
};
179 static const int roffparent_Oc
[] = { ROFF_Oo
, ROFF_MAX
};
180 static const int roffparent_It
[] = { ROFF_Bl
, ROFF_It
, ROFF_MAX
};
181 static const int roffparent_Re
[] = { ROFF_Rs
, ROFF_MAX
};
183 /* Table of all known tokens. */
184 static const struct rofftok tokens
[ROFF_MAX
] = {
185 { roff_noop
, NULL
, NULL
, NULL
, 0, ROFF_COMMENT
, 0 }, /* \" */
186 { roff_Dd
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Dd */
187 { roff_Dt
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Dt */
188 { roff_Os
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Os */
189 { roff_layout
, NULL
, NULL
, NULL
, ROFF_Sh
, ROFF_LAYOUT
, 0 }, /* Sh */
190 { roff_layout
, NULL
, NULL
, NULL
, ROFF_Ss
, ROFF_LAYOUT
, 0 }, /* Ss */
191 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Pp */ /* XXX 0 args */
192 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_LSCOPE
}, /* D1 */
193 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_LSCOPE
}, /* Dl */
194 { roff_layout
, roffarg_Bd
, NULL
, NULL
, 0, ROFF_LAYOUT
, 0 }, /* Bd */
195 { roff_noop
, NULL
, NULL
, NULL
, ROFF_Bd
, ROFF_LAYOUT
, 0 }, /* Ed */
196 { roff_layout
, roffarg_Bl
, NULL
, roffchild_Bl
, 0, ROFF_LAYOUT
, 0 }, /* Bl */
197 { roff_noop
, NULL
, roffparent_El
, NULL
, ROFF_Bl
, ROFF_LAYOUT
, 0 }, /* El */
198 { roff_layout
, NULL
, roffparent_It
, NULL
, ROFF_It
, ROFF_LAYOUT
, ROFF_PARSED
| ROFF_SHALLOW
}, /* It */
199 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ad */ /* FIXME */
200 { roff_text
, roffarg_An
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* An */
201 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ar */
202 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Cd */ /* XXX man.4 only */
203 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Cm */
204 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Dv */ /* XXX needs arg */
205 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Er */ /* XXX needs arg */
206 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ev */ /* XXX needs arg */
207 { roff_text
, roffarg_Ex
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Ex */
208 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Fa */ /* XXX needs arg */
209 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Fd */
210 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Fl */
211 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Fn */ /* XXX needs arg */ /* FIXME */
212 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Ft */
213 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ic */ /* XXX needs arg */
214 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* In */
215 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Li */
216 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Nd */
217 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Nm */ /* FIXME */
218 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
| ROFF_LSCOPE
}, /* Op */
219 { roff_depr
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Ot */
220 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Pa */
221 { roff_text
, roffarg_Rv
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Rv */
222 { roff_text
, roffarg_St
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* St */
223 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Va */
224 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Vt */ /* XXX needs arg */
225 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Xr */ /* XXX needs arg */
226 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* %A */
227 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* %B */
228 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* %D */
229 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* %I */
230 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* %J */
231 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* %N */
232 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* %O */
233 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* %P */
234 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* %R */
235 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* %T */
236 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* %V */
237 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ac */
238 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ao */
239 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
| ROFF_LSCOPE
}, /* Aq */
240 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* At */ /* XXX at most 2 args */
241 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Bc */
242 { roff_layout
, NULL
, NULL
, NULL
, 0, ROFF_LAYOUT
, 0 }, /* Bf */ /* FIXME */
243 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Bo */
244 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
| ROFF_LSCOPE
}, /* Bq */
245 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Bsx */
246 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Bx */
247 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_SPECIAL
, 0 }, /* Db */
248 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Dc */
249 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Do */
250 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
| ROFF_LSCOPE
}, /* Dq */
251 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ec */
252 { roff_noop
, NULL
, NULL
, NULL
, ROFF_Bf
, ROFF_LAYOUT
, 0 }, /* Ef */
253 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Em */ /* XXX needs arg */
254 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Eo */
255 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Fx */
256 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Ms */
257 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* No */
258 { roff_Ns
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ns */
259 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Nx */
260 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Ox */
261 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Pc */
262 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Pf */
263 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Po */
264 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
| ROFF_LSCOPE
}, /* Pq */
265 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Qc */
266 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ql */
267 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Qo */
268 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
| ROFF_LSCOPE
}, /* Qq */
269 { roff_noop
, NULL
, roffparent_Re
, NULL
, ROFF_Rs
, ROFF_LAYOUT
, 0 }, /* Re */
270 { roff_layout
, NULL
, NULL
, roffchild_Rs
, 0, ROFF_LAYOUT
, 0 }, /* Rs */
271 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Sc */
272 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* So */
273 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
| ROFF_LSCOPE
}, /* Sq */
274 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_SPECIAL
, 0 }, /* Sm */
275 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Sx */
276 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Sy */
277 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Tn */
278 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Ux */
279 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Xc */
280 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Xo */
281 { roff_layout
, NULL
, NULL
, roffchild_Fo
, 0, ROFF_LAYOUT
, 0 }, /* Fo */
282 { roff_noop
, NULL
, roffparent_Fc
, NULL
, ROFF_Fo
, ROFF_LAYOUT
, 0 }, /* Fc */
283 { roff_layout
, NULL
, NULL
, NULL
, 0, ROFF_LAYOUT
, 0 }, /* Oo */
284 { roff_noop
, NULL
, roffparent_Oc
, NULL
, ROFF_Oo
, ROFF_LAYOUT
, 0 }, /* Oc */
285 { roff_layout
, roffarg_Bk
, NULL
, NULL
, 0, ROFF_LAYOUT
, 0 }, /* Bk */
286 { roff_noop
, NULL
, NULL
, NULL
, ROFF_Bk
, ROFF_LAYOUT
, 0 }, /* Ek */
287 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Bt */
288 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Hf */
289 { roff_depr
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Fr */
290 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Ud */
293 /* Table of all known token arguments. */
294 static const int tokenargs
[ROFF_ARGMAX
] = {
296 0, ROFF_VALUE
, ROFF_VALUE
, 0,
312 const char *const toknamesp
[ROFF_MAX
] = {
313 "\\\"", "Dd", "Dt", "Os",
314 "Sh", "Ss", "Pp", "D1",
315 "Dl", "Bd", "Ed", "Bl",
316 "El", "It", "Ad", "An",
317 "Ar", "Cd", "Cm", "Dv",
318 "Er", "Ev", "Ex", "Fa",
319 "Fd", "Fl", "Fn", "Ft",
320 "Ic", "In", "Li", "Nd",
321 "Nm", "Op", "Ot", "Pa",
322 "Rv", "St", "Va", "Vt",
324 "Xr", "\%A", "\%B", "\%D",
326 "\%I", "\%J", "\%N", "\%O",
328 "\%P", "\%R", "\%T", "\%V",
329 "Ac", "Ao", "Aq", "At",
330 "Bc", "Bf", "Bo", "Bq",
331 "Bsx", "Bx", "Db", "Dc",
332 "Do", "Dq", "Ec", "Ef",
333 "Em", "Eo", "Fx", "Ms",
334 "No", "Ns", "Nx", "Ox",
335 "Pc", "Pf", "Po", "Pq",
336 "Qc", "Ql", "Qo", "Qq",
337 "Re", "Rs", "Sc", "So",
338 "Sq", "Sm", "Sx", "Sy",
339 "Tn", "Ux", "Xc", "Xo",
340 "Fo", "Fc", "Oo", "Oc",
341 "Bk", "Ek", "Bt", "Hf",
345 const char *const tokargnamesp
[ROFF_ARGMAX
] = {
346 "split", "nosplit", "ragged",
347 "unfilled", "literal", "file",
348 "offset", "bullet", "dash",
349 "hyphen", "item", "enum",
350 "tag", "diag", "hang",
351 "ohang", "inset", "column",
352 "width", "compact", "std",
353 "p1003.1-88", "p1003.1-90", "p1003.1-96",
354 "p1003.1-2001", "p1003.1-2004", "p1003.1",
355 "p1003.1b", "p1003.1b-93", "p1003.1c-95",
356 "p1003.1g-2000", "p1003.2-92", "p1387.2-95",
357 "p1003.2", "p1387.2", "isoC-90",
358 "isoC-amd1", "isoC-tcor1", "isoC-tcor2",
359 "isoC-99", "ansiC", "ansiC-89",
360 "ansiC-99", "ieee754", "iso8802-3",
361 "xpg3", "xpg4", "xpg4.2",
362 "xpg4.3", "xbd5", "xcu5",
363 "xsh5", "xns5", "xns5.2d2.0",
364 "xcurses4.2", "susv2", "susv3",
365 "svid4", "filled", "words",
368 const char *const *toknames
= toknamesp
;
369 const char *const *tokargnames
= tokargnamesp
;
373 roff_free(struct rofftree
*tree
, int flush
)
385 if (ROFF_PRELUDE
& tree
->state
) {
386 roff_err(tree
, NULL
, "prelude never finished");
390 for (n
= tree
->last
; n
; n
= n
->parent
) {
391 if (0 != tokens
[n
->tok
].ctx
)
393 roff_err(tree
, NULL
, "closing explicit scope `%s'",
400 if ( ! (*tokens
[t
].cb
)(t
, tree
, NULL
, ROFF_EXIT
))
404 if ( ! (*tree
->cb
.rofftail
)(tree
->arg
))
416 return(error
? 0 : 1);
421 roff_alloc(const struct roffcb
*cb
, void *args
)
423 struct rofftree
*tree
;
428 if (NULL
== (tree
= calloc(1, sizeof(struct rofftree
))))
431 tree
->state
= ROFF_PRELUDE
;
434 (void)memcpy(&tree
->cb
, cb
, sizeof(struct roffcb
));
441 roff_engine(struct rofftree
*tree
, char *buf
)
448 roff_err(tree
, buf
, "blank line");
450 } else if ('.' != *buf
)
451 return(textparse(tree
, buf
));
453 return(roffparse(tree
, buf
));
458 textparse(const struct rofftree
*tree
, char *buf
)
461 if ( ! (ROFF_BODY
& tree
->state
)) {
462 roff_err(tree
, buf
, "data not in body");
465 return((*tree
->cb
.roffdata
)(tree
->arg
, 1, buf
));
470 roffargs(const struct rofftree
*tree
,
471 int tok
, char *buf
, char **argv
)
476 assert(tok
>= 0 && tok
< ROFF_MAX
);
482 for (i
= 0; *buf
&& i
< ROFF_MAXARG
; i
++) {
485 while (*buf
&& '\"' != *buf
)
488 roff_err(tree
, argv
[i
], "unclosed "
496 while (*buf
&& ! isspace(*buf
))
502 while (*buf
&& isspace(*buf
))
507 if (ROFF_MAXARG
== i
&& *buf
) {
508 roff_err(tree
, p
, "too many arguments for `%s'", toknames
519 roffscan(int tok
, const int *tokv
)
525 for ( ; ROFF_MAX
!= *tokv
; tokv
++)
534 roffparse(struct rofftree
*tree
, char *buf
)
538 char *argv
[ROFF_MAXARG
];
541 if (0 != *buf
&& 0 != *(buf
+ 1) && 0 != *(buf
+ 2))
542 if (0 == strncmp(buf
, ".\\\"", 3))
545 if (ROFF_MAX
== (tok
= rofffindtok(buf
+ 1))) {
546 roff_err(tree
, buf
+ 1, "bogus line macro");
548 } else if (NULL
== tokens
[tok
].cb
) {
549 roff_err(tree
, buf
+ 1, "unsupported macro `%s'",
554 assert(ROFF___
!= tok
);
555 if ( ! roffargs(tree
, tok
, buf
, argv
))
558 argvp
= (char **)argv
;
561 * Prelude macros break some assumptions, so branch now.
564 if (ROFF_PRELUDE
& tree
->state
) {
565 assert(NULL
== tree
->last
);
566 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
569 assert(ROFF_BODY
& tree
->state
);
572 * First check that our possible parents and parent's possible
573 * children are satisfied.
576 if (tree
->last
&& ! roffscan
577 (tree
->last
->tok
, tokens
[tok
].parents
)) {
578 roff_err(tree
, *argvp
, "`%s' has invalid parent `%s'",
580 toknames
[tree
->last
->tok
]);
584 if (tree
->last
&& ! roffscan
585 (tok
, tokens
[tree
->last
->tok
].children
)) {
586 roff_err(tree
, *argvp
, "`%s' is invalid child of `%s'",
588 toknames
[tree
->last
->tok
]);
593 * Branch if we're not a layout token.
596 if (ROFF_LAYOUT
!= tokens
[tok
].type
)
597 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
598 if (0 == tokens
[tok
].ctx
)
599 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
602 * First consider implicit-end tags, like as follows:
605 * In this, we want to close the scope of the NAME section. If
606 * there's an intermediary implicit-end tag, such as
610 * then it must be closed as well.
613 if (tok
== tokens
[tok
].ctx
) {
615 * First search up to the point where we must close.
616 * If one doesn't exist, then we can open a new scope.
619 for (n
= tree
->last
; n
; n
= n
->parent
) {
620 assert(0 == tokens
[n
->tok
].ctx
||
621 n
->tok
== tokens
[n
->tok
].ctx
);
624 if (ROFF_SHALLOW
& tokens
[tok
].flags
) {
628 if (tokens
[n
->tok
].ctx
== n
->tok
)
630 roff_err(tree
, *argv
, "`%s' breaks `%s' scope",
631 toknames
[tok
], toknames
[n
->tok
]);
636 * Create a new scope, as no previous one exists to
641 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
644 * Close out all intermediary scoped blocks, then hang
645 * the current scope from our predecessor's parent.
650 if ( ! (*tokens
[t
].cb
)(t
, tree
, NULL
, ROFF_EXIT
))
654 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
658 * Now consider explicit-end tags, where we want to close back
659 * to a specific tag. Example:
663 * In this, the `El' tag closes out the scope of `Bl'.
666 assert(tok
!= tokens
[tok
].ctx
&& 0 != tokens
[tok
].ctx
);
668 for (n
= tree
->last
; n
; n
= n
->parent
)
669 if (n
->tok
!= tokens
[tok
].ctx
) {
670 if (n
->tok
== tokens
[n
->tok
].ctx
)
672 roff_err(tree
, *argv
, "`%s' breaks `%s' scope",
673 toknames
[tok
], toknames
[n
->tok
]);
680 roff_err(tree
, *argv
, "`%s' has no starting tag `%s'",
682 toknames
[tokens
[tok
].ctx
]);
689 if ( ! (*tokens
[t
].cb
)(t
, tree
, NULL
, ROFF_EXIT
))
691 } while (t
!= tokens
[tok
].ctx
);
698 rofffindarg(const char *name
)
702 /* FIXME: use a table, this is slow but ok for now. */
705 for (i
= 0; i
< ROFF_ARGMAX
; i
++)
707 if (0 == strcmp(name
, tokargnames
[i
]))
715 rofffindtok(const char *buf
)
720 for (i
= 0; *buf
&& ! isspace(*buf
) && i
< 3; i
++, buf
++)
728 /* FIXME: use a table, this is slow but ok for now. */
731 for (i
= 0; i
< ROFF_MAX
; i
++)
733 if (0 == strcmp(toknames
[i
], token
))
741 roffispunct(const char *p
)
783 rofffindcallable(const char *name
)
787 if (ROFF_MAX
== (c
= rofffindtok(name
)))
789 assert(c
>= 0 && c
< ROFF_MAX
);
790 return(ROFF_CALLABLE
& tokens
[c
].flags
? c
: ROFF_MAX
);
794 static struct roffnode
*
795 roffnode_new(int tokid
, struct rofftree
*tree
)
799 if (NULL
== (p
= malloc(sizeof(struct roffnode
))))
803 p
->parent
= tree
->last
;
811 roffargok(int tokid
, int argid
)
815 if (NULL
== (c
= tokens
[tokid
].args
))
818 for ( ; ROFF_ARGMAX
!= *c
; c
++)
827 roffnode_free(struct rofftree
*tree
)
834 tree
->last
= tree
->last
->parent
;
840 roffnextopt(const struct rofftree
*tree
, int tok
,
841 char ***in
, char **val
)
850 if (NULL
== (arg
= *argv
))
855 if (ROFF_ARGMAX
== (v
= rofffindarg(arg
+ 1))) {
856 roff_warn(tree
, arg
, "argument-like parameter `%s' to "
857 "`%s'", &arg
[1], toknames
[tok
]);
861 if ( ! roffargok(tok
, v
)) {
862 roff_warn(tree
, arg
, "invalid argument parameter `%s' to "
863 "`%s'", tokargnames
[v
], toknames
[tok
]);
867 if ( ! (ROFF_VALUE
& tokenargs
[v
]))
873 roff_err(tree
, arg
, "empty value of `%s' for `%s'",
874 tokargnames
[v
], toknames
[tok
]);
883 roffpurgepunct(struct rofftree
*tree
, char **argv
)
891 if ( ! roffispunct(argv
[--i
]))
893 while (i
>= 0 && roffispunct(argv
[i
]))
899 if ( ! (*tree
->cb
.roffdata
)(tree
->arg
, 0, argv
[i
++]))
906 roffparseopts(struct rofftree
*tree
, int tok
,
907 char ***args
, int *argc
, char **argv
)
914 while (-1 != (c
= roffnextopt(tree
, tok
, args
, &v
))) {
915 if (ROFF_ARGMAX
== c
)
924 argc
[i
] = ROFF_ARGMAX
;
932 roff_Dd(ROFFCALL_ARGS
)
937 if (ROFF_BODY
& tree
->state
) {
938 assert( ! (ROFF_PRELUDE
& tree
->state
));
939 assert(ROFF_PRELUDE_Dd
& tree
->state
);
940 return(roff_text(tok
, tree
, argv
, type
));
943 assert(ROFF_PRELUDE
& tree
->state
);
944 assert( ! (ROFF_BODY
& tree
->state
));
946 if (ROFF_PRELUDE_Dd
& tree
->state
) {
947 roff_err(tree
, *argv
, "repeated `Dd' in prelude");
949 } else if (ROFF_PRELUDE_Dt
& tree
->state
) {
950 roff_err(tree
, *argv
, "out-of-order `Dd' in prelude");
954 assert(NULL
== tree
->last
);
958 if (0 == strcmp(*argv
, "$Mdocdate: December 2 2008 $")) {
960 if (NULL
== localtime_r(&t
, &tree
->tm
))
961 err(1, "localtime_r");
962 tree
->state
|= ROFF_PRELUDE_Dd
;
966 /* Build this from Mdocdate or raw date. */
971 if (0 != strcmp(*argv
, "$Mdocdate:")) {
973 if (strlcat(buf
, *argv
++, sizeof(buf
))
976 roff_err(tree
, p
, "bad `Dd' date");
979 if (strptime(buf
, "%b%d,%Y", &tree
->tm
)) {
980 tree
->state
|= ROFF_PRELUDE_Dd
;
983 roff_err(tree
, *argv
, "bad `Dd' date");
988 while (*argv
&& **argv
!= '$') {
989 if (strlcat(buf
, *argv
++, sizeof(buf
))
991 roff_err(tree
, p
, "bad `Dd' Mdocdate");
994 if (strlcat(buf
, " ", sizeof(buf
))
996 roff_err(tree
, p
, "bad `Dd' Mdocdate");
1000 if (NULL
== *argv
) {
1001 roff_err(tree
, p
, "bad `Dd' Mdocdate");
1005 if (NULL
== strptime(buf
, "%b %d %Y", &tree
->tm
)) {
1006 roff_err(tree
, *argv
, "bad `Dd' Mdocdate");
1010 tree
->state
|= ROFF_PRELUDE_Dd
;
1017 roff_Dt(ROFFCALL_ARGS
)
1020 if (ROFF_BODY
& tree
->state
) {
1021 assert( ! (ROFF_PRELUDE
& tree
->state
));
1022 assert(ROFF_PRELUDE_Dt
& tree
->state
);
1023 return(roff_text(tok
, tree
, argv
, type
));
1026 assert(ROFF_PRELUDE
& tree
->state
);
1027 assert( ! (ROFF_BODY
& tree
->state
));
1029 if ( ! (ROFF_PRELUDE_Dd
& tree
->state
)) {
1030 roff_err(tree
, *argv
, "out-of-order `Dt' in prelude");
1032 } else if (ROFF_PRELUDE_Dt
& tree
->state
) {
1033 roff_err(tree
, *argv
, "repeated `Dt' in prelude");
1038 if (NULL
== *argv
) {
1039 roff_err(tree
, *argv
, "`Dt' needs document title");
1041 } else if (strlcpy(tree
->title
, *argv
, sizeof(tree
->title
))
1042 >= sizeof(tree
->title
)) {
1043 roff_err(tree
, *argv
, "`Dt' document title too long");
1048 if (NULL
== *argv
) {
1049 roff_err(tree
, *argv
, "`Dt' needs section");
1051 } else if (strlcpy(tree
->section
, *argv
, sizeof(tree
->section
))
1052 >= sizeof(tree
->section
)) {
1053 roff_err(tree
, *argv
, "`Dt' section too long");
1058 if (NULL
== *argv
) {
1059 tree
->volume
[0] = 0;
1060 } else if (strlcpy(tree
->volume
, *argv
, sizeof(tree
->volume
))
1061 >= sizeof(tree
->volume
)) {
1062 roff_err(tree
, *argv
, "`Dt' volume too long");
1066 assert(NULL
== tree
->last
);
1067 tree
->state
|= ROFF_PRELUDE_Dt
;
1075 roff_Ns(ROFFCALL_ARGS
)
1079 first
= (*argv
== tree
->cur
);
1083 if (ROFF_MAX
!= (c
= rofffindcallable(*argv
))) {
1084 if (NULL
== tokens
[c
].cb
) {
1085 roff_err(tree
, *argv
, "unsupported macro `%s'",
1089 if ( ! (*tree
->cb
.roffspecial
)(tree
->arg
, tok
))
1091 if ( ! (*tokens
[c
].cb
)(c
, tree
, argv
, ROFF_ENTER
))
1095 return(roffpurgepunct(tree
, argv
));
1098 if ( ! (*tree
->cb
.roffdata
)(tree
->arg
, 0, *argv
++))
1102 if (ROFF_MAX
== (c
= rofffindcallable(*argv
))) {
1103 if ( ! roffispunct(*argv
)) {
1104 if ( ! (*tree
->cb
.roffdata
)
1105 (tree
->arg
, 1, *argv
++))
1110 /* FIXME: this is identical to that of
1113 /* See if only punctuation remains. */
1115 for (j
= 0; argv
[j
]; j
++)
1116 if ( ! roffispunct(argv
[j
]))
1120 if ( ! (*tree
->cb
.roffdata
)
1121 (tree
->arg
, 0, *argv
++))
1126 /* Only punctuation remains. */
1130 if (NULL
== tokens
[c
].cb
) {
1131 roff_err(tree
, *argv
, "unsupported macro `%s'",
1135 if ( ! (*tokens
[c
].cb
)(c
, tree
, argv
, ROFF_ENTER
))
1142 return(roffpurgepunct(tree
, argv
));
1148 roff_Os(ROFFCALL_ARGS
)
1152 if (ROFF_BODY
& tree
->state
) {
1153 assert( ! (ROFF_PRELUDE
& tree
->state
));
1154 assert(ROFF_PRELUDE_Os
& tree
->state
);
1155 return(roff_text(tok
, tree
, argv
, type
));
1158 assert(ROFF_PRELUDE
& tree
->state
);
1159 if ( ! (ROFF_PRELUDE_Dt
& tree
->state
) ||
1160 ! (ROFF_PRELUDE_Dd
& tree
->state
)) {
1161 roff_err(tree
, *argv
, "out-of-order `Os' in prelude");
1170 if (strlcat(tree
->os
, *argv
++, sizeof(tree
->os
))
1173 roff_err(tree
, p
, "`Os' value too long");
1177 if (0 == tree
->os
[0])
1178 if (strlcpy(tree
->os
, "LOCAL", sizeof(tree
->os
))
1179 >= sizeof(tree
->os
)) {
1180 roff_err(tree
, p
, "`Os' value too long");
1184 tree
->state
|= ROFF_PRELUDE_Os
;
1185 tree
->state
&= ~ROFF_PRELUDE
;
1186 tree
->state
|= ROFF_BODY
;
1188 assert(NULL
== tree
->last
);
1190 return((*tree
->cb
.roffhead
)(tree
->arg
));
1196 roff_layout(ROFFCALL_ARGS
)
1198 int i
, c
, argcp
[ROFF_MAXARG
];
1199 char *argvp
[ROFF_MAXARG
];
1201 if (ROFF_PRELUDE
& tree
->state
) {
1202 roff_err(tree
, *argv
, "bad `%s' in prelude",
1205 } else if (ROFF_EXIT
== type
) {
1206 roffnode_free(tree
);
1207 return((*tree
->cb
.roffblkout
)(tree
->arg
, tok
));
1210 assert( ! (ROFF_CALLABLE
& tokens
[tok
].flags
));
1214 if ( ! roffparseopts(tree
, tok
, &argv
, argcp
, argvp
))
1216 if (NULL
== roffnode_new(tok
, tree
))
1220 * Layouts have two parts: the layout body and header. The
1221 * layout header is the trailing text of the line macro, while
1222 * the layout body is everything following until termination.
1225 if ( ! (*tree
->cb
.roffblkin
)(tree
->arg
, tok
, argcp
, argvp
))
1229 if ( ! (*tree
->cb
.roffin
)(tree
->arg
, tok
, argcp
, argvp
))
1233 * If there are no parsable parts, then write remaining tokens
1234 * into the layout header and exit.
1237 if ( ! (ROFF_PARSED
& tokens
[tok
].flags
)) {
1240 if ( ! (*tree
->cb
.roffdata
)(tree
->arg
, i
, *argv
++))
1244 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1248 * Parsable elements may be in the header (or be the header, for
1249 * that matter). Follow the regular parsing rules for these.
1254 if (ROFF_MAX
== (c
= rofffindcallable(*argv
))) {
1256 if ( ! (*tree
->cb
.roffdata
)
1257 (tree
->arg
, i
, *argv
++))
1263 if (NULL
== tokens
[c
].cb
) {
1264 roff_err(tree
, *argv
, "unsupported macro `%s'",
1269 if ( ! (*tokens
[c
].cb
)(c
, tree
, argv
, ROFF_ENTER
))
1276 * If there's trailing punctuation in the header, then write it
1277 * out now. Here we mimic the behaviour of a line-dominant text
1282 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1285 * Expensive. Scan to the end of line then work backwards until
1286 * a token isn't punctuation.
1289 if ( ! roffpurgepunct(tree
, argv
))
1292 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1298 roff_text(ROFFCALL_ARGS
)
1300 int i
, j
, first
, c
, argcp
[ROFF_MAXARG
];
1301 char *argvp
[ROFF_MAXARG
];
1303 if (ROFF_PRELUDE
& tree
->state
) {
1304 roff_err(tree
, *argv
, "`%s' disallowed in prelude",
1309 first
= (*argv
== tree
->cur
);
1312 if ( ! roffparseopts(tree
, tok
, &argv
, argcp
, argvp
))
1314 if ( ! (*tree
->cb
.roffin
)(tree
->arg
, tok
, argcp
, argvp
))
1317 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1319 if ( ! (ROFF_PARSED
& tokens
[tok
].flags
)) {
1322 if ( ! (*tree
->cb
.roffdata
)(tree
->arg
, i
, *argv
++))
1326 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1330 * Deal with punctuation. Ugly. Work ahead until we encounter
1331 * terminating punctuation. If we encounter it and all
1332 * subsequent tokens are punctuation, then stop processing (the
1333 * line-dominant macro will print these tokens after closure).
1338 if (ROFF_MAX
== (c
= rofffindcallable(*argv
))) {
1339 if ( ! roffispunct(*argv
)) {
1340 if ( ! (*tree
->cb
.roffdata
)
1341 (tree
->arg
, i
, *argv
++))
1347 /* See if only punctuation remains. */
1350 for (j
= 0; argv
[j
]; j
++)
1351 if ( ! roffispunct(argv
[j
]))
1355 if ( ! (*tree
->cb
.roffdata
)
1356 (tree
->arg
, 0, *argv
++))
1361 /* Only punctuation remains. */
1363 if ( ! (*tree
->cb
.roffout
)(tree
->arg
, tok
))
1369 * A sub-command has been found. Execute it and
1370 * discontinue parsing for arguments. If we're
1371 * line-scoped, then close out after it returns; if we
1372 * aren't, then close out before.
1375 if (NULL
== tokens
[c
].cb
) {
1376 roff_err(tree
, *argv
, "unsupported macro `%s'",
1381 if ( ! (ROFF_LSCOPE
& tokens
[tok
].flags
))
1382 if ( ! (*tree
->cb
.roffout
)(tree
->arg
, tok
))
1385 if ( ! (*tokens
[c
].cb
)(c
, tree
, argv
, ROFF_ENTER
))
1388 if (ROFF_LSCOPE
& tokens
[tok
].flags
)
1389 if ( ! (*tree
->cb
.roffout
)(tree
->arg
, tok
))
1396 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1400 return(roffpurgepunct(tree
, argv
));
1406 roff_noop(ROFFCALL_ARGS
)
1415 roff_depr(ROFFCALL_ARGS
)
1418 roff_err(tree
, *argv
, "`%s' is deprecated", toknames
[tok
]);
1424 roff_warn(const struct rofftree
*tree
, const char *pos
, char *fmt
, ...)
1430 (void)vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
1433 (*tree
->cb
.roffmsg
)(tree
->arg
,
1434 ROFF_WARN
, tree
->cur
, pos
, buf
);
1439 roff_err(const struct rofftree
*tree
, const char *pos
, char *fmt
, ...)
1445 (void)vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
1448 (*tree
->cb
.roffmsg
)(tree
->arg
,
1449 ROFF_ERROR
, tree
->cur
, pos
, buf
);