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