]> git.cameronkatri.com Git - mandoc.git/blob - roff.c
Mainly quotes.
[mandoc.git] / roff.c
1 /* $Id: roff.c,v 1.9 2008/11/27 11:23:51 kristaps Exp $ */
2 /*
3 * Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the
7 * above copyright notice and this permission notice appear in all
8 * copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
18 */
19 #include <assert.h>
20 #include <ctype.h>
21 #include <err.h>
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <time.h>
26
27 #include "libmdocml.h"
28 #include "private.h"
29
30 /* FIXME: warn if Pp occurs before/after Sh etc. (see mdoc.samples). */
31
32 /* FIXME: warn about "X section only" macros. */
33
34 /* FIXME: warn about empty lists. */
35
36 /* FIXME: ; : } ) (etc.) after text macros? */
37
38 #define ROFF_MAXARG 10
39
40 enum roffd {
41 ROFF_ENTER = 0,
42 ROFF_EXIT
43 };
44
45 enum rofftype {
46 ROFF_COMMENT,
47 ROFF_TEXT,
48 ROFF_LAYOUT
49 };
50
51 #define ROFFCALL_ARGS \
52 int tok, struct rofftree *tree, \
53 const char *argv[], enum roffd type
54
55 struct rofftree;
56
57 struct rofftok {
58 int (*cb)(ROFFCALL_ARGS); /* Callback. */
59 const int *args; /* Args (or NULL). */
60 const int *parents;
61 const int *children;
62 int ctx;
63 enum rofftype type; /* Type of macro. */
64 int flags;
65 #define ROFF_PARSED (1 << 0) /* "Parsed". */
66 #define ROFF_CALLABLE (1 << 1) /* "Callable". */
67 #define ROFF_QUOTES (1 << 2) /* Quoted args. */
68 #define ROFF_NOBLKCHILD (1 << 3) /* No blk children. */
69 };
70
71 struct roffarg {
72 int flags;
73 #define ROFF_VALUE (1 << 0) /* Has a value. */
74 };
75
76 struct roffnode {
77 int tok; /* Token id. */
78 struct roffnode *parent; /* Parent (or NULL). */
79 size_t line; /* Parsed at line. */
80 };
81
82 struct rofftree {
83 struct roffnode *last; /* Last parsed node. */
84 time_t date; /* `Dd' results. */
85 char os[64]; /* `Os' results. */
86 char title[64]; /* `Dt' results. */
87 char section[64]; /* `Dt' results. */
88 char volume[64]; /* `Dt' results. */
89 int state;
90 #define ROFF_PRELUDE (1 << 1) /* In roff prelude. */
91 /* FIXME: if we had prev ptrs, this wouldn't be necessary. */
92 #define ROFF_PRELUDE_Os (1 << 2) /* `Os' is parsed. */
93 #define ROFF_PRELUDE_Dt (1 << 3) /* `Dt' is parsed. */
94 #define ROFF_PRELUDE_Dd (1 << 4) /* `Dd' is parsed. */
95 #define ROFF_BODY (1 << 5) /* In roff body. */
96 struct md_mbuf *mbuf; /* Output (or NULL). */
97 const struct md_args *args; /* Global args. */
98 const struct md_rbuf *rbuf; /* Input. */
99 const struct roffcb *cb;
100 };
101
102 static int roff_Dd(ROFFCALL_ARGS);
103 static int roff_Dt(ROFFCALL_ARGS);
104 static int roff_Os(ROFFCALL_ARGS);
105
106 static int roff_layout(ROFFCALL_ARGS);
107 static int roff_text(ROFFCALL_ARGS);
108 static int roff_comment(ROFFCALL_ARGS);
109 static int roff_close(ROFFCALL_ARGS);
110
111 static struct roffnode *roffnode_new(int, struct rofftree *);
112 static void roffnode_free(int, struct rofftree *);
113
114 static int roffscan(int, const int *);
115 static int rofffindtok(const char *);
116 static int rofffindarg(const char *);
117 static int rofffindcallable(const char *);
118 static int roffargs(int, char *, char **);
119 static int roffargok(int, int);
120 static int roffnextopt(int, const char ***, char **);
121 static int roffparse(struct rofftree *, char *, size_t);
122 static int textparse(const struct rofftree *,
123 const char *, size_t);
124
125
126 static const int roffarg_An[] = { ROFF_Split, ROFF_Nosplit,
127 ROFF_ARGMAX };
128 static const int roffarg_Bd[] = { ROFF_Ragged, ROFF_Unfilled,
129 ROFF_Literal, ROFF_File, ROFF_Offset, ROFF_ARGMAX };
130 static const int roffarg_Ex[] = { ROFF_Std, ROFF_ARGMAX };
131 static const int roffarg_Rv[] = { ROFF_Std, ROFF_ARGMAX };
132 static const int roffarg_Bl[] = { ROFF_Bullet, ROFF_Dash,
133 ROFF_Hyphen, ROFF_Item, ROFF_Enum, ROFF_Tag, ROFF_Diag,
134 ROFF_Hang, ROFF_Ohang, ROFF_Inset, ROFF_Column, ROFF_Offset,
135 ROFF_ARGMAX };
136 static const int roffarg_St[] = {
137 ROFF_p1003_1_88, ROFF_p1003_1_90, ROFF_p1003_1_96,
138 ROFF_p1003_1_2001, ROFF_p1003_1_2004, ROFF_p1003_1,
139 ROFF_p1003_1b, ROFF_p1003_1b_93, ROFF_p1003_1c_95,
140 ROFF_p1003_1g_2000, ROFF_p1003_2_92, ROFF_p1387_2_95,
141 ROFF_p1003_2, ROFF_p1387_2, ROFF_isoC_90, ROFF_isoC_amd1,
142 ROFF_isoC_tcor1, ROFF_isoC_tcor2, ROFF_isoC_99, ROFF_ansiC,
143 ROFF_ansiC_89, ROFF_ansiC_99, ROFF_ieee754, ROFF_iso8802_3,
144 ROFF_xpg3, ROFF_xpg4, ROFF_xpg4_2, ROFF_xpg4_3, ROFF_xbd5,
145 ROFF_xcu5, ROFF_xsh5, ROFF_xns5, ROFF_xns5_2d2_0,
146 ROFF_xcurses4_2, ROFF_susv2, ROFF_susv3, ROFF_svid4,
147 ROFF_ARGMAX };
148
149 static const int roffchild_Bl[] = { ROFF_It, ROFF_El, ROFF_MAX };
150 static const int roffchild_Fo[] = { ROFF_Fa, ROFF_Fc, ROFF_MAX };
151 static const int roffchild_Oo[] = { ROFF_Op, ROFF_Oc, ROFF_MAX };
152 static const int roffchild_Rs[] = { ROFF_Re, ROFF__A, ROFF__B,
153 ROFF__D, ROFF__I, ROFF__J, ROFF__N, ROFF__O, ROFF__P,
154 ROFF__R, ROFF__T, ROFF__V, ROFF_MAX };
155
156 static const int roffparent_El[] = { ROFF_Bl, ROFF_It, ROFF_MAX };
157 static const int roffparent_Fc[] = { ROFF_Fo, ROFF_Fa, ROFF_MAX };
158 static const int roffparent_Oc[] = { ROFF_Oo, ROFF_Oc, ROFF_MAX };
159 static const int roffparent_It[] = { ROFF_Bl, ROFF_It, ROFF_MAX };
160 static const int roffparent_Re[] = { ROFF_Rs, ROFF_MAX };
161
162 /* Table of all known tokens. */
163 static const struct rofftok tokens[ROFF_MAX] = {
164 {roff_comment, NULL, NULL, NULL, 0, ROFF_COMMENT, 0 }, /* \" */
165 { roff_Dd, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Dd */
166 { roff_Dt, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Dt */
167 { roff_Os, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Os */
168 { roff_layout, NULL, NULL, NULL, ROFF_Sh, ROFF_LAYOUT, ROFF_PARSED }, /* Sh */
169 { roff_layout, NULL, NULL, NULL, ROFF_Ss, ROFF_LAYOUT, ROFF_PARSED }, /* Ss */
170 { roff_text, NULL, NULL, NULL, ROFF_Pp, ROFF_TEXT, 0 }, /* Pp */
171 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* D1 */
172 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Dl */
173 { roff_layout, NULL, NULL, NULL, 0, ROFF_LAYOUT, 0 }, /* Bd */
174 { roff_close, NULL, NULL, NULL, ROFF_Bd, ROFF_LAYOUT, 0 }, /* Ed */
175 { roff_layout, roffarg_Bl, NULL, roffchild_Bl, 0, ROFF_LAYOUT, 0 }, /* Bl */
176 { roff_close, NULL, roffparent_El, NULL, ROFF_Bl, ROFF_LAYOUT, 0 }, /* El */
177 { roff_layout, NULL, roffparent_It, NULL, ROFF_It, ROFF_LAYOUT, 0 }, /* It */
178 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ad */
179 { roff_text, roffarg_An, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* An */
180 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ar */
181 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_QUOTES }, /* Cd */ /* XXX man.4 only */
182 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Cm */
183 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Dv */ /* XXX needs arg */
184 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Er */ /* XXX needs arg */
185 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ev */ /* XXX needs arg */
186 { roff_text, roffarg_Ex, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Ex */
187 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Fa */ /* XXX needs arg */
188 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Fd */
189 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Fl */
190 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Fn */ /* XXX needs arg */ /* FIXME */
191 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Ft */
192 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ic */ /* XXX needs arg */
193 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* In */
194 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Li */
195 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Nd */ /* FIXME */
196 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Nm */ /* FIXME */
197 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Op */
198 { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Ot */ /* XXX deprecated */
199 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Pa */
200 { roff_text, roffarg_Rv, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Rv */
201 { roff_text, roffarg_St, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* St */
202 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Va */
203 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Vt */ /* XXX needs arg */
204 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Xr */ /* XXX needs arg */
205 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* %A */
206 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE}, /* %B */
207 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* %D */
208 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE}, /* %I */
209 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE}, /* %J */
210 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* %N */
211 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* %O */
212 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* %P */
213 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* %R */
214 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* %T */
215 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* %V */
216 { roff_close, NULL, NULL, NULL, ROFF_Ao, ROFF_LAYOUT, 0 }, /* Ac */
217 { roff_layout, NULL, NULL, NULL, 0, ROFF_LAYOUT, 0 }, /* Ao */
218 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Aq */
219 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* At */ /* XXX at most 2 args */
220 { roff_close, NULL, NULL, NULL, ROFF_Bo, ROFF_LAYOUT, 0 }, /* Bc */
221 { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Bf */ /* FIXME */
222 { roff_layout, NULL, NULL, NULL, 0, ROFF_LAYOUT, 0 }, /* Bo */
223 { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Bq */
224 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Bsx */
225 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Bx */
226 { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Db */ /* XXX */
227 { roff_close, NULL, NULL, NULL, ROFF_Do, ROFF_LAYOUT, 0 }, /* Dc */
228 { roff_layout, NULL, NULL, NULL, 0, ROFF_LAYOUT, 0 }, /* Do */
229 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Dq */
230 { roff_close, NULL, NULL, NULL, ROFF_Eo, ROFF_LAYOUT, 0 }, /* Ec */
231 { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Ef */ /* FIXME */
232 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Em */ /* XXX needs arg */
233 { roff_layout, NULL, NULL, NULL, 0, ROFF_LAYOUT, 0 }, /* Eo */
234 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Fx */
235 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Ms */
236 { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* No */
237 { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ns */
238 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Nx */
239 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Ox */
240 { roff_close, NULL, NULL, NULL, ROFF_Po, ROFF_LAYOUT, 0 }, /* Pc */
241 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Pf */
242 { roff_layout, NULL, NULL, NULL, 0, ROFF_LAYOUT, 0 }, /* Po */
243 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Pq */
244 { roff_close, NULL, NULL, NULL, ROFF_Qo, ROFF_LAYOUT, 0 }, /* Qc */
245 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ql */
246 { roff_layout, NULL, NULL, NULL, 0, ROFF_LAYOUT, 0 }, /* Qo */
247 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Qq */
248 { roff_close, NULL, roffparent_Re, NULL, ROFF_Rs, ROFF_LAYOUT, 0 }, /* Re */
249 { roff_layout, NULL, NULL, roffchild_Rs, 0, ROFF_LAYOUT, 0 }, /* Rs */
250 { roff_close, NULL, NULL, NULL, ROFF_So, ROFF_LAYOUT, 0 }, /* Sc */
251 { roff_layout, NULL, NULL, NULL, 0, ROFF_LAYOUT, 0 }, /* So */
252 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Sq */
253 { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Sm */
254 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Sx */
255 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE | ROFF_QUOTES }, /* Sy */
256 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Tn */
257 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Ux */
258 { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Xc */
259 { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Xo */
260 { roff_layout, NULL, NULL, NULL, 0, ROFF_LAYOUT, 0 }, /* Fo */
261 { roff_close, NULL, roffparent_Fc, NULL, ROFF_Fo, ROFF_LAYOUT, 0 }, /* Fc */
262 { roff_layout, NULL, NULL, NULL, 0, ROFF_LAYOUT, 0 }, /* Oo */
263 { roff_close, NULL, roffparent_Oc, NULL, ROFF_Oo, ROFF_LAYOUT, 0 }, /* Oc */
264 { roff_layout, NULL, NULL, NULL, 0, ROFF_LAYOUT, 0 }, /* Bk */
265 { roff_close, NULL, NULL, NULL, ROFF_Bk, ROFF_LAYOUT, 0 }, /* Ek */
266 };
267
268 /* Table of all known token arguments. */
269 static const struct roffarg tokenargs[ROFF_ARGMAX] = {
270 { 0 }, /* split */
271 { 0 }, /* nosplit */
272 { 0 }, /* ragged */
273 { 0 }, /* unfilled */
274 { 0 }, /* literal */
275 { ROFF_VALUE }, /* file */
276 { ROFF_VALUE }, /* offset */
277 { 0 }, /* bullet */
278 { 0 }, /* dash */
279 { 0 }, /* hyphen */
280 { 0 }, /* item */
281 { 0 }, /* enum */
282 { 0 }, /* tag */
283 { 0 }, /* diag */
284 { 0 }, /* hang */
285 { 0 }, /* ohang */
286 { 0 }, /* inset */
287 { 0 }, /* column */
288 { 0 }, /* width */
289 { 0 }, /* compact */
290 { 0 }, /* std */
291 { 0 }, /* p1003_1_88 */
292 { 0 }, /* p1003_1_90 */
293 { 0 }, /* p1003_1_96 */
294 { 0 }, /* p1003_1_2001 */
295 { 0 }, /* p1003_1_2004 */
296 { 0 }, /* p1003_1 */
297 { 0 }, /* p1003_1b */
298 { 0 }, /* p1003_1b_93 */
299 { 0 }, /* p1003_1c_95 */
300 { 0 }, /* p1003_1g_2000 */
301 { 0 }, /* p1003_2_92 */
302 { 0 }, /* p1387_2_95 */
303 { 0 }, /* p1003_2 */
304 { 0 }, /* p1387_2 */
305 { 0 }, /* isoC_90 */
306 { 0 }, /* isoC_amd1 */
307 { 0 }, /* isoC_tcor1 */
308 { 0 }, /* isoC_tcor2 */
309 { 0 }, /* isoC_99 */
310 { 0 }, /* ansiC */
311 { 0 }, /* ansiC_89 */
312 { 0 }, /* ansiC_99 */
313 { 0 }, /* ieee754 */
314 { 0 }, /* iso8802_3 */
315 { 0 }, /* xpg3 */
316 { 0 }, /* xpg4 */
317 { 0 }, /* xpg4_2 */
318 { 0 }, /* xpg4_3 */
319 { 0 }, /* xbd5 */
320 { 0 }, /* xcu5 */
321 { 0 }, /* xsh5 */
322 { 0 }, /* xns5 */
323 { 0 }, /* xns5_2d2_0 */
324 { 0 }, /* xcurses4_2 */
325 { 0 }, /* susv2 */
326 { 0 }, /* susv3 */
327 { 0 }, /* svid4 */
328 };
329
330 const char *const toknamesp[ROFF_MAX] =
331 {
332 "\\\"", "Dd", "Dt", "Os",
333 "Sh", "Ss", "Pp", "D1",
334 "Dl", "Bd", "Ed", "Bl",
335 "El", "It", "Ad", "An",
336 "Ar", "Cd", "Cm", "Dv",
337 "Er", "Ev", "Ex", "Fa",
338 "Fd", "Fl", "Fn", "Ft",
339 "Ic", "In", "Li", "Nd",
340 "Nm", "Op", "Ot", "Pa",
341 "Rv", "St", "Va", "Vt",
342 "Xr", "\%A", "\%B", "\%D",
343 "\%I", "\%J", "\%N", "\%O",
344 "\%P", "\%R", "\%T", "\%V",
345 "Ac", "Ao", "Aq", "At",
346 "Bc", "Bf", "Bo", "Bq",
347 "Bsx", "Bx", "Db", "Dc",
348 "Do", "Dq", "Ec", "Ef",
349 "Em", "Eo", "Fx", "Ms",
350 "No", "Ns", "Nx", "Ox",
351 "Pc", "Pf", "Po", "Pq",
352 "Qc", "Ql", "Qo", "Qq",
353 "Re", "Rs", "Sc", "So",
354 "Sq", "Sm", "Sx", "Sy",
355 "Tn", "Ux", "Xc", "Xo",
356 "Fo", "Fc", "Oo", "Oc",
357 "Bk", "Ek",
358 };
359
360 const char *const tokargnamesp[ROFF_ARGMAX] =
361 {
362 "split", "nosplit", "ragged",
363 "unfilled", "literal", "file",
364 "offset", "bullet", "dash",
365 "hyphen", "item", "enum",
366 "tag", "diag", "hang",
367 "ohang", "inset", "column",
368 "width", "compact", "std",
369 "p1003.1-88", "p1003.1-90", "p1003.1-96",
370 "p1003.1-2001", "p1003.1-2004", "p1003.1",
371 "p1003.1b", "p1003.1b-93", "p1003.1c-95",
372 "p1003.1g-2000", "p1003.2-92", "p1387.2-95",
373 "p1003.2", "p1387.2", "isoC-90",
374 "isoC-amd1", "isoC-tcor1", "isoC-tcor2",
375 "isoC-99", "ansiC", "ansiC-89",
376 "ansiC-99", "ieee754", "iso8802-3",
377 "xpg3", "xpg4", "xpg4.2",
378 "xpg4.3", "xbd5", "xcu5",
379 "xsh5", "xns5", "xns5.2d2.0",
380 "xcurses4.2", "susv2", "susv3",
381 "svid4"
382 };
383
384 const char *const *toknames = toknamesp;
385 const char *const *tokargnames = tokargnamesp;
386
387
388 int
389 roff_free(struct rofftree *tree, int flush)
390 {
391 int error;
392
393 assert(tree->mbuf);
394 if ( ! flush)
395 tree->mbuf = NULL;
396
397 /* LINTED */
398 while (tree->last)
399 if ( ! (*tokens[tree->last->tok].cb)
400 (tree->last->tok, tree, NULL, ROFF_EXIT))
401 /* Disallow flushing. */
402 tree->mbuf = NULL;
403
404 error = tree->mbuf ? 0 : 1;
405
406 if (tree->mbuf && (ROFF_PRELUDE & tree->state)) {
407 warnx("%s: prelude never finished",
408 tree->rbuf->name);
409 error = 1;
410 }
411
412 free(tree);
413 return(error ? 0 : 1);
414 }
415
416
417 struct rofftree *
418 roff_alloc(const struct md_args *args, struct md_mbuf *out,
419 const struct md_rbuf *in, const struct roffcb *cb)
420 {
421 struct rofftree *tree;
422
423 if (NULL == (tree = calloc(1, sizeof(struct rofftree)))) {
424 warn("malloc");
425 return(NULL);
426 }
427
428 tree->state = ROFF_PRELUDE;
429 tree->args = args;
430 tree->mbuf = out;
431 tree->rbuf = in;
432 tree->cb = cb;
433
434 return(tree);
435 }
436
437
438 int
439 roff_engine(struct rofftree *tree, char *buf, size_t sz)
440 {
441
442 if (0 == sz) {
443 warnx("%s: blank line (line %zu)",
444 tree->rbuf->name,
445 tree->rbuf->line);
446 return(0);
447 } else if ('.' != *buf)
448 return(textparse(tree, buf, sz));
449
450 return(roffparse(tree, buf, sz));
451 }
452
453
454 static int
455 textparse(const struct rofftree *tree, const char *buf, size_t sz)
456 {
457
458 if (NULL == tree->last) {
459 warnx("%s: unexpected text (line %zu)",
460 tree->rbuf->name,
461 tree->rbuf->line);
462 return(0);
463 } else if (NULL == tree->last->parent) {
464 warnx("%s: disallowed text (line %zu)",
465 tree->rbuf->name,
466 tree->rbuf->line);
467 return(0);
468 }
469
470 /* Print text. */
471
472 return(1);
473 }
474
475
476 static int
477 roffargs(int tok, char *buf, char **argv)
478 {
479 int i;
480
481 (void)tok;/* FIXME: quotable strings? */
482
483 assert(tok >= 0 && tok < ROFF_MAX);
484 assert('.' == *buf);
485
486 /* LINTED */
487 for (i = 0; *buf && i < ROFF_MAXARG; i++) {
488 argv[i] = buf++;
489 while (*buf && ! isspace(*buf))
490 buf++;
491 if (0 == *buf) {
492 continue;
493 }
494 *buf++ = 0;
495 while (*buf && isspace(*buf))
496 buf++;
497 }
498
499 assert(i > 0);
500 if (i < ROFF_MAXARG)
501 argv[i] = NULL;
502
503 return(ROFF_MAXARG > i);
504 }
505
506
507 /* XXX */
508 static int
509 roffscan(int tok, const int *tokv)
510 {
511
512 if (NULL == tokv)
513 return(1);
514
515 for ( ; ROFF_MAX != *tokv; tokv++)
516 if (tok == *tokv)
517 return(1);
518
519 return(0);
520 }
521
522
523 static int
524 roffparse(struct rofftree *tree, char *buf, size_t sz)
525 {
526 int tok, t;
527 struct roffnode *n;
528 char *argv[ROFF_MAXARG];
529 const char **argvp;
530
531 assert(sz > 0);
532
533 if (ROFF_MAX == (tok = rofffindtok(buf + 1))) {
534 warnx("%s: unknown line macro (line %zu)",
535 tree->rbuf->name, tree->rbuf->line);
536 return(0);
537 } else if (NULL == tokens[tok].cb) {
538 warnx("%s: macro `%s' not supported (line %zu)",
539 tree->rbuf->name, toknames[tok],
540 tree->rbuf->line);
541 return(0);
542 } else if (ROFF_COMMENT == tokens[tok].type)
543 return(1);
544
545 if ( ! roffargs(tok, buf, argv)) {
546 warnx("%s: too many args to `%s' (line %zu)",
547 tree->rbuf->name, toknames[tok],
548 tree->rbuf->line);
549 return(0);
550 } else
551 argvp = (const char **)argv + 1;
552
553 /*
554 * Prelude macros break some assumptions: branch now.
555 */
556
557 if (ROFF_PRELUDE & tree->state) {
558 assert(NULL == tree->last);
559 return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER));
560 } else
561 assert(tree->last);
562
563 assert(ROFF_BODY & tree->state);
564
565 /*
566 * First check that our possible parents and parent's possible
567 * children are satisfied.
568 */
569
570 if ( ! roffscan(tree->last->tok, tokens[tok].parents)) {
571 warnx("%s: invalid parent `%s' for `%s' (line %zu)",
572 tree->rbuf->name,
573 toknames[tree->last->tok],
574 toknames[tok], tree->rbuf->line);
575 return(0);
576 }
577
578 if ( ! roffscan(tok, tokens[tree->last->tok].children)) {
579 warnx("%s: invalid child `%s' for `%s' (line %zu)",
580 tree->rbuf->name, toknames[tok],
581 toknames[tree->last->tok],
582 tree->rbuf->line);
583 return(0);
584 }
585
586 /*
587 * Branch if we're not a layout token.
588 */
589
590 if (ROFF_LAYOUT != tokens[tok].type)
591 return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER));
592
593 /*
594 * Check our scope rules.
595 */
596
597 if (0 == tokens[tok].ctx)
598 return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER));
599
600 if (tok == tokens[tok].ctx) {
601 for (n = tree->last; n; n = n->parent) {
602 assert(0 == tokens[n->tok].ctx ||
603 n->tok == tokens[n->tok].ctx);
604 if (n->tok == tok)
605 break;
606 }
607 if (NULL == n) {
608 #ifdef DEBUG
609 (void)printf("scope: new `%s'\n",
610 toknames[tok]);
611 #endif
612 return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER));
613 }
614 do {
615 t = tree->last->tok;
616 #ifdef DEBUG
617 (void)printf("scope: closing `%s'\n",
618 toknames[t]);
619 #endif
620 if ( ! (*tokens[t].cb)(t, tree, NULL, ROFF_EXIT))
621 return(0);
622 } while (t != tok);
623
624 #ifdef DEBUG
625 (void)printf("scope: new parent of `%s' is `%s'\n",
626 toknames[tok],
627 toknames[tree->last->tok]);
628 #endif
629
630 return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER));
631 }
632
633 assert(tree->last);
634 assert(tok != tokens[tok].ctx && 0 != tokens[tok].ctx);
635
636 do {
637 t = tree->last->tok;
638 #ifdef DEBUG
639 (void)printf("scope: closing `%s'\n", toknames[t]);
640 #endif
641 if ( ! (*tokens[t].cb)(t, tree, NULL, ROFF_EXIT))
642 return(0);
643 } while (t != tokens[tok].ctx);
644
645 assert(tree->last);
646 return(1);
647 }
648
649
650 static int
651 rofffindarg(const char *name)
652 {
653 size_t i;
654
655 /* FIXME: use a table, this is slow but ok for now. */
656
657 /* LINTED */
658 for (i = 0; i < ROFF_ARGMAX; i++)
659 /* LINTED */
660 if (0 == strcmp(name, tokargnames[i]))
661 return((int)i);
662
663 return(ROFF_ARGMAX);
664 }
665
666
667 static int
668 rofffindtok(const char *buf)
669 {
670 char token[4];
671 size_t i;
672
673 for (i = 0; *buf && ! isspace(*buf) && i < 3; i++, buf++)
674 token[i] = *buf;
675
676 if (i == 3)
677 return(ROFF_MAX);
678
679 token[i] = 0;
680
681 /* FIXME: use a table, this is slow but ok for now. */
682
683 /* LINTED */
684 for (i = 0; i < ROFF_MAX; i++)
685 /* LINTED */
686 if (0 == strcmp(toknames[i], token)) {
687 #ifdef DEBUG
688 (void)printf("lookup (good): `%s' (%d)\n",
689 token, (int)i);
690 #endif
691 return((int)i);
692 }
693
694 #ifdef DEBUG
695 (void)printf("lookup (bad): `%s'\n", token);
696 #endif
697
698 return(ROFF_MAX);
699 }
700
701
702 static int
703 rofffindcallable(const char *name)
704 {
705 int c;
706
707 if (ROFF_MAX == (c = rofffindtok(name)))
708 return(ROFF_MAX);
709 assert(c >= 0 && c < ROFF_MAX);
710 return(ROFF_CALLABLE & tokens[c].flags ? c : ROFF_MAX);
711 }
712
713
714 static struct roffnode *
715 roffnode_new(int tokid, struct rofftree *tree)
716 {
717 struct roffnode *p;
718
719 if (NULL == (p = malloc(sizeof(struct roffnode)))) {
720 warn("malloc");
721 return(NULL);
722 }
723
724 p->line = tree->rbuf->line;
725 p->tok = tokid;
726 p->parent = tree->last;
727 tree->last = p;
728
729 #ifdef DEBUG
730 (void)printf("scope: new `%s' child of `%s'\n",
731 toknames[tree->last->tok],
732 tree->last->parent ?
733 toknames[tree->last->parent->tok] :
734 "<root>");
735 #endif
736
737 return(p);
738 }
739
740
741 static int
742 roffargok(int tokid, int argid)
743 {
744 const int *c;
745
746 if (NULL == (c = tokens[tokid].args))
747 return(0);
748
749 for ( ; ROFF_ARGMAX != *c; c++)
750 if (argid == *c)
751 return(1);
752
753 return(0);
754 }
755
756
757 static void
758 roffnode_free(int tokid, struct rofftree *tree)
759 {
760 struct roffnode *p;
761
762 assert(tree->last);
763 assert(tree->last->tok == tokid);
764
765 #ifdef DEBUG
766 (void)printf("scope: closing `%s' back to `%s'\n",
767 toknames[tree->last->tok],
768 tree->last->parent ?
769 toknames[tree->last->parent->tok] :
770 "<root>");
771 #endif
772
773 p = tree->last;
774 tree->last = tree->last->parent;
775 free(p);
776 }
777
778
779 static int
780 roffnextopt(int tok, const char ***in, char **val)
781 {
782 const char *arg, **argv;
783 int v;
784
785 *val = NULL;
786 argv = *in;
787 assert(argv);
788
789 if (NULL == (arg = *argv))
790 return(-1);
791 if ('-' != *arg)
792 return(-1);
793
794 /* FIXME: should we let this slide... ? */
795
796 if (ROFF_ARGMAX == (v = rofffindarg(&arg[1])))
797 return(-1);
798
799 /* FIXME: should we let this slide... ? */
800
801 if ( ! roffargok(tok, v))
802 return(-1);
803 if ( ! (ROFF_VALUE & tokenargs[v].flags))
804 return(v);
805
806 *in = ++argv;
807
808 /* FIXME: what if this looks like a roff token or argument? */
809
810 return(*argv ? v : ROFF_ARGMAX);
811 }
812
813
814 /* ARGSUSED */
815 static int
816 roff_Dd(ROFFCALL_ARGS)
817 {
818
819 if (ROFF_BODY & tree->state) {
820 assert( ! (ROFF_PRELUDE & tree->state));
821 assert(ROFF_PRELUDE_Dd & tree->state);
822 return(roff_text(tok, tree, argv, type));
823 }
824
825 assert(ROFF_PRELUDE & tree->state);
826 assert( ! (ROFF_BODY & tree->state));
827
828 if (ROFF_PRELUDE_Dd & tree->state) {
829 warnx("%s: prelude `Dd' repeated (line %zu)",
830 tree->rbuf->name, tree->rbuf->line);
831 return(0);
832 } else if (ROFF_PRELUDE_Dt & tree->state) {
833 warnx("%s: prelude `Dd' out-of-order (line %zu)",
834 tree->rbuf->name, tree->rbuf->line);
835 return(0);
836 }
837
838 /* TODO: parse date. */
839
840 assert(NULL == tree->last);
841 tree->state |= ROFF_PRELUDE_Dd;
842
843 return(1);
844 }
845
846
847 /* ARGSUSED */
848 static int
849 roff_Dt(ROFFCALL_ARGS)
850 {
851
852 if (ROFF_BODY & tree->state) {
853 assert( ! (ROFF_PRELUDE & tree->state));
854 assert(ROFF_PRELUDE_Dt & tree->state);
855 return(roff_text(tok, tree, argv, type));
856 }
857
858 assert(ROFF_PRELUDE & tree->state);
859 assert( ! (ROFF_BODY & tree->state));
860
861 if ( ! (ROFF_PRELUDE_Dd & tree->state)) {
862 warnx("%s: prelude `Dt' out-of-order (line %zu)",
863 tree->rbuf->name, tree->rbuf->line);
864 return(0);
865 } else if (ROFF_PRELUDE_Dt & tree->state) {
866 warnx("%s: prelude `Dt' repeated (line %zu)",
867 tree->rbuf->name, tree->rbuf->line);
868 return(0);
869 }
870
871 /* TODO: parse date. */
872
873 assert(NULL == tree->last);
874 tree->state |= ROFF_PRELUDE_Dt;
875
876 return(1);
877 }
878
879
880 /* ARGSUSED */
881 static int
882 roff_Os(ROFFCALL_ARGS)
883 {
884
885 if (ROFF_EXIT == type) {
886 assert(ROFF_PRELUDE_Os & tree->state);
887 return(roff_layout(tok, tree, argv, type));
888 } else if (ROFF_BODY & tree->state) {
889 assert( ! (ROFF_PRELUDE & tree->state));
890 assert(ROFF_PRELUDE_Os & tree->state);
891 return(roff_text(tok, tree, argv, type));
892 }
893
894 assert(ROFF_PRELUDE & tree->state);
895 if ( ! (ROFF_PRELUDE_Dt & tree->state) ||
896 ! (ROFF_PRELUDE_Dd & tree->state)) {
897 warnx("%s: prelude `Os' out-of-order (line %zu)",
898 tree->rbuf->name, tree->rbuf->line);
899 return(0);
900 }
901
902 /* TODO: extract OS. */
903
904 tree->state |= ROFF_PRELUDE_Os;
905 tree->state &= ~ROFF_PRELUDE;
906 tree->state |= ROFF_BODY;
907
908 assert(NULL == tree->last);
909
910 return(roff_layout(tok, tree, argv, type));
911 }
912
913
914 /* ARGSUSED */
915 static int
916 roff_layout(ROFFCALL_ARGS)
917 {
918 int i, c, argcp[ROFF_MAXARG];
919 char *v, *argvp[ROFF_MAXARG];
920
921 if (ROFF_PRELUDE & tree->state) {
922 warnx("%s: macro `%s' called in prelude (line %zu)",
923 tree->rbuf->name,
924 toknames[tok],
925 tree->rbuf->line);
926 return(0);
927 }
928
929 if (ROFF_EXIT == type) {
930 roffnode_free(tok, tree);
931 return((*tree->cb->roffblkout)(tok));
932 }
933
934 i = 0;
935
936 while (-1 != (c = roffnextopt(tok, &argv, &v))) {
937 if (ROFF_ARGMAX == c) {
938 warnx("%s: error parsing `%s' args (line %zu)",
939 tree->rbuf->name,
940 toknames[tok],
941 tree->rbuf->line);
942 return(0);
943 } else if ( ! roffargok(tok, c)) {
944 warnx("%s: arg `%s' not for `%s' (line %zu)",
945 tree->rbuf->name,
946 tokargnames[c],
947 toknames[tok],
948 tree->rbuf->line);
949 return(0);
950 }
951 argcp[i] = c;
952 argvp[i] = v;
953 i++;
954 argv++;
955 }
956
957 argcp[i] = ROFF_ARGMAX;
958 argvp[i] = NULL;
959
960 if (NULL == roffnode_new(tok, tree))
961 return(0);
962
963 if ( ! (*tree->cb->roffin)(tok, argcp, argvp))
964 return(0);
965
966 if ( ! (ROFF_PARSED & tokens[tok].flags)) {
967 /* TODO: print all tokens. */
968
969 if ( ! ((*tree->cb->roffout)(tok)))
970 return(0);
971 return((*tree->cb->roffblkin)(tok));
972 }
973
974 while (*argv) {
975 if (ROFF_MAX != (c = rofffindcallable(*argv))) {
976 if (NULL == tokens[c].cb) {
977 warnx("%s: macro `%s' not supported "
978 "(line %zu)",
979 tree->rbuf->name,
980 toknames[c],
981 tree->rbuf->line);
982 return(0);
983 }
984 if ( ! (*tokens[c].cb)(c, tree,
985 argv + 1, ROFF_ENTER))
986 return(0);
987 }
988 /* TODO: print token. */
989 argv++;
990 }
991
992 if ( ! ((*tree->cb->roffout)(tok)))
993 return(0);
994
995 return((*tree->cb->roffblkin)(tok));
996 }
997
998
999 /* ARGSUSED */
1000 static int
1001 roff_text(ROFFCALL_ARGS)
1002 {
1003 int i, c, argcp[ROFF_MAXARG];
1004 char *v, *argvp[ROFF_MAXARG];
1005
1006 if (ROFF_PRELUDE & tree->state) {
1007 warnx("%s: macro `%s' called in prelude (line %zu)",
1008 tree->rbuf->name,
1009 toknames[tok],
1010 tree->rbuf->line);
1011 return(0);
1012 }
1013
1014 i = 0;
1015
1016 while (-1 != (c = roffnextopt(tok, &argv, &v))) {
1017 if (ROFF_ARGMAX == c) {
1018 warnx("%s: error parsing `%s' args (line %zu)",
1019 tree->rbuf->name,
1020 toknames[tok],
1021 tree->rbuf->line);
1022 return(0);
1023 }
1024 argcp[i] = c;
1025 argvp[i] = v;
1026 i++;
1027 argv++;
1028 }
1029
1030 argcp[i] = ROFF_ARGMAX;
1031 argvp[i] = NULL;
1032
1033 if ( ! (*tree->cb->roffin)(tok, argcp, argvp))
1034 return(0);
1035
1036 if ( ! (ROFF_PARSED & tokens[tok].flags)) {
1037 /* TODO: print all tokens. */
1038 return((*tree->cb->roffout)(tok));
1039 }
1040
1041 while (*argv) {
1042 if (ROFF_MAX != (c = rofffindcallable(*argv))) {
1043 if (NULL == tokens[c].cb) {
1044 warnx("%s: macro `%s' not supported "
1045 "(line %zu)",
1046 tree->rbuf->name,
1047 toknames[c],
1048 tree->rbuf->line);
1049 return(0);
1050 }
1051 if ( ! (*tokens[c].cb)(c, tree,
1052 argv + 1, ROFF_ENTER))
1053 return(0);
1054 }
1055 /* TODO: print token. */
1056 argv++;
1057 }
1058
1059 return((*tree->cb->roffout)(tok));
1060 }
1061
1062
1063 /* ARGSUSED */
1064 static int
1065 roff_comment(ROFFCALL_ARGS)
1066 {
1067
1068 return(1);
1069 }
1070
1071
1072 /* ARGSUSED */
1073 static int
1074 roff_close(ROFFCALL_ARGS)
1075 {
1076
1077 return(1);
1078 }