]>
git.cameronkatri.com Git - mandoc.git/blob - roff.c
1 /* $Id: roff.c,v 1.23 2008/11/30 20:53:34 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.
28 #include "libmdocml.h"
31 /* FIXME: warn if Pp occurs before/after Sh etc. (see mdoc.samples). */
33 /* FIXME: warn about "X section only" macros. */
35 /* FIXME: warn about empty lists. */
37 /* FIXME: roff_layout and roff_text have identical-ish lower bodies. */
39 /* FIXME: NAME section needs specific elements. */
41 #define ROFF_MAXARG 32
55 #define ROFFCALL_ARGS \
56 int tok, struct rofftree *tree, \
57 char *argv[], enum roffd type
62 int (*cb
)(ROFFCALL_ARGS
); /* Callback. */
63 const int *args
; /* Args (or NULL). */
67 enum rofftype type
; /* Type of macro. */
69 #define ROFF_PARSED (1 << 0) /* "Parsed". */
70 #define ROFF_CALLABLE (1 << 1) /* "Callable". */
71 #define ROFF_SHALLOW (1 << 2) /* Nesting block. */
76 #define ROFF_VALUE (1 << 0) /* Has a value. */
80 int tok
; /* Token id. */
81 struct roffnode
*parent
; /* Parent (or NULL). */
85 struct roffnode
*last
; /* Last parsed node. */
88 time_t date
; /* `Dd' results. */
89 char os
[64]; /* `Os' results. */
90 char title
[64]; /* `Dt' results. */
91 char section
[64]; /* `Dt' results. */
92 char volume
[64]; /* `Dt' results. */
95 #define ROFF_PRELUDE (1 << 1) /* In roff prelude. */
96 #define ROFF_PRELUDE_Os (1 << 2) /* `Os' is parsed. */
97 #define ROFF_PRELUDE_Dt (1 << 3) /* `Dt' is parsed. */
98 #define ROFF_PRELUDE_Dd (1 << 4) /* `Dd' is parsed. */
99 #define ROFF_BODY (1 << 5) /* In roff body. */
105 static int roff_Dd(ROFFCALL_ARGS
);
106 static int roff_Dt(ROFFCALL_ARGS
);
107 static int roff_Os(ROFFCALL_ARGS
);
109 static int roff_layout(ROFFCALL_ARGS
);
110 static int roff_text(ROFFCALL_ARGS
);
111 static int roff_comment(ROFFCALL_ARGS
);
112 static int roff_close(ROFFCALL_ARGS
);
113 static int roff_special(ROFFCALL_ARGS
);
115 static struct roffnode
*roffnode_new(int, struct rofftree
*);
116 static void roffnode_free(struct rofftree
*);
118 static void roff_warn(const struct rofftree
*,
119 const char *, char *, ...);
120 static void roff_err(const struct rofftree
*,
121 const char *, char *, ...);
123 static int roffscan(int, const int *);
124 static int rofffindtok(const char *);
125 static int rofffindarg(const char *);
126 static int rofffindcallable(const char *);
127 static int roffargs(const struct rofftree
*,
128 int, char *, char **);
129 static int roffargok(int, int);
130 static int roffnextopt(const struct rofftree
*,
131 int, char ***, char **);
132 static int roffparse(struct rofftree
*, char *);
133 static int textparse(const struct rofftree
*, char *);
136 static const int roffarg_An
[] = { ROFF_Split
, ROFF_Nosplit
,
138 static const int roffarg_Bd
[] = { ROFF_Ragged
, ROFF_Unfilled
,
139 ROFF_Literal
, ROFF_File
, ROFF_Offset
, ROFF_Filled
,
140 ROFF_Compact
, ROFF_ARGMAX
};
141 static const int roffarg_Bk
[] = { ROFF_Words
, ROFF_ARGMAX
};
142 static const int roffarg_Ex
[] = { ROFF_Std
, ROFF_ARGMAX
};
143 static const int roffarg_Rv
[] = { ROFF_Std
, ROFF_ARGMAX
};
144 static const int roffarg_Bl
[] = { ROFF_Bullet
, ROFF_Dash
,
145 ROFF_Hyphen
, ROFF_Item
, ROFF_Enum
, ROFF_Tag
, ROFF_Diag
,
146 ROFF_Hang
, ROFF_Ohang
, ROFF_Inset
, ROFF_Column
, ROFF_Offset
,
147 ROFF_Width
, ROFF_Compact
, ROFF_ARGMAX
};
148 static const int roffarg_St
[] = {
149 ROFF_p1003_1_88
, ROFF_p1003_1_90
, ROFF_p1003_1_96
,
150 ROFF_p1003_1_2001
, ROFF_p1003_1_2004
, ROFF_p1003_1
,
151 ROFF_p1003_1b
, ROFF_p1003_1b_93
, ROFF_p1003_1c_95
,
152 ROFF_p1003_1g_2000
, ROFF_p1003_2_92
, ROFF_p1387_2_95
,
153 ROFF_p1003_2
, ROFF_p1387_2
, ROFF_isoC_90
, ROFF_isoC_amd1
,
154 ROFF_isoC_tcor1
, ROFF_isoC_tcor2
, ROFF_isoC_99
, ROFF_ansiC
,
155 ROFF_ansiC_89
, ROFF_ansiC_99
, ROFF_ieee754
, ROFF_iso8802_3
,
156 ROFF_xpg3
, ROFF_xpg4
, ROFF_xpg4_2
, ROFF_xpg4_3
, ROFF_xbd5
,
157 ROFF_xcu5
, ROFF_xsh5
, ROFF_xns5
, ROFF_xns5_2d2_0
,
158 ROFF_xcurses4_2
, ROFF_susv2
, ROFF_susv3
, ROFF_svid4
,
161 static const int roffchild_Bl
[] = { ROFF_It
, ROFF_El
, ROFF_MAX
};
162 static const int roffchild_Fo
[] = { ROFF_Fa
, ROFF_Fc
, ROFF_MAX
};
163 static const int roffchild_Oo
[] = { ROFF_Op
, ROFF_Oc
, ROFF_MAX
};
164 static const int roffchild_Rs
[] = { ROFF_Re
, ROFF__A
, ROFF__B
,
165 ROFF__D
, ROFF__I
, ROFF__J
, ROFF__N
, ROFF__O
, ROFF__P
,
166 ROFF__R
, ROFF__T
, ROFF__V
, ROFF_MAX
};
168 static const int roffparent_El
[] = { ROFF_Bl
, ROFF_It
, ROFF_MAX
};
169 static const int roffparent_Fc
[] = { ROFF_Fo
, ROFF_Fa
, ROFF_MAX
};
170 static const int roffparent_Oc
[] = { ROFF_Oo
, ROFF_Oc
, ROFF_MAX
};
171 static const int roffparent_It
[] = { ROFF_Bl
, ROFF_It
, ROFF_MAX
};
172 static const int roffparent_Re
[] = { ROFF_Rs
, ROFF_MAX
};
174 /* Table of all known tokens. */
175 static const struct rofftok tokens
[ROFF_MAX
] = {
176 {roff_comment
, NULL
, NULL
, NULL
, 0, ROFF_COMMENT
, 0 }, /* \" */
177 { roff_Dd
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Dd */
178 { roff_Dt
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Dt */
179 { roff_Os
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Os */
180 { roff_layout
, NULL
, NULL
, NULL
, ROFF_Sh
, ROFF_LAYOUT
, 0 }, /* Sh */
181 { roff_layout
, NULL
, NULL
, NULL
, ROFF_Ss
, ROFF_LAYOUT
, 0 }, /* Ss */
182 { roff_text
, NULL
, NULL
, NULL
, ROFF_Pp
, ROFF_TEXT
, 0 }, /* Pp */
183 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* D1 */
184 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Dl */
185 { roff_layout
, roffarg_Bd
, NULL
, NULL
, 0, ROFF_LAYOUT
, 0 }, /* Bd */
186 { roff_close
, NULL
, NULL
, NULL
, ROFF_Bd
, ROFF_LAYOUT
, 0 }, /* Ed */
187 { roff_layout
, roffarg_Bl
, NULL
, roffchild_Bl
, 0, ROFF_LAYOUT
, 0 }, /* Bl */
188 { roff_close
, NULL
, roffparent_El
, NULL
, ROFF_Bl
, ROFF_LAYOUT
, 0 }, /* El */
189 { roff_layout
, NULL
, roffparent_It
, NULL
, ROFF_It
, ROFF_LAYOUT
, ROFF_PARSED
| ROFF_SHALLOW
}, /* It */
190 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ad */ /* FIXME */
191 { roff_text
, roffarg_An
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* An */
192 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ar */
193 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Cd */ /* XXX man.4 only */
194 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Cm */
195 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Dv */ /* XXX needs arg */
196 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Er */ /* XXX needs arg */
197 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ev */ /* XXX needs arg */
198 { roff_text
, roffarg_Ex
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Ex */
199 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Fa */ /* XXX needs arg */
200 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Fd */
201 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Fl */
202 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Fn */ /* XXX needs arg */ /* FIXME */
203 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Ft */
204 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ic */ /* XXX needs arg */
205 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* In */
206 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Li */
207 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Nd */
208 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Nm */ /* FIXME */
209 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Op */
210 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Ot */ /* XXX deprecated */
211 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Pa */
212 { roff_text
, roffarg_Rv
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Rv */
213 { roff_text
, roffarg_St
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* St */
214 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Va */
215 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Vt */ /* XXX needs arg */
216 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Xr */ /* XXX needs arg */
217 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* %A */
218 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* %B */
219 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* %D */
220 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* %I */
221 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* %J */
222 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* %N */
223 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* %O */
224 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* %P */
225 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* %R */
226 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* %T */
227 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* %V */
228 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ac */
229 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ao */
230 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Aq */
231 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* At */ /* XXX at most 2 args */
232 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Bc */
233 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Bf */ /* FIXME */
234 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Bo */
235 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Bq */
236 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Bsx */
237 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Bx */
238 {roff_special
, NULL
, NULL
, NULL
, 0, ROFF_SPECIAL
, 0 }, /* Db */
239 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Dc */
240 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Do */
241 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Dq */
242 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ec */
243 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Ef */ /* FIXME */
244 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Em */ /* XXX needs arg */
245 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Eo */
246 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Fx */
247 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Ms */
248 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* No */
249 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ns */
250 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Nx */
251 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Ox */
252 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Pc */
253 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Pf */
254 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_LAYOUT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Po */
255 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Pq */
256 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Qc */
257 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ql */
258 { roff_layout
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Qo */
259 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Qq */
260 { roff_close
, NULL
, roffparent_Re
, NULL
, ROFF_Rs
, ROFF_LAYOUT
, 0 }, /* Re */
261 { roff_layout
, NULL
, NULL
, roffchild_Rs
, 0, ROFF_LAYOUT
, 0 }, /* Rs */
262 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Sc */
263 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* So */
264 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Sq */
265 {roff_special
, NULL
, NULL
, NULL
, 0, ROFF_SPECIAL
, 0 }, /* Sm */
266 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Sx */
267 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Sy */
268 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Tn */
269 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Ux */
270 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Xc */
271 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Xo */
272 { roff_layout
, NULL
, NULL
, roffchild_Fo
, 0, ROFF_LAYOUT
, 0 }, /* Fo */
273 { roff_close
, NULL
, roffparent_Fc
, NULL
, ROFF_Fo
, ROFF_LAYOUT
, 0 }, /* Fc */
274 { roff_layout
, NULL
, NULL
, roffchild_Oo
, 0, ROFF_LAYOUT
, 0 }, /* Oo */
275 { roff_close
, NULL
, roffparent_Oc
, NULL
, ROFF_Oo
, ROFF_LAYOUT
, 0 }, /* Oc */
276 { roff_layout
, roffarg_Bk
, NULL
, NULL
, 0, ROFF_LAYOUT
, 0 }, /* Bk */
277 { roff_close
, NULL
, NULL
, NULL
, ROFF_Bk
, ROFF_LAYOUT
, 0 }, /* Ek */
280 /* Table of all known token arguments. */
281 static const int tokenargs
[ROFF_ARGMAX
] = {
283 0, ROFF_VALUE
, ROFF_VALUE
, 0,
299 const char *const toknamesp
[ROFF_MAX
] = {
300 "\\\"", "Dd", "Dt", "Os",
301 "Sh", "Ss", "Pp", "D1",
302 "Dl", "Bd", "Ed", "Bl",
303 "El", "It", "Ad", "An",
304 "Ar", "Cd", "Cm", "Dv",
305 "Er", "Ev", "Ex", "Fa",
306 "Fd", "Fl", "Fn", "Ft",
307 "Ic", "In", "Li", "Nd",
308 "Nm", "Op", "Ot", "Pa",
309 "Rv", "St", "Va", "Vt",
311 "Xr", "\%A", "\%B", "\%D",
313 "\%I", "\%J", "\%N", "\%O",
315 "\%P", "\%R", "\%T", "\%V",
316 "Ac", "Ao", "Aq", "At",
317 "Bc", "Bf", "Bo", "Bq",
318 "Bsx", "Bx", "Db", "Dc",
319 "Do", "Dq", "Ec", "Ef",
320 "Em", "Eo", "Fx", "Ms",
321 "No", "Ns", "Nx", "Ox",
322 "Pc", "Pf", "Po", "Pq",
323 "Qc", "Ql", "Qo", "Qq",
324 "Re", "Rs", "Sc", "So",
325 "Sq", "Sm", "Sx", "Sy",
326 "Tn", "Ux", "Xc", "Xo",
327 "Fo", "Fc", "Oo", "Oc",
331 const char *const tokargnamesp
[ROFF_ARGMAX
] = {
332 "split", "nosplit", "ragged",
333 "unfilled", "literal", "file",
334 "offset", "bullet", "dash",
335 "hyphen", "item", "enum",
336 "tag", "diag", "hang",
337 "ohang", "inset", "column",
338 "width", "compact", "std",
339 "p1003.1-88", "p1003.1-90", "p1003.1-96",
340 "p1003.1-2001", "p1003.1-2004", "p1003.1",
341 "p1003.1b", "p1003.1b-93", "p1003.1c-95",
342 "p1003.1g-2000", "p1003.2-92", "p1387.2-95",
343 "p1003.2", "p1387.2", "isoC-90",
344 "isoC-amd1", "isoC-tcor1", "isoC-tcor2",
345 "isoC-99", "ansiC", "ansiC-89",
346 "ansiC-99", "ieee754", "iso8802-3",
347 "xpg3", "xpg4", "xpg4.2",
348 "xpg4.3", "xbd5", "xcu5",
349 "xsh5", "xns5", "xns5.2d2.0",
350 "xcurses4.2", "susv2", "susv3",
351 "svid4", "filled", "words",
354 const char *const *toknames
= toknamesp
;
355 const char *const *tokargnames
= tokargnamesp
;
359 roff_free(struct rofftree
*tree
, int flush
)
371 if (ROFF_PRELUDE
& tree
->state
) {
372 roff_warn(tree
, NULL
, "prelude never finished");
376 for (n
= tree
->last
; n
->parent
; n
= n
->parent
) {
377 if (0 != tokens
[n
->tok
].ctx
)
379 roff_warn(tree
, NULL
, "closing explicit scope `%s'",
386 if ( ! (*tokens
[t
].cb
)(t
, tree
, NULL
, ROFF_EXIT
))
399 return(error
? 0 : 1);
404 roff_alloc(const struct roffcb
*cb
, void *args
)
406 struct rofftree
*tree
;
411 if (NULL
== (tree
= calloc(1, sizeof(struct rofftree
))))
414 tree
->state
= ROFF_PRELUDE
;
417 (void)memcpy(&tree
->cb
, cb
, sizeof(struct roffcb
));
424 roff_engine(struct rofftree
*tree
, char *buf
)
431 roff_warn(tree
, buf
, "blank line");
433 } else if ('.' != *buf
)
434 return(textparse(tree
, buf
));
436 return(roffparse(tree
, buf
));
441 textparse(const struct rofftree
*tree
, char *buf
)
444 return((*tree
->cb
.roffdata
)(tree
->arg
, 1, buf
));
449 roffargs(const struct rofftree
*tree
,
450 int tok
, char *buf
, char **argv
)
455 assert(tok
>= 0 && tok
< ROFF_MAX
);
461 for (i
= 0; *buf
&& i
< ROFF_MAXARG
; i
++) {
464 while (*buf
&& '\"' != *buf
)
467 roff_err(tree
, argv
[i
], "unclosed "
475 while (*buf
&& ! isspace(*buf
))
481 while (*buf
&& isspace(*buf
))
486 if (ROFF_MAXARG
== i
&& *buf
) {
487 roff_err(tree
, p
, "too many arguments for `%s'", toknames
499 roffscan(int tok
, const int *tokv
)
505 for ( ; ROFF_MAX
!= *tokv
; tokv
++)
514 roffparse(struct rofftree
*tree
, char *buf
)
518 char *argv
[ROFF_MAXARG
];
521 if (ROFF_MAX
== (tok
= rofffindtok(buf
+ 1))) {
522 roff_err(tree
, buf
+ 1, "bogus line macro");
524 } else if (NULL
== tokens
[tok
].cb
) {
525 roff_err(tree
, buf
+ 1, "unsupported macro `%s'",
528 } else if (ROFF_COMMENT
== tokens
[tok
].type
)
531 if ( ! roffargs(tree
, tok
, buf
, argv
))
534 argvp
= (char **)argv
;
537 * Prelude macros break some assumptions, so branch now.
540 if (ROFF_PRELUDE
& tree
->state
) {
541 assert(NULL
== tree
->last
);
542 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
546 assert(ROFF_BODY
& tree
->state
);
549 * First check that our possible parents and parent's possible
550 * children are satisfied.
553 if ( ! roffscan(tree
->last
->tok
, tokens
[tok
].parents
)) {
554 roff_err(tree
, *argvp
, "`%s' has invalid parent `%s'",
556 toknames
[tree
->last
->tok
]);
560 if ( ! roffscan(tok
, tokens
[tree
->last
->tok
].children
)) {
561 roff_err(tree
, *argvp
, "`%s' is invalid child of `%s'",
563 toknames
[tree
->last
->tok
]);
568 * Branch if we're not a layout token.
571 if (ROFF_LAYOUT
!= tokens
[tok
].type
)
572 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
575 * Check our scope rules.
578 if (0 == tokens
[tok
].ctx
)
579 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
582 * First consider implicit-end tags, like as follows:
585 * In this, we want to close the scope of the NAME section. If
586 * there's an intermediary implicit-end tag, such as
590 * then it must be closed as well.
593 if (tok
== tokens
[tok
].ctx
) {
595 * First search up to the point where we must close.
596 * If one doesn't exist, then we can open a new scope.
599 for (n
= tree
->last
; n
; n
= n
->parent
) {
600 assert(0 == tokens
[n
->tok
].ctx
||
601 n
->tok
== tokens
[n
->tok
].ctx
);
604 if (ROFF_SHALLOW
& tokens
[tok
].flags
) {
611 * Create a new scope, as no previous one exists to
616 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
619 * Close out all intermediary scoped blocks, then hang
620 * the current scope from our predecessor's parent.
625 if ( ! (*tokens
[t
].cb
)(t
, tree
, NULL
, ROFF_EXIT
))
629 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
633 * Now consider explicit-end tags, where we want to close back
634 * to a specific tag. Example:
638 * In this, the `El' tag closes out the scope of `Bl'.
642 assert(tok
!= tokens
[tok
].ctx
&& 0 != tokens
[tok
].ctx
);
647 if ( ! (*tokens
[t
].cb
)(t
, tree
, NULL
, ROFF_EXIT
))
649 } while (t
!= tokens
[tok
].ctx
);
657 rofffindarg(const char *name
)
661 /* FIXME: use a table, this is slow but ok for now. */
664 for (i
= 0; i
< ROFF_ARGMAX
; i
++)
666 if (0 == strcmp(name
, tokargnames
[i
]))
674 rofffindtok(const char *buf
)
679 for (i
= 0; *buf
&& ! isspace(*buf
) && i
< 3; i
++, buf
++)
687 /* FIXME: use a table, this is slow but ok for now. */
690 for (i
= 0; i
< ROFF_MAX
; i
++)
692 if (0 == strcmp(toknames
[i
], token
))
700 roffispunct(const char *p
)
742 rofffindcallable(const char *name
)
746 if (ROFF_MAX
== (c
= rofffindtok(name
)))
748 assert(c
>= 0 && c
< ROFF_MAX
);
749 return(ROFF_CALLABLE
& tokens
[c
].flags
? c
: ROFF_MAX
);
753 static struct roffnode
*
754 roffnode_new(int tokid
, struct rofftree
*tree
)
758 if (NULL
== (p
= malloc(sizeof(struct roffnode
))))
762 p
->parent
= tree
->last
;
770 roffargok(int tokid
, int argid
)
774 if (NULL
== (c
= tokens
[tokid
].args
))
777 for ( ; ROFF_ARGMAX
!= *c
; c
++)
786 roffnode_free(struct rofftree
*tree
)
793 tree
->last
= tree
->last
->parent
;
799 roffnextopt(const struct rofftree
*tree
, int tok
,
800 char ***in
, char **val
)
809 if (NULL
== (arg
= *argv
))
814 if (ROFF_ARGMAX
== (v
= rofffindarg(arg
+ 1))) {
815 roff_warn(tree
, arg
, "argument-like parameter `%s' to "
816 "`%s'", &arg
[1], toknames
[tok
]);
820 if ( ! roffargok(tok
, v
)) {
821 roff_warn(tree
, arg
, "invalid argument parameter `%s' to "
822 "`%s'", tokargnames
[v
], toknames
[tok
]);
826 if ( ! (ROFF_VALUE
& tokenargs
[v
]))
832 roff_err(tree
, arg
, "empty value of `%s' for `%s'",
833 tokargnames
[v
], toknames
[tok
]);
843 roff_Dd(ROFFCALL_ARGS
)
846 if (ROFF_BODY
& tree
->state
) {
847 assert( ! (ROFF_PRELUDE
& tree
->state
));
848 assert(ROFF_PRELUDE_Dd
& tree
->state
);
849 return(roff_text(tok
, tree
, argv
, type
));
852 assert(ROFF_PRELUDE
& tree
->state
);
853 assert( ! (ROFF_BODY
& tree
->state
));
855 if (ROFF_PRELUDE_Dd
& tree
->state
) {
856 roff_err(tree
, *argv
, "repeated `Dd' in prelude");
858 } else if (ROFF_PRELUDE_Dt
& tree
->state
) {
859 roff_err(tree
, *argv
, "out-of-order `Dd' in prelude");
863 /* TODO: parse date. */
865 assert(NULL
== tree
->last
);
866 tree
->state
|= ROFF_PRELUDE_Dd
;
874 roff_Dt(ROFFCALL_ARGS
)
877 if (ROFF_BODY
& tree
->state
) {
878 assert( ! (ROFF_PRELUDE
& tree
->state
));
879 assert(ROFF_PRELUDE_Dt
& tree
->state
);
880 return(roff_text(tok
, tree
, argv
, type
));
883 assert(ROFF_PRELUDE
& tree
->state
);
884 assert( ! (ROFF_BODY
& tree
->state
));
886 if ( ! (ROFF_PRELUDE_Dd
& tree
->state
)) {
887 roff_err(tree
, *argv
, "out-of-order `Dt' in prelude");
889 } else if (ROFF_PRELUDE_Dt
& tree
->state
) {
890 roff_err(tree
, *argv
, "repeated `Dt' in prelude");
894 /* TODO: parse date. */
896 assert(NULL
== tree
->last
);
897 tree
->state
|= ROFF_PRELUDE_Dt
;
905 roff_Os(ROFFCALL_ARGS
)
908 if (ROFF_EXIT
== type
) {
910 return((*tree
->cb
.rofftail
)(tree
->arg
));
911 } else if (ROFF_BODY
& tree
->state
) {
912 assert( ! (ROFF_PRELUDE
& tree
->state
));
913 assert(ROFF_PRELUDE_Os
& tree
->state
);
914 return(roff_text(tok
, tree
, argv
, type
));
917 assert(ROFF_PRELUDE
& tree
->state
);
918 if ( ! (ROFF_PRELUDE_Dt
& tree
->state
) ||
919 ! (ROFF_PRELUDE_Dd
& tree
->state
)) {
920 roff_err(tree
, *argv
, "out-of-order `Os' in prelude");
924 /* TODO: extract OS. */
926 tree
->state
|= ROFF_PRELUDE_Os
;
927 tree
->state
&= ~ROFF_PRELUDE
;
928 tree
->state
|= ROFF_BODY
;
930 assert(NULL
== tree
->last
);
932 if (NULL
== roffnode_new(tok
, tree
))
935 return((*tree
->cb
.roffhead
)(tree
->arg
));
941 roff_layout(ROFFCALL_ARGS
)
943 int i
, c
, argcp
[ROFF_MAXARG
];
944 char *v
, *argvp
[ROFF_MAXARG
];
946 if (ROFF_PRELUDE
& tree
->state
) {
947 roff_err(tree
, *argv
, "`%s' disallowed in prelude",
952 if (ROFF_EXIT
== type
) {
954 return((*tree
->cb
.roffblkout
)(tree
->arg
, tok
));
960 while (-1 != (c
= roffnextopt(tree
, tok
, &argv
, &v
))) {
961 if (ROFF_ARGMAX
== c
)
970 argcp
[i
] = ROFF_ARGMAX
;
973 if (NULL
== roffnode_new(tok
, tree
))
976 if ( ! (*tree
->cb
.roffblkin
)(tree
->arg
, tok
, argcp
, argvp
))
982 if ( ! (*tree
->cb
.roffin
)(tree
->arg
, tok
, 0, argcp
, argvp
))
985 if ( ! (ROFF_PARSED
& tokens
[tok
].flags
)) {
988 if ( ! (*tree
->cb
.roffdata
)(tree
->arg
, i
, *argv
++))
992 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
997 if (ROFF_MAX
!= (c
= rofffindcallable(*argv
))) {
998 if (NULL
== tokens
[c
].cb
) {
999 roff_err(tree
, *argv
, "unsupported "
1004 if ( ! (*tokens
[c
].cb
)(c
, tree
, argv
, ROFF_ENTER
))
1010 if ( ! (*tree
->cb
.roffdata
)(tree
->arg
, i
, *argv
++))
1016 * If we're the first parser (*argv == tree->cur) then purge out
1017 * any additional punctuation, should there be any remaining at
1021 if (ROFF_PARSED
& tokens
[tok
].flags
&& *argv
) {
1027 if ( ! roffispunct(argv
[--i
]))
1028 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1030 while (i
>= 0 && roffispunct(argv
[i
]))
1038 if ( ! (*tree
->cb
.roffdata
)(tree
->arg
, 0, argv
[i
++]))
1042 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1048 roff_text(ROFFCALL_ARGS
)
1050 int i
, j
, first
, c
, argcp
[ROFF_MAXARG
];
1051 char *v
, *argvp
[ROFF_MAXARG
];
1053 if (ROFF_PRELUDE
& tree
->state
) {
1054 roff_err(tree
, *argv
, "`%s' disallowed in prelude",
1059 /* FIXME: breaks if passed from roff_layout. */
1060 first
= *argv
== tree
->cur
;
1065 while (-1 != (c
= roffnextopt(tree
, tok
, &argv
, &v
))) {
1066 if (ROFF_ARGMAX
== c
)
1075 argcp
[i
] = ROFF_ARGMAX
;
1078 if ( ! (*tree
->cb
.roffin
)(tree
->arg
, tok
, 1, argcp
, argvp
))
1081 if ( ! (ROFF_PARSED
& tokens
[tok
].flags
)) {
1084 if ( ! (*tree
->cb
.roffdata
)(tree
->arg
, i
, *argv
++))
1088 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1093 if (ROFF_MAX
== (c
= rofffindcallable(*argv
))) {
1095 * If all that remains is roff punctuation, then
1096 * close out our scope and return.
1098 if (roffispunct(*argv
)) {
1099 for (j
= 0; argv
[j
]; j
++)
1100 if ( ! roffispunct(argv
[j
]))
1102 if (NULL
== argv
[j
])
1107 if ( ! (*tree
->cb
.roffdata
)(tree
->arg
, i
, *argv
++))
1115 * A sub-command has been found. Execute it and
1116 * discontinue parsing for arguments.
1119 if (NULL
== tokens
[c
].cb
) {
1120 roff_err(tree
, *argv
, "unsupported macro `%s'",
1125 if ( ! (*tokens
[c
].cb
)(c
, tree
, argv
, ROFF_ENTER
))
1131 if ( ! (*tree
->cb
.roffout
)(tree
->arg
, tok
))
1135 * If we're the first parser (*argv == tree->cur) then purge out
1136 * any additional punctuation, should there be any remaining at
1140 if (first
&& *argv
) {
1146 if ( ! roffispunct(argv
[--i
]))
1149 while (i
>= 0 && roffispunct(argv
[i
]))
1157 if ( ! (*tree
->cb
.roffdata
)(tree
->arg
, 0, argv
[i
++]))
1167 roff_comment(ROFFCALL_ARGS
)
1176 roff_close(ROFFCALL_ARGS
)
1185 roff_special(ROFFCALL_ARGS
)
1188 return((*tree
->cb
.roffspecial
)(tree
->arg
, tok
));
1193 roff_warn(const struct rofftree
*tree
, const char *pos
, char *fmt
, ...)
1199 (void)vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
1202 (*tree
->cb
.roffmsg
)(tree
->arg
,
1203 ROFF_WARN
, tree
->cur
, pos
, buf
);
1208 roff_err(const struct rofftree
*tree
, const char *pos
, char *fmt
, ...)
1214 (void)vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
1217 (*tree
->cb
.roffmsg
)(tree
->arg
,
1218 ROFF_ERROR
, tree
->cur
, pos
, buf
);