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