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