]>
git.cameronkatri.com Git - mandoc.git/blob - roff.c
1 /* $Id: roff.c,v 1.30 2008/12/01 21:25:48 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.
19 #include <sys/param.h>
30 #include "libmdocml.h"
33 /* TODO: warn if Pp occurs before/after Sh etc. (see mdoc.samples). */
35 /* TODO: warn about "X section only" macros. */
37 /* TODO: warn about empty lists. */
39 /* TODO: (warn) some sections need specific elements. */
41 /* TODO: (warn) NAME section has particular order. */
43 /* TODO: unify empty-content tags a la <br />. */
45 /* TODO: macros with a set number of arguments? */
47 #define ROFF_MAXARG 32
61 #define ROFFCALL_ARGS \
62 int tok, struct rofftree *tree, \
63 char *argv[], enum roffd type
68 int (*cb
)(ROFFCALL_ARGS
); /* Callback. */
69 const int *args
; /* Args (or NULL). */
73 enum rofftype type
; /* Type of macro. */
75 #define ROFF_PARSED (1 << 0) /* "Parsed". */
76 #define ROFF_CALLABLE (1 << 1) /* "Callable". */
77 #define ROFF_SHALLOW (1 << 2) /* Nesting block. */
78 #define ROFF_LSCOPE (1 << 3)
83 #define ROFF_VALUE (1 << 0) /* Has a value. */
87 int tok
; /* Token id. */
88 struct roffnode
*parent
; /* Parent (or NULL). */
92 struct roffnode
*last
; /* Last parsed node. */
95 struct tm tm
; /* `Dd' results. */
96 char os
[64]; /* `Os' results. */
97 char title
[64]; /* `Dt' results. */
98 char section
[64]; /* `Dt' results. */
99 char volume
[64]; /* `Dt' results. */
102 #define ROFF_PRELUDE (1 << 1) /* In roff prelude. */
103 #define ROFF_PRELUDE_Os (1 << 2) /* `Os' is parsed. */
104 #define ROFF_PRELUDE_Dt (1 << 3) /* `Dt' is parsed. */
105 #define ROFF_PRELUDE_Dd (1 << 4) /* `Dd' is parsed. */
106 #define ROFF_BODY (1 << 5) /* In roff body. */
112 static int roff_Dd(ROFFCALL_ARGS
);
113 static int roff_Dt(ROFFCALL_ARGS
);
114 static int roff_Os(ROFFCALL_ARGS
);
116 static int roff_layout(ROFFCALL_ARGS
);
117 static int roff_text(ROFFCALL_ARGS
);
118 static int roff_noop(ROFFCALL_ARGS
);
119 static int roff_depr(ROFFCALL_ARGS
);
121 static struct roffnode
*roffnode_new(int, struct rofftree
*);
122 static void roffnode_free(struct rofftree
*);
124 static void roff_warn(const struct rofftree
*,
125 const char *, char *, ...);
126 static void roff_err(const struct rofftree
*,
127 const char *, char *, ...);
129 static int roffscan(int, const int *);
130 static int rofffindtok(const char *);
131 static int rofffindarg(const char *);
132 static int rofffindcallable(const char *);
133 static int roffargs(const struct rofftree
*,
134 int, char *, char **);
135 static int roffargok(int, int);
136 static int roffnextopt(const struct rofftree
*,
137 int, char ***, char **);
138 static int roffparseopts(struct rofftree
*, int,
139 char ***, int *, char **);
140 static int roffparse(struct rofftree
*, char *);
141 static int textparse(const struct rofftree
*, char *);
144 static const int roffarg_An
[] = { ROFF_Split
, ROFF_Nosplit
,
146 static const int roffarg_Bd
[] = { ROFF_Ragged
, ROFF_Unfilled
,
147 ROFF_Literal
, ROFF_File
, ROFF_Offset
, ROFF_Filled
,
148 ROFF_Compact
, ROFF_ARGMAX
};
149 static const int roffarg_Bk
[] = { ROFF_Words
, ROFF_ARGMAX
};
150 static const int roffarg_Ex
[] = { ROFF_Std
, ROFF_ARGMAX
};
151 static const int roffarg_Rv
[] = { ROFF_Std
, ROFF_ARGMAX
};
152 static const int roffarg_Bl
[] = { ROFF_Bullet
, ROFF_Dash
,
153 ROFF_Hyphen
, ROFF_Item
, ROFF_Enum
, ROFF_Tag
, ROFF_Diag
,
154 ROFF_Hang
, ROFF_Ohang
, ROFF_Inset
, ROFF_Column
, ROFF_Offset
,
155 ROFF_Width
, ROFF_Compact
, ROFF_ARGMAX
};
156 static const int roffarg_St
[] = {
157 ROFF_p1003_1_88
, ROFF_p1003_1_90
, ROFF_p1003_1_96
,
158 ROFF_p1003_1_2001
, ROFF_p1003_1_2004
, ROFF_p1003_1
,
159 ROFF_p1003_1b
, ROFF_p1003_1b_93
, ROFF_p1003_1c_95
,
160 ROFF_p1003_1g_2000
, ROFF_p1003_2_92
, ROFF_p1387_2_95
,
161 ROFF_p1003_2
, ROFF_p1387_2
, ROFF_isoC_90
, ROFF_isoC_amd1
,
162 ROFF_isoC_tcor1
, ROFF_isoC_tcor2
, ROFF_isoC_99
, ROFF_ansiC
,
163 ROFF_ansiC_89
, ROFF_ansiC_99
, ROFF_ieee754
, ROFF_iso8802_3
,
164 ROFF_xpg3
, ROFF_xpg4
, ROFF_xpg4_2
, ROFF_xpg4_3
, ROFF_xbd5
,
165 ROFF_xcu5
, ROFF_xsh5
, ROFF_xns5
, ROFF_xns5_2d2_0
,
166 ROFF_xcurses4_2
, ROFF_susv2
, ROFF_susv3
, ROFF_svid4
,
169 static const int roffchild_Bl
[] = { ROFF_It
, ROFF_El
, ROFF_MAX
};
170 static const int roffchild_Fo
[] = { ROFF_Fa
, ROFF_Fc
, ROFF_MAX
};
171 static const int roffchild_Rs
[] = { ROFF_Re
, ROFF__A
, ROFF__B
,
172 ROFF__D
, ROFF__I
, ROFF__J
, ROFF__N
, ROFF__O
, ROFF__P
,
173 ROFF__R
, ROFF__T
, ROFF__V
, ROFF_MAX
};
175 static const int roffparent_El
[] = { ROFF_Bl
, ROFF_It
, ROFF_MAX
};
176 static const int roffparent_Fc
[] = { ROFF_Fo
, ROFF_Fa
, ROFF_MAX
};
177 static const int roffparent_Oc
[] = { ROFF_Oo
, ROFF_MAX
};
178 static const int roffparent_It
[] = { ROFF_Bl
, ROFF_It
, ROFF_MAX
};
179 static const int roffparent_Re
[] = { ROFF_Rs
, ROFF_MAX
};
181 /* Table of all known tokens. */
182 static const struct rofftok tokens
[ROFF_MAX
] = {
183 { roff_noop
, NULL
, NULL
, NULL
, 0, ROFF_COMMENT
, 0 }, /* \" */
184 { roff_Dd
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Dd */
185 { roff_Dt
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Dt */
186 { roff_Os
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Os */
187 { roff_layout
, NULL
, NULL
, NULL
, ROFF_Sh
, ROFF_LAYOUT
, 0 }, /* Sh */
188 { roff_layout
, NULL
, NULL
, NULL
, ROFF_Ss
, ROFF_LAYOUT
, 0 }, /* Ss */
189 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Pp */ /* XXX 0 args */
190 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_LSCOPE
}, /* D1 */
191 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_LSCOPE
}, /* Dl */
192 { roff_layout
, roffarg_Bd
, NULL
, NULL
, 0, ROFF_LAYOUT
, 0 }, /* Bd */
193 { roff_noop
, NULL
, NULL
, NULL
, ROFF_Bd
, ROFF_LAYOUT
, 0 }, /* Ed */
194 { roff_layout
, roffarg_Bl
, NULL
, roffchild_Bl
, 0, ROFF_LAYOUT
, 0 }, /* Bl */
195 { roff_noop
, NULL
, roffparent_El
, NULL
, ROFF_Bl
, ROFF_LAYOUT
, 0 }, /* El */
196 { roff_layout
, NULL
, roffparent_It
, NULL
, ROFF_It
, ROFF_LAYOUT
, ROFF_PARSED
| ROFF_SHALLOW
}, /* It */
197 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ad */ /* FIXME */
198 { roff_text
, roffarg_An
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* An */
199 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ar */
200 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Cd */ /* XXX man.4 only */
201 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Cm */
202 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Dv */ /* XXX needs arg */
203 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Er */ /* XXX needs arg */
204 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ev */ /* XXX needs arg */
205 { roff_text
, roffarg_Ex
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Ex */
206 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Fa */ /* XXX needs arg */
207 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Fd */
208 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Fl */
209 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Fn */ /* XXX needs arg */ /* FIXME */
210 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Ft */
211 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ic */ /* XXX needs arg */
212 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* In */
213 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Li */
214 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Nd */
215 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Nm */ /* FIXME */
216 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
| ROFF_LSCOPE
}, /* Op */
217 { roff_depr
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Ot */
218 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Pa */
219 { roff_text
, roffarg_Rv
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Rv */
220 { roff_text
, roffarg_St
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* St */
221 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Va */
222 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Vt */ /* XXX needs arg */
223 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Xr */ /* XXX needs arg */
224 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* %A */
225 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* %B */
226 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* %D */
227 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* %I */
228 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* %J */
229 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* %N */
230 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* %O */
231 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* %P */
232 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* %R */
233 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* %T */
234 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* %V */
235 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ac */
236 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ao */
237 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
| ROFF_LSCOPE
}, /* Aq */
238 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* At */ /* XXX at most 2 args */
239 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Bc */
240 { roff_layout
, NULL
, NULL
, NULL
, 0, ROFF_LAYOUT
, 0 }, /* Bf */ /* FIXME */
241 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Bo */
242 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
| ROFF_LSCOPE
}, /* Bq */
243 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Bsx */
244 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Bx */
245 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_SPECIAL
, 0 }, /* Db */
246 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Dc */
247 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Do */
248 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
| ROFF_LSCOPE
}, /* Dq */
249 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ec */
250 { roff_noop
, NULL
, NULL
, NULL
, ROFF_Bf
, ROFF_LAYOUT
, 0 }, /* Ef */
251 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Em */ /* XXX needs arg */
252 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Eo */
253 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Fx */
254 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Ms */
255 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* No */
256 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ns */
257 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Nx */
258 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Ox */
259 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Pc */
260 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Pf */
261 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Po */
262 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
| ROFF_LSCOPE
}, /* Pq */
263 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Qc */
264 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ql */
265 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Qo */
266 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
| ROFF_LSCOPE
}, /* Qq */
267 { roff_noop
, NULL
, roffparent_Re
, NULL
, ROFF_Rs
, ROFF_LAYOUT
, 0 }, /* Re */
268 { roff_layout
, NULL
, NULL
, roffchild_Rs
, 0, ROFF_LAYOUT
, 0 }, /* Rs */
269 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Sc */
270 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* So */
271 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
| ROFF_LSCOPE
}, /* Sq */
272 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_SPECIAL
, 0 }, /* Sm */
273 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Sx */
274 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Sy */
275 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Tn */
276 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Ux */
277 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Xc */
278 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Xo */
279 { roff_layout
, NULL
, NULL
, roffchild_Fo
, 0, ROFF_LAYOUT
, 0 }, /* Fo */
280 { roff_noop
, NULL
, roffparent_Fc
, NULL
, ROFF_Fo
, ROFF_LAYOUT
, 0 }, /* Fc */
281 { roff_layout
, NULL
, NULL
, NULL
, 0, ROFF_LAYOUT
, 0 }, /* Oo */
282 { roff_noop
, NULL
, roffparent_Oc
, NULL
, ROFF_Oo
, ROFF_LAYOUT
, 0 }, /* Oc */
283 { roff_layout
, roffarg_Bk
, NULL
, NULL
, 0, ROFF_LAYOUT
, 0 }, /* Bk */
284 { roff_noop
, NULL
, NULL
, NULL
, ROFF_Bk
, ROFF_LAYOUT
, 0 }, /* Ek */
285 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Bt */
286 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Hf */
287 { roff_depr
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Fr */
288 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Ud */
291 /* Table of all known token arguments. */
292 static const int tokenargs
[ROFF_ARGMAX
] = {
294 0, ROFF_VALUE
, ROFF_VALUE
, 0,
310 const char *const toknamesp
[ROFF_MAX
] = {
311 "\\\"", "Dd", "Dt", "Os",
312 "Sh", "Ss", "Pp", "D1",
313 "Dl", "Bd", "Ed", "Bl",
314 "El", "It", "Ad", "An",
315 "Ar", "Cd", "Cm", "Dv",
316 "Er", "Ev", "Ex", "Fa",
317 "Fd", "Fl", "Fn", "Ft",
318 "Ic", "In", "Li", "Nd",
319 "Nm", "Op", "Ot", "Pa",
320 "Rv", "St", "Va", "Vt",
322 "Xr", "\%A", "\%B", "\%D",
324 "\%I", "\%J", "\%N", "\%O",
326 "\%P", "\%R", "\%T", "\%V",
327 "Ac", "Ao", "Aq", "At",
328 "Bc", "Bf", "Bo", "Bq",
329 "Bsx", "Bx", "Db", "Dc",
330 "Do", "Dq", "Ec", "Ef",
331 "Em", "Eo", "Fx", "Ms",
332 "No", "Ns", "Nx", "Ox",
333 "Pc", "Pf", "Po", "Pq",
334 "Qc", "Ql", "Qo", "Qq",
335 "Re", "Rs", "Sc", "So",
336 "Sq", "Sm", "Sx", "Sy",
337 "Tn", "Ux", "Xc", "Xo",
338 "Fo", "Fc", "Oo", "Oc",
339 "Bk", "Ek", "Bt", "Hf",
343 const char *const tokargnamesp
[ROFF_ARGMAX
] = {
344 "split", "nosplit", "ragged",
345 "unfilled", "literal", "file",
346 "offset", "bullet", "dash",
347 "hyphen", "item", "enum",
348 "tag", "diag", "hang",
349 "ohang", "inset", "column",
350 "width", "compact", "std",
351 "p1003.1-88", "p1003.1-90", "p1003.1-96",
352 "p1003.1-2001", "p1003.1-2004", "p1003.1",
353 "p1003.1b", "p1003.1b-93", "p1003.1c-95",
354 "p1003.1g-2000", "p1003.2-92", "p1387.2-95",
355 "p1003.2", "p1387.2", "isoC-90",
356 "isoC-amd1", "isoC-tcor1", "isoC-tcor2",
357 "isoC-99", "ansiC", "ansiC-89",
358 "ansiC-99", "ieee754", "iso8802-3",
359 "xpg3", "xpg4", "xpg4.2",
360 "xpg4.3", "xbd5", "xcu5",
361 "xsh5", "xns5", "xns5.2d2.0",
362 "xcurses4.2", "susv2", "susv3",
363 "svid4", "filled", "words",
366 const char *const *toknames
= toknamesp
;
367 const char *const *tokargnames
= tokargnamesp
;
371 roff_free(struct rofftree
*tree
, int flush
)
383 if (ROFF_PRELUDE
& tree
->state
) {
384 roff_err(tree
, NULL
, "prelude never finished");
388 for (n
= tree
->last
; n
; n
= n
->parent
) {
389 if (0 != tokens
[n
->tok
].ctx
)
391 roff_err(tree
, NULL
, "closing explicit scope `%s'",
398 if ( ! (*tokens
[t
].cb
)(t
, tree
, NULL
, ROFF_EXIT
))
402 if ( ! (*tree
->cb
.rofftail
)(tree
->arg
))
414 return(error
? 0 : 1);
419 roff_alloc(const struct roffcb
*cb
, void *args
)
421 struct rofftree
*tree
;
426 if (NULL
== (tree
= calloc(1, sizeof(struct rofftree
))))
429 tree
->state
= ROFF_PRELUDE
;
432 (void)memcpy(&tree
->cb
, cb
, sizeof(struct roffcb
));
439 roff_engine(struct rofftree
*tree
, char *buf
)
446 roff_err(tree
, buf
, "blank line");
448 } else if ('.' != *buf
)
449 return(textparse(tree
, buf
));
451 return(roffparse(tree
, buf
));
456 textparse(const struct rofftree
*tree
, char *buf
)
459 if ( ! (ROFF_BODY
& tree
->state
)) {
460 roff_err(tree
, buf
, "data not in body");
463 return((*tree
->cb
.roffdata
)(tree
->arg
, 1, buf
));
468 roffargs(const struct rofftree
*tree
,
469 int tok
, char *buf
, char **argv
)
474 assert(tok
>= 0 && tok
< ROFF_MAX
);
480 for (i
= 0; *buf
&& i
< ROFF_MAXARG
; i
++) {
483 while (*buf
&& '\"' != *buf
)
486 roff_err(tree
, argv
[i
], "unclosed "
494 while (*buf
&& ! isspace(*buf
))
500 while (*buf
&& isspace(*buf
))
505 if (ROFF_MAXARG
== i
&& *buf
) {
506 roff_err(tree
, p
, "too many arguments for `%s'", toknames
517 roffscan(int tok
, const int *tokv
)
523 for ( ; ROFF_MAX
!= *tokv
; tokv
++)
532 roffparse(struct rofftree
*tree
, char *buf
)
536 char *argv
[ROFF_MAXARG
];
539 if (0 != *buf
&& 0 != *(buf
+ 1) && 0 != *(buf
+ 2))
540 if (0 == strncmp(buf
, ".\\\"", 3))
543 if (ROFF_MAX
== (tok
= rofffindtok(buf
+ 1))) {
544 roff_err(tree
, buf
+ 1, "bogus line macro");
546 } else if (NULL
== tokens
[tok
].cb
) {
547 roff_err(tree
, buf
+ 1, "unsupported macro `%s'",
552 assert(ROFF___
!= tok
);
553 if ( ! roffargs(tree
, tok
, buf
, argv
))
556 argvp
= (char **)argv
;
559 * Prelude macros break some assumptions, so branch now.
562 if (ROFF_PRELUDE
& tree
->state
) {
563 assert(NULL
== tree
->last
);
564 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
567 assert(ROFF_BODY
& tree
->state
);
570 * First check that our possible parents and parent's possible
571 * children are satisfied.
574 if (tree
->last
&& ! roffscan
575 (tree
->last
->tok
, tokens
[tok
].parents
)) {
576 roff_err(tree
, *argvp
, "`%s' has invalid parent `%s'",
578 toknames
[tree
->last
->tok
]);
582 if (tree
->last
&& ! roffscan
583 (tok
, tokens
[tree
->last
->tok
].children
)) {
584 roff_err(tree
, *argvp
, "`%s' is invalid child of `%s'",
586 toknames
[tree
->last
->tok
]);
591 * Branch if we're not a layout token.
594 if (ROFF_LAYOUT
!= tokens
[tok
].type
)
595 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
596 if (0 == tokens
[tok
].ctx
)
597 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
600 * First consider implicit-end tags, like as follows:
603 * In this, we want to close the scope of the NAME section. If
604 * there's an intermediary implicit-end tag, such as
608 * then it must be closed as well.
611 if (tok
== tokens
[tok
].ctx
) {
613 * First search up to the point where we must close.
614 * If one doesn't exist, then we can open a new scope.
617 for (n
= tree
->last
; n
; n
= n
->parent
) {
618 assert(0 == tokens
[n
->tok
].ctx
||
619 n
->tok
== tokens
[n
->tok
].ctx
);
622 if (ROFF_SHALLOW
& tokens
[tok
].flags
) {
626 if (tokens
[n
->tok
].ctx
== n
->tok
)
628 roff_err(tree
, *argv
, "`%s' breaks `%s' scope",
629 toknames
[tok
], toknames
[n
->tok
]);
634 * Create a new scope, as no previous one exists to
639 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
642 * Close out all intermediary scoped blocks, then hang
643 * the current scope from our predecessor's parent.
648 if ( ! (*tokens
[t
].cb
)(t
, tree
, NULL
, ROFF_EXIT
))
652 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
656 * Now consider explicit-end tags, where we want to close back
657 * to a specific tag. Example:
661 * In this, the `El' tag closes out the scope of `Bl'.
664 assert(tok
!= tokens
[tok
].ctx
&& 0 != tokens
[tok
].ctx
);
666 for (n
= tree
->last
; n
; n
= n
->parent
)
667 if (n
->tok
!= tokens
[tok
].ctx
) {
668 if (n
->tok
== tokens
[n
->tok
].ctx
)
670 roff_err(tree
, *argv
, "`%s' breaks `%s' scope",
671 toknames
[tok
], toknames
[n
->tok
]);
678 roff_err(tree
, *argv
, "`%s' has no starting tag `%s'",
680 toknames
[tokens
[tok
].ctx
]);
687 if ( ! (*tokens
[t
].cb
)(t
, tree
, NULL
, ROFF_EXIT
))
689 } while (t
!= tokens
[tok
].ctx
);
696 rofffindarg(const char *name
)
700 /* FIXME: use a table, this is slow but ok for now. */
703 for (i
= 0; i
< ROFF_ARGMAX
; i
++)
705 if (0 == strcmp(name
, tokargnames
[i
]))
713 rofffindtok(const char *buf
)
718 for (i
= 0; *buf
&& ! isspace(*buf
) && i
< 3; i
++, buf
++)
726 /* FIXME: use a table, this is slow but ok for now. */
729 for (i
= 0; i
< ROFF_MAX
; i
++)
731 if (0 == strcmp(toknames
[i
], token
))
739 roffispunct(const char *p
)
781 rofffindcallable(const char *name
)
785 if (ROFF_MAX
== (c
= rofffindtok(name
)))
787 assert(c
>= 0 && c
< ROFF_MAX
);
788 return(ROFF_CALLABLE
& tokens
[c
].flags
? c
: ROFF_MAX
);
792 static struct roffnode
*
793 roffnode_new(int tokid
, struct rofftree
*tree
)
797 if (NULL
== (p
= malloc(sizeof(struct roffnode
))))
801 p
->parent
= tree
->last
;
809 roffargok(int tokid
, int argid
)
813 if (NULL
== (c
= tokens
[tokid
].args
))
816 for ( ; ROFF_ARGMAX
!= *c
; c
++)
825 roffnode_free(struct rofftree
*tree
)
832 tree
->last
= tree
->last
->parent
;
838 roffnextopt(const struct rofftree
*tree
, int tok
,
839 char ***in
, char **val
)
848 if (NULL
== (arg
= *argv
))
853 if (ROFF_ARGMAX
== (v
= rofffindarg(arg
+ 1))) {
854 roff_warn(tree
, arg
, "argument-like parameter `%s' to "
855 "`%s'", &arg
[1], toknames
[tok
]);
859 if ( ! roffargok(tok
, v
)) {
860 roff_warn(tree
, arg
, "invalid argument parameter `%s' to "
861 "`%s'", tokargnames
[v
], toknames
[tok
]);
865 if ( ! (ROFF_VALUE
& tokenargs
[v
]))
871 roff_err(tree
, arg
, "empty value of `%s' for `%s'",
872 tokargnames
[v
], toknames
[tok
]);
881 roffparseopts(struct rofftree
*tree
, int tok
,
882 char ***args
, int *argc
, char **argv
)
889 while (-1 != (c
= roffnextopt(tree
, tok
, args
, &v
))) {
890 if (ROFF_ARGMAX
== c
)
899 argc
[i
] = ROFF_ARGMAX
;
907 roff_Dd(ROFFCALL_ARGS
)
912 if (ROFF_BODY
& tree
->state
) {
913 assert( ! (ROFF_PRELUDE
& tree
->state
));
914 assert(ROFF_PRELUDE_Dd
& tree
->state
);
915 return(roff_text(tok
, tree
, argv
, type
));
918 assert(ROFF_PRELUDE
& tree
->state
);
919 assert( ! (ROFF_BODY
& tree
->state
));
921 if (ROFF_PRELUDE_Dd
& tree
->state
) {
922 roff_err(tree
, *argv
, "repeated `Dd' in prelude");
924 } else if (ROFF_PRELUDE_Dt
& tree
->state
) {
925 roff_err(tree
, *argv
, "out-of-order `Dd' in prelude");
929 assert(NULL
== tree
->last
);
933 if (0 == strcmp(*argv
, "$Mdocdate: December 1 2008 $")) {
935 if (NULL
== localtime_r(&t
, &tree
->tm
))
936 err(1, "localtime_r");
937 tree
->state
|= ROFF_PRELUDE_Dd
;
941 /* Build this from Mdocdate or raw date. */
946 if (0 != strcmp(*argv
, "$Mdocdate:")) {
948 if (strlcat(buf
, *argv
++, sizeof(buf
))
951 roff_err(tree
, p
, "bad `Dd' date");
954 if (strptime(buf
, "%b%d,%Y", &tree
->tm
)) {
955 tree
->state
|= ROFF_PRELUDE_Dd
;
958 roff_err(tree
, *argv
, "bad `Dd' date");
963 while (*argv
&& **argv
!= '$') {
964 if (strlcat(buf
, *argv
++, sizeof(buf
))
966 roff_err(tree
, p
, "bad `Dd' Mdocdate");
969 if (strlcat(buf
, " ", sizeof(buf
))
971 roff_err(tree
, p
, "bad `Dd' Mdocdate");
976 roff_err(tree
, p
, "bad `Dd' Mdocdate");
980 if (NULL
== strptime(buf
, "%b %d %Y", &tree
->tm
)) {
981 roff_err(tree
, *argv
, "bad `Dd' Mdocdate");
985 tree
->state
|= ROFF_PRELUDE_Dd
;
992 roff_Dt(ROFFCALL_ARGS
)
995 if (ROFF_BODY
& tree
->state
) {
996 assert( ! (ROFF_PRELUDE
& tree
->state
));
997 assert(ROFF_PRELUDE_Dt
& tree
->state
);
998 return(roff_text(tok
, tree
, argv
, type
));
1001 assert(ROFF_PRELUDE
& tree
->state
);
1002 assert( ! (ROFF_BODY
& tree
->state
));
1004 if ( ! (ROFF_PRELUDE_Dd
& tree
->state
)) {
1005 roff_err(tree
, *argv
, "out-of-order `Dt' in prelude");
1007 } else if (ROFF_PRELUDE_Dt
& tree
->state
) {
1008 roff_err(tree
, *argv
, "repeated `Dt' in prelude");
1013 if (NULL
== *argv
) {
1014 roff_err(tree
, *argv
, "`Dt' needs document title");
1016 } else if (strlcpy(tree
->title
, *argv
, sizeof(tree
->title
))
1017 >= sizeof(tree
->title
)) {
1018 roff_err(tree
, *argv
, "`Dt' document title too long");
1023 if (NULL
== *argv
) {
1024 roff_err(tree
, *argv
, "`Dt' needs section");
1026 } else if (strlcpy(tree
->section
, *argv
, sizeof(tree
->section
))
1027 >= sizeof(tree
->section
)) {
1028 roff_err(tree
, *argv
, "`Dt' section too long");
1033 if (NULL
== *argv
) {
1034 tree
->volume
[0] = 0;
1035 } else if (strlcpy(tree
->volume
, *argv
, sizeof(tree
->volume
))
1036 >= sizeof(tree
->volume
)) {
1037 roff_err(tree
, *argv
, "`Dt' volume too long");
1041 assert(NULL
== tree
->last
);
1042 tree
->state
|= ROFF_PRELUDE_Dt
;
1050 roff_Os(ROFFCALL_ARGS
)
1054 if (ROFF_BODY
& tree
->state
) {
1055 assert( ! (ROFF_PRELUDE
& tree
->state
));
1056 assert(ROFF_PRELUDE_Os
& tree
->state
);
1057 return(roff_text(tok
, tree
, argv
, type
));
1060 assert(ROFF_PRELUDE
& tree
->state
);
1061 if ( ! (ROFF_PRELUDE_Dt
& tree
->state
) ||
1062 ! (ROFF_PRELUDE_Dd
& tree
->state
)) {
1063 roff_err(tree
, *argv
, "out-of-order `Os' in prelude");
1072 if (strlcat(tree
->os
, *argv
++, sizeof(tree
->os
))
1075 roff_err(tree
, p
, "`Os' value too long");
1079 if (0 == tree
->os
[0])
1080 if (strlcpy(tree
->os
, "LOCAL", sizeof(tree
->os
))
1081 >= sizeof(tree
->os
)) {
1082 roff_err(tree
, p
, "`Os' value too long");
1086 tree
->state
|= ROFF_PRELUDE_Os
;
1087 tree
->state
&= ~ROFF_PRELUDE
;
1088 tree
->state
|= ROFF_BODY
;
1090 assert(NULL
== tree
->last
);
1092 return((*tree
->cb
.roffhead
)(tree
->arg
));
1098 roff_layout(ROFFCALL_ARGS
)
1100 int i
, c
, argcp
[ROFF_MAXARG
];
1101 char *argvp
[ROFF_MAXARG
];
1103 if (ROFF_PRELUDE
& tree
->state
) {
1104 roff_err(tree
, *argv
, "bad `%s' in prelude",
1107 } else if (ROFF_EXIT
== type
) {
1108 roffnode_free(tree
);
1109 return((*tree
->cb
.roffblkout
)(tree
->arg
, tok
));
1112 assert( ! (ROFF_CALLABLE
& tokens
[tok
].flags
));
1116 if ( ! roffparseopts(tree
, tok
, &argv
, argcp
, argvp
))
1118 if (NULL
== roffnode_new(tok
, tree
))
1122 * Layouts have two parts: the layout body and header. The
1123 * layout header is the trailing text of the line macro, while
1124 * the layout body is everything following until termination.
1127 if ( ! (*tree
->cb
.roffblkin
)(tree
->arg
, tok
, argcp
, argvp
))
1131 if ( ! (*tree
->cb
.roffin
)(tree
->arg
, tok
, argcp
, argvp
))
1135 * If there are no parsable parts, then write remaining tokens
1136 * into the layout header and exit.
1139 if ( ! (ROFF_PARSED
& tokens
[tok
].flags
)) {
1142 if ( ! (*tree
->cb
.roffdata
)(tree
->arg
, i
, *argv
++))
1146 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1150 * Parsable elements may be in the header (or be the header, for
1151 * that matter). Follow the regular parsing rules for these.
1156 if (ROFF_MAX
== (c
= rofffindcallable(*argv
))) {
1158 if ( ! (*tree
->cb
.roffdata
)
1159 (tree
->arg
, i
, *argv
++))
1165 if (NULL
== tokens
[c
].cb
) {
1166 roff_err(tree
, *argv
, "unsupported macro `%s'",
1171 if ( ! (*tokens
[c
].cb
)(c
, tree
, argv
, ROFF_ENTER
))
1178 * If there's trailing punctuation in the header, then write it
1179 * out now. Here we mimic the behaviour of a line-dominant text
1184 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1187 * Expensive. Scan to the end of line then work backwards until
1188 * a token isn't punctuation.
1196 if ( ! roffispunct(argv
[--i
]))
1197 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1199 while (i
>= 0 && roffispunct(argv
[i
]))
1206 if ( ! (*tree
->cb
.roffdata
)(tree
->arg
, 0, argv
[i
++]))
1209 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1215 roff_text(ROFFCALL_ARGS
)
1217 int i
, j
, first
, c
, argcp
[ROFF_MAXARG
];
1218 char *argvp
[ROFF_MAXARG
];
1220 if (ROFF_PRELUDE
& tree
->state
) {
1221 roff_err(tree
, *argv
, "`%s' disallowed in prelude",
1226 first
= (*argv
== tree
->cur
);
1229 if ( ! roffparseopts(tree
, tok
, &argv
, argcp
, argvp
))
1231 if ( ! (*tree
->cb
.roffin
)(tree
->arg
, tok
, argcp
, argvp
))
1234 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1236 if ( ! (ROFF_PARSED
& tokens
[tok
].flags
)) {
1239 if ( ! (*tree
->cb
.roffdata
)(tree
->arg
, i
, *argv
++))
1243 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1247 * Deal with punctuation. Ugly. Work ahead until we encounter
1248 * terminating punctuation. If we encounter it and all
1249 * subsequent tokens are punctuation, then stop processing (the
1250 * line-dominant macro will print these tokens after closure).
1255 if (ROFF_MAX
== (c
= rofffindcallable(*argv
))) {
1256 if ( ! roffispunct(*argv
)) {
1257 if ( ! (*tree
->cb
.roffdata
)
1258 (tree
->arg
, i
, *argv
++))
1264 /* See if only punctuation remains. */
1267 for (j
= 0; argv
[j
]; j
++)
1268 if ( ! roffispunct(argv
[j
]))
1272 if ( ! (*tree
->cb
.roffdata
)
1273 (tree
->arg
, 0, *argv
++))
1278 /* Only punctuation remains. */
1280 if ( ! (*tree
->cb
.roffout
)(tree
->arg
, tok
))
1286 * A sub-command has been found. Execute it and
1287 * discontinue parsing for arguments. If we're
1288 * line-scoped, then close out after it returns; if we
1289 * aren't, then close out before.
1292 if (NULL
== tokens
[c
].cb
) {
1293 roff_err(tree
, *argv
, "unsupported macro `%s'",
1298 if ( ! (ROFF_LSCOPE
& tokens
[tok
].flags
))
1299 if ( ! (*tree
->cb
.roffout
)(tree
->arg
, tok
))
1302 if ( ! (*tokens
[c
].cb
)(c
, tree
, argv
, ROFF_ENTER
))
1305 if (ROFF_LSCOPE
& tokens
[tok
].flags
)
1306 if ( ! (*tree
->cb
.roffout
)(tree
->arg
, tok
))
1313 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1318 * We're the line-dominant macro. Check if there's remaining
1319 * punctuation. If there is, then flush it out before exiting.
1327 if ( ! roffispunct(argv
[--i
]))
1330 while (i
>= 0 && roffispunct(argv
[i
]))
1336 if ( ! (*tree
->cb
.roffdata
)(tree
->arg
, 0, argv
[i
++]))
1345 roff_noop(ROFFCALL_ARGS
)
1354 roff_depr(ROFFCALL_ARGS
)
1357 roff_err(tree
, *argv
, "`%s' is deprecated", toknames
[tok
]);
1363 roff_warn(const struct rofftree
*tree
, const char *pos
, char *fmt
, ...)
1369 (void)vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
1372 (*tree
->cb
.roffmsg
)(tree
->arg
,
1373 ROFF_WARN
, tree
->cur
, pos
, buf
);
1378 roff_err(const struct rofftree
*tree
, const char *pos
, char *fmt
, ...)
1384 (void)vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
1387 (*tree
->cb
.roffmsg
)(tree
->arg
,
1388 ROFF_ERROR
, tree
->cur
, pos
, buf
);