]>
git.cameronkatri.com Git - mandoc.git/blob - roff.c
1 /* $Id: roff.c,v 1.26 2008/12/01 09:25:18 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_Ns(ROFFCALL_ARGS
);
112 static int roff_layout(ROFFCALL_ARGS
);
113 static int roff_text(ROFFCALL_ARGS
);
114 static int roff_comment(ROFFCALL_ARGS
);
115 static int roff_close(ROFFCALL_ARGS
);
117 static struct roffnode
*roffnode_new(int, struct rofftree
*);
118 static void roffnode_free(struct rofftree
*);
120 static void roff_warn(const struct rofftree
*,
121 const char *, char *, ...);
122 static void roff_err(const struct rofftree
*,
123 const char *, char *, ...);
125 static int roffscan(int, const int *);
126 static int rofffindtok(const char *);
127 static int rofffindarg(const char *);
128 static int rofffindcallable(const char *);
129 static int roffargs(const struct rofftree
*,
130 int, char *, char **);
131 static int roffargok(int, int);
132 static int roffnextopt(const struct rofftree
*,
133 int, char ***, char **);
134 static int roffparse(struct rofftree
*, char *);
135 static int textparse(const struct rofftree
*, char *);
138 static const int roffarg_An
[] = { ROFF_Split
, ROFF_Nosplit
,
140 static const int roffarg_Bd
[] = { ROFF_Ragged
, ROFF_Unfilled
,
141 ROFF_Literal
, ROFF_File
, ROFF_Offset
, ROFF_Filled
,
142 ROFF_Compact
, ROFF_ARGMAX
};
143 static const int roffarg_Bk
[] = { ROFF_Words
, ROFF_ARGMAX
};
144 static const int roffarg_Ex
[] = { ROFF_Std
, ROFF_ARGMAX
};
145 static const int roffarg_Rv
[] = { ROFF_Std
, ROFF_ARGMAX
};
146 static const int roffarg_Bl
[] = { ROFF_Bullet
, ROFF_Dash
,
147 ROFF_Hyphen
, ROFF_Item
, ROFF_Enum
, ROFF_Tag
, ROFF_Diag
,
148 ROFF_Hang
, ROFF_Ohang
, ROFF_Inset
, ROFF_Column
, ROFF_Offset
,
149 ROFF_Width
, ROFF_Compact
, ROFF_ARGMAX
};
150 static const int roffarg_St
[] = {
151 ROFF_p1003_1_88
, ROFF_p1003_1_90
, ROFF_p1003_1_96
,
152 ROFF_p1003_1_2001
, ROFF_p1003_1_2004
, ROFF_p1003_1
,
153 ROFF_p1003_1b
, ROFF_p1003_1b_93
, ROFF_p1003_1c_95
,
154 ROFF_p1003_1g_2000
, ROFF_p1003_2_92
, ROFF_p1387_2_95
,
155 ROFF_p1003_2
, ROFF_p1387_2
, ROFF_isoC_90
, ROFF_isoC_amd1
,
156 ROFF_isoC_tcor1
, ROFF_isoC_tcor2
, ROFF_isoC_99
, ROFF_ansiC
,
157 ROFF_ansiC_89
, ROFF_ansiC_99
, ROFF_ieee754
, ROFF_iso8802_3
,
158 ROFF_xpg3
, ROFF_xpg4
, ROFF_xpg4_2
, ROFF_xpg4_3
, ROFF_xbd5
,
159 ROFF_xcu5
, ROFF_xsh5
, ROFF_xns5
, ROFF_xns5_2d2_0
,
160 ROFF_xcurses4_2
, ROFF_susv2
, ROFF_susv3
, ROFF_svid4
,
163 static const int roffchild_Bl
[] = { ROFF_It
, ROFF_El
, ROFF_MAX
};
164 static const int roffchild_Fo
[] = { ROFF_Fa
, ROFF_Fc
, ROFF_MAX
};
165 static const int roffchild_Oo
[] = { ROFF_Op
, ROFF_Oc
, ROFF_MAX
};
166 static const int roffchild_Rs
[] = { ROFF_Re
, ROFF__A
, ROFF__B
,
167 ROFF__D
, ROFF__I
, ROFF__J
, ROFF__N
, ROFF__O
, ROFF__P
,
168 ROFF__R
, ROFF__T
, ROFF__V
, ROFF_MAX
};
170 static const int roffparent_El
[] = { ROFF_Bl
, ROFF_It
, ROFF_MAX
};
171 static const int roffparent_Fc
[] = { ROFF_Fo
, ROFF_Fa
, ROFF_MAX
};
172 static const int roffparent_Oc
[] = { ROFF_Oo
, ROFF_Oc
, ROFF_MAX
};
173 static const int roffparent_It
[] = { ROFF_Bl
, ROFF_It
, ROFF_MAX
};
174 static const int roffparent_Re
[] = { ROFF_Rs
, ROFF_MAX
};
176 /* Table of all known tokens. */
177 static const struct rofftok tokens
[ROFF_MAX
] = {
178 {roff_comment
, NULL
, NULL
, NULL
, 0, ROFF_COMMENT
, 0 }, /* \" */
179 { roff_Dd
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Dd */
180 { roff_Dt
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Dt */
181 { roff_Os
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Os */
182 { roff_layout
, NULL
, NULL
, NULL
, ROFF_Sh
, ROFF_LAYOUT
, 0 }, /* Sh */
183 { roff_layout
, NULL
, NULL
, NULL
, ROFF_Ss
, ROFF_LAYOUT
, 0 }, /* Ss */
184 { roff_text
, NULL
, NULL
, NULL
, ROFF_Pp
, ROFF_TEXT
, 0 }, /* Pp */
185 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* D1 */
186 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Dl */
187 { roff_layout
, roffarg_Bd
, NULL
, NULL
, 0, ROFF_LAYOUT
, 0 }, /* Bd */
188 { roff_close
, NULL
, NULL
, NULL
, ROFF_Bd
, ROFF_LAYOUT
, 0 }, /* Ed */
189 { roff_layout
, roffarg_Bl
, NULL
, roffchild_Bl
, 0, ROFF_LAYOUT
, 0 }, /* Bl */
190 { roff_close
, NULL
, roffparent_El
, NULL
, ROFF_Bl
, ROFF_LAYOUT
, 0 }, /* El */
191 { roff_layout
, NULL
, roffparent_It
, NULL
, ROFF_It
, ROFF_LAYOUT
, ROFF_PARSED
| ROFF_SHALLOW
}, /* It */
192 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ad */ /* FIXME */
193 { roff_text
, roffarg_An
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* An */
194 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ar */
195 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Cd */ /* XXX man.4 only */
196 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Cm */
197 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Dv */ /* XXX needs arg */
198 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Er */ /* XXX needs arg */
199 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ev */ /* XXX needs arg */
200 { roff_text
, roffarg_Ex
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Ex */
201 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Fa */ /* XXX needs arg */
202 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Fd */
203 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Fl */
204 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Fn */ /* XXX needs arg */ /* FIXME */
205 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Ft */
206 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ic */ /* XXX needs arg */
207 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* In */
208 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Li */
209 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Nd */
210 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Nm */ /* FIXME */
211 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Op */
212 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Ot */ /* XXX deprecated */
213 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Pa */
214 { roff_text
, roffarg_Rv
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Rv */
215 { roff_text
, roffarg_St
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* St */
216 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Va */
217 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Vt */ /* XXX needs arg */
218 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Xr */ /* XXX needs arg */
219 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* %A */
220 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* %B */
221 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* %D */
222 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* %I */
223 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* %J */
224 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* %N */
225 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* %O */
226 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* %P */
227 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* %R */
228 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* %T */
229 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* %V */
230 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ac */
231 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ao */
232 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Aq */
233 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* At */ /* XXX at most 2 args */
234 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Bc */
235 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Bf */ /* FIXME */
236 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Bo */
237 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Bq */
238 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Bsx */
239 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Bx */
240 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_SPECIAL
, 0 }, /* Db */
241 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Dc */
242 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Do */
243 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Dq */
244 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ec */
245 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Ef */ /* FIXME */
246 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Em */ /* XXX needs arg */
247 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Eo */
248 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Fx */
249 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Ms */
250 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* No */
251 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ns */
252 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Nx */
253 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Ox */
254 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Pc */
255 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Pf */
256 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_LAYOUT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Po */
257 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Pq */
258 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Qc */
259 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ql */
260 { roff_layout
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Qo */
261 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Qq */
262 { roff_close
, NULL
, roffparent_Re
, NULL
, ROFF_Rs
, ROFF_LAYOUT
, 0 }, /* Re */
263 { roff_layout
, NULL
, NULL
, roffchild_Rs
, 0, ROFF_LAYOUT
, 0 }, /* Rs */
264 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Sc */
265 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* So */
266 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Sq */
267 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_SPECIAL
, 0 }, /* Sm */
268 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Sx */
269 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Sy */
270 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Tn */
271 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Ux */
272 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Xc */
273 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Xo */
274 { roff_layout
, NULL
, NULL
, roffchild_Fo
, 0, ROFF_LAYOUT
, 0 }, /* Fo */
275 { roff_close
, NULL
, roffparent_Fc
, NULL
, ROFF_Fo
, ROFF_LAYOUT
, 0 }, /* Fc */
276 { roff_layout
, NULL
, NULL
, roffchild_Oo
, 0, ROFF_LAYOUT
, 0 }, /* Oo */
277 { roff_close
, NULL
, roffparent_Oc
, NULL
, ROFF_Oo
, ROFF_LAYOUT
, 0 }, /* Oc */
278 { roff_layout
, roffarg_Bk
, NULL
, NULL
, 0, ROFF_LAYOUT
, 0 }, /* Bk */
279 { roff_close
, NULL
, NULL
, NULL
, ROFF_Bk
, ROFF_LAYOUT
, 0 }, /* Ek */
282 /* Table of all known token arguments. */
283 static const int tokenargs
[ROFF_ARGMAX
] = {
285 0, ROFF_VALUE
, ROFF_VALUE
, 0,
301 const char *const toknamesp
[ROFF_MAX
] = {
302 "\\\"", "Dd", "Dt", "Os",
303 "Sh", "Ss", "Pp", "D1",
304 "Dl", "Bd", "Ed", "Bl",
305 "El", "It", "Ad", "An",
306 "Ar", "Cd", "Cm", "Dv",
307 "Er", "Ev", "Ex", "Fa",
308 "Fd", "Fl", "Fn", "Ft",
309 "Ic", "In", "Li", "Nd",
310 "Nm", "Op", "Ot", "Pa",
311 "Rv", "St", "Va", "Vt",
313 "Xr", "\%A", "\%B", "\%D",
315 "\%I", "\%J", "\%N", "\%O",
317 "\%P", "\%R", "\%T", "\%V",
318 "Ac", "Ao", "Aq", "At",
319 "Bc", "Bf", "Bo", "Bq",
320 "Bsx", "Bx", "Db", "Dc",
321 "Do", "Dq", "Ec", "Ef",
322 "Em", "Eo", "Fx", "Ms",
323 "No", "Ns", "Nx", "Ox",
324 "Pc", "Pf", "Po", "Pq",
325 "Qc", "Ql", "Qo", "Qq",
326 "Re", "Rs", "Sc", "So",
327 "Sq", "Sm", "Sx", "Sy",
328 "Tn", "Ux", "Xc", "Xo",
329 "Fo", "Fc", "Oo", "Oc",
333 const char *const tokargnamesp
[ROFF_ARGMAX
] = {
334 "split", "nosplit", "ragged",
335 "unfilled", "literal", "file",
336 "offset", "bullet", "dash",
337 "hyphen", "item", "enum",
338 "tag", "diag", "hang",
339 "ohang", "inset", "column",
340 "width", "compact", "std",
341 "p1003.1-88", "p1003.1-90", "p1003.1-96",
342 "p1003.1-2001", "p1003.1-2004", "p1003.1",
343 "p1003.1b", "p1003.1b-93", "p1003.1c-95",
344 "p1003.1g-2000", "p1003.2-92", "p1387.2-95",
345 "p1003.2", "p1387.2", "isoC-90",
346 "isoC-amd1", "isoC-tcor1", "isoC-tcor2",
347 "isoC-99", "ansiC", "ansiC-89",
348 "ansiC-99", "ieee754", "iso8802-3",
349 "xpg3", "xpg4", "xpg4.2",
350 "xpg4.3", "xbd5", "xcu5",
351 "xsh5", "xns5", "xns5.2d2.0",
352 "xcurses4.2", "susv2", "susv3",
353 "svid4", "filled", "words",
356 const char *const *toknames
= toknamesp
;
357 const char *const *tokargnames
= tokargnamesp
;
361 roff_free(struct rofftree
*tree
, int flush
)
373 if (ROFF_PRELUDE
& tree
->state
) {
374 roff_warn(tree
, NULL
, "prelude never finished");
378 for (n
= tree
->last
; n
->parent
; n
= n
->parent
) {
379 if (0 != tokens
[n
->tok
].ctx
)
381 roff_warn(tree
, NULL
, "closing explicit scope `%s'",
388 if ( ! (*tokens
[t
].cb
)(t
, tree
, NULL
, ROFF_EXIT
))
401 return(error
? 0 : 1);
406 roff_alloc(const struct roffcb
*cb
, void *args
)
408 struct rofftree
*tree
;
413 if (NULL
== (tree
= calloc(1, sizeof(struct rofftree
))))
416 tree
->state
= ROFF_PRELUDE
;
419 (void)memcpy(&tree
->cb
, cb
, sizeof(struct roffcb
));
426 roff_engine(struct rofftree
*tree
, char *buf
)
433 roff_warn(tree
, buf
, "blank line");
435 } else if ('.' != *buf
)
436 return(textparse(tree
, buf
));
438 return(roffparse(tree
, buf
));
443 textparse(const struct rofftree
*tree
, char *buf
)
446 return((*tree
->cb
.roffdata
)(tree
->arg
, 1, buf
));
451 roffargs(const struct rofftree
*tree
,
452 int tok
, char *buf
, char **argv
)
457 assert(tok
>= 0 && tok
< ROFF_MAX
);
463 for (i
= 0; *buf
&& i
< ROFF_MAXARG
; i
++) {
466 while (*buf
&& '\"' != *buf
)
469 roff_err(tree
, argv
[i
], "unclosed "
477 while (*buf
&& ! isspace(*buf
))
483 while (*buf
&& isspace(*buf
))
488 if (ROFF_MAXARG
== i
&& *buf
) {
489 roff_err(tree
, p
, "too many arguments for `%s'", toknames
500 roffscan(int tok
, const int *tokv
)
506 for ( ; ROFF_MAX
!= *tokv
; tokv
++)
515 roffparse(struct rofftree
*tree
, char *buf
)
519 char *argv
[ROFF_MAXARG
];
522 if (0 != *buf
&& 0 != *(buf
+ 1) && 0 != *(buf
+ 2))
523 if (0 == strncmp(buf
, ".\\\"", 3))
526 if (ROFF_MAX
== (tok
= rofffindtok(buf
+ 1))) {
527 roff_err(tree
, buf
+ 1, "bogus line macro");
529 } else if (NULL
== tokens
[tok
].cb
) {
530 roff_err(tree
, buf
+ 1, "unsupported macro `%s'",
535 assert(ROFF___
!= tok
);
536 if ( ! roffargs(tree
, tok
, buf
, argv
))
539 argvp
= (char **)argv
;
542 * Prelude macros break some assumptions, so branch now.
545 if (ROFF_PRELUDE
& tree
->state
) {
546 assert(NULL
== tree
->last
);
547 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
551 assert(ROFF_BODY
& tree
->state
);
554 * First check that our possible parents and parent's possible
555 * children are satisfied.
558 if ( ! roffscan(tree
->last
->tok
, tokens
[tok
].parents
)) {
559 roff_err(tree
, *argvp
, "`%s' has invalid parent `%s'",
561 toknames
[tree
->last
->tok
]);
565 if ( ! roffscan(tok
, tokens
[tree
->last
->tok
].children
)) {
566 roff_err(tree
, *argvp
, "`%s' is invalid child of `%s'",
568 toknames
[tree
->last
->tok
]);
573 * Branch if we're not a layout token.
576 if (ROFF_LAYOUT
!= tokens
[tok
].type
)
577 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
580 * Check our scope rules.
583 if (0 == tokens
[tok
].ctx
)
584 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
587 * First consider implicit-end tags, like as follows:
590 * In this, we want to close the scope of the NAME section. If
591 * there's an intermediary implicit-end tag, such as
595 * then it must be closed as well.
598 if (tok
== tokens
[tok
].ctx
) {
600 * First search up to the point where we must close.
601 * If one doesn't exist, then we can open a new scope.
604 for (n
= tree
->last
; n
; n
= n
->parent
) {
605 assert(0 == tokens
[n
->tok
].ctx
||
606 n
->tok
== tokens
[n
->tok
].ctx
);
609 if (ROFF_SHALLOW
& tokens
[tok
].flags
) {
616 * Create a new scope, as no previous one exists to
621 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
624 * Close out all intermediary scoped blocks, then hang
625 * the current scope from our predecessor's parent.
630 if ( ! (*tokens
[t
].cb
)(t
, tree
, NULL
, ROFF_EXIT
))
634 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
638 * Now consider explicit-end tags, where we want to close back
639 * to a specific tag. Example:
643 * In this, the `El' tag closes out the scope of `Bl'.
647 assert(tok
!= tokens
[tok
].ctx
&& 0 != tokens
[tok
].ctx
);
652 if ( ! (*tokens
[t
].cb
)(t
, tree
, NULL
, ROFF_EXIT
))
654 } while (t
!= tokens
[tok
].ctx
);
662 rofffindarg(const char *name
)
666 /* FIXME: use a table, this is slow but ok for now. */
669 for (i
= 0; i
< ROFF_ARGMAX
; i
++)
671 if (0 == strcmp(name
, tokargnames
[i
]))
679 rofffindtok(const char *buf
)
684 for (i
= 0; *buf
&& ! isspace(*buf
) && i
< 3; i
++, buf
++)
692 /* FIXME: use a table, this is slow but ok for now. */
695 for (i
= 0; i
< ROFF_MAX
; i
++)
697 if (0 == strcmp(toknames
[i
], token
))
705 roffispunct(const char *p
)
747 rofffindcallable(const char *name
)
751 if (ROFF_MAX
== (c
= rofffindtok(name
)))
753 assert(c
>= 0 && c
< ROFF_MAX
);
754 return(ROFF_CALLABLE
& tokens
[c
].flags
? c
: ROFF_MAX
);
758 static struct roffnode
*
759 roffnode_new(int tokid
, struct rofftree
*tree
)
763 if (NULL
== (p
= malloc(sizeof(struct roffnode
))))
767 p
->parent
= tree
->last
;
775 roffargok(int tokid
, int argid
)
779 if (NULL
== (c
= tokens
[tokid
].args
))
782 for ( ; ROFF_ARGMAX
!= *c
; c
++)
791 roffnode_free(struct rofftree
*tree
)
798 tree
->last
= tree
->last
->parent
;
804 roffnextopt(const struct rofftree
*tree
, int tok
,
805 char ***in
, char **val
)
814 if (NULL
== (arg
= *argv
))
819 if (ROFF_ARGMAX
== (v
= rofffindarg(arg
+ 1))) {
820 roff_warn(tree
, arg
, "argument-like parameter `%s' to "
821 "`%s'", &arg
[1], toknames
[tok
]);
825 if ( ! roffargok(tok
, v
)) {
826 roff_warn(tree
, arg
, "invalid argument parameter `%s' to "
827 "`%s'", tokargnames
[v
], toknames
[tok
]);
831 if ( ! (ROFF_VALUE
& tokenargs
[v
]))
837 roff_err(tree
, arg
, "empty value of `%s' for `%s'",
838 tokargnames
[v
], toknames
[tok
]);
848 roff_Dd(ROFFCALL_ARGS
)
851 if (ROFF_BODY
& tree
->state
) {
852 assert( ! (ROFF_PRELUDE
& tree
->state
));
853 assert(ROFF_PRELUDE_Dd
& tree
->state
);
854 return(roff_text(tok
, tree
, argv
, type
));
857 assert(ROFF_PRELUDE
& tree
->state
);
858 assert( ! (ROFF_BODY
& tree
->state
));
860 if (ROFF_PRELUDE_Dd
& tree
->state
) {
861 roff_err(tree
, *argv
, "repeated `Dd' in prelude");
863 } else if (ROFF_PRELUDE_Dt
& tree
->state
) {
864 roff_err(tree
, *argv
, "out-of-order `Dd' in prelude");
868 /* TODO: parse date. */
870 assert(NULL
== tree
->last
);
871 tree
->state
|= ROFF_PRELUDE_Dd
;
879 roff_Dt(ROFFCALL_ARGS
)
882 if (ROFF_BODY
& tree
->state
) {
883 assert( ! (ROFF_PRELUDE
& tree
->state
));
884 assert(ROFF_PRELUDE_Dt
& tree
->state
);
885 return(roff_text(tok
, tree
, argv
, type
));
888 assert(ROFF_PRELUDE
& tree
->state
);
889 assert( ! (ROFF_BODY
& tree
->state
));
891 if ( ! (ROFF_PRELUDE_Dd
& tree
->state
)) {
892 roff_err(tree
, *argv
, "out-of-order `Dt' in prelude");
894 } else if (ROFF_PRELUDE_Dt
& tree
->state
) {
895 roff_err(tree
, *argv
, "repeated `Dt' in prelude");
899 /* TODO: parse date. */
901 assert(NULL
== tree
->last
);
902 tree
->state
|= ROFF_PRELUDE_Dt
;
910 roff_Os(ROFFCALL_ARGS
)
913 if (ROFF_EXIT
== type
) {
915 return((*tree
->cb
.rofftail
)(tree
->arg
));
916 } else if (ROFF_BODY
& tree
->state
) {
917 assert( ! (ROFF_PRELUDE
& tree
->state
));
918 assert(ROFF_PRELUDE_Os
& tree
->state
);
919 return(roff_text(tok
, tree
, argv
, type
));
922 assert(ROFF_PRELUDE
& tree
->state
);
923 if ( ! (ROFF_PRELUDE_Dt
& tree
->state
) ||
924 ! (ROFF_PRELUDE_Dd
& tree
->state
)) {
925 roff_err(tree
, *argv
, "out-of-order `Os' in prelude");
929 /* TODO: extract OS. */
931 tree
->state
|= ROFF_PRELUDE_Os
;
932 tree
->state
&= ~ROFF_PRELUDE
;
933 tree
->state
|= ROFF_BODY
;
935 assert(NULL
== tree
->last
);
937 if (NULL
== roffnode_new(tok
, tree
))
940 return((*tree
->cb
.roffhead
)(tree
->arg
));
946 roff_layout(ROFFCALL_ARGS
)
948 int i
, c
, argcp
[ROFF_MAXARG
];
949 char *v
, *argvp
[ROFF_MAXARG
];
951 if (ROFF_PRELUDE
& tree
->state
) {
952 roff_err(tree
, *argv
, "`%s' disallowed in prelude",
957 if (ROFF_EXIT
== type
) {
959 return((*tree
->cb
.roffblkout
)(tree
->arg
, tok
));
965 while (-1 != (c
= roffnextopt(tree
, tok
, &argv
, &v
))) {
966 if (ROFF_ARGMAX
== c
)
975 argcp
[i
] = ROFF_ARGMAX
;
978 if (NULL
== roffnode_new(tok
, tree
))
981 if ( ! (*tree
->cb
.roffblkin
)(tree
->arg
, tok
, argcp
, argvp
))
987 if ( ! (*tree
->cb
.roffin
)(tree
->arg
, tok
, argcp
, argvp
))
990 if ( ! (ROFF_PARSED
& tokens
[tok
].flags
)) {
993 if ( ! (*tree
->cb
.roffdata
)(tree
->arg
, i
, *argv
++))
997 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1002 if (ROFF_MAX
== (c
= rofffindcallable(*argv
))) {
1004 if ( ! (*tree
->cb
.roffdata
)
1005 (tree
->arg
, i
, *argv
++))
1011 if (NULL
== tokens
[c
].cb
) {
1012 roff_err(tree
, *argv
, "unsupported macro `%s'",
1017 if ( ! (*tokens
[c
].cb
)(c
, tree
, argv
, ROFF_ENTER
))
1024 * If we're the first parser (*argv == tree->cur) then purge out
1025 * any additional punctuation, should there be any remaining at
1029 if ( ! (ROFF_PARSED
& tokens
[tok
].flags
&& *argv
))
1030 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1037 if ( ! roffispunct(argv
[--i
]))
1038 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1040 while (i
>= 0 && roffispunct(argv
[i
]))
1048 if ( ! (*tree
->cb
.roffdata
)(tree
->arg
, 0, argv
[i
++]))
1051 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1057 roff_text(ROFFCALL_ARGS
)
1059 int i
, j
, first
, c
, argcp
[ROFF_MAXARG
];
1060 char *v
, *argvp
[ROFF_MAXARG
];
1062 if (ROFF_PRELUDE
& tree
->state
) {
1063 roff_err(tree
, *argv
, "`%s' disallowed in prelude",
1068 /* FIXME: breaks if passed from roff_layout. */
1069 first
= *argv
== tree
->cur
;
1074 while (-1 != (c
= roffnextopt(tree
, tok
, &argv
, &v
))) {
1075 if (ROFF_ARGMAX
== c
)
1084 argcp
[i
] = ROFF_ARGMAX
;
1087 if ( ! (*tree
->cb
.roffin
)(tree
->arg
, tok
, argcp
, argvp
))
1090 if ( ! (ROFF_PARSED
& tokens
[tok
].flags
)) {
1093 if ( ! (*tree
->cb
.roffdata
)(tree
->arg
, i
, *argv
++))
1097 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1102 if (ROFF_MAX
== (c
= rofffindcallable(*argv
))) {
1104 * If all that remains is roff punctuation, then
1105 * close out our scope and return.
1107 if (roffispunct(*argv
)) {
1108 for (j
= 0; argv
[j
]; j
++)
1109 if ( ! roffispunct(argv
[j
]))
1111 if (NULL
== argv
[j
])
1116 if ( ! (*tree
->cb
.roffdata
)
1117 (tree
->arg
, i
, *argv
++))
1125 * A sub-command has been found. Execute it and
1126 * discontinue parsing for arguments.
1129 if (NULL
== tokens
[c
].cb
) {
1130 roff_err(tree
, *argv
, "unsupported macro `%s'",
1135 if ( ! (*tokens
[c
].cb
)(c
, tree
, argv
, ROFF_ENTER
))
1141 if ( ! (*tree
->cb
.roffout
)(tree
->arg
, tok
))
1145 * If we're the first parser (*argv == tree->cur) then purge out
1146 * any additional punctuation, should there be any remaining at
1150 if ( ! (first
&& *argv
))
1158 if ( ! roffispunct(argv
[--i
]))
1161 while (i
>= 0 && roffispunct(argv
[i
]))
1169 if ( ! (*tree
->cb
.roffdata
)(tree
->arg
, 0, argv
[i
++]))
1178 roff_comment(ROFFCALL_ARGS
)
1187 roff_close(ROFFCALL_ARGS
)
1197 roff_Ns(ROFFCALL_ARGS
)
1203 if (ROFF_MAX
!= (c
= rofffindcallable(*argv
))) {
1204 if (NULL
== tokens
[c
].cb
) {
1205 roff_err(tree
, *argv
, "unsupported macro `%s'",
1209 if ( ! (*tree
->cb
.roffspecial
)(tree
->arg
, tok
))
1211 if ( ! (*tokens
[c
].cb
)(c
, tree
, argv
, ROFF_ENTER
))
1215 } else if ( ! (*tree
->cb
.roffdata
)(tree
->arg
, 0, *argv
++))
1219 if (ROFF_MAX
== (c
= rofffindcallable(*argv
))) {
1221 if ( ! (*tree
->cb
.roffdata
)
1222 (tree
->arg
, 1, *argv
++))
1226 if (NULL
== tokens
[c
].cb
) {
1227 roff_err(tree
, *argv
, "unsupported macro `%s'",
1231 if ( ! (*tokens
[c
].cb
)(c
, tree
, argv
, ROFF_ENTER
))
1243 roff_warn(const struct rofftree
*tree
, const char *pos
, char *fmt
, ...)
1249 (void)vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
1252 (*tree
->cb
.roffmsg
)(tree
->arg
,
1253 ROFF_WARN
, tree
->cur
, pos
, buf
);
1258 roff_err(const struct rofftree
*tree
, const char *pos
, char *fmt
, ...)
1264 (void)vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
1267 (*tree
->cb
.roffmsg
)(tree
->arg
,
1268 ROFF_ERROR
, tree
->cur
, pos
, buf
);