]>
git.cameronkatri.com Git - mandoc.git/blob - roff.c
1 /* $Id: roff.c,v 1.15 2008/11/28 15:25:49 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: ; : } ) (etc.) after text macros? */
39 /* FIXME: NAME section needs specific elements. */
41 #define ROFF_MAXARG 32
55 #define ROFFCALL_ARGS \
56 int tok, struct rofftree *tree, \
57 const 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_QUOTES (1 << 2) /* Quoted args. */
72 #define ROFF_SHALLOW (1 << 3) /* Nesting block. */
77 #define ROFF_VALUE (1 << 0) /* Has a value. */
81 int tok
; /* Token id. */
82 struct roffnode
*parent
; /* Parent (or NULL). */
86 struct roffnode
*last
; /* Last parsed node. */
89 time_t date
; /* `Dd' results. */
90 char os
[64]; /* `Os' results. */
91 char title
[64]; /* `Dt' results. */
92 char section
[64]; /* `Dt' results. */
93 char volume
[64]; /* `Dt' results. */
96 #define ROFF_PRELUDE (1 << 1) /* In roff prelude. */
97 #define ROFF_PRELUDE_Os (1 << 2) /* `Os' is parsed. */
98 #define ROFF_PRELUDE_Dt (1 << 3) /* `Dt' is parsed. */
99 #define ROFF_PRELUDE_Dd (1 << 4) /* `Dd' is parsed. */
100 #define ROFF_BODY (1 << 5) /* In roff body. */
106 static int roff_Dd(ROFFCALL_ARGS
);
107 static int roff_Dt(ROFFCALL_ARGS
);
108 static int roff_Os(ROFFCALL_ARGS
);
110 static int roff_layout(ROFFCALL_ARGS
);
111 static int roff_text(ROFFCALL_ARGS
);
112 static int roff_comment(ROFFCALL_ARGS
);
113 static int roff_close(ROFFCALL_ARGS
);
114 static int roff_special(ROFFCALL_ARGS
);
116 static struct roffnode
*roffnode_new(int, struct rofftree
*);
117 static void roffnode_free(struct rofftree
*);
119 static void roff_warn(const struct rofftree
*,
120 const char *, char *, ...);
121 static void roff_err(const struct rofftree
*,
122 const char *, char *, ...);
124 static int roffscan(int, const int *);
125 static int rofffindtok(const char *);
126 static int rofffindarg(const char *);
127 static int rofffindcallable(const char *);
128 static int roffargs(const struct rofftree
*,
129 int, char *, char **);
130 static int roffargok(int, int);
131 static int roffnextopt(const struct rofftree
*,
132 int, const char ***, char **);
133 static int roffparse(struct rofftree
*, char *, size_t);
134 static int textparse(const struct rofftree
*,
135 const char *, size_t);
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
, ROFF_QUOTES
}, /* Os */
182 { roff_layout
, NULL
, NULL
, NULL
, ROFF_Sh
, ROFF_LAYOUT
, ROFF_PARSED
}, /* Sh */
183 { roff_layout
, NULL
, NULL
, NULL
, ROFF_Ss
, ROFF_LAYOUT
, ROFF_PARSED
}, /* 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_SHALLOW
}, /* It */
192 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ad */
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
, ROFF_QUOTES
}, /* 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
, ROFF_QUOTES
}, /* 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 { NULL
, 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 {roff_special
, 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 { roff_text
, 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 {roff_special
, 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",
312 "Xr", "\%A", "\%B", "\%D",
313 "\%I", "\%J", "\%N", "\%O",
314 "\%P", "\%R", "\%T", "\%V",
315 "Ac", "Ao", "Aq", "At",
316 "Bc", "Bf", "Bo", "Bq",
317 "Bsx", "Bx", "Db", "Dc",
318 "Do", "Dq", "Ec", "Ef",
319 "Em", "Eo", "Fx", "Ms",
320 "No", "Ns", "Nx", "Ox",
321 "Pc", "Pf", "Po", "Pq",
322 "Qc", "Ql", "Qo", "Qq",
323 "Re", "Rs", "Sc", "So",
324 "Sq", "Sm", "Sx", "Sy",
325 "Tn", "Ux", "Xc", "Xo",
326 "Fo", "Fc", "Oo", "Oc",
330 const char *const tokargnamesp
[ROFF_ARGMAX
] = {
331 "split", "nosplit", "ragged",
332 "unfilled", "literal", "file",
333 "offset", "bullet", "dash",
334 "hyphen", "item", "enum",
335 "tag", "diag", "hang",
336 "ohang", "inset", "column",
337 "width", "compact", "std",
338 "p1003.1-88", "p1003.1-90", "p1003.1-96",
339 "p1003.1-2001", "p1003.1-2004", "p1003.1",
340 "p1003.1b", "p1003.1b-93", "p1003.1c-95",
341 "p1003.1g-2000", "p1003.2-92", "p1387.2-95",
342 "p1003.2", "p1387.2", "isoC-90",
343 "isoC-amd1", "isoC-tcor1", "isoC-tcor2",
344 "isoC-99", "ansiC", "ansiC-89",
345 "ansiC-99", "ieee754", "iso8802-3",
346 "xpg3", "xpg4", "xpg4.2",
347 "xpg4.3", "xbd5", "xcu5",
348 "xsh5", "xns5", "xns5.2d2.0",
349 "xcurses4.2", "susv2", "susv3",
350 "svid4", "filled", "words",
353 const char *const *toknames
= toknamesp
;
354 const char *const *tokargnames
= tokargnamesp
;
358 roff_free(struct rofftree
*tree
, int flush
)
365 for (n
= tree
->last
; n
->parent
; n
= n
->parent
)
366 if (tokens
[n
->tok
].ctx
== 0) {
367 roff_warn(tree
, NULL
, "closing explicit scope "
368 "of `%s'", toknames
[n
->tok
]);
372 if (0 == error
&& (ROFF_PRELUDE
& tree
->state
)) {
373 roff_warn(tree
, NULL
, "prelude never finished");
381 return(error
? 0 : 1);
386 roff_alloc(const struct roffcb
*cb
, void *args
)
388 struct rofftree
*tree
;
390 if (NULL
== (tree
= calloc(1, sizeof(struct rofftree
))))
393 tree
->state
= ROFF_PRELUDE
;
396 (void)memcpy(&tree
->cb
, cb
, sizeof(struct roffcb
));
403 roff_engine(struct rofftree
*tree
, char *buf
, size_t sz
)
409 roff_warn(tree
, buf
, "blank line");
411 } else if ('.' != *buf
)
412 return(textparse(tree
, buf
, sz
));
414 return(roffparse(tree
, buf
, sz
));
419 textparse(const struct rofftree
*tree
, const char *buf
, size_t sz
)
428 roffargs(const struct rofftree
*tree
,
429 int tok
, char *buf
, char **argv
)
434 assert(tok
>= 0 && tok
< ROFF_MAX
);
440 for (i
= 0; *buf
&& i
< ROFF_MAXARG
; i
++) {
443 while (*buf
&& '\"' != *buf
)
446 roff_err(tree
, argv
[i
], "unclosed "
454 while (*buf
&& ! isspace(*buf
))
460 while (*buf
&& isspace(*buf
))
465 if (ROFF_MAXARG
== i
&& *buf
) {
466 roff_err(tree
, p
, "too many arguments for `%s'", toknames
478 roffscan(int tok
, const int *tokv
)
484 for ( ; ROFF_MAX
!= *tokv
; tokv
++)
493 roffparse(struct rofftree
*tree
, char *buf
, size_t sz
)
497 char *argv
[ROFF_MAXARG
];
502 if (ROFF_MAX
== (tok
= rofffindtok(buf
+ 1))) {
503 roff_err(tree
, buf
+ 1, "bogus line macro");
505 } else if (NULL
== tokens
[tok
].cb
) {
506 roff_err(tree
, buf
+ 1, "unsupported macro `%s'",
509 } else if (ROFF_COMMENT
== tokens
[tok
].type
)
512 if ( ! roffargs(tree
, tok
, buf
, argv
))
515 argvp
= (const char **)argv
;
518 * Prelude macros break some assumptions, so branch now.
521 if (ROFF_PRELUDE
& tree
->state
) {
522 assert(NULL
== tree
->last
);
523 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
527 assert(ROFF_BODY
& tree
->state
);
530 * First check that our possible parents and parent's possible
531 * children are satisfied.
534 if ( ! roffscan(tree
->last
->tok
, tokens
[tok
].parents
)) {
535 roff_err(tree
, *argvp
, "`%s' has invalid parent `%s'",
537 toknames
[tree
->last
->tok
]);
541 if ( ! roffscan(tok
, tokens
[tree
->last
->tok
].children
)) {
542 roff_err(tree
, *argvp
, "`%s' is invalid child `%s'",
543 toknames
[tree
->last
->tok
],
549 * Branch if we're not a layout token.
552 if (ROFF_LAYOUT
!= tokens
[tok
].type
)
553 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
556 * Check our scope rules.
559 if (0 == tokens
[tok
].ctx
)
560 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
563 * First consider implicit-end tags, like as follows:
566 * In this, we want to close the scope of the NAME section. If
567 * there's an intermediary implicit-end tag, such as
571 * then it must be closed as well.
574 if (tok
== tokens
[tok
].ctx
) {
576 * First search up to the point where we must close.
577 * If one doesn't exist, then we can open a new scope.
580 for (n
= tree
->last
; n
; n
= n
->parent
) {
581 assert(0 == tokens
[n
->tok
].ctx
||
582 n
->tok
== tokens
[n
->tok
].ctx
);
585 if (ROFF_SHALLOW
& tokens
[tok
].flags
) {
592 * Create a new scope, as no previous one exists to
597 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
600 * Close out all intermediary scoped blocks, then hang
601 * the current scope from our predecessor's parent.
606 if ( ! (*tokens
[t
].cb
)(t
, tree
, NULL
, ROFF_EXIT
))
610 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
614 * Now consider explicit-end tags, where we want to close back
615 * to a specific tag. Example:
619 * In this, the `El' tag closes out the scope of `Bl'.
623 assert(tok
!= tokens
[tok
].ctx
&& 0 != tokens
[tok
].ctx
);
628 if ( ! (*tokens
[t
].cb
)(t
, tree
, NULL
, ROFF_EXIT
))
630 } while (t
!= tokens
[tok
].ctx
);
638 rofffindarg(const char *name
)
642 /* FIXME: use a table, this is slow but ok for now. */
645 for (i
= 0; i
< ROFF_ARGMAX
; i
++)
647 if (0 == strcmp(name
, tokargnames
[i
]))
655 rofffindtok(const char *buf
)
660 for (i
= 0; *buf
&& ! isspace(*buf
) && i
< 3; i
++, buf
++)
668 /* FIXME: use a table, this is slow but ok for now. */
671 for (i
= 0; i
< ROFF_MAX
; i
++)
673 if (0 == strcmp(toknames
[i
], token
))
681 rofffindcallable(const char *name
)
685 if (ROFF_MAX
== (c
= rofffindtok(name
)))
687 assert(c
>= 0 && c
< ROFF_MAX
);
688 return(ROFF_CALLABLE
& tokens
[c
].flags
? c
: ROFF_MAX
);
692 static struct roffnode
*
693 roffnode_new(int tokid
, struct rofftree
*tree
)
697 if (NULL
== (p
= malloc(sizeof(struct roffnode
))))
701 p
->parent
= tree
->last
;
709 roffargok(int tokid
, int argid
)
713 if (NULL
== (c
= tokens
[tokid
].args
))
716 for ( ; ROFF_ARGMAX
!= *c
; c
++)
725 roffnode_free(struct rofftree
*tree
)
732 tree
->last
= tree
->last
->parent
;
738 roffnextopt(const struct rofftree
*tree
, int tok
,
739 const char ***in
, char **val
)
741 const char *arg
, **argv
;
748 if (NULL
== (arg
= *argv
))
753 if (ROFF_ARGMAX
== (v
= rofffindarg(arg
+ 1))) {
754 roff_warn(tree
, arg
, "argument-like parameter `%s' to "
755 "`%s'", &arg
[1], toknames
[tok
]);
759 if ( ! roffargok(tok
, v
)) {
760 roff_warn(tree
, arg
, "invalid argument parameter `%s' to "
761 "`%s'", tokargnames
[v
], toknames
[tok
]);
765 if ( ! (ROFF_VALUE
& tokenargs
[v
]))
771 roff_err(tree
, arg
, "empty value of `%s' for `%s'",
772 tokargnames
[v
], toknames
[tok
]);
782 roff_Dd(ROFFCALL_ARGS
)
785 if (ROFF_BODY
& tree
->state
) {
786 assert( ! (ROFF_PRELUDE
& tree
->state
));
787 assert(ROFF_PRELUDE_Dd
& tree
->state
);
788 return(roff_text(tok
, tree
, argv
, type
));
791 assert(ROFF_PRELUDE
& tree
->state
);
792 assert( ! (ROFF_BODY
& tree
->state
));
794 if (ROFF_PRELUDE_Dd
& tree
->state
) {
795 roff_err(tree
, *argv
, "repeated `Dd' in prelude");
797 } else if (ROFF_PRELUDE_Dt
& tree
->state
) {
798 roff_err(tree
, *argv
, "out-of-order `Dd' in prelude");
802 /* TODO: parse date. */
804 assert(NULL
== tree
->last
);
805 tree
->state
|= ROFF_PRELUDE_Dd
;
813 roff_Dt(ROFFCALL_ARGS
)
816 if (ROFF_BODY
& tree
->state
) {
817 assert( ! (ROFF_PRELUDE
& tree
->state
));
818 assert(ROFF_PRELUDE_Dt
& tree
->state
);
819 return(roff_text(tok
, tree
, argv
, type
));
822 assert(ROFF_PRELUDE
& tree
->state
);
823 assert( ! (ROFF_BODY
& tree
->state
));
825 if ( ! (ROFF_PRELUDE_Dd
& tree
->state
)) {
826 roff_err(tree
, *argv
, "out-of-order `Dt' in prelude");
828 } else if (ROFF_PRELUDE_Dt
& tree
->state
) {
829 roff_err(tree
, *argv
, "repeated `Dt' in prelude");
833 /* TODO: parse date. */
835 assert(NULL
== tree
->last
);
836 tree
->state
|= ROFF_PRELUDE_Dt
;
844 roff_Os(ROFFCALL_ARGS
)
847 if (ROFF_EXIT
== type
) {
848 assert(ROFF_PRELUDE_Os
& tree
->state
);
849 return(roff_layout(tok
, tree
, argv
, type
));
850 } else if (ROFF_BODY
& tree
->state
) {
851 assert( ! (ROFF_PRELUDE
& tree
->state
));
852 assert(ROFF_PRELUDE_Os
& tree
->state
);
853 return(roff_text(tok
, tree
, argv
, type
));
856 assert(ROFF_PRELUDE
& tree
->state
);
857 if ( ! (ROFF_PRELUDE_Dt
& tree
->state
) ||
858 ! (ROFF_PRELUDE_Dd
& tree
->state
)) {
859 roff_err(tree
, *argv
, "out-of-order `Os' in prelude");
863 /* TODO: extract OS. */
865 tree
->state
|= ROFF_PRELUDE_Os
;
866 tree
->state
&= ~ROFF_PRELUDE
;
867 tree
->state
|= ROFF_BODY
;
869 assert(NULL
== tree
->last
);
871 return(roff_layout(tok
, tree
, argv
, type
));
877 roff_layout(ROFFCALL_ARGS
)
879 int i
, c
, argcp
[ROFF_MAXARG
];
880 char *v
, *argvp
[ROFF_MAXARG
];
882 if (ROFF_PRELUDE
& tree
->state
) {
883 roff_err(tree
, *argv
, "`%s' disallowed in prelude",
888 if (ROFF_EXIT
== type
) {
890 return((*tree
->cb
.roffblkout
)(tree
->arg
, tok
));
896 while (-1 != (c
= roffnextopt(tree
, tok
, &argv
, &v
))) {
897 if (ROFF_ARGMAX
== c
)
906 argcp
[i
] = ROFF_ARGMAX
;
909 if (NULL
== roffnode_new(tok
, tree
))
912 if ( ! (*tree
->cb
.roffin
)(tree
->arg
, tok
, argcp
, argvp
))
915 if ( ! (ROFF_PARSED
& tokens
[tok
].flags
)) {
917 if ( ! (*tree
->cb
.roffdata
)
918 (tree
->arg
, *argv
++))
922 if ( ! ((*tree
->cb
.roffout
)(tree
->arg
, tok
)))
924 return((*tree
->cb
.roffblkin
)(tree
->arg
, tok
));
928 if (ROFF_MAX
!= (c
= rofffindcallable(*argv
))) {
929 if (NULL
== tokens
[c
].cb
) {
930 roff_err(tree
, *argv
, "unsupported "
935 if ( ! (*tokens
[c
].cb
)(c
, tree
, argv
, ROFF_ENTER
))
940 if ( ! (*tree
->cb
.roffdata
)(tree
->arg
, *argv
++))
944 if ( ! ((*tree
->cb
.roffout
)(tree
->arg
, tok
)))
946 return((*tree
->cb
.roffblkin
)(tree
->arg
, tok
));
952 roff_text(ROFFCALL_ARGS
)
954 int i
, c
, argcp
[ROFF_MAXARG
];
955 char *v
, *argvp
[ROFF_MAXARG
];
957 if (ROFF_PRELUDE
& tree
->state
) {
958 roff_err(tree
, *argv
, "`%s' disallowed in prelude",
966 while (-1 != (c
= roffnextopt(tree
, tok
, &argv
, &v
))) {
967 if (ROFF_ARGMAX
== c
)
976 argcp
[i
] = ROFF_ARGMAX
;
979 if ( ! (*tree
->cb
.roffin
)(tree
->arg
, tok
, argcp
, argvp
))
982 if ( ! (ROFF_PARSED
& tokens
[tok
].flags
)) {
984 if ( ! (*tree
->cb
.roffdata
)(tree
->arg
, *argv
++))
987 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
991 if (ROFF_MAX
!= (c
= rofffindcallable(*argv
))) {
992 if (NULL
== tokens
[c
].cb
) {
993 roff_err(tree
, *argv
, "unsupported "
998 if ( ! (*tokens
[c
].cb
)(c
, tree
,
1003 if ( ! (*tree
->cb
.roffdata
)(tree
->arg
, *argv
++))
1007 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1013 roff_comment(ROFFCALL_ARGS
)
1022 roff_close(ROFFCALL_ARGS
)
1031 roff_special(ROFFCALL_ARGS
)
1034 return((*tree
->cb
.roffspecial
)(tree
->arg
, tok
));
1039 roff_warn(const struct rofftree
*tree
, const char *pos
, char *fmt
, ...)
1045 (void)vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
1048 (*tree
->cb
.roffmsg
)(tree
->arg
,
1049 ROFF_WARN
, tree
->cur
, pos
, buf
);
1054 roff_err(const struct rofftree
*tree
, const char *pos
, char *fmt
, ...)
1060 (void)vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
1063 (*tree
->cb
.roffmsg
)(tree
->arg
,
1064 ROFF_ERROR
, tree
->cur
, pos
, buf
);