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