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