]>
git.cameronkatri.com Git - mandoc.git/blob - roff.c
1 /* $Id: roff.c,v 1.25 2008/11/30 23:05:57 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 (0 != *buf
&& 0 != *(buf
+ 1) && 0 != *(buf
+ 2))
522 if (0 == strncmp(buf
, ".\\\"", 3))
525 if (ROFF_MAX
== (tok
= rofffindtok(buf
+ 1))) {
526 roff_err(tree
, buf
+ 1, "bogus line macro");
528 } else if (NULL
== tokens
[tok
].cb
) {
529 roff_err(tree
, buf
+ 1, "unsupported macro `%s'",
534 assert(ROFF___
!= tok
);
535 if ( ! roffargs(tree
, tok
, buf
, argv
))
538 argvp
= (char **)argv
;
541 * Prelude macros break some assumptions, so branch now.
544 if (ROFF_PRELUDE
& tree
->state
) {
545 assert(NULL
== tree
->last
);
546 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
550 assert(ROFF_BODY
& tree
->state
);
553 * First check that our possible parents and parent's possible
554 * children are satisfied.
557 if ( ! roffscan(tree
->last
->tok
, tokens
[tok
].parents
)) {
558 roff_err(tree
, *argvp
, "`%s' has invalid parent `%s'",
560 toknames
[tree
->last
->tok
]);
564 if ( ! roffscan(tok
, tokens
[tree
->last
->tok
].children
)) {
565 roff_err(tree
, *argvp
, "`%s' is invalid child of `%s'",
567 toknames
[tree
->last
->tok
]);
572 * Branch if we're not a layout token.
575 if (ROFF_LAYOUT
!= tokens
[tok
].type
)
576 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
579 * Check our scope rules.
582 if (0 == tokens
[tok
].ctx
)
583 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
586 * First consider implicit-end tags, like as follows:
589 * In this, we want to close the scope of the NAME section. If
590 * there's an intermediary implicit-end tag, such as
594 * then it must be closed as well.
597 if (tok
== tokens
[tok
].ctx
) {
599 * First search up to the point where we must close.
600 * If one doesn't exist, then we can open a new scope.
603 for (n
= tree
->last
; n
; n
= n
->parent
) {
604 assert(0 == tokens
[n
->tok
].ctx
||
605 n
->tok
== tokens
[n
->tok
].ctx
);
608 if (ROFF_SHALLOW
& tokens
[tok
].flags
) {
615 * Create a new scope, as no previous one exists to
620 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
623 * Close out all intermediary scoped blocks, then hang
624 * the current scope from our predecessor's parent.
629 if ( ! (*tokens
[t
].cb
)(t
, tree
, NULL
, ROFF_EXIT
))
633 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
637 * Now consider explicit-end tags, where we want to close back
638 * to a specific tag. Example:
642 * In this, the `El' tag closes out the scope of `Bl'.
646 assert(tok
!= tokens
[tok
].ctx
&& 0 != tokens
[tok
].ctx
);
651 if ( ! (*tokens
[t
].cb
)(t
, tree
, NULL
, ROFF_EXIT
))
653 } while (t
!= tokens
[tok
].ctx
);
661 rofffindarg(const char *name
)
665 /* FIXME: use a table, this is slow but ok for now. */
668 for (i
= 0; i
< ROFF_ARGMAX
; i
++)
670 if (0 == strcmp(name
, tokargnames
[i
]))
678 rofffindtok(const char *buf
)
683 for (i
= 0; *buf
&& ! isspace(*buf
) && i
< 3; i
++, buf
++)
691 /* FIXME: use a table, this is slow but ok for now. */
694 for (i
= 0; i
< ROFF_MAX
; i
++)
696 if (0 == strcmp(toknames
[i
], token
))
704 roffispunct(const char *p
)
746 rofffindcallable(const char *name
)
750 if (ROFF_MAX
== (c
= rofffindtok(name
)))
752 assert(c
>= 0 && c
< ROFF_MAX
);
753 return(ROFF_CALLABLE
& tokens
[c
].flags
? c
: ROFF_MAX
);
757 static struct roffnode
*
758 roffnode_new(int tokid
, struct rofftree
*tree
)
762 if (NULL
== (p
= malloc(sizeof(struct roffnode
))))
766 p
->parent
= tree
->last
;
774 roffargok(int tokid
, int argid
)
778 if (NULL
== (c
= tokens
[tokid
].args
))
781 for ( ; ROFF_ARGMAX
!= *c
; c
++)
790 roffnode_free(struct rofftree
*tree
)
797 tree
->last
= tree
->last
->parent
;
803 roffnextopt(const struct rofftree
*tree
, int tok
,
804 char ***in
, char **val
)
813 if (NULL
== (arg
= *argv
))
818 if (ROFF_ARGMAX
== (v
= rofffindarg(arg
+ 1))) {
819 roff_warn(tree
, arg
, "argument-like parameter `%s' to "
820 "`%s'", &arg
[1], toknames
[tok
]);
824 if ( ! roffargok(tok
, v
)) {
825 roff_warn(tree
, arg
, "invalid argument parameter `%s' to "
826 "`%s'", tokargnames
[v
], toknames
[tok
]);
830 if ( ! (ROFF_VALUE
& tokenargs
[v
]))
836 roff_err(tree
, arg
, "empty value of `%s' for `%s'",
837 tokargnames
[v
], toknames
[tok
]);
847 roff_Dd(ROFFCALL_ARGS
)
850 if (ROFF_BODY
& tree
->state
) {
851 assert( ! (ROFF_PRELUDE
& tree
->state
));
852 assert(ROFF_PRELUDE_Dd
& tree
->state
);
853 return(roff_text(tok
, tree
, argv
, type
));
856 assert(ROFF_PRELUDE
& tree
->state
);
857 assert( ! (ROFF_BODY
& tree
->state
));
859 if (ROFF_PRELUDE_Dd
& tree
->state
) {
860 roff_err(tree
, *argv
, "repeated `Dd' in prelude");
862 } else if (ROFF_PRELUDE_Dt
& tree
->state
) {
863 roff_err(tree
, *argv
, "out-of-order `Dd' in prelude");
867 /* TODO: parse date. */
869 assert(NULL
== tree
->last
);
870 tree
->state
|= ROFF_PRELUDE_Dd
;
878 roff_Dt(ROFFCALL_ARGS
)
881 if (ROFF_BODY
& tree
->state
) {
882 assert( ! (ROFF_PRELUDE
& tree
->state
));
883 assert(ROFF_PRELUDE_Dt
& tree
->state
);
884 return(roff_text(tok
, tree
, argv
, type
));
887 assert(ROFF_PRELUDE
& tree
->state
);
888 assert( ! (ROFF_BODY
& tree
->state
));
890 if ( ! (ROFF_PRELUDE_Dd
& tree
->state
)) {
891 roff_err(tree
, *argv
, "out-of-order `Dt' in prelude");
893 } else if (ROFF_PRELUDE_Dt
& tree
->state
) {
894 roff_err(tree
, *argv
, "repeated `Dt' in prelude");
898 /* TODO: parse date. */
900 assert(NULL
== tree
->last
);
901 tree
->state
|= ROFF_PRELUDE_Dt
;
909 roff_Os(ROFFCALL_ARGS
)
912 if (ROFF_EXIT
== type
) {
914 return((*tree
->cb
.rofftail
)(tree
->arg
));
915 } else if (ROFF_BODY
& tree
->state
) {
916 assert( ! (ROFF_PRELUDE
& tree
->state
));
917 assert(ROFF_PRELUDE_Os
& tree
->state
);
918 return(roff_text(tok
, tree
, argv
, type
));
921 assert(ROFF_PRELUDE
& tree
->state
);
922 if ( ! (ROFF_PRELUDE_Dt
& tree
->state
) ||
923 ! (ROFF_PRELUDE_Dd
& tree
->state
)) {
924 roff_err(tree
, *argv
, "out-of-order `Os' in prelude");
928 /* TODO: extract OS. */
930 tree
->state
|= ROFF_PRELUDE_Os
;
931 tree
->state
&= ~ROFF_PRELUDE
;
932 tree
->state
|= ROFF_BODY
;
934 assert(NULL
== tree
->last
);
936 if (NULL
== roffnode_new(tok
, tree
))
939 return((*tree
->cb
.roffhead
)(tree
->arg
));
945 roff_layout(ROFFCALL_ARGS
)
947 int i
, c
, argcp
[ROFF_MAXARG
];
948 char *v
, *argvp
[ROFF_MAXARG
];
950 if (ROFF_PRELUDE
& tree
->state
) {
951 roff_err(tree
, *argv
, "`%s' disallowed in prelude",
956 if (ROFF_EXIT
== type
) {
958 return((*tree
->cb
.roffblkout
)(tree
->arg
, tok
));
964 while (-1 != (c
= roffnextopt(tree
, tok
, &argv
, &v
))) {
965 if (ROFF_ARGMAX
== c
)
974 argcp
[i
] = ROFF_ARGMAX
;
977 if (NULL
== roffnode_new(tok
, tree
))
980 if ( ! (*tree
->cb
.roffblkin
)(tree
->arg
, tok
, argcp
, argvp
))
986 if ( ! (*tree
->cb
.roffin
)(tree
->arg
, tok
, argcp
, argvp
))
989 if ( ! (ROFF_PARSED
& tokens
[tok
].flags
)) {
992 if ( ! (*tree
->cb
.roffdata
)(tree
->arg
, i
, *argv
++))
996 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1001 if (ROFF_MAX
!= (c
= rofffindcallable(*argv
))) {
1002 if (NULL
== tokens
[c
].cb
) {
1003 roff_err(tree
, *argv
, "unsupported "
1008 if ( ! (*tokens
[c
].cb
)(c
, tree
, argv
, ROFF_ENTER
))
1014 if ( ! (*tree
->cb
.roffdata
)(tree
->arg
, i
, *argv
++))
1020 * If we're the first parser (*argv == tree->cur) then purge out
1021 * any additional punctuation, should there be any remaining at
1025 if (ROFF_PARSED
& tokens
[tok
].flags
&& *argv
) {
1031 if ( ! roffispunct(argv
[--i
]))
1032 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1034 while (i
>= 0 && roffispunct(argv
[i
]))
1042 if ( ! (*tree
->cb
.roffdata
)(tree
->arg
, 0, argv
[i
++]))
1046 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1052 roff_text(ROFFCALL_ARGS
)
1054 int i
, j
, first
, c
, argcp
[ROFF_MAXARG
];
1055 char *v
, *argvp
[ROFF_MAXARG
];
1057 if (ROFF_PRELUDE
& tree
->state
) {
1058 roff_err(tree
, *argv
, "`%s' disallowed in prelude",
1063 /* FIXME: breaks if passed from roff_layout. */
1064 first
= *argv
== tree
->cur
;
1069 while (-1 != (c
= roffnextopt(tree
, tok
, &argv
, &v
))) {
1070 if (ROFF_ARGMAX
== c
)
1079 argcp
[i
] = ROFF_ARGMAX
;
1082 if ( ! (*tree
->cb
.roffin
)(tree
->arg
, tok
, argcp
, argvp
))
1085 if ( ! (ROFF_PARSED
& tokens
[tok
].flags
)) {
1088 if ( ! (*tree
->cb
.roffdata
)(tree
->arg
, i
, *argv
++))
1092 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1097 if (ROFF_MAX
== (c
= rofffindcallable(*argv
))) {
1099 * If all that remains is roff punctuation, then
1100 * close out our scope and return.
1102 if (roffispunct(*argv
)) {
1103 for (j
= 0; argv
[j
]; j
++)
1104 if ( ! roffispunct(argv
[j
]))
1106 if (NULL
== argv
[j
])
1111 if ( ! (*tree
->cb
.roffdata
)(tree
->arg
, i
, *argv
++))
1119 * A sub-command has been found. Execute it and
1120 * discontinue parsing for arguments.
1123 if (NULL
== tokens
[c
].cb
) {
1124 roff_err(tree
, *argv
, "unsupported macro `%s'",
1129 if ( ! (*tokens
[c
].cb
)(c
, tree
, argv
, ROFF_ENTER
))
1135 if ( ! (*tree
->cb
.roffout
)(tree
->arg
, tok
))
1139 * If we're the first parser (*argv == tree->cur) then purge out
1140 * any additional punctuation, should there be any remaining at
1144 if (first
&& *argv
) {
1150 if ( ! roffispunct(argv
[--i
]))
1153 while (i
>= 0 && roffispunct(argv
[i
]))
1161 if ( ! (*tree
->cb
.roffdata
)(tree
->arg
, 0, argv
[i
++]))
1171 roff_comment(ROFFCALL_ARGS
)
1180 roff_close(ROFFCALL_ARGS
)
1189 roff_special(ROFFCALL_ARGS
)
1192 return((*tree
->cb
.roffspecial
)(tree
->arg
, tok
));
1197 roff_warn(const struct rofftree
*tree
, const char *pos
, char *fmt
, ...)
1203 (void)vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
1206 (*tree
->cb
.roffmsg
)(tree
->arg
,
1207 ROFF_WARN
, tree
->cur
, pos
, buf
);
1212 roff_err(const struct rofftree
*tree
, const char *pos
, char *fmt
, ...)
1218 (void)vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
1221 (*tree
->cb
.roffmsg
)(tree
->arg
,
1222 ROFF_ERROR
, tree
->cur
, pos
, buf
);