]> git.cameronkatri.com Git - mandoc.git/blob - roff.c
Considerably improved roff_text parser.
[mandoc.git] / roff.c
1 /* $Id: roff.c,v 1.27 2008/12/01 15:32:36 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 { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, 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 { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Ef */ /* FIXME */
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_LAYOUT, 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_warn(tree, NULL, "prelude never finished");
384 goto end;
385 }
386
387 for (n = tree->last; n->parent; n = n->parent) {
388 if (0 != tokens[n->tok].ctx)
389 continue;
390 roff_warn(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 } else
558 assert(tree->last);
559
560 assert(ROFF_BODY & tree->state);
561
562 /*
563 * First check that our possible parents and parent's possible
564 * children are satisfied.
565 */
566
567 if ( ! roffscan(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 ( ! roffscan(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 }
618
619 /*
620 * Create a new scope, as no previous one exists to
621 * close out.
622 */
623
624 if (NULL == n)
625 return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER));
626
627 /*
628 * Close out all intermediary scoped blocks, then hang
629 * the current scope from our predecessor's parent.
630 */
631
632 do {
633 t = tree->last->tok;
634 if ( ! (*tokens[t].cb)(t, tree, NULL, ROFF_EXIT))
635 return(0);
636 } while (t != tok);
637
638 return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER));
639 }
640
641 /*
642 * Now consider explicit-end tags, where we want to close back
643 * to a specific tag. Example:
644 * .Bl
645 * .It Item.
646 * .El
647 * In this, the `El' tag closes out the scope of `Bl'.
648 */
649
650 assert(tree->last);
651 assert(tok != tokens[tok].ctx && 0 != tokens[tok].ctx);
652
653 /* LINTED */
654 do {
655 t = tree->last->tok;
656 if ( ! (*tokens[t].cb)(t, tree, NULL, ROFF_EXIT))
657 return(0);
658 } while (t != tokens[tok].ctx);
659
660 assert(tree->last);
661 return(1);
662 }
663
664
665 static int
666 rofffindarg(const char *name)
667 {
668 size_t i;
669
670 /* FIXME: use a table, this is slow but ok for now. */
671
672 /* LINTED */
673 for (i = 0; i < ROFF_ARGMAX; i++)
674 /* LINTED */
675 if (0 == strcmp(name, tokargnames[i]))
676 return((int)i);
677
678 return(ROFF_ARGMAX);
679 }
680
681
682 static int
683 rofffindtok(const char *buf)
684 {
685 char token[4];
686 int i;
687
688 for (i = 0; *buf && ! isspace(*buf) && i < 3; i++, buf++)
689 token[i] = *buf;
690
691 if (i == 3)
692 return(ROFF_MAX);
693
694 token[i] = 0;
695
696 /* FIXME: use a table, this is slow but ok for now. */
697
698 /* LINTED */
699 for (i = 0; i < ROFF_MAX; i++)
700 /* LINTED */
701 if (0 == strcmp(toknames[i], token))
702 return((int)i);
703
704 return(ROFF_MAX);
705 }
706
707
708 static int
709 roffispunct(const char *p)
710 {
711
712 if (0 == *p)
713 return(0);
714 if (0 != *(p + 1))
715 return(0);
716
717 switch (*p) {
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 /* FALLTHROUGH */
738 case(']'):
739 /* FALLTHROUGH */
740 case('}'):
741 return(1);
742 default:
743 break;
744 }
745
746 return(0);
747 }
748
749
750 static int
751 rofffindcallable(const char *name)
752 {
753 int c;
754
755 if (ROFF_MAX == (c = rofffindtok(name)))
756 return(ROFF_MAX);
757 assert(c >= 0 && c < ROFF_MAX);
758 return(ROFF_CALLABLE & tokens[c].flags ? c : ROFF_MAX);
759 }
760
761
762 static struct roffnode *
763 roffnode_new(int tokid, struct rofftree *tree)
764 {
765 struct roffnode *p;
766
767 if (NULL == (p = malloc(sizeof(struct roffnode))))
768 err(1, "malloc");
769
770 p->tok = tokid;
771 p->parent = tree->last;
772 tree->last = p;
773
774 return(p);
775 }
776
777
778 static int
779 roffargok(int tokid, int argid)
780 {
781 const int *c;
782
783 if (NULL == (c = tokens[tokid].args))
784 return(0);
785
786 for ( ; ROFF_ARGMAX != *c; c++)
787 if (argid == *c)
788 return(1);
789
790 return(0);
791 }
792
793
794 static void
795 roffnode_free(struct rofftree *tree)
796 {
797 struct roffnode *p;
798
799 assert(tree->last);
800
801 p = tree->last;
802 tree->last = tree->last->parent;
803 free(p);
804 }
805
806
807 static int
808 roffnextopt(const struct rofftree *tree, int tok,
809 char ***in, char **val)
810 {
811 char *arg, **argv;
812 int v;
813
814 *val = NULL;
815 argv = *in;
816 assert(argv);
817
818 if (NULL == (arg = *argv))
819 return(-1);
820 if ('-' != *arg)
821 return(-1);
822
823 if (ROFF_ARGMAX == (v = rofffindarg(arg + 1))) {
824 roff_warn(tree, arg, "argument-like parameter `%s' to "
825 "`%s'", &arg[1], toknames[tok]);
826 return(-1);
827 }
828
829 if ( ! roffargok(tok, v)) {
830 roff_warn(tree, arg, "invalid argument parameter `%s' to "
831 "`%s'", tokargnames[v], toknames[tok]);
832 return(-1);
833 }
834
835 if ( ! (ROFF_VALUE & tokenargs[v]))
836 return(v);
837
838 *in = ++argv;
839
840 if (NULL == *argv) {
841 roff_err(tree, arg, "empty value of `%s' for `%s'",
842 tokargnames[v], toknames[tok]);
843 return(ROFF_ARGMAX);
844 }
845
846 return(v);
847 }
848
849
850 static int
851 roffparseopts(struct rofftree *tree, int tok,
852 char ***args, int *argc, char **argv)
853 {
854 int i, c;
855 char *v;
856
857 i = 0;
858
859 while (-1 != (c = roffnextopt(tree, tok, args, &v))) {
860 if (ROFF_ARGMAX == c)
861 return(0);
862
863 argc[i] = c;
864 argv[i] = v;
865 i++;
866 *args = *args + 1;
867 }
868
869 argc[i] = ROFF_ARGMAX;
870 argv[i] = NULL;
871 return(1);
872 }
873
874
875 /* ARGSUSED */
876 static int
877 roff_Dd(ROFFCALL_ARGS)
878 {
879
880 if (ROFF_BODY & tree->state) {
881 assert( ! (ROFF_PRELUDE & tree->state));
882 assert(ROFF_PRELUDE_Dd & tree->state);
883 return(roff_text(tok, tree, argv, type));
884 }
885
886 assert(ROFF_PRELUDE & tree->state);
887 assert( ! (ROFF_BODY & tree->state));
888
889 if (ROFF_PRELUDE_Dd & tree->state) {
890 roff_err(tree, *argv, "repeated `Dd' in prelude");
891 return(0);
892 } else if (ROFF_PRELUDE_Dt & tree->state) {
893 roff_err(tree, *argv, "out-of-order `Dd' in prelude");
894 return(0);
895 }
896
897 /* TODO: parse date. */
898
899 assert(NULL == tree->last);
900 tree->state |= ROFF_PRELUDE_Dd;
901
902 return(1);
903 }
904
905
906 /* ARGSUSED */
907 static int
908 roff_Dt(ROFFCALL_ARGS)
909 {
910
911 if (ROFF_BODY & tree->state) {
912 assert( ! (ROFF_PRELUDE & tree->state));
913 assert(ROFF_PRELUDE_Dt & tree->state);
914 return(roff_text(tok, tree, argv, type));
915 }
916
917 assert(ROFF_PRELUDE & tree->state);
918 assert( ! (ROFF_BODY & tree->state));
919
920 if ( ! (ROFF_PRELUDE_Dd & tree->state)) {
921 roff_err(tree, *argv, "out-of-order `Dt' in prelude");
922 return(0);
923 } else if (ROFF_PRELUDE_Dt & tree->state) {
924 roff_err(tree, *argv, "repeated `Dt' in prelude");
925 return(0);
926 }
927
928 /* TODO: parse date. */
929
930 assert(NULL == tree->last);
931 tree->state |= ROFF_PRELUDE_Dt;
932
933 return(1);
934 }
935
936
937 /* ARGSUSED */
938 static int
939 roff_Os(ROFFCALL_ARGS)
940 {
941
942 if (ROFF_EXIT == type) {
943 roffnode_free(tree);
944 return((*tree->cb.rofftail)(tree->arg));
945 } else if (ROFF_BODY & tree->state) {
946 assert( ! (ROFF_PRELUDE & tree->state));
947 assert(ROFF_PRELUDE_Os & tree->state);
948 return(roff_text(tok, tree, argv, type));
949 }
950
951 assert(ROFF_PRELUDE & tree->state);
952 if ( ! (ROFF_PRELUDE_Dt & tree->state) ||
953 ! (ROFF_PRELUDE_Dd & tree->state)) {
954 roff_err(tree, *argv, "out-of-order `Os' in prelude");
955 return(0);
956 }
957
958 /* TODO: extract OS. */
959
960 tree->state |= ROFF_PRELUDE_Os;
961 tree->state &= ~ROFF_PRELUDE;
962 tree->state |= ROFF_BODY;
963
964 assert(NULL == tree->last);
965
966 if (NULL == roffnode_new(tok, tree))
967 return(0);
968
969 return((*tree->cb.roffhead)(tree->arg));
970 }
971
972
973 /* ARGSUSED */
974 static int
975 roff_layout(ROFFCALL_ARGS)
976 {
977 int i, c, argcp[ROFF_MAXARG];
978 char *argvp[ROFF_MAXARG];
979
980 if (ROFF_PRELUDE & tree->state) {
981 roff_err(tree, *argv, "bad `%s' in prelude",
982 toknames[tok]);
983 return(0);
984 } else if (ROFF_EXIT == type) {
985 roffnode_free(tree);
986 return((*tree->cb.roffblkout)(tree->arg, tok));
987 }
988
989 assert( ! (ROFF_CALLABLE & tokens[tok].flags));
990
991 ++argv;
992
993 if ( ! roffparseopts(tree, tok, &argv, argcp, argvp))
994 return(0);
995 if (NULL == roffnode_new(tok, tree))
996 return(0);
997
998 /*
999 * Layouts have two parts: the layout body and header. The
1000 * layout header is the trailing text of the line macro, while
1001 * the layout body is everything following until termination.
1002 */
1003
1004 if ( ! (*tree->cb.roffblkin)(tree->arg, tok, argcp, argvp))
1005 return(0);
1006 if (NULL == *argv)
1007 return(1);
1008 if ( ! (*tree->cb.roffin)(tree->arg, tok, argcp, argvp))
1009 return(0);
1010
1011 /*
1012 * If there are no parsable parts, then write remaining tokens
1013 * into the layout header and exit.
1014 */
1015
1016 if ( ! (ROFF_PARSED & tokens[tok].flags)) {
1017 i = 0;
1018 while (*argv) {
1019 if ( ! (*tree->cb.roffdata)(tree->arg, i, *argv++))
1020 return(0);
1021 i = 1;
1022 }
1023 return((*tree->cb.roffout)(tree->arg, tok));
1024 }
1025
1026 /*
1027 * Parsable elements may be in the header (or be the header, for
1028 * that matter). Follow the regular parsing rules for these.
1029 */
1030
1031 i = 0;
1032 while (*argv) {
1033 if (ROFF_MAX == (c = rofffindcallable(*argv))) {
1034 assert(tree->arg);
1035 if ( ! (*tree->cb.roffdata)
1036 (tree->arg, i, *argv++))
1037 return(0);
1038 i = 1;
1039 continue;
1040 }
1041
1042 if (NULL == tokens[c].cb) {
1043 roff_err(tree, *argv, "unsupported macro `%s'",
1044 toknames[c]);
1045 return(0);
1046 }
1047
1048 if ( ! (*tokens[c].cb)(c, tree, argv, ROFF_ENTER))
1049 return(0);
1050
1051 break;
1052 }
1053
1054 /*
1055 * If there's trailing punctuation in the header, then write it
1056 * out now. Here we mimic the behaviour of a line-dominant text
1057 * macro.
1058 */
1059
1060 if (NULL == *argv)
1061 return((*tree->cb.roffout)(tree->arg, tok));
1062
1063 /*
1064 * Expensive. Scan to the end of line then work backwards until
1065 * a token isn't punctuation.
1066 */
1067
1068 i = 0;
1069 while (argv[i])
1070 i++;
1071
1072 assert(i > 0);
1073 if ( ! roffispunct(argv[--i]))
1074 return((*tree->cb.roffout)(tree->arg, tok));
1075
1076 while (i >= 0 && roffispunct(argv[i]))
1077 i--;
1078
1079 assert(0 != i);
1080 i++;
1081
1082 /* LINTED */
1083 while (argv[i])
1084 if ( ! (*tree->cb.roffdata)(tree->arg, 0, argv[i++]))
1085 return(0);
1086
1087 return((*tree->cb.roffout)(tree->arg, tok));
1088 }
1089
1090
1091 /* ARGSUSED */
1092 static int
1093 roff_text(ROFFCALL_ARGS)
1094 {
1095 int i, j, first, c, argcp[ROFF_MAXARG];
1096 char *argvp[ROFF_MAXARG];
1097
1098 if (ROFF_PRELUDE & tree->state) {
1099 roff_err(tree, *argv, "`%s' disallowed in prelude",
1100 toknames[tok]);
1101 return(0);
1102 }
1103
1104 first = (*argv == tree->cur);
1105 argv++;
1106
1107 if ( ! roffparseopts(tree, tok, &argv, argcp, argvp))
1108 return(0);
1109 if ( ! (*tree->cb.roffin)(tree->arg, tok, argcp, argvp))
1110 return(0);
1111 if (NULL == *argv)
1112 return((*tree->cb.roffout)(tree->arg, tok));
1113
1114 if ( ! (ROFF_PARSED & tokens[tok].flags)) {
1115 i = 0;
1116 while (*argv) {
1117 if ( ! (*tree->cb.roffdata)(tree->arg, i, *argv++))
1118 return(0);
1119 i = 1;
1120 }
1121 return((*tree->cb.roffout)(tree->arg, tok));
1122 }
1123
1124 /*
1125 * Deal with punctuation. Ugly. Work ahead until we encounter
1126 * terminating punctuation. If we encounter it and all
1127 * subsequent tokens are punctuation, then stop processing (the
1128 * line-dominant macro will print these tokens after closure).
1129 */
1130
1131 i = 0;
1132 while (*argv) {
1133 if (ROFF_MAX == (c = rofffindcallable(*argv))) {
1134 if ( ! roffispunct(*argv)) {
1135 if ( ! (*tree->cb.roffdata)
1136 (tree->arg, i, *argv++))
1137 return(0);
1138 i = 1;
1139 continue;
1140 }
1141
1142 /* See if only punctuation remains. */
1143
1144 i = 1;
1145 for (j = 0; argv[j]; j++)
1146 if ( ! roffispunct(argv[j]))
1147 break;
1148
1149 if (argv[j]) {
1150 if ( ! (*tree->cb.roffdata)
1151 (tree->arg, 0, *argv++))
1152 return(0);
1153 continue;
1154 }
1155
1156 /* Only punctuation remains. */
1157
1158 if ( ! (*tree->cb.roffout)(tree->arg, tok))
1159 return(0);
1160 break;
1161 }
1162
1163 /*
1164 * A sub-command has been found. Execute it and
1165 * discontinue parsing for arguments. If we're
1166 * line-scoped, then close out after it returns; if we
1167 * aren't, then close out before.
1168 */
1169
1170 if (NULL == tokens[c].cb) {
1171 roff_err(tree, *argv, "unsupported macro `%s'",
1172 toknames[c]);
1173 return(0);
1174 }
1175
1176 if ( ! (ROFF_LSCOPE & tokens[tok].flags))
1177 if ( ! (*tree->cb.roffout)(tree->arg, tok))
1178 return(0);
1179
1180 if ( ! (*tokens[c].cb)(c, tree, argv, ROFF_ENTER))
1181 return(0);
1182
1183 if (ROFF_LSCOPE & tokens[tok].flags)
1184 if ( ! (*tree->cb.roffout)(tree->arg, tok))
1185 return(0);
1186
1187 break;
1188 }
1189
1190 if (NULL == *argv)
1191 return((*tree->cb.roffout)(tree->arg, tok));
1192 if ( ! first)
1193 return(1);
1194
1195 /*
1196 * We're the line-dominant macro. Check if there's remaining
1197 * punctuation. If there is, then flush it out before exiting.
1198 */
1199
1200 i = 0;
1201 while (argv[i])
1202 i++;
1203
1204 assert(i > 0);
1205 if ( ! roffispunct(argv[--i]))
1206 return(1);
1207
1208 while (i >= 0 && roffispunct(argv[i]))
1209 i--;
1210
1211 assert(0 != i);
1212 i++;
1213
1214 /* LINTED */
1215 while (argv[i])
1216 if ( ! (*tree->cb.roffdata)(tree->arg, 0, argv[i++]))
1217 return(0);
1218
1219 return(1);
1220 }
1221
1222
1223 /* ARGSUSED */
1224 static int
1225 roff_noop(ROFFCALL_ARGS)
1226 {
1227
1228 return(1);
1229 }
1230
1231
1232 /* ARGSUSED */
1233 static int
1234 roff_depr(ROFFCALL_ARGS)
1235 {
1236
1237 roff_err(tree, *argv, "`%s' is deprecated", toknames[tok]);
1238 return(0);
1239 }
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 }