]>
git.cameronkatri.com Git - mandoc.git/blob - roff.c
1 /* $Id: roff.c,v 1.9 2008/11/27 11:23:51 kristaps Exp $ */
3 * Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
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
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.
27 #include "libmdocml.h"
30 /* FIXME: warn if Pp occurs before/after Sh etc. (see mdoc.samples). */
32 /* FIXME: warn about "X section only" macros. */
34 /* FIXME: warn about empty lists. */
36 /* FIXME: ; : } ) (etc.) after text macros? */
38 #define ROFF_MAXARG 10
51 #define ROFFCALL_ARGS \
52 int tok, struct rofftree *tree, \
53 const char *argv[], enum roffd type
58 int (*cb
)(ROFFCALL_ARGS
); /* Callback. */
59 const int *args
; /* Args (or NULL). */
63 enum rofftype type
; /* Type of macro. */
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. */
73 #define ROFF_VALUE (1 << 0) /* Has a value. */
77 int tok
; /* Token id. */
78 struct roffnode
*parent
; /* Parent (or NULL). */
79 size_t line
; /* Parsed at line. */
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. */
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
;
102 static int roff_Dd(ROFFCALL_ARGS
);
103 static int roff_Dt(ROFFCALL_ARGS
);
104 static int roff_Os(ROFFCALL_ARGS
);
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
);
111 static struct roffnode
*roffnode_new(int, struct rofftree
*);
112 static void roffnode_free(int, struct rofftree
*);
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);
126 static const int roffarg_An
[] = { ROFF_Split
, ROFF_Nosplit
,
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
,
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
,
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
};
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
};
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 */
268 /* Table of all known token arguments. */
269 static const struct roffarg tokenargs
[ROFF_ARGMAX
] = {
273 { 0 }, /* unfilled */
275 { ROFF_VALUE
}, /* file */
276 { ROFF_VALUE
}, /* offset */
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 */
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 */
306 { 0 }, /* isoC_amd1 */
307 { 0 }, /* isoC_tcor1 */
308 { 0 }, /* isoC_tcor2 */
311 { 0 }, /* ansiC_89 */
312 { 0 }, /* ansiC_99 */
314 { 0 }, /* iso8802_3 */
323 { 0 }, /* xns5_2d2_0 */
324 { 0 }, /* xcurses4_2 */
330 const char *const toknamesp
[ROFF_MAX
] =
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",
360 const char *const tokargnamesp
[ROFF_ARGMAX
] =
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",
384 const char *const *toknames
= toknamesp
;
385 const char *const *tokargnames
= tokargnamesp
;
389 roff_free(struct rofftree
*tree
, int flush
)
399 if ( ! (*tokens
[tree
->last
->tok
].cb
)
400 (tree
->last
->tok
, tree
, NULL
, ROFF_EXIT
))
401 /* Disallow flushing. */
404 error
= tree
->mbuf
? 0 : 1;
406 if (tree
->mbuf
&& (ROFF_PRELUDE
& tree
->state
)) {
407 warnx("%s: prelude never finished",
413 return(error
? 0 : 1);
418 roff_alloc(const struct md_args
*args
, struct md_mbuf
*out
,
419 const struct md_rbuf
*in
, const struct roffcb
*cb
)
421 struct rofftree
*tree
;
423 if (NULL
== (tree
= calloc(1, sizeof(struct rofftree
)))) {
428 tree
->state
= ROFF_PRELUDE
;
439 roff_engine(struct rofftree
*tree
, char *buf
, size_t sz
)
443 warnx("%s: blank line (line %zu)",
447 } else if ('.' != *buf
)
448 return(textparse(tree
, buf
, sz
));
450 return(roffparse(tree
, buf
, sz
));
455 textparse(const struct rofftree
*tree
, const char *buf
, size_t sz
)
458 if (NULL
== tree
->last
) {
459 warnx("%s: unexpected text (line %zu)",
463 } else if (NULL
== tree
->last
->parent
) {
464 warnx("%s: disallowed text (line %zu)",
477 roffargs(int tok
, char *buf
, char **argv
)
481 (void)tok
;/* FIXME: quotable strings? */
483 assert(tok
>= 0 && tok
< ROFF_MAX
);
487 for (i
= 0; *buf
&& i
< ROFF_MAXARG
; i
++) {
489 while (*buf
&& ! isspace(*buf
))
495 while (*buf
&& isspace(*buf
))
503 return(ROFF_MAXARG
> i
);
509 roffscan(int tok
, const int *tokv
)
515 for ( ; ROFF_MAX
!= *tokv
; tokv
++)
524 roffparse(struct rofftree
*tree
, char *buf
, size_t sz
)
528 char *argv
[ROFF_MAXARG
];
533 if (ROFF_MAX
== (tok
= rofffindtok(buf
+ 1))) {
534 warnx("%s: unknown line macro (line %zu)",
535 tree
->rbuf
->name
, tree
->rbuf
->line
);
537 } else if (NULL
== tokens
[tok
].cb
) {
538 warnx("%s: macro `%s' not supported (line %zu)",
539 tree
->rbuf
->name
, toknames
[tok
],
542 } else if (ROFF_COMMENT
== tokens
[tok
].type
)
545 if ( ! roffargs(tok
, buf
, argv
)) {
546 warnx("%s: too many args to `%s' (line %zu)",
547 tree
->rbuf
->name
, toknames
[tok
],
551 argvp
= (const char **)argv
+ 1;
554 * Prelude macros break some assumptions: branch now.
557 if (ROFF_PRELUDE
& tree
->state
) {
558 assert(NULL
== tree
->last
);
559 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
563 assert(ROFF_BODY
& tree
->state
);
566 * First check that our possible parents and parent's possible
567 * children are satisfied.
570 if ( ! roffscan(tree
->last
->tok
, tokens
[tok
].parents
)) {
571 warnx("%s: invalid parent `%s' for `%s' (line %zu)",
573 toknames
[tree
->last
->tok
],
574 toknames
[tok
], tree
->rbuf
->line
);
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
],
587 * Branch if we're not a layout token.
590 if (ROFF_LAYOUT
!= tokens
[tok
].type
)
591 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
594 * Check our scope rules.
597 if (0 == tokens
[tok
].ctx
)
598 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
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
);
609 (void)printf("scope: new `%s'\n",
612 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
617 (void)printf("scope: closing `%s'\n",
620 if ( ! (*tokens
[t
].cb
)(t
, tree
, NULL
, ROFF_EXIT
))
625 (void)printf("scope: new parent of `%s' is `%s'\n",
627 toknames
[tree
->last
->tok
]);
630 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
634 assert(tok
!= tokens
[tok
].ctx
&& 0 != tokens
[tok
].ctx
);
639 (void)printf("scope: closing `%s'\n", toknames
[t
]);
641 if ( ! (*tokens
[t
].cb
)(t
, tree
, NULL
, ROFF_EXIT
))
643 } while (t
!= tokens
[tok
].ctx
);
651 rofffindarg(const char *name
)
655 /* FIXME: use a table, this is slow but ok for now. */
658 for (i
= 0; i
< ROFF_ARGMAX
; i
++)
660 if (0 == strcmp(name
, tokargnames
[i
]))
668 rofffindtok(const char *buf
)
673 for (i
= 0; *buf
&& ! isspace(*buf
) && i
< 3; i
++, buf
++)
681 /* FIXME: use a table, this is slow but ok for now. */
684 for (i
= 0; i
< ROFF_MAX
; i
++)
686 if (0 == strcmp(toknames
[i
], token
)) {
688 (void)printf("lookup (good): `%s' (%d)\n",
695 (void)printf("lookup (bad): `%s'\n", token
);
703 rofffindcallable(const char *name
)
707 if (ROFF_MAX
== (c
= rofffindtok(name
)))
709 assert(c
>= 0 && c
< ROFF_MAX
);
710 return(ROFF_CALLABLE
& tokens
[c
].flags
? c
: ROFF_MAX
);
714 static struct roffnode
*
715 roffnode_new(int tokid
, struct rofftree
*tree
)
719 if (NULL
== (p
= malloc(sizeof(struct roffnode
)))) {
724 p
->line
= tree
->rbuf
->line
;
726 p
->parent
= tree
->last
;
730 (void)printf("scope: new `%s' child of `%s'\n",
731 toknames
[tree
->last
->tok
],
733 toknames
[tree
->last
->parent
->tok
] :
742 roffargok(int tokid
, int argid
)
746 if (NULL
== (c
= tokens
[tokid
].args
))
749 for ( ; ROFF_ARGMAX
!= *c
; c
++)
758 roffnode_free(int tokid
, struct rofftree
*tree
)
763 assert(tree
->last
->tok
== tokid
);
766 (void)printf("scope: closing `%s' back to `%s'\n",
767 toknames
[tree
->last
->tok
],
769 toknames
[tree
->last
->parent
->tok
] :
774 tree
->last
= tree
->last
->parent
;
780 roffnextopt(int tok
, const char ***in
, char **val
)
782 const char *arg
, **argv
;
789 if (NULL
== (arg
= *argv
))
794 /* FIXME: should we let this slide... ? */
796 if (ROFF_ARGMAX
== (v
= rofffindarg(&arg
[1])))
799 /* FIXME: should we let this slide... ? */
801 if ( ! roffargok(tok
, v
))
803 if ( ! (ROFF_VALUE
& tokenargs
[v
].flags
))
808 /* FIXME: what if this looks like a roff token or argument? */
810 return(*argv
? v
: ROFF_ARGMAX
);
816 roff_Dd(ROFFCALL_ARGS
)
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
));
825 assert(ROFF_PRELUDE
& tree
->state
);
826 assert( ! (ROFF_BODY
& tree
->state
));
828 if (ROFF_PRELUDE_Dd
& tree
->state
) {
829 warnx("%s: prelude `Dd' repeated (line %zu)",
830 tree
->rbuf
->name
, tree
->rbuf
->line
);
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
);
838 /* TODO: parse date. */
840 assert(NULL
== tree
->last
);
841 tree
->state
|= ROFF_PRELUDE_Dd
;
849 roff_Dt(ROFFCALL_ARGS
)
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
));
858 assert(ROFF_PRELUDE
& tree
->state
);
859 assert( ! (ROFF_BODY
& tree
->state
));
861 if ( ! (ROFF_PRELUDE_Dd
& tree
->state
)) {
862 warnx("%s: prelude `Dt' out-of-order (line %zu)",
863 tree
->rbuf
->name
, tree
->rbuf
->line
);
865 } else if (ROFF_PRELUDE_Dt
& tree
->state
) {
866 warnx("%s: prelude `Dt' repeated (line %zu)",
867 tree
->rbuf
->name
, tree
->rbuf
->line
);
871 /* TODO: parse date. */
873 assert(NULL
== tree
->last
);
874 tree
->state
|= ROFF_PRELUDE_Dt
;
882 roff_Os(ROFFCALL_ARGS
)
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
));
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
);
902 /* TODO: extract OS. */
904 tree
->state
|= ROFF_PRELUDE_Os
;
905 tree
->state
&= ~ROFF_PRELUDE
;
906 tree
->state
|= ROFF_BODY
;
908 assert(NULL
== tree
->last
);
910 return(roff_layout(tok
, tree
, argv
, type
));
916 roff_layout(ROFFCALL_ARGS
)
918 int i
, c
, argcp
[ROFF_MAXARG
];
919 char *v
, *argvp
[ROFF_MAXARG
];
921 if (ROFF_PRELUDE
& tree
->state
) {
922 warnx("%s: macro `%s' called in prelude (line %zu)",
929 if (ROFF_EXIT
== type
) {
930 roffnode_free(tok
, tree
);
931 return((*tree
->cb
->roffblkout
)(tok
));
936 while (-1 != (c
= roffnextopt(tok
, &argv
, &v
))) {
937 if (ROFF_ARGMAX
== c
) {
938 warnx("%s: error parsing `%s' args (line %zu)",
943 } else if ( ! roffargok(tok
, c
)) {
944 warnx("%s: arg `%s' not for `%s' (line %zu)",
957 argcp
[i
] = ROFF_ARGMAX
;
960 if (NULL
== roffnode_new(tok
, tree
))
963 if ( ! (*tree
->cb
->roffin
)(tok
, argcp
, argvp
))
966 if ( ! (ROFF_PARSED
& tokens
[tok
].flags
)) {
967 /* TODO: print all tokens. */
969 if ( ! ((*tree
->cb
->roffout
)(tok
)))
971 return((*tree
->cb
->roffblkin
)(tok
));
975 if (ROFF_MAX
!= (c
= rofffindcallable(*argv
))) {
976 if (NULL
== tokens
[c
].cb
) {
977 warnx("%s: macro `%s' not supported "
984 if ( ! (*tokens
[c
].cb
)(c
, tree
,
985 argv
+ 1, ROFF_ENTER
))
988 /* TODO: print token. */
992 if ( ! ((*tree
->cb
->roffout
)(tok
)))
995 return((*tree
->cb
->roffblkin
)(tok
));
1001 roff_text(ROFFCALL_ARGS
)
1003 int i
, c
, argcp
[ROFF_MAXARG
];
1004 char *v
, *argvp
[ROFF_MAXARG
];
1006 if (ROFF_PRELUDE
& tree
->state
) {
1007 warnx("%s: macro `%s' called in prelude (line %zu)",
1016 while (-1 != (c
= roffnextopt(tok
, &argv
, &v
))) {
1017 if (ROFF_ARGMAX
== c
) {
1018 warnx("%s: error parsing `%s' args (line %zu)",
1030 argcp
[i
] = ROFF_ARGMAX
;
1033 if ( ! (*tree
->cb
->roffin
)(tok
, argcp
, argvp
))
1036 if ( ! (ROFF_PARSED
& tokens
[tok
].flags
)) {
1037 /* TODO: print all tokens. */
1038 return((*tree
->cb
->roffout
)(tok
));
1042 if (ROFF_MAX
!= (c
= rofffindcallable(*argv
))) {
1043 if (NULL
== tokens
[c
].cb
) {
1044 warnx("%s: macro `%s' not supported "
1051 if ( ! (*tokens
[c
].cb
)(c
, tree
,
1052 argv
+ 1, ROFF_ENTER
))
1055 /* TODO: print token. */
1059 return((*tree
->cb
->roffout
)(tok
));
1065 roff_comment(ROFFCALL_ARGS
)
1074 roff_close(ROFFCALL_ARGS
)