]> git.cameronkatri.com Git - mandoc.git/blob - roff.c
*** empty log message ***
[mandoc.git] / roff.c
1 /* $Id: roff.c,v 1.26 2008/12/01 09:25:18 kristaps Exp $ */
2 /*
3 * Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
4 *
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
8 * copies.
9 *
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.
18 */
19 #include <assert.h>
20 #include <ctype.h>
21 #include <err.h>
22 #include <stdarg.h>
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <time.h>
27
28 #include "libmdocml.h"
29 #include "private.h"
30
31 /* FIXME: warn if Pp occurs before/after Sh etc. (see mdoc.samples). */
32
33 /* FIXME: warn about "X section only" macros. */
34
35 /* FIXME: warn about empty lists. */
36
37 /* FIXME: roff_layout and roff_text have identical-ish lower bodies. */
38
39 /* FIXME: NAME section needs specific elements. */
40
41 #define ROFF_MAXARG 32
42
43 enum roffd {
44 ROFF_ENTER = 0,
45 ROFF_EXIT
46 };
47
48 enum rofftype {
49 ROFF_COMMENT,
50 ROFF_TEXT,
51 ROFF_LAYOUT,
52 ROFF_SPECIAL
53 };
54
55 #define ROFFCALL_ARGS \
56 int tok, struct rofftree *tree, \
57 char *argv[], enum roffd type
58
59 struct rofftree;
60
61 struct rofftok {
62 int (*cb)(ROFFCALL_ARGS); /* Callback. */
63 const int *args; /* Args (or NULL). */
64 const int *parents;
65 const int *children;
66 int ctx;
67 enum rofftype type; /* Type of macro. */
68 int flags;
69 #define ROFF_PARSED (1 << 0) /* "Parsed". */
70 #define ROFF_CALLABLE (1 << 1) /* "Callable". */
71 #define ROFF_SHALLOW (1 << 2) /* Nesting block. */
72 };
73
74 struct roffarg {
75 int flags;
76 #define ROFF_VALUE (1 << 0) /* Has a value. */
77 };
78
79 struct roffnode {
80 int tok; /* Token id. */
81 struct roffnode *parent; /* Parent (or NULL). */
82 };
83
84 struct rofftree {
85 struct roffnode *last; /* Last parsed node. */
86 char *cur;
87
88 time_t date; /* `Dd' results. */
89 char os[64]; /* `Os' results. */
90 char title[64]; /* `Dt' results. */
91 char section[64]; /* `Dt' results. */
92 char volume[64]; /* `Dt' results. */
93
94 int state;
95 #define ROFF_PRELUDE (1 << 1) /* In roff prelude. */
96 #define ROFF_PRELUDE_Os (1 << 2) /* `Os' is parsed. */
97 #define ROFF_PRELUDE_Dt (1 << 3) /* `Dt' is parsed. */
98 #define ROFF_PRELUDE_Dd (1 << 4) /* `Dd' is parsed. */
99 #define ROFF_BODY (1 << 5) /* In roff body. */
100
101 struct roffcb cb;
102 void *arg;
103 };
104
105 static int roff_Dd(ROFFCALL_ARGS);
106 static int roff_Dt(ROFFCALL_ARGS);
107 static int roff_Os(ROFFCALL_ARGS);
108 #ifdef notyet
109 static int roff_Ns(ROFFCALL_ARGS);
110 #endif
111
112 static int roff_layout(ROFFCALL_ARGS);
113 static int roff_text(ROFFCALL_ARGS);
114 static int roff_comment(ROFFCALL_ARGS);
115 static int roff_close(ROFFCALL_ARGS);
116
117 static struct roffnode *roffnode_new(int, struct rofftree *);
118 static void roffnode_free(struct rofftree *);
119
120 static void roff_warn(const struct rofftree *,
121 const char *, char *, ...);
122 static void roff_err(const struct rofftree *,
123 const char *, char *, ...);
124
125 static int roffscan(int, const int *);
126 static int rofffindtok(const char *);
127 static int rofffindarg(const char *);
128 static int rofffindcallable(const char *);
129 static int roffargs(const struct rofftree *,
130 int, char *, char **);
131 static int roffargok(int, int);
132 static int roffnextopt(const struct rofftree *,
133 int, char ***, char **);
134 static int roffparse(struct rofftree *, char *);
135 static int textparse(const struct rofftree *, char *);
136
137
138 static const int roffarg_An[] = { ROFF_Split, ROFF_Nosplit,
139 ROFF_ARGMAX };
140 static const int roffarg_Bd[] = { ROFF_Ragged, ROFF_Unfilled,
141 ROFF_Literal, ROFF_File, ROFF_Offset, ROFF_Filled,
142 ROFF_Compact, ROFF_ARGMAX };
143 static const int roffarg_Bk[] = { ROFF_Words, ROFF_ARGMAX };
144 static const int roffarg_Ex[] = { ROFF_Std, ROFF_ARGMAX };
145 static const int roffarg_Rv[] = { ROFF_Std, ROFF_ARGMAX };
146 static const int roffarg_Bl[] = { ROFF_Bullet, ROFF_Dash,
147 ROFF_Hyphen, ROFF_Item, ROFF_Enum, ROFF_Tag, ROFF_Diag,
148 ROFF_Hang, ROFF_Ohang, ROFF_Inset, ROFF_Column, ROFF_Offset,
149 ROFF_Width, ROFF_Compact, ROFF_ARGMAX };
150 static const int roffarg_St[] = {
151 ROFF_p1003_1_88, ROFF_p1003_1_90, ROFF_p1003_1_96,
152 ROFF_p1003_1_2001, ROFF_p1003_1_2004, ROFF_p1003_1,
153 ROFF_p1003_1b, ROFF_p1003_1b_93, ROFF_p1003_1c_95,
154 ROFF_p1003_1g_2000, ROFF_p1003_2_92, ROFF_p1387_2_95,
155 ROFF_p1003_2, ROFF_p1387_2, ROFF_isoC_90, ROFF_isoC_amd1,
156 ROFF_isoC_tcor1, ROFF_isoC_tcor2, ROFF_isoC_99, ROFF_ansiC,
157 ROFF_ansiC_89, ROFF_ansiC_99, ROFF_ieee754, ROFF_iso8802_3,
158 ROFF_xpg3, ROFF_xpg4, ROFF_xpg4_2, ROFF_xpg4_3, ROFF_xbd5,
159 ROFF_xcu5, ROFF_xsh5, ROFF_xns5, ROFF_xns5_2d2_0,
160 ROFF_xcurses4_2, ROFF_susv2, ROFF_susv3, ROFF_svid4,
161 ROFF_ARGMAX };
162
163 static const int roffchild_Bl[] = { ROFF_It, ROFF_El, ROFF_MAX };
164 static const int roffchild_Fo[] = { ROFF_Fa, ROFF_Fc, ROFF_MAX };
165 static const int roffchild_Oo[] = { ROFF_Op, ROFF_Oc, ROFF_MAX };
166 static const int roffchild_Rs[] = { ROFF_Re, ROFF__A, ROFF__B,
167 ROFF__D, ROFF__I, ROFF__J, ROFF__N, ROFF__O, ROFF__P,
168 ROFF__R, ROFF__T, ROFF__V, ROFF_MAX };
169
170 static const int roffparent_El[] = { ROFF_Bl, ROFF_It, ROFF_MAX };
171 static const int roffparent_Fc[] = { ROFF_Fo, ROFF_Fa, ROFF_MAX };
172 static const int roffparent_Oc[] = { ROFF_Oo, ROFF_Oc, ROFF_MAX };
173 static const int roffparent_It[] = { ROFF_Bl, ROFF_It, ROFF_MAX };
174 static const int roffparent_Re[] = { ROFF_Rs, ROFF_MAX };
175
176 /* Table of all known tokens. */
177 static const struct rofftok tokens[ROFF_MAX] = {
178 {roff_comment, NULL, NULL, NULL, 0, ROFF_COMMENT, 0 }, /* \" */
179 { roff_Dd, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Dd */
180 { roff_Dt, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Dt */
181 { roff_Os, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Os */
182 { roff_layout, NULL, NULL, NULL, ROFF_Sh, ROFF_LAYOUT, 0 }, /* Sh */
183 { roff_layout, NULL, NULL, NULL, ROFF_Ss, ROFF_LAYOUT, 0 }, /* Ss */
184 { roff_text, NULL, NULL, NULL, ROFF_Pp, ROFF_TEXT, 0 }, /* Pp */
185 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* D1 */
186 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Dl */
187 { roff_layout, roffarg_Bd, NULL, NULL, 0, ROFF_LAYOUT, 0 }, /* Bd */
188 { roff_close, NULL, NULL, NULL, ROFF_Bd, ROFF_LAYOUT, 0 }, /* Ed */
189 { roff_layout, roffarg_Bl, NULL, roffchild_Bl, 0, ROFF_LAYOUT, 0 }, /* Bl */
190 { roff_close, NULL, roffparent_El, NULL, ROFF_Bl, ROFF_LAYOUT, 0 }, /* El */
191 { roff_layout, NULL, roffparent_It, NULL, ROFF_It, ROFF_LAYOUT, ROFF_PARSED | ROFF_SHALLOW }, /* It */
192 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ad */ /* FIXME */
193 { roff_text, roffarg_An, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* An */
194 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ar */
195 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Cd */ /* XXX man.4 only */
196 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Cm */
197 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Dv */ /* XXX needs arg */
198 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Er */ /* XXX needs arg */
199 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ev */ /* XXX needs arg */
200 { roff_text, roffarg_Ex, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Ex */
201 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Fa */ /* XXX needs arg */
202 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Fd */
203 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Fl */
204 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Fn */ /* XXX needs arg */ /* FIXME */
205 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Ft */
206 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ic */ /* XXX needs arg */
207 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* In */
208 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Li */
209 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Nd */
210 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Nm */ /* FIXME */
211 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Op */
212 { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Ot */ /* XXX deprecated */
213 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Pa */
214 { roff_text, roffarg_Rv, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Rv */
215 { roff_text, roffarg_St, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* St */
216 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Va */
217 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Vt */ /* XXX needs arg */
218 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Xr */ /* XXX needs arg */
219 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* %A */
220 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE}, /* %B */
221 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* %D */
222 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE}, /* %I */
223 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE}, /* %J */
224 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* %N */
225 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* %O */
226 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* %P */
227 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* %R */
228 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* %T */
229 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* %V */
230 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ac */
231 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ao */
232 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Aq */
233 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* At */ /* XXX at most 2 args */
234 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Bc */
235 { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Bf */ /* FIXME */
236 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Bo */
237 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Bq */
238 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Bsx */
239 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Bx */
240 { NULL, NULL, NULL, NULL, 0, ROFF_SPECIAL, 0 }, /* Db */
241 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Dc */
242 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Do */
243 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Dq */
244 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ec */
245 { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Ef */ /* FIXME */
246 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Em */ /* XXX needs arg */
247 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Eo */
248 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Fx */
249 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Ms */
250 { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* No */
251 { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ns */
252 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Nx */
253 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Ox */
254 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Pc */
255 { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Pf */
256 { roff_text, NULL, NULL, NULL, 0, ROFF_LAYOUT, ROFF_PARSED | ROFF_CALLABLE }, /* Po */
257 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Pq */
258 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Qc */
259 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ql */
260 { roff_layout, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Qo */
261 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Qq */
262 { roff_close, NULL, roffparent_Re, NULL, ROFF_Rs, ROFF_LAYOUT, 0 }, /* Re */
263 { roff_layout, NULL, NULL, roffchild_Rs, 0, ROFF_LAYOUT, 0 }, /* Rs */
264 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Sc */
265 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* So */
266 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Sq */
267 { NULL, NULL, NULL, NULL, 0, ROFF_SPECIAL, 0 }, /* Sm */
268 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Sx */
269 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Sy */
270 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Tn */
271 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Ux */
272 { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Xc */
273 { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Xo */
274 { roff_layout, NULL, NULL, roffchild_Fo, 0, ROFF_LAYOUT, 0 }, /* Fo */
275 { roff_close, NULL, roffparent_Fc, NULL, ROFF_Fo, ROFF_LAYOUT, 0 }, /* Fc */
276 { roff_layout, NULL, NULL, roffchild_Oo, 0, ROFF_LAYOUT, 0 }, /* Oo */
277 { roff_close, NULL, roffparent_Oc, NULL, ROFF_Oo, ROFF_LAYOUT, 0 }, /* Oc */
278 { roff_layout, roffarg_Bk, NULL, NULL, 0, ROFF_LAYOUT, 0 }, /* Bk */
279 { roff_close, NULL, NULL, NULL, ROFF_Bk, ROFF_LAYOUT, 0 }, /* Ek */
280 };
281
282 /* Table of all known token arguments. */
283 static const int tokenargs[ROFF_ARGMAX] = {
284 0, 0, 0, 0,
285 0, ROFF_VALUE, ROFF_VALUE, 0,
286 0, 0, 0, 0,
287 0, 0, 0, 0,
288 0, 0, ROFF_VALUE, 0,
289 0, 0, 0, 0,
290 0, 0, 0, 0,
291 0, 0, 0, 0,
292 0, 0, 0, 0,
293 0, 0, 0, 0,
294 0, 0, 0, 0,
295 0, 0, 0, 0,
296 0, 0, 0, 0,
297 0, 0, 0, 0,
298 0, 0, 0, 0,
299 };
300
301 const char *const toknamesp[ROFF_MAX] = {
302 "\\\"", "Dd", "Dt", "Os",
303 "Sh", "Ss", "Pp", "D1",
304 "Dl", "Bd", "Ed", "Bl",
305 "El", "It", "Ad", "An",
306 "Ar", "Cd", "Cm", "Dv",
307 "Er", "Ev", "Ex", "Fa",
308 "Fd", "Fl", "Fn", "Ft",
309 "Ic", "In", "Li", "Nd",
310 "Nm", "Op", "Ot", "Pa",
311 "Rv", "St", "Va", "Vt",
312 /* LINTED */
313 "Xr", "\%A", "\%B", "\%D",
314 /* LINTED */
315 "\%I", "\%J", "\%N", "\%O",
316 /* LINTED */
317 "\%P", "\%R", "\%T", "\%V",
318 "Ac", "Ao", "Aq", "At",
319 "Bc", "Bf", "Bo", "Bq",
320 "Bsx", "Bx", "Db", "Dc",
321 "Do", "Dq", "Ec", "Ef",
322 "Em", "Eo", "Fx", "Ms",
323 "No", "Ns", "Nx", "Ox",
324 "Pc", "Pf", "Po", "Pq",
325 "Qc", "Ql", "Qo", "Qq",
326 "Re", "Rs", "Sc", "So",
327 "Sq", "Sm", "Sx", "Sy",
328 "Tn", "Ux", "Xc", "Xo",
329 "Fo", "Fc", "Oo", "Oc",
330 "Bk", "Ek",
331 };
332
333 const char *const tokargnamesp[ROFF_ARGMAX] = {
334 "split", "nosplit", "ragged",
335 "unfilled", "literal", "file",
336 "offset", "bullet", "dash",
337 "hyphen", "item", "enum",
338 "tag", "diag", "hang",
339 "ohang", "inset", "column",
340 "width", "compact", "std",
341 "p1003.1-88", "p1003.1-90", "p1003.1-96",
342 "p1003.1-2001", "p1003.1-2004", "p1003.1",
343 "p1003.1b", "p1003.1b-93", "p1003.1c-95",
344 "p1003.1g-2000", "p1003.2-92", "p1387.2-95",
345 "p1003.2", "p1387.2", "isoC-90",
346 "isoC-amd1", "isoC-tcor1", "isoC-tcor2",
347 "isoC-99", "ansiC", "ansiC-89",
348 "ansiC-99", "ieee754", "iso8802-3",
349 "xpg3", "xpg4", "xpg4.2",
350 "xpg4.3", "xbd5", "xcu5",
351 "xsh5", "xns5", "xns5.2d2.0",
352 "xcurses4.2", "susv2", "susv3",
353 "svid4", "filled", "words",
354 };
355
356 const char *const *toknames = toknamesp;
357 const char *const *tokargnames = tokargnamesp;
358
359
360 int
361 roff_free(struct rofftree *tree, int flush)
362 {
363 int error, t;
364 struct roffnode *n;
365
366 error = 0;
367
368 if ( ! flush)
369 goto end;
370
371 error = 1;
372
373 if (ROFF_PRELUDE & tree->state) {
374 roff_warn(tree, NULL, "prelude never finished");
375 goto end;
376 }
377
378 for (n = tree->last; n->parent; n = n->parent) {
379 if (0 != tokens[n->tok].ctx)
380 continue;
381 roff_warn(tree, NULL, "closing explicit scope `%s'",
382 toknames[n->tok]);
383 goto end;
384 }
385
386 while (tree->last) {
387 t = tree->last->tok;
388 if ( ! (*tokens[t].cb)(t, tree, NULL, ROFF_EXIT))
389 goto end;
390 }
391
392 error = 0;
393
394 end:
395
396 while (tree->last)
397 roffnode_free(tree);
398
399 free(tree);
400
401 return(error ? 0 : 1);
402 }
403
404
405 struct rofftree *
406 roff_alloc(const struct roffcb *cb, void *args)
407 {
408 struct rofftree *tree;
409
410 assert(args);
411 assert(cb);
412
413 if (NULL == (tree = calloc(1, sizeof(struct rofftree))))
414 err(1, "calloc");
415
416 tree->state = ROFF_PRELUDE;
417 tree->arg = args;
418
419 (void)memcpy(&tree->cb, cb, sizeof(struct roffcb));
420
421 return(tree);
422 }
423
424
425 int
426 roff_engine(struct rofftree *tree, char *buf)
427 {
428
429 tree->cur = buf;
430 assert(buf);
431
432 if (0 == *buf) {
433 roff_warn(tree, buf, "blank line");
434 return(0);
435 } else if ('.' != *buf)
436 return(textparse(tree, buf));
437
438 return(roffparse(tree, buf));
439 }
440
441
442 static int
443 textparse(const struct rofftree *tree, char *buf)
444 {
445
446 return((*tree->cb.roffdata)(tree->arg, 1, buf));
447 }
448
449
450 static int
451 roffargs(const struct rofftree *tree,
452 int tok, char *buf, char **argv)
453 {
454 int i;
455 char *p;
456
457 assert(tok >= 0 && tok < ROFF_MAX);
458 assert('.' == *buf);
459
460 p = buf;
461
462 /* LINTED */
463 for (i = 0; *buf && i < ROFF_MAXARG; i++) {
464 if ('\"' == *buf) {
465 argv[i] = ++buf;
466 while (*buf && '\"' != *buf)
467 buf++;
468 if (0 == *buf) {
469 roff_err(tree, argv[i], "unclosed "
470 "quote in argument "
471 "list for `%s'",
472 toknames[tok]);
473 return(0);
474 }
475 } else {
476 argv[i] = buf++;
477 while (*buf && ! isspace(*buf))
478 buf++;
479 if (0 == *buf)
480 continue;
481 }
482 *buf++ = 0;
483 while (*buf && isspace(*buf))
484 buf++;
485 }
486
487 assert(i > 0);
488 if (ROFF_MAXARG == i && *buf) {
489 roff_err(tree, p, "too many arguments for `%s'", toknames
490 [tok]);
491 return(0);
492 }
493
494 argv[i] = NULL;
495 return(1);
496 }
497
498
499 static int
500 roffscan(int tok, const int *tokv)
501 {
502
503 if (NULL == tokv)
504 return(1);
505
506 for ( ; ROFF_MAX != *tokv; tokv++)
507 if (tok == *tokv)
508 return(1);
509
510 return(0);
511 }
512
513
514 static int
515 roffparse(struct rofftree *tree, char *buf)
516 {
517 int tok, t;
518 struct roffnode *n;
519 char *argv[ROFF_MAXARG];
520 char **argvp;
521
522 if (0 != *buf && 0 != *(buf + 1) && 0 != *(buf + 2))
523 if (0 == strncmp(buf, ".\\\"", 3))
524 return(1);
525
526 if (ROFF_MAX == (tok = rofffindtok(buf + 1))) {
527 roff_err(tree, buf + 1, "bogus line macro");
528 return(0);
529 } else if (NULL == tokens[tok].cb) {
530 roff_err(tree, buf + 1, "unsupported macro `%s'",
531 toknames[tok]);
532 return(0);
533 }
534
535 assert(ROFF___ != tok);
536 if ( ! roffargs(tree, tok, buf, argv))
537 return(0);
538
539 argvp = (char **)argv;
540
541 /*
542 * Prelude macros break some assumptions, so branch now.
543 */
544
545 if (ROFF_PRELUDE & tree->state) {
546 assert(NULL == tree->last);
547 return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER));
548 } else
549 assert(tree->last);
550
551 assert(ROFF_BODY & tree->state);
552
553 /*
554 * First check that our possible parents and parent's possible
555 * children are satisfied.
556 */
557
558 if ( ! roffscan(tree->last->tok, tokens[tok].parents)) {
559 roff_err(tree, *argvp, "`%s' has invalid parent `%s'",
560 toknames[tok],
561 toknames[tree->last->tok]);
562 return(0);
563 }
564
565 if ( ! roffscan(tok, tokens[tree->last->tok].children)) {
566 roff_err(tree, *argvp, "`%s' is invalid child of `%s'",
567 toknames[tok],
568 toknames[tree->last->tok]);
569 return(0);
570 }
571
572 /*
573 * Branch if we're not a layout token.
574 */
575
576 if (ROFF_LAYOUT != tokens[tok].type)
577 return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER));
578
579 /*
580 * Check our scope rules.
581 */
582
583 if (0 == tokens[tok].ctx)
584 return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER));
585
586 /*
587 * First consider implicit-end tags, like as follows:
588 * .Sh SECTION 1
589 * .Sh SECTION 2
590 * In this, we want to close the scope of the NAME section. If
591 * there's an intermediary implicit-end tag, such as
592 * .Sh SECTION 1
593 * .Ss Subsection 1
594 * .Sh SECTION 2
595 * then it must be closed as well.
596 */
597
598 if (tok == tokens[tok].ctx) {
599 /*
600 * First search up to the point where we must close.
601 * If one doesn't exist, then we can open a new scope.
602 */
603
604 for (n = tree->last; n; n = n->parent) {
605 assert(0 == tokens[n->tok].ctx ||
606 n->tok == tokens[n->tok].ctx);
607 if (n->tok == tok)
608 break;
609 if (ROFF_SHALLOW & tokens[tok].flags) {
610 n = NULL;
611 break;
612 }
613 }
614
615 /*
616 * Create a new scope, as no previous one exists to
617 * close out.
618 */
619
620 if (NULL == n)
621 return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER));
622
623 /*
624 * Close out all intermediary scoped blocks, then hang
625 * the current scope from our predecessor's parent.
626 */
627
628 do {
629 t = tree->last->tok;
630 if ( ! (*tokens[t].cb)(t, tree, NULL, ROFF_EXIT))
631 return(0);
632 } while (t != tok);
633
634 return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER));
635 }
636
637 /*
638 * Now consider explicit-end tags, where we want to close back
639 * to a specific tag. Example:
640 * .Bl
641 * .It Item.
642 * .El
643 * In this, the `El' tag closes out the scope of `Bl'.
644 */
645
646 assert(tree->last);
647 assert(tok != tokens[tok].ctx && 0 != tokens[tok].ctx);
648
649 /* LINTED */
650 do {
651 t = tree->last->tok;
652 if ( ! (*tokens[t].cb)(t, tree, NULL, ROFF_EXIT))
653 return(0);
654 } while (t != tokens[tok].ctx);
655
656 assert(tree->last);
657 return(1);
658 }
659
660
661 static int
662 rofffindarg(const char *name)
663 {
664 size_t i;
665
666 /* FIXME: use a table, this is slow but ok for now. */
667
668 /* LINTED */
669 for (i = 0; i < ROFF_ARGMAX; i++)
670 /* LINTED */
671 if (0 == strcmp(name, tokargnames[i]))
672 return((int)i);
673
674 return(ROFF_ARGMAX);
675 }
676
677
678 static int
679 rofffindtok(const char *buf)
680 {
681 char token[4];
682 int i;
683
684 for (i = 0; *buf && ! isspace(*buf) && i < 3; i++, buf++)
685 token[i] = *buf;
686
687 if (i == 3)
688 return(ROFF_MAX);
689
690 token[i] = 0;
691
692 /* FIXME: use a table, this is slow but ok for now. */
693
694 /* LINTED */
695 for (i = 0; i < ROFF_MAX; i++)
696 /* LINTED */
697 if (0 == strcmp(toknames[i], token))
698 return((int)i);
699
700 return(ROFF_MAX);
701 }
702
703
704 static int
705 roffispunct(const char *p)
706 {
707
708 if (0 == *p)
709 return(0);
710 if (0 != *(p + 1))
711 return(0);
712
713 switch (*p) {
714 case('{'):
715 /* FALLTHROUGH */
716 case('.'):
717 /* FALLTHROUGH */
718 case(','):
719 /* FALLTHROUGH */
720 case(';'):
721 /* FALLTHROUGH */
722 case(':'):
723 /* FALLTHROUGH */
724 case('?'):
725 /* FALLTHROUGH */
726 case('!'):
727 /* FALLTHROUGH */
728 case('('):
729 /* FALLTHROUGH */
730 case(')'):
731 /* FALLTHROUGH */
732 case('['):
733 /* FALLTHROUGH */
734 case(']'):
735 /* FALLTHROUGH */
736 case('}'):
737 return(1);
738 default:
739 break;
740 }
741
742 return(0);
743 }
744
745
746 static int
747 rofffindcallable(const char *name)
748 {
749 int c;
750
751 if (ROFF_MAX == (c = rofffindtok(name)))
752 return(ROFF_MAX);
753 assert(c >= 0 && c < ROFF_MAX);
754 return(ROFF_CALLABLE & tokens[c].flags ? c : ROFF_MAX);
755 }
756
757
758 static struct roffnode *
759 roffnode_new(int tokid, struct rofftree *tree)
760 {
761 struct roffnode *p;
762
763 if (NULL == (p = malloc(sizeof(struct roffnode))))
764 err(1, "malloc");
765
766 p->tok = tokid;
767 p->parent = tree->last;
768 tree->last = p;
769
770 return(p);
771 }
772
773
774 static int
775 roffargok(int tokid, int argid)
776 {
777 const int *c;
778
779 if (NULL == (c = tokens[tokid].args))
780 return(0);
781
782 for ( ; ROFF_ARGMAX != *c; c++)
783 if (argid == *c)
784 return(1);
785
786 return(0);
787 }
788
789
790 static void
791 roffnode_free(struct rofftree *tree)
792 {
793 struct roffnode *p;
794
795 assert(tree->last);
796
797 p = tree->last;
798 tree->last = tree->last->parent;
799 free(p);
800 }
801
802
803 static int
804 roffnextopt(const struct rofftree *tree, int tok,
805 char ***in, char **val)
806 {
807 char *arg, **argv;
808 int v;
809
810 *val = NULL;
811 argv = *in;
812 assert(argv);
813
814 if (NULL == (arg = *argv))
815 return(-1);
816 if ('-' != *arg)
817 return(-1);
818
819 if (ROFF_ARGMAX == (v = rofffindarg(arg + 1))) {
820 roff_warn(tree, arg, "argument-like parameter `%s' to "
821 "`%s'", &arg[1], toknames[tok]);
822 return(-1);
823 }
824
825 if ( ! roffargok(tok, v)) {
826 roff_warn(tree, arg, "invalid argument parameter `%s' to "
827 "`%s'", tokargnames[v], toknames[tok]);
828 return(-1);
829 }
830
831 if ( ! (ROFF_VALUE & tokenargs[v]))
832 return(v);
833
834 *in = ++argv;
835
836 if (NULL == *argv) {
837 roff_err(tree, arg, "empty value of `%s' for `%s'",
838 tokargnames[v], toknames[tok]);
839 return(ROFF_ARGMAX);
840 }
841
842 return(v);
843 }
844
845
846 /* ARGSUSED */
847 static int
848 roff_Dd(ROFFCALL_ARGS)
849 {
850
851 if (ROFF_BODY & tree->state) {
852 assert( ! (ROFF_PRELUDE & tree->state));
853 assert(ROFF_PRELUDE_Dd & tree->state);
854 return(roff_text(tok, tree, argv, type));
855 }
856
857 assert(ROFF_PRELUDE & tree->state);
858 assert( ! (ROFF_BODY & tree->state));
859
860 if (ROFF_PRELUDE_Dd & tree->state) {
861 roff_err(tree, *argv, "repeated `Dd' in prelude");
862 return(0);
863 } else if (ROFF_PRELUDE_Dt & tree->state) {
864 roff_err(tree, *argv, "out-of-order `Dd' in prelude");
865 return(0);
866 }
867
868 /* TODO: parse date. */
869
870 assert(NULL == tree->last);
871 tree->state |= ROFF_PRELUDE_Dd;
872
873 return(1);
874 }
875
876
877 /* ARGSUSED */
878 static int
879 roff_Dt(ROFFCALL_ARGS)
880 {
881
882 if (ROFF_BODY & tree->state) {
883 assert( ! (ROFF_PRELUDE & tree->state));
884 assert(ROFF_PRELUDE_Dt & tree->state);
885 return(roff_text(tok, tree, argv, type));
886 }
887
888 assert(ROFF_PRELUDE & tree->state);
889 assert( ! (ROFF_BODY & tree->state));
890
891 if ( ! (ROFF_PRELUDE_Dd & tree->state)) {
892 roff_err(tree, *argv, "out-of-order `Dt' in prelude");
893 return(0);
894 } else if (ROFF_PRELUDE_Dt & tree->state) {
895 roff_err(tree, *argv, "repeated `Dt' in prelude");
896 return(0);
897 }
898
899 /* TODO: parse date. */
900
901 assert(NULL == tree->last);
902 tree->state |= ROFF_PRELUDE_Dt;
903
904 return(1);
905 }
906
907
908 /* ARGSUSED */
909 static int
910 roff_Os(ROFFCALL_ARGS)
911 {
912
913 if (ROFF_EXIT == type) {
914 roffnode_free(tree);
915 return((*tree->cb.rofftail)(tree->arg));
916 } else if (ROFF_BODY & tree->state) {
917 assert( ! (ROFF_PRELUDE & tree->state));
918 assert(ROFF_PRELUDE_Os & tree->state);
919 return(roff_text(tok, tree, argv, type));
920 }
921
922 assert(ROFF_PRELUDE & tree->state);
923 if ( ! (ROFF_PRELUDE_Dt & tree->state) ||
924 ! (ROFF_PRELUDE_Dd & tree->state)) {
925 roff_err(tree, *argv, "out-of-order `Os' in prelude");
926 return(0);
927 }
928
929 /* TODO: extract OS. */
930
931 tree->state |= ROFF_PRELUDE_Os;
932 tree->state &= ~ROFF_PRELUDE;
933 tree->state |= ROFF_BODY;
934
935 assert(NULL == tree->last);
936
937 if (NULL == roffnode_new(tok, tree))
938 return(0);
939
940 return((*tree->cb.roffhead)(tree->arg));
941 }
942
943
944 /* ARGSUSED */
945 static int
946 roff_layout(ROFFCALL_ARGS)
947 {
948 int i, c, argcp[ROFF_MAXARG];
949 char *v, *argvp[ROFF_MAXARG];
950
951 if (ROFF_PRELUDE & tree->state) {
952 roff_err(tree, *argv, "`%s' disallowed in prelude",
953 toknames[tok]);
954 return(0);
955 }
956
957 if (ROFF_EXIT == type) {
958 roffnode_free(tree);
959 return((*tree->cb.roffblkout)(tree->arg, tok));
960 }
961
962 i = 0;
963 argv++;
964
965 while (-1 != (c = roffnextopt(tree, tok, &argv, &v))) {
966 if (ROFF_ARGMAX == c)
967 return(0);
968
969 argcp[i] = c;
970 argvp[i] = v;
971 i++;
972 argv++;
973 }
974
975 argcp[i] = ROFF_ARGMAX;
976 argvp[i] = NULL;
977
978 if (NULL == roffnode_new(tok, tree))
979 return(0);
980
981 if ( ! (*tree->cb.roffblkin)(tree->arg, tok, argcp, argvp))
982 return(0);
983
984 if (NULL == *argv)
985 return(1);
986
987 if ( ! (*tree->cb.roffin)(tree->arg, tok, argcp, argvp))
988 return(0);
989
990 if ( ! (ROFF_PARSED & tokens[tok].flags)) {
991 i = 0;
992 while (*argv) {
993 if ( ! (*tree->cb.roffdata)(tree->arg, i, *argv++))
994 return(0);
995 i = 1;
996 }
997 return((*tree->cb.roffout)(tree->arg, tok));
998 }
999
1000 i = 0;
1001 while (*argv) {
1002 if (ROFF_MAX == (c = rofffindcallable(*argv))) {
1003 assert(tree->arg);
1004 if ( ! (*tree->cb.roffdata)
1005 (tree->arg, i, *argv++))
1006 return(0);
1007 i = 1;
1008 continue;
1009 }
1010
1011 if (NULL == tokens[c].cb) {
1012 roff_err(tree, *argv, "unsupported macro `%s'",
1013 toknames[c]);
1014 return(0);
1015 }
1016
1017 if ( ! (*tokens[c].cb)(c, tree, argv, ROFF_ENTER))
1018 return(0);
1019
1020 break;
1021 }
1022
1023 /*
1024 * If we're the first parser (*argv == tree->cur) then purge out
1025 * any additional punctuation, should there be any remaining at
1026 * the end of line.
1027 */
1028
1029 if ( ! (ROFF_PARSED & tokens[tok].flags && *argv))
1030 return((*tree->cb.roffout)(tree->arg, tok));
1031
1032 i = 0;
1033 while (argv[i])
1034 i++;
1035
1036 assert(i > 0);
1037 if ( ! roffispunct(argv[--i]))
1038 return((*tree->cb.roffout)(tree->arg, tok));
1039
1040 while (i >= 0 && roffispunct(argv[i]))
1041 i--;
1042
1043 assert(0 != i);
1044 i++;
1045
1046 /* LINTED */
1047 while (argv[i])
1048 if ( ! (*tree->cb.roffdata)(tree->arg, 0, argv[i++]))
1049 return(0);
1050
1051 return((*tree->cb.roffout)(tree->arg, tok));
1052 }
1053
1054
1055 /* ARGSUSED */
1056 static int
1057 roff_text(ROFFCALL_ARGS)
1058 {
1059 int i, j, first, c, argcp[ROFF_MAXARG];
1060 char *v, *argvp[ROFF_MAXARG];
1061
1062 if (ROFF_PRELUDE & tree->state) {
1063 roff_err(tree, *argv, "`%s' disallowed in prelude",
1064 toknames[tok]);
1065 return(0);
1066 }
1067
1068 /* FIXME: breaks if passed from roff_layout. */
1069 first = *argv == tree->cur;
1070
1071 i = 0;
1072 argv++;
1073
1074 while (-1 != (c = roffnextopt(tree, tok, &argv, &v))) {
1075 if (ROFF_ARGMAX == c)
1076 return(0);
1077
1078 argcp[i] = c;
1079 argvp[i] = v;
1080 i++;
1081 argv++;
1082 }
1083
1084 argcp[i] = ROFF_ARGMAX;
1085 argvp[i] = NULL;
1086
1087 if ( ! (*tree->cb.roffin)(tree->arg, tok, argcp, argvp))
1088 return(0);
1089
1090 if ( ! (ROFF_PARSED & tokens[tok].flags)) {
1091 i = 0;
1092 while (*argv) {
1093 if ( ! (*tree->cb.roffdata)(tree->arg, i, *argv++))
1094 return(0);
1095 i = 1;
1096 }
1097 return((*tree->cb.roffout)(tree->arg, tok));
1098 }
1099
1100 i = 0;
1101 while (*argv) {
1102 if (ROFF_MAX == (c = rofffindcallable(*argv))) {
1103 /*
1104 * If all that remains is roff punctuation, then
1105 * close out our scope and return.
1106 */
1107 if (roffispunct(*argv)) {
1108 for (j = 0; argv[j]; j++)
1109 if ( ! roffispunct(argv[j]))
1110 break;
1111 if (NULL == argv[j])
1112 break;
1113 i = 1;
1114 }
1115
1116 if ( ! (*tree->cb.roffdata)
1117 (tree->arg, i, *argv++))
1118 return(0);
1119
1120 i = 1;
1121 continue;
1122 }
1123
1124 /*
1125 * A sub-command has been found. Execute it and
1126 * discontinue parsing for arguments.
1127 */
1128
1129 if (NULL == tokens[c].cb) {
1130 roff_err(tree, *argv, "unsupported macro `%s'",
1131 toknames[c]);
1132 return(0);
1133 }
1134
1135 if ( ! (*tokens[c].cb)(c, tree, argv, ROFF_ENTER))
1136 return(0);
1137
1138 break;
1139 }
1140
1141 if ( ! (*tree->cb.roffout)(tree->arg, tok))
1142 return(0);
1143
1144 /*
1145 * If we're the first parser (*argv == tree->cur) then purge out
1146 * any additional punctuation, should there be any remaining at
1147 * the end of line.
1148 */
1149
1150 if ( ! (first && *argv))
1151 return(1);
1152
1153 i = 0;
1154 while (argv[i])
1155 i++;
1156
1157 assert(i > 0);
1158 if ( ! roffispunct(argv[--i]))
1159 return(1);
1160
1161 while (i >= 0 && roffispunct(argv[i]))
1162 i--;
1163
1164 assert(0 != i);
1165 i++;
1166
1167 /* LINTED */
1168 while (argv[i])
1169 if ( ! (*tree->cb.roffdata)(tree->arg, 0, argv[i++]))
1170 return(0);
1171
1172 return(1);
1173 }
1174
1175
1176 /* ARGSUSED */
1177 static int
1178 roff_comment(ROFFCALL_ARGS)
1179 {
1180
1181 return(1);
1182 }
1183
1184
1185 /* ARGSUSED */
1186 static int
1187 roff_close(ROFFCALL_ARGS)
1188 {
1189
1190 return(1);
1191 }
1192
1193
1194 #if notyet
1195 /* ARGSUSED */
1196 static int
1197 roff_Ns(ROFFCALL_ARGS)
1198 {
1199 int c;
1200
1201 argv++;
1202
1203 if (ROFF_MAX != (c = rofffindcallable(*argv))) {
1204 if (NULL == tokens[c].cb) {
1205 roff_err(tree, *argv, "unsupported macro `%s'",
1206 toknames[c]);
1207 return(0);
1208 }
1209 if ( ! (*tree->cb.roffspecial)(tree->arg, tok))
1210 return(0);
1211 if ( ! (*tokens[c].cb)(c, tree, argv, ROFF_ENTER))
1212 return(0);
1213
1214 return(1);
1215 } else if ( ! (*tree->cb.roffdata)(tree->arg, 0, *argv++))
1216 return(0);
1217
1218 while (*argv) {
1219 if (ROFF_MAX == (c = rofffindcallable(*argv))) {
1220 assert(tree->arg);
1221 if ( ! (*tree->cb.roffdata)
1222 (tree->arg, 1, *argv++))
1223 return(0);
1224 continue;
1225 }
1226 if (NULL == tokens[c].cb) {
1227 roff_err(tree, *argv, "unsupported macro `%s'",
1228 toknames[c]);
1229 return(0);
1230 }
1231 if ( ! (*tokens[c].cb)(c, tree, argv, ROFF_ENTER))
1232 return(0);
1233
1234 break;
1235 }
1236
1237 return(1);
1238 }
1239 #endif
1240
1241
1242 static void
1243 roff_warn(const struct rofftree *tree, const char *pos, char *fmt, ...)
1244 {
1245 va_list ap;
1246 char buf[128];
1247
1248 va_start(ap, fmt);
1249 (void)vsnprintf(buf, sizeof(buf), fmt, ap);
1250 va_end(ap);
1251
1252 (*tree->cb.roffmsg)(tree->arg,
1253 ROFF_WARN, tree->cur, pos, buf);
1254 }
1255
1256
1257 static void
1258 roff_err(const struct rofftree *tree, const char *pos, char *fmt, ...)
1259 {
1260 va_list ap;
1261 char buf[128];
1262
1263 va_start(ap, fmt);
1264 (void)vsnprintf(buf, sizeof(buf), fmt, ap);
1265 va_end(ap);
1266
1267 (*tree->cb.roffmsg)(tree->arg,
1268 ROFF_ERROR, tree->cur, pos, buf);
1269 }