]>
git.cameronkatri.com Git - mandoc.git/blob - roff.c
1 /* $Id: roff.c,v 1.29 2008/12/01 16:14: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 /* TODO: warn if Pp occurs before/after Sh etc. (see mdoc.samples). */
33 /* TODO: warn about "X section only" macros. */
35 /* TODO: warn about empty lists. */
37 /* TODO: (warn) some sections need specific elements. */
39 /* TODO: (warn) NAME section has particular order. */
41 /* TODO: unify empty-content tags a la <br />. */
43 /* TODO: macros with a set number of arguments? */
45 #define ROFF_MAXARG 32
59 #define ROFFCALL_ARGS \
60 int tok, struct rofftree *tree, \
61 char *argv[], enum roffd type
66 int (*cb
)(ROFFCALL_ARGS
); /* Callback. */
67 const int *args
; /* Args (or NULL). */
71 enum rofftype type
; /* Type of macro. */
73 #define ROFF_PARSED (1 << 0) /* "Parsed". */
74 #define ROFF_CALLABLE (1 << 1) /* "Callable". */
75 #define ROFF_SHALLOW (1 << 2) /* Nesting block. */
76 #define ROFF_LSCOPE (1 << 3)
81 #define ROFF_VALUE (1 << 0) /* Has a value. */
85 int tok
; /* Token id. */
86 struct roffnode
*parent
; /* Parent (or NULL). */
90 struct roffnode
*last
; /* Last parsed node. */
93 time_t date
; /* `Dd' results. */
94 char os
[64]; /* `Os' results. */
95 char title
[64]; /* `Dt' results. */
96 char section
[64]; /* `Dt' results. */
97 char volume
[64]; /* `Dt' results. */
100 #define ROFF_PRELUDE (1 << 1) /* In roff prelude. */
101 #define ROFF_PRELUDE_Os (1 << 2) /* `Os' is parsed. */
102 #define ROFF_PRELUDE_Dt (1 << 3) /* `Dt' is parsed. */
103 #define ROFF_PRELUDE_Dd (1 << 4) /* `Dd' is parsed. */
104 #define ROFF_BODY (1 << 5) /* In roff body. */
110 static int roff_Dd(ROFFCALL_ARGS
);
111 static int roff_Dt(ROFFCALL_ARGS
);
112 static int roff_Os(ROFFCALL_ARGS
);
114 static int roff_layout(ROFFCALL_ARGS
);
115 static int roff_text(ROFFCALL_ARGS
);
116 static int roff_noop(ROFFCALL_ARGS
);
117 static int roff_depr(ROFFCALL_ARGS
);
119 static struct roffnode
*roffnode_new(int, struct rofftree
*);
120 static void roffnode_free(struct rofftree
*);
122 static void roff_warn(const struct rofftree
*,
123 const char *, char *, ...);
124 static void roff_err(const struct rofftree
*,
125 const char *, char *, ...);
127 static int roffscan(int, const int *);
128 static int rofffindtok(const char *);
129 static int rofffindarg(const char *);
130 static int rofffindcallable(const char *);
131 static int roffargs(const struct rofftree
*,
132 int, char *, char **);
133 static int roffargok(int, int);
134 static int roffnextopt(const struct rofftree
*,
135 int, char ***, char **);
136 static int roffparseopts(struct rofftree
*, int,
137 char ***, int *, char **);
138 static int roffparse(struct rofftree
*, char *);
139 static int textparse(const struct rofftree
*, char *);
142 static const int roffarg_An
[] = { ROFF_Split
, ROFF_Nosplit
,
144 static const int roffarg_Bd
[] = { ROFF_Ragged
, ROFF_Unfilled
,
145 ROFF_Literal
, ROFF_File
, ROFF_Offset
, ROFF_Filled
,
146 ROFF_Compact
, ROFF_ARGMAX
};
147 static const int roffarg_Bk
[] = { ROFF_Words
, ROFF_ARGMAX
};
148 static const int roffarg_Ex
[] = { ROFF_Std
, ROFF_ARGMAX
};
149 static const int roffarg_Rv
[] = { ROFF_Std
, ROFF_ARGMAX
};
150 static const int roffarg_Bl
[] = { ROFF_Bullet
, ROFF_Dash
,
151 ROFF_Hyphen
, ROFF_Item
, ROFF_Enum
, ROFF_Tag
, ROFF_Diag
,
152 ROFF_Hang
, ROFF_Ohang
, ROFF_Inset
, ROFF_Column
, ROFF_Offset
,
153 ROFF_Width
, ROFF_Compact
, ROFF_ARGMAX
};
154 static const int roffarg_St
[] = {
155 ROFF_p1003_1_88
, ROFF_p1003_1_90
, ROFF_p1003_1_96
,
156 ROFF_p1003_1_2001
, ROFF_p1003_1_2004
, ROFF_p1003_1
,
157 ROFF_p1003_1b
, ROFF_p1003_1b_93
, ROFF_p1003_1c_95
,
158 ROFF_p1003_1g_2000
, ROFF_p1003_2_92
, ROFF_p1387_2_95
,
159 ROFF_p1003_2
, ROFF_p1387_2
, ROFF_isoC_90
, ROFF_isoC_amd1
,
160 ROFF_isoC_tcor1
, ROFF_isoC_tcor2
, ROFF_isoC_99
, ROFF_ansiC
,
161 ROFF_ansiC_89
, ROFF_ansiC_99
, ROFF_ieee754
, ROFF_iso8802_3
,
162 ROFF_xpg3
, ROFF_xpg4
, ROFF_xpg4_2
, ROFF_xpg4_3
, ROFF_xbd5
,
163 ROFF_xcu5
, ROFF_xsh5
, ROFF_xns5
, ROFF_xns5_2d2_0
,
164 ROFF_xcurses4_2
, ROFF_susv2
, ROFF_susv3
, ROFF_svid4
,
167 static const int roffchild_Bl
[] = { ROFF_It
, ROFF_El
, ROFF_MAX
};
168 static const int roffchild_Fo
[] = { ROFF_Fa
, ROFF_Fc
, ROFF_MAX
};
169 static const int roffchild_Rs
[] = { ROFF_Re
, ROFF__A
, ROFF__B
,
170 ROFF__D
, ROFF__I
, ROFF__J
, ROFF__N
, ROFF__O
, ROFF__P
,
171 ROFF__R
, ROFF__T
, ROFF__V
, ROFF_MAX
};
173 static const int roffparent_El
[] = { ROFF_Bl
, ROFF_It
, ROFF_MAX
};
174 static const int roffparent_Fc
[] = { ROFF_Fo
, ROFF_Fa
, ROFF_MAX
};
175 static const int roffparent_Oc
[] = { ROFF_Oo
, ROFF_MAX
};
176 static const int roffparent_It
[] = { ROFF_Bl
, ROFF_It
, ROFF_MAX
};
177 static const int roffparent_Re
[] = { ROFF_Rs
, ROFF_MAX
};
179 /* Table of all known tokens. */
180 static const struct rofftok tokens
[ROFF_MAX
] = {
181 { roff_noop
, NULL
, NULL
, NULL
, 0, ROFF_COMMENT
, 0 }, /* \" */
182 { roff_Dd
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Dd */
183 { roff_Dt
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Dt */
184 { roff_Os
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Os */
185 { roff_layout
, NULL
, NULL
, NULL
, ROFF_Sh
, ROFF_LAYOUT
, 0 }, /* Sh */
186 { roff_layout
, NULL
, NULL
, NULL
, ROFF_Ss
, ROFF_LAYOUT
, 0 }, /* Ss */
187 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Pp */ /* XXX 0 args */
188 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_LSCOPE
}, /* D1 */
189 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_LSCOPE
}, /* Dl */
190 { roff_layout
, roffarg_Bd
, NULL
, NULL
, 0, ROFF_LAYOUT
, 0 }, /* Bd */
191 { roff_noop
, NULL
, NULL
, NULL
, ROFF_Bd
, ROFF_LAYOUT
, 0 }, /* Ed */
192 { roff_layout
, roffarg_Bl
, NULL
, roffchild_Bl
, 0, ROFF_LAYOUT
, 0 }, /* Bl */
193 { roff_noop
, NULL
, roffparent_El
, NULL
, ROFF_Bl
, ROFF_LAYOUT
, 0 }, /* El */
194 { roff_layout
, NULL
, roffparent_It
, NULL
, ROFF_It
, ROFF_LAYOUT
, ROFF_PARSED
| ROFF_SHALLOW
}, /* It */
195 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ad */ /* FIXME */
196 { roff_text
, roffarg_An
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* An */
197 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ar */
198 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Cd */ /* XXX man.4 only */
199 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Cm */
200 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Dv */ /* XXX needs arg */
201 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Er */ /* XXX needs arg */
202 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ev */ /* XXX needs arg */
203 { roff_text
, roffarg_Ex
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Ex */
204 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Fa */ /* XXX needs arg */
205 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Fd */
206 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Fl */
207 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Fn */ /* XXX needs arg */ /* FIXME */
208 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Ft */
209 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ic */ /* XXX needs arg */
210 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* In */
211 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Li */
212 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Nd */
213 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Nm */ /* FIXME */
214 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
| ROFF_LSCOPE
}, /* Op */
215 { roff_depr
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Ot */
216 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Pa */
217 { roff_text
, roffarg_Rv
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Rv */
218 { roff_text
, roffarg_St
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* St */
219 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Va */
220 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Vt */ /* XXX needs arg */
221 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Xr */ /* XXX needs arg */
222 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* %A */
223 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* %B */
224 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* %D */
225 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* %I */
226 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* %J */
227 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* %N */
228 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* %O */
229 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* %P */
230 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* %R */
231 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* %T */
232 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* %V */
233 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ac */
234 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ao */
235 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
| ROFF_LSCOPE
}, /* Aq */
236 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* At */ /* XXX at most 2 args */
237 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Bc */
238 { roff_layout
, NULL
, NULL
, NULL
, 0, ROFF_LAYOUT
, 0 }, /* Bf */ /* FIXME */
239 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Bo */
240 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
| ROFF_LSCOPE
}, /* Bq */
241 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Bsx */
242 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Bx */
243 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_SPECIAL
, 0 }, /* Db */
244 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Dc */
245 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Do */
246 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
| ROFF_LSCOPE
}, /* Dq */
247 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ec */
248 { roff_noop
, NULL
, NULL
, NULL
, ROFF_Bf
, ROFF_LAYOUT
, 0 }, /* Ef */
249 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Em */ /* XXX needs arg */
250 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Eo */
251 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Fx */
252 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Ms */
253 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* No */
254 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ns */
255 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Nx */
256 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Ox */
257 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Pc */
258 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Pf */
259 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Po */
260 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
| ROFF_LSCOPE
}, /* Pq */
261 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Qc */
262 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ql */
263 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Qo */
264 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
| ROFF_LSCOPE
}, /* Qq */
265 { roff_noop
, NULL
, roffparent_Re
, NULL
, ROFF_Rs
, ROFF_LAYOUT
, 0 }, /* Re */
266 { roff_layout
, NULL
, NULL
, roffchild_Rs
, 0, ROFF_LAYOUT
, 0 }, /* Rs */
267 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Sc */
268 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* So */
269 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
| ROFF_LSCOPE
}, /* Sq */
270 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_SPECIAL
, 0 }, /* Sm */
271 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Sx */
272 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Sy */
273 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Tn */
274 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Ux */
275 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Xc */
276 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Xo */
277 { roff_layout
, NULL
, NULL
, roffchild_Fo
, 0, ROFF_LAYOUT
, 0 }, /* Fo */
278 { roff_noop
, NULL
, roffparent_Fc
, NULL
, ROFF_Fo
, ROFF_LAYOUT
, 0 }, /* Fc */
279 { roff_layout
, NULL
, NULL
, NULL
, 0, ROFF_LAYOUT
, 0 }, /* Oo */
280 { roff_noop
, NULL
, roffparent_Oc
, NULL
, ROFF_Oo
, ROFF_LAYOUT
, 0 }, /* Oc */
281 { roff_layout
, roffarg_Bk
, NULL
, NULL
, 0, ROFF_LAYOUT
, 0 }, /* Bk */
282 { roff_noop
, NULL
, NULL
, NULL
, ROFF_Bk
, ROFF_LAYOUT
, 0 }, /* Ek */
283 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Bt */
284 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Hf */
285 { roff_depr
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Fr */
286 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Ud */
289 /* Table of all known token arguments. */
290 static const int tokenargs
[ROFF_ARGMAX
] = {
292 0, ROFF_VALUE
, ROFF_VALUE
, 0,
308 const char *const toknamesp
[ROFF_MAX
] = {
309 "\\\"", "Dd", "Dt", "Os",
310 "Sh", "Ss", "Pp", "D1",
311 "Dl", "Bd", "Ed", "Bl",
312 "El", "It", "Ad", "An",
313 "Ar", "Cd", "Cm", "Dv",
314 "Er", "Ev", "Ex", "Fa",
315 "Fd", "Fl", "Fn", "Ft",
316 "Ic", "In", "Li", "Nd",
317 "Nm", "Op", "Ot", "Pa",
318 "Rv", "St", "Va", "Vt",
320 "Xr", "\%A", "\%B", "\%D",
322 "\%I", "\%J", "\%N", "\%O",
324 "\%P", "\%R", "\%T", "\%V",
325 "Ac", "Ao", "Aq", "At",
326 "Bc", "Bf", "Bo", "Bq",
327 "Bsx", "Bx", "Db", "Dc",
328 "Do", "Dq", "Ec", "Ef",
329 "Em", "Eo", "Fx", "Ms",
330 "No", "Ns", "Nx", "Ox",
331 "Pc", "Pf", "Po", "Pq",
332 "Qc", "Ql", "Qo", "Qq",
333 "Re", "Rs", "Sc", "So",
334 "Sq", "Sm", "Sx", "Sy",
335 "Tn", "Ux", "Xc", "Xo",
336 "Fo", "Fc", "Oo", "Oc",
337 "Bk", "Ek", "Bt", "Hf",
341 const char *const tokargnamesp
[ROFF_ARGMAX
] = {
342 "split", "nosplit", "ragged",
343 "unfilled", "literal", "file",
344 "offset", "bullet", "dash",
345 "hyphen", "item", "enum",
346 "tag", "diag", "hang",
347 "ohang", "inset", "column",
348 "width", "compact", "std",
349 "p1003.1-88", "p1003.1-90", "p1003.1-96",
350 "p1003.1-2001", "p1003.1-2004", "p1003.1",
351 "p1003.1b", "p1003.1b-93", "p1003.1c-95",
352 "p1003.1g-2000", "p1003.2-92", "p1387.2-95",
353 "p1003.2", "p1387.2", "isoC-90",
354 "isoC-amd1", "isoC-tcor1", "isoC-tcor2",
355 "isoC-99", "ansiC", "ansiC-89",
356 "ansiC-99", "ieee754", "iso8802-3",
357 "xpg3", "xpg4", "xpg4.2",
358 "xpg4.3", "xbd5", "xcu5",
359 "xsh5", "xns5", "xns5.2d2.0",
360 "xcurses4.2", "susv2", "susv3",
361 "svid4", "filled", "words",
364 const char *const *toknames
= toknamesp
;
365 const char *const *tokargnames
= tokargnamesp
;
369 roff_free(struct rofftree
*tree
, int flush
)
381 if (ROFF_PRELUDE
& tree
->state
) {
382 roff_err(tree
, NULL
, "prelude never finished");
386 for (n
= tree
->last
; n
; n
= n
->parent
) {
387 if (0 != tokens
[n
->tok
].ctx
)
389 roff_err(tree
, NULL
, "closing explicit scope `%s'",
396 if ( ! (*tokens
[t
].cb
)(t
, tree
, NULL
, ROFF_EXIT
))
409 return(error
? 0 : 1);
414 roff_alloc(const struct roffcb
*cb
, void *args
)
416 struct rofftree
*tree
;
421 if (NULL
== (tree
= calloc(1, sizeof(struct rofftree
))))
424 tree
->state
= ROFF_PRELUDE
;
427 (void)memcpy(&tree
->cb
, cb
, sizeof(struct roffcb
));
434 roff_engine(struct rofftree
*tree
, char *buf
)
441 roff_err(tree
, buf
, "blank line");
443 } else if ('.' != *buf
)
444 return(textparse(tree
, buf
));
446 return(roffparse(tree
, buf
));
451 textparse(const struct rofftree
*tree
, char *buf
)
454 return((*tree
->cb
.roffdata
)(tree
->arg
, 1, buf
));
459 roffargs(const struct rofftree
*tree
,
460 int tok
, char *buf
, char **argv
)
465 assert(tok
>= 0 && tok
< ROFF_MAX
);
471 for (i
= 0; *buf
&& i
< ROFF_MAXARG
; i
++) {
474 while (*buf
&& '\"' != *buf
)
477 roff_err(tree
, argv
[i
], "unclosed "
485 while (*buf
&& ! isspace(*buf
))
491 while (*buf
&& isspace(*buf
))
496 if (ROFF_MAXARG
== i
&& *buf
) {
497 roff_err(tree
, p
, "too many arguments for `%s'", toknames
508 roffscan(int tok
, const int *tokv
)
514 for ( ; ROFF_MAX
!= *tokv
; tokv
++)
523 roffparse(struct rofftree
*tree
, char *buf
)
527 char *argv
[ROFF_MAXARG
];
530 if (0 != *buf
&& 0 != *(buf
+ 1) && 0 != *(buf
+ 2))
531 if (0 == strncmp(buf
, ".\\\"", 3))
534 if (ROFF_MAX
== (tok
= rofffindtok(buf
+ 1))) {
535 roff_err(tree
, buf
+ 1, "bogus line macro");
537 } else if (NULL
== tokens
[tok
].cb
) {
538 roff_err(tree
, buf
+ 1, "unsupported macro `%s'",
543 assert(ROFF___
!= tok
);
544 if ( ! roffargs(tree
, tok
, buf
, argv
))
547 argvp
= (char **)argv
;
550 * Prelude macros break some assumptions, so branch now.
553 if (ROFF_PRELUDE
& tree
->state
) {
554 assert(NULL
== tree
->last
);
555 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
558 assert(ROFF_BODY
& tree
->state
);
561 * First check that our possible parents and parent's possible
562 * children are satisfied.
565 if (tree
->last
&& ! roffscan
566 (tree
->last
->tok
, tokens
[tok
].parents
)) {
567 roff_err(tree
, *argvp
, "`%s' has invalid parent `%s'",
569 toknames
[tree
->last
->tok
]);
573 if (tree
->last
&& ! roffscan
574 (tok
, tokens
[tree
->last
->tok
].children
)) {
575 roff_err(tree
, *argvp
, "`%s' is invalid child of `%s'",
577 toknames
[tree
->last
->tok
]);
582 * Branch if we're not a layout token.
585 if (ROFF_LAYOUT
!= tokens
[tok
].type
)
586 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
587 if (0 == tokens
[tok
].ctx
)
588 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
591 * First consider implicit-end tags, like as follows:
594 * In this, we want to close the scope of the NAME section. If
595 * there's an intermediary implicit-end tag, such as
599 * then it must be closed as well.
602 if (tok
== tokens
[tok
].ctx
) {
604 * First search up to the point where we must close.
605 * If one doesn't exist, then we can open a new scope.
608 for (n
= tree
->last
; n
; n
= n
->parent
) {
609 assert(0 == tokens
[n
->tok
].ctx
||
610 n
->tok
== tokens
[n
->tok
].ctx
);
613 if (ROFF_SHALLOW
& tokens
[tok
].flags
) {
617 if (tokens
[n
->tok
].ctx
== n
->tok
)
619 roff_err(tree
, *argv
, "`%s' breaks `%s' scope",
620 toknames
[tok
], toknames
[n
->tok
]);
625 * Create a new scope, as no previous one exists to
630 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
633 * Close out all intermediary scoped blocks, then hang
634 * the current scope from our predecessor's parent.
639 if ( ! (*tokens
[t
].cb
)(t
, tree
, NULL
, ROFF_EXIT
))
643 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
647 * Now consider explicit-end tags, where we want to close back
648 * to a specific tag. Example:
652 * In this, the `El' tag closes out the scope of `Bl'.
655 assert(tok
!= tokens
[tok
].ctx
&& 0 != tokens
[tok
].ctx
);
657 for (n
= tree
->last
; n
; n
= n
->parent
)
658 if (n
->tok
!= tokens
[tok
].ctx
) {
659 if (n
->tok
== tokens
[n
->tok
].ctx
)
661 roff_err(tree
, *argv
, "`%s' breaks `%s' scope",
662 toknames
[tok
], toknames
[n
->tok
]);
669 roff_err(tree
, *argv
, "`%s' has no starting tag `%s'",
671 toknames
[tokens
[tok
].ctx
]);
678 if ( ! (*tokens
[t
].cb
)(t
, tree
, NULL
, ROFF_EXIT
))
680 } while (t
!= tokens
[tok
].ctx
);
687 rofffindarg(const char *name
)
691 /* FIXME: use a table, this is slow but ok for now. */
694 for (i
= 0; i
< ROFF_ARGMAX
; i
++)
696 if (0 == strcmp(name
, tokargnames
[i
]))
704 rofffindtok(const char *buf
)
709 for (i
= 0; *buf
&& ! isspace(*buf
) && i
< 3; i
++, buf
++)
717 /* FIXME: use a table, this is slow but ok for now. */
720 for (i
= 0; i
< ROFF_MAX
; i
++)
722 if (0 == strcmp(toknames
[i
], token
))
730 roffispunct(const char *p
)
772 rofffindcallable(const char *name
)
776 if (ROFF_MAX
== (c
= rofffindtok(name
)))
778 assert(c
>= 0 && c
< ROFF_MAX
);
779 return(ROFF_CALLABLE
& tokens
[c
].flags
? c
: ROFF_MAX
);
783 static struct roffnode
*
784 roffnode_new(int tokid
, struct rofftree
*tree
)
788 if (NULL
== (p
= malloc(sizeof(struct roffnode
))))
792 p
->parent
= tree
->last
;
800 roffargok(int tokid
, int argid
)
804 if (NULL
== (c
= tokens
[tokid
].args
))
807 for ( ; ROFF_ARGMAX
!= *c
; c
++)
816 roffnode_free(struct rofftree
*tree
)
823 tree
->last
= tree
->last
->parent
;
829 roffnextopt(const struct rofftree
*tree
, int tok
,
830 char ***in
, char **val
)
839 if (NULL
== (arg
= *argv
))
844 if (ROFF_ARGMAX
== (v
= rofffindarg(arg
+ 1))) {
845 roff_warn(tree
, arg
, "argument-like parameter `%s' to "
846 "`%s'", &arg
[1], toknames
[tok
]);
850 if ( ! roffargok(tok
, v
)) {
851 roff_warn(tree
, arg
, "invalid argument parameter `%s' to "
852 "`%s'", tokargnames
[v
], toknames
[tok
]);
856 if ( ! (ROFF_VALUE
& tokenargs
[v
]))
862 roff_err(tree
, arg
, "empty value of `%s' for `%s'",
863 tokargnames
[v
], toknames
[tok
]);
872 roffparseopts(struct rofftree
*tree
, int tok
,
873 char ***args
, int *argc
, char **argv
)
880 while (-1 != (c
= roffnextopt(tree
, tok
, args
, &v
))) {
881 if (ROFF_ARGMAX
== c
)
890 argc
[i
] = ROFF_ARGMAX
;
898 roff_Dd(ROFFCALL_ARGS
)
901 if (ROFF_BODY
& tree
->state
) {
902 assert( ! (ROFF_PRELUDE
& tree
->state
));
903 assert(ROFF_PRELUDE_Dd
& tree
->state
);
904 return(roff_text(tok
, tree
, argv
, type
));
907 assert(ROFF_PRELUDE
& tree
->state
);
908 assert( ! (ROFF_BODY
& tree
->state
));
910 if (ROFF_PRELUDE_Dd
& tree
->state
) {
911 roff_err(tree
, *argv
, "repeated `Dd' in prelude");
913 } else if (ROFF_PRELUDE_Dt
& tree
->state
) {
914 roff_err(tree
, *argv
, "out-of-order `Dd' in prelude");
918 /* TODO: parse date. */
920 assert(NULL
== tree
->last
);
921 tree
->state
|= ROFF_PRELUDE_Dd
;
929 roff_Dt(ROFFCALL_ARGS
)
932 if (ROFF_BODY
& tree
->state
) {
933 assert( ! (ROFF_PRELUDE
& tree
->state
));
934 assert(ROFF_PRELUDE_Dt
& tree
->state
);
935 return(roff_text(tok
, tree
, argv
, type
));
938 assert(ROFF_PRELUDE
& tree
->state
);
939 assert( ! (ROFF_BODY
& tree
->state
));
941 if ( ! (ROFF_PRELUDE_Dd
& tree
->state
)) {
942 roff_err(tree
, *argv
, "out-of-order `Dt' in prelude");
944 } else if (ROFF_PRELUDE_Dt
& tree
->state
) {
945 roff_err(tree
, *argv
, "repeated `Dt' in prelude");
949 /* TODO: parse date. */
951 assert(NULL
== tree
->last
);
952 tree
->state
|= ROFF_PRELUDE_Dt
;
960 roff_Os(ROFFCALL_ARGS
)
963 if (ROFF_EXIT
== type
) {
964 return((*tree
->cb
.rofftail
)(tree
->arg
));
965 } else if (ROFF_BODY
& tree
->state
) {
966 assert( ! (ROFF_PRELUDE
& tree
->state
));
967 assert(ROFF_PRELUDE_Os
& tree
->state
);
968 return(roff_text(tok
, tree
, argv
, type
));
971 assert(ROFF_PRELUDE
& tree
->state
);
972 if ( ! (ROFF_PRELUDE_Dt
& tree
->state
) ||
973 ! (ROFF_PRELUDE_Dd
& tree
->state
)) {
974 roff_err(tree
, *argv
, "out-of-order `Os' in prelude");
978 /* TODO: extract OS. */
980 tree
->state
|= ROFF_PRELUDE_Os
;
981 tree
->state
&= ~ROFF_PRELUDE
;
982 tree
->state
|= ROFF_BODY
;
984 assert(NULL
== tree
->last
);
986 return((*tree
->cb
.roffhead
)(tree
->arg
));
992 roff_layout(ROFFCALL_ARGS
)
994 int i
, c
, argcp
[ROFF_MAXARG
];
995 char *argvp
[ROFF_MAXARG
];
997 if (ROFF_PRELUDE
& tree
->state
) {
998 roff_err(tree
, *argv
, "bad `%s' in prelude",
1001 } else if (ROFF_EXIT
== type
) {
1002 roffnode_free(tree
);
1003 return((*tree
->cb
.roffblkout
)(tree
->arg
, tok
));
1006 assert( ! (ROFF_CALLABLE
& tokens
[tok
].flags
));
1010 if ( ! roffparseopts(tree
, tok
, &argv
, argcp
, argvp
))
1012 if (NULL
== roffnode_new(tok
, tree
))
1016 * Layouts have two parts: the layout body and header. The
1017 * layout header is the trailing text of the line macro, while
1018 * the layout body is everything following until termination.
1021 if ( ! (*tree
->cb
.roffblkin
)(tree
->arg
, tok
, argcp
, argvp
))
1025 if ( ! (*tree
->cb
.roffin
)(tree
->arg
, tok
, argcp
, argvp
))
1029 * If there are no parsable parts, then write remaining tokens
1030 * into the layout header and exit.
1033 if ( ! (ROFF_PARSED
& tokens
[tok
].flags
)) {
1036 if ( ! (*tree
->cb
.roffdata
)(tree
->arg
, i
, *argv
++))
1040 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1044 * Parsable elements may be in the header (or be the header, for
1045 * that matter). Follow the regular parsing rules for these.
1050 if (ROFF_MAX
== (c
= rofffindcallable(*argv
))) {
1052 if ( ! (*tree
->cb
.roffdata
)
1053 (tree
->arg
, i
, *argv
++))
1059 if (NULL
== tokens
[c
].cb
) {
1060 roff_err(tree
, *argv
, "unsupported macro `%s'",
1065 if ( ! (*tokens
[c
].cb
)(c
, tree
, argv
, ROFF_ENTER
))
1072 * If there's trailing punctuation in the header, then write it
1073 * out now. Here we mimic the behaviour of a line-dominant text
1078 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1081 * Expensive. Scan to the end of line then work backwards until
1082 * a token isn't punctuation.
1090 if ( ! roffispunct(argv
[--i
]))
1091 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1093 while (i
>= 0 && roffispunct(argv
[i
]))
1101 if ( ! (*tree
->cb
.roffdata
)(tree
->arg
, 0, argv
[i
++]))
1104 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1110 roff_text(ROFFCALL_ARGS
)
1112 int i
, j
, first
, c
, argcp
[ROFF_MAXARG
];
1113 char *argvp
[ROFF_MAXARG
];
1115 if (ROFF_PRELUDE
& tree
->state
) {
1116 roff_err(tree
, *argv
, "`%s' disallowed in prelude",
1121 first
= (*argv
== tree
->cur
);
1124 if ( ! roffparseopts(tree
, tok
, &argv
, argcp
, argvp
))
1126 if ( ! (*tree
->cb
.roffin
)(tree
->arg
, tok
, argcp
, argvp
))
1129 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1131 if ( ! (ROFF_PARSED
& tokens
[tok
].flags
)) {
1134 if ( ! (*tree
->cb
.roffdata
)(tree
->arg
, i
, *argv
++))
1138 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1142 * Deal with punctuation. Ugly. Work ahead until we encounter
1143 * terminating punctuation. If we encounter it and all
1144 * subsequent tokens are punctuation, then stop processing (the
1145 * line-dominant macro will print these tokens after closure).
1150 if (ROFF_MAX
== (c
= rofffindcallable(*argv
))) {
1151 if ( ! roffispunct(*argv
)) {
1152 if ( ! (*tree
->cb
.roffdata
)
1153 (tree
->arg
, i
, *argv
++))
1159 /* See if only punctuation remains. */
1162 for (j
= 0; argv
[j
]; j
++)
1163 if ( ! roffispunct(argv
[j
]))
1167 if ( ! (*tree
->cb
.roffdata
)
1168 (tree
->arg
, 0, *argv
++))
1173 /* Only punctuation remains. */
1175 if ( ! (*tree
->cb
.roffout
)(tree
->arg
, tok
))
1181 * A sub-command has been found. Execute it and
1182 * discontinue parsing for arguments. If we're
1183 * line-scoped, then close out after it returns; if we
1184 * aren't, then close out before.
1187 if (NULL
== tokens
[c
].cb
) {
1188 roff_err(tree
, *argv
, "unsupported macro `%s'",
1193 if ( ! (ROFF_LSCOPE
& tokens
[tok
].flags
))
1194 if ( ! (*tree
->cb
.roffout
)(tree
->arg
, tok
))
1197 if ( ! (*tokens
[c
].cb
)(c
, tree
, argv
, ROFF_ENTER
))
1200 if (ROFF_LSCOPE
& tokens
[tok
].flags
)
1201 if ( ! (*tree
->cb
.roffout
)(tree
->arg
, tok
))
1208 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1213 * We're the line-dominant macro. Check if there's remaining
1214 * punctuation. If there is, then flush it out before exiting.
1222 if ( ! roffispunct(argv
[--i
]))
1225 while (i
>= 0 && roffispunct(argv
[i
]))
1233 if ( ! (*tree
->cb
.roffdata
)(tree
->arg
, 0, argv
[i
++]))
1242 roff_noop(ROFFCALL_ARGS
)
1251 roff_depr(ROFFCALL_ARGS
)
1254 roff_err(tree
, *argv
, "`%s' is deprecated", toknames
[tok
]);
1260 roff_warn(const struct rofftree
*tree
, const char *pos
, char *fmt
, ...)
1266 (void)vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
1269 (*tree
->cb
.roffmsg
)(tree
->arg
,
1270 ROFF_WARN
, tree
->cur
, pos
, buf
);
1275 roff_err(const struct rofftree
*tree
, const char *pos
, char *fmt
, ...)
1281 (void)vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
1284 (*tree
->cb
.roffmsg
)(tree
->arg
,
1285 ROFF_ERROR
, tree
->cur
, pos
, buf
);