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