]>
git.cameronkatri.com Git - mandoc.git/blob - roff.c
1 /* $Id: roff.c,v 1.28 2008/12/01 16:01:28 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_Oo
[] = { ROFF_Op
, ROFF_Oc
, ROFF_MAX
};
170 static const int roffchild_Rs
[] = { ROFF_Re
, ROFF__A
, ROFF__B
,
171 ROFF__D
, ROFF__I
, ROFF__J
, ROFF__N
, ROFF__O
, ROFF__P
,
172 ROFF__R
, ROFF__T
, ROFF__V
, ROFF_MAX
};
174 static const int roffparent_El
[] = { ROFF_Bl
, ROFF_It
, ROFF_MAX
};
175 static const int roffparent_Fc
[] = { ROFF_Fo
, ROFF_Fa
, ROFF_MAX
};
176 static const int roffparent_Oc
[] = { ROFF_Oo
, ROFF_Oc
, ROFF_MAX
};
177 static const int roffparent_It
[] = { ROFF_Bl
, ROFF_It
, ROFF_MAX
};
178 static const int roffparent_Re
[] = { ROFF_Rs
, ROFF_MAX
};
180 /* Table of all known tokens. */
181 static const struct rofftok tokens
[ROFF_MAX
] = {
182 { roff_noop
, NULL
, NULL
, NULL
, 0, ROFF_COMMENT
, 0 }, /* \" */
183 { roff_Dd
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Dd */
184 { roff_Dt
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Dt */
185 { roff_Os
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Os */
186 { roff_layout
, NULL
, NULL
, NULL
, ROFF_Sh
, ROFF_LAYOUT
, 0 }, /* Sh */
187 { roff_layout
, NULL
, NULL
, NULL
, ROFF_Ss
, ROFF_LAYOUT
, 0 }, /* Ss */
188 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Pp */ /* XXX 0 args */
189 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_LSCOPE
}, /* D1 */
190 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_LSCOPE
}, /* Dl */
191 { roff_layout
, roffarg_Bd
, NULL
, NULL
, 0, ROFF_LAYOUT
, 0 }, /* Bd */
192 { roff_noop
, NULL
, NULL
, NULL
, ROFF_Bd
, ROFF_LAYOUT
, 0 }, /* Ed */
193 { roff_layout
, roffarg_Bl
, NULL
, roffchild_Bl
, 0, ROFF_LAYOUT
, 0 }, /* Bl */
194 { roff_noop
, NULL
, roffparent_El
, NULL
, ROFF_Bl
, ROFF_LAYOUT
, 0 }, /* El */
195 { roff_layout
, NULL
, roffparent_It
, NULL
, ROFF_It
, ROFF_LAYOUT
, ROFF_PARSED
| ROFF_SHALLOW
}, /* It */
196 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ad */ /* FIXME */
197 { roff_text
, roffarg_An
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* An */
198 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ar */
199 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Cd */ /* XXX man.4 only */
200 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Cm */
201 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Dv */ /* XXX needs arg */
202 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Er */ /* XXX needs arg */
203 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ev */ /* XXX needs arg */
204 { roff_text
, roffarg_Ex
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Ex */
205 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Fa */ /* XXX needs arg */
206 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Fd */
207 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Fl */
208 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Fn */ /* XXX needs arg */ /* FIXME */
209 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Ft */
210 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ic */ /* XXX needs arg */
211 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* In */
212 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Li */
213 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Nd */
214 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Nm */ /* FIXME */
215 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
| ROFF_LSCOPE
}, /* Op */
216 { roff_depr
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Ot */
217 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Pa */
218 { roff_text
, roffarg_Rv
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Rv */
219 { roff_text
, roffarg_St
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* St */
220 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Va */
221 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Vt */ /* XXX needs arg */
222 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Xr */ /* XXX needs arg */
223 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* %A */
224 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* %B */
225 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* %D */
226 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* %I */
227 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* %J */
228 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* %N */
229 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* %O */
230 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* %P */
231 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* %R */
232 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* %T */
233 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* %V */
234 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ac */
235 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ao */
236 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
| ROFF_LSCOPE
}, /* Aq */
237 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* At */ /* XXX at most 2 args */
238 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Bc */
239 { roff_layout
, NULL
, NULL
, NULL
, 0, ROFF_LAYOUT
, 0 }, /* Bf */ /* FIXME */
240 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Bo */
241 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
| ROFF_LSCOPE
}, /* Bq */
242 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Bsx */
243 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Bx */
244 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_SPECIAL
, 0 }, /* Db */
245 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Dc */
246 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Do */
247 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
| ROFF_LSCOPE
}, /* Dq */
248 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ec */
249 { roff_noop
, NULL
, NULL
, NULL
, ROFF_Bf
, ROFF_LAYOUT
, 0 }, /* Ef */
250 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Em */ /* XXX needs arg */
251 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Eo */
252 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Fx */
253 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Ms */
254 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* No */
255 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ns */
256 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Nx */
257 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Ox */
258 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Pc */
259 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Pf */
260 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Po */
261 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
| ROFF_LSCOPE
}, /* Pq */
262 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Qc */
263 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ql */
264 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Qo */
265 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
| ROFF_LSCOPE
}, /* Qq */
266 { roff_noop
, NULL
, roffparent_Re
, NULL
, ROFF_Rs
, ROFF_LAYOUT
, 0 }, /* Re */
267 { roff_layout
, NULL
, NULL
, roffchild_Rs
, 0, ROFF_LAYOUT
, 0 }, /* Rs */
268 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Sc */
269 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* So */
270 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
| ROFF_LSCOPE
}, /* Sq */
271 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_SPECIAL
, 0 }, /* Sm */
272 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Sx */
273 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Sy */
274 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Tn */
275 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Ux */
276 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Xc */
277 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Xo */
278 { roff_layout
, NULL
, NULL
, roffchild_Fo
, 0, ROFF_LAYOUT
, 0 }, /* Fo */
279 { roff_noop
, NULL
, roffparent_Fc
, NULL
, ROFF_Fo
, ROFF_LAYOUT
, 0 }, /* Fc */
280 { roff_layout
, NULL
, NULL
, roffchild_Oo
, 0, ROFF_LAYOUT
, 0 }, /* Oo */
281 { roff_noop
, NULL
, roffparent_Oc
, NULL
, ROFF_Oo
, ROFF_LAYOUT
, 0 }, /* Oc */
282 { roff_layout
, roffarg_Bk
, NULL
, NULL
, 0, ROFF_LAYOUT
, 0 }, /* Bk */
283 { roff_noop
, NULL
, NULL
, NULL
, ROFF_Bk
, ROFF_LAYOUT
, 0 }, /* Ek */
284 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Bt */
285 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Hf */
286 { roff_depr
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Fr */
287 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Ud */
290 /* Table of all known token arguments. */
291 static const int tokenargs
[ROFF_ARGMAX
] = {
293 0, ROFF_VALUE
, ROFF_VALUE
, 0,
309 const char *const toknamesp
[ROFF_MAX
] = {
310 "\\\"", "Dd", "Dt", "Os",
311 "Sh", "Ss", "Pp", "D1",
312 "Dl", "Bd", "Ed", "Bl",
313 "El", "It", "Ad", "An",
314 "Ar", "Cd", "Cm", "Dv",
315 "Er", "Ev", "Ex", "Fa",
316 "Fd", "Fl", "Fn", "Ft",
317 "Ic", "In", "Li", "Nd",
318 "Nm", "Op", "Ot", "Pa",
319 "Rv", "St", "Va", "Vt",
321 "Xr", "\%A", "\%B", "\%D",
323 "\%I", "\%J", "\%N", "\%O",
325 "\%P", "\%R", "\%T", "\%V",
326 "Ac", "Ao", "Aq", "At",
327 "Bc", "Bf", "Bo", "Bq",
328 "Bsx", "Bx", "Db", "Dc",
329 "Do", "Dq", "Ec", "Ef",
330 "Em", "Eo", "Fx", "Ms",
331 "No", "Ns", "Nx", "Ox",
332 "Pc", "Pf", "Po", "Pq",
333 "Qc", "Ql", "Qo", "Qq",
334 "Re", "Rs", "Sc", "So",
335 "Sq", "Sm", "Sx", "Sy",
336 "Tn", "Ux", "Xc", "Xo",
337 "Fo", "Fc", "Oo", "Oc",
338 "Bk", "Ek", "Bt", "Hf",
342 const char *const tokargnamesp
[ROFF_ARGMAX
] = {
343 "split", "nosplit", "ragged",
344 "unfilled", "literal", "file",
345 "offset", "bullet", "dash",
346 "hyphen", "item", "enum",
347 "tag", "diag", "hang",
348 "ohang", "inset", "column",
349 "width", "compact", "std",
350 "p1003.1-88", "p1003.1-90", "p1003.1-96",
351 "p1003.1-2001", "p1003.1-2004", "p1003.1",
352 "p1003.1b", "p1003.1b-93", "p1003.1c-95",
353 "p1003.1g-2000", "p1003.2-92", "p1387.2-95",
354 "p1003.2", "p1387.2", "isoC-90",
355 "isoC-amd1", "isoC-tcor1", "isoC-tcor2",
356 "isoC-99", "ansiC", "ansiC-89",
357 "ansiC-99", "ieee754", "iso8802-3",
358 "xpg3", "xpg4", "xpg4.2",
359 "xpg4.3", "xbd5", "xcu5",
360 "xsh5", "xns5", "xns5.2d2.0",
361 "xcurses4.2", "susv2", "susv3",
362 "svid4", "filled", "words",
365 const char *const *toknames
= toknamesp
;
366 const char *const *tokargnames
= tokargnamesp
;
370 roff_free(struct rofftree
*tree
, int flush
)
382 if (ROFF_PRELUDE
& tree
->state
) {
383 roff_err(tree
, NULL
, "prelude never finished");
387 for (n
= tree
->last
; n
; n
= n
->parent
) {
388 if (0 != tokens
[n
->tok
].ctx
)
390 roff_err(tree
, NULL
, "closing explicit scope `%s'",
397 if ( ! (*tokens
[t
].cb
)(t
, tree
, NULL
, ROFF_EXIT
))
410 return(error
? 0 : 1);
415 roff_alloc(const struct roffcb
*cb
, void *args
)
417 struct rofftree
*tree
;
422 if (NULL
== (tree
= calloc(1, sizeof(struct rofftree
))))
425 tree
->state
= ROFF_PRELUDE
;
428 (void)memcpy(&tree
->cb
, cb
, sizeof(struct roffcb
));
435 roff_engine(struct rofftree
*tree
, char *buf
)
442 roff_warn(tree
, buf
, "blank line");
444 } else if ('.' != *buf
)
445 return(textparse(tree
, buf
));
447 return(roffparse(tree
, buf
));
452 textparse(const struct rofftree
*tree
, char *buf
)
455 return((*tree
->cb
.roffdata
)(tree
->arg
, 1, buf
));
460 roffargs(const struct rofftree
*tree
,
461 int tok
, char *buf
, char **argv
)
466 assert(tok
>= 0 && tok
< ROFF_MAX
);
472 for (i
= 0; *buf
&& i
< ROFF_MAXARG
; i
++) {
475 while (*buf
&& '\"' != *buf
)
478 roff_err(tree
, argv
[i
], "unclosed "
486 while (*buf
&& ! isspace(*buf
))
492 while (*buf
&& isspace(*buf
))
497 if (ROFF_MAXARG
== i
&& *buf
) {
498 roff_err(tree
, p
, "too many arguments for `%s'", toknames
509 roffscan(int tok
, const int *tokv
)
515 for ( ; ROFF_MAX
!= *tokv
; tokv
++)
524 roffparse(struct rofftree
*tree
, char *buf
)
528 char *argv
[ROFF_MAXARG
];
531 if (0 != *buf
&& 0 != *(buf
+ 1) && 0 != *(buf
+ 2))
532 if (0 == strncmp(buf
, ".\\\"", 3))
535 if (ROFF_MAX
== (tok
= rofffindtok(buf
+ 1))) {
536 roff_err(tree
, buf
+ 1, "bogus line macro");
538 } else if (NULL
== tokens
[tok
].cb
) {
539 roff_err(tree
, buf
+ 1, "unsupported macro `%s'",
544 assert(ROFF___
!= tok
);
545 if ( ! roffargs(tree
, tok
, buf
, argv
))
548 argvp
= (char **)argv
;
551 * Prelude macros break some assumptions, so branch now.
554 if (ROFF_PRELUDE
& tree
->state
) {
555 assert(NULL
== tree
->last
);
556 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
559 assert(ROFF_BODY
& tree
->state
);
562 * First check that our possible parents and parent's possible
563 * children are satisfied.
566 if (tree
->last
&& ! roffscan
567 (tree
->last
->tok
, tokens
[tok
].parents
)) {
568 roff_err(tree
, *argvp
, "`%s' has invalid parent `%s'",
570 toknames
[tree
->last
->tok
]);
574 if (tree
->last
&& ! roffscan
575 (tok
, tokens
[tree
->last
->tok
].children
)) {
576 roff_err(tree
, *argvp
, "`%s' is invalid child of `%s'",
578 toknames
[tree
->last
->tok
]);
583 * Branch if we're not a layout token.
586 if (ROFF_LAYOUT
!= tokens
[tok
].type
)
587 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
588 if (0 == tokens
[tok
].ctx
)
589 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
592 * First consider implicit-end tags, like as follows:
595 * In this, we want to close the scope of the NAME section. If
596 * there's an intermediary implicit-end tag, such as
600 * then it must be closed as well.
603 if (tok
== tokens
[tok
].ctx
) {
605 * First search up to the point where we must close.
606 * If one doesn't exist, then we can open a new scope.
609 for (n
= tree
->last
; n
; n
= n
->parent
) {
610 assert(0 == tokens
[n
->tok
].ctx
||
611 n
->tok
== tokens
[n
->tok
].ctx
);
614 if (ROFF_SHALLOW
& tokens
[tok
].flags
) {
618 if (tokens
[n
->tok
].ctx
== n
->tok
)
620 roff_err(tree
, *argv
, "`%s' breaks `%s' scope",
621 toknames
[tok
], toknames
[n
->tok
]);
626 * Create a new scope, as no previous one exists to
631 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
634 * Close out all intermediary scoped blocks, then hang
635 * the current scope from our predecessor's parent.
640 if ( ! (*tokens
[t
].cb
)(t
, tree
, NULL
, ROFF_EXIT
))
644 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
648 * Now consider explicit-end tags, where we want to close back
649 * to a specific tag. Example:
653 * In this, the `El' tag closes out the scope of `Bl'.
656 assert(tok
!= tokens
[tok
].ctx
&& 0 != tokens
[tok
].ctx
);
658 for (n
= tree
->last
; n
; n
= n
->parent
)
659 if (n
->tok
!= tokens
[tok
].ctx
) {
660 if (n
->tok
== tokens
[n
->tok
].ctx
)
662 roff_err(tree
, *argv
, "`%s' breaks `%s' scope",
663 toknames
[tok
], toknames
[n
->tok
]);
670 roff_err(tree
, *argv
, "`%s' has no starting tag `%s'",
672 toknames
[tokens
[tok
].ctx
]);
679 if ( ! (*tokens
[t
].cb
)(t
, tree
, NULL
, ROFF_EXIT
))
681 } while (t
!= tokens
[tok
].ctx
);
688 rofffindarg(const char *name
)
692 /* FIXME: use a table, this is slow but ok for now. */
695 for (i
= 0; i
< ROFF_ARGMAX
; i
++)
697 if (0 == strcmp(name
, tokargnames
[i
]))
705 rofffindtok(const char *buf
)
710 for (i
= 0; *buf
&& ! isspace(*buf
) && i
< 3; i
++, buf
++)
718 /* FIXME: use a table, this is slow but ok for now. */
721 for (i
= 0; i
< ROFF_MAX
; i
++)
723 if (0 == strcmp(toknames
[i
], token
))
731 roffispunct(const char *p
)
773 rofffindcallable(const char *name
)
777 if (ROFF_MAX
== (c
= rofffindtok(name
)))
779 assert(c
>= 0 && c
< ROFF_MAX
);
780 return(ROFF_CALLABLE
& tokens
[c
].flags
? c
: ROFF_MAX
);
784 static struct roffnode
*
785 roffnode_new(int tokid
, struct rofftree
*tree
)
789 if (NULL
== (p
= malloc(sizeof(struct roffnode
))))
793 p
->parent
= tree
->last
;
801 roffargok(int tokid
, int argid
)
805 if (NULL
== (c
= tokens
[tokid
].args
))
808 for ( ; ROFF_ARGMAX
!= *c
; c
++)
817 roffnode_free(struct rofftree
*tree
)
824 tree
->last
= tree
->last
->parent
;
830 roffnextopt(const struct rofftree
*tree
, int tok
,
831 char ***in
, char **val
)
840 if (NULL
== (arg
= *argv
))
845 if (ROFF_ARGMAX
== (v
= rofffindarg(arg
+ 1))) {
846 roff_warn(tree
, arg
, "argument-like parameter `%s' to "
847 "`%s'", &arg
[1], toknames
[tok
]);
851 if ( ! roffargok(tok
, v
)) {
852 roff_warn(tree
, arg
, "invalid argument parameter `%s' to "
853 "`%s'", tokargnames
[v
], toknames
[tok
]);
857 if ( ! (ROFF_VALUE
& tokenargs
[v
]))
863 roff_err(tree
, arg
, "empty value of `%s' for `%s'",
864 tokargnames
[v
], toknames
[tok
]);
873 roffparseopts(struct rofftree
*tree
, int tok
,
874 char ***args
, int *argc
, char **argv
)
881 while (-1 != (c
= roffnextopt(tree
, tok
, args
, &v
))) {
882 if (ROFF_ARGMAX
== c
)
891 argc
[i
] = ROFF_ARGMAX
;
899 roff_Dd(ROFFCALL_ARGS
)
902 if (ROFF_BODY
& tree
->state
) {
903 assert( ! (ROFF_PRELUDE
& tree
->state
));
904 assert(ROFF_PRELUDE_Dd
& tree
->state
);
905 return(roff_text(tok
, tree
, argv
, type
));
908 assert(ROFF_PRELUDE
& tree
->state
);
909 assert( ! (ROFF_BODY
& tree
->state
));
911 if (ROFF_PRELUDE_Dd
& tree
->state
) {
912 roff_err(tree
, *argv
, "repeated `Dd' in prelude");
914 } else if (ROFF_PRELUDE_Dt
& tree
->state
) {
915 roff_err(tree
, *argv
, "out-of-order `Dd' in prelude");
919 /* TODO: parse date. */
921 assert(NULL
== tree
->last
);
922 tree
->state
|= ROFF_PRELUDE_Dd
;
930 roff_Dt(ROFFCALL_ARGS
)
933 if (ROFF_BODY
& tree
->state
) {
934 assert( ! (ROFF_PRELUDE
& tree
->state
));
935 assert(ROFF_PRELUDE_Dt
& tree
->state
);
936 return(roff_text(tok
, tree
, argv
, type
));
939 assert(ROFF_PRELUDE
& tree
->state
);
940 assert( ! (ROFF_BODY
& tree
->state
));
942 if ( ! (ROFF_PRELUDE_Dd
& tree
->state
)) {
943 roff_err(tree
, *argv
, "out-of-order `Dt' in prelude");
945 } else if (ROFF_PRELUDE_Dt
& tree
->state
) {
946 roff_err(tree
, *argv
, "repeated `Dt' in prelude");
950 /* TODO: parse date. */
952 assert(NULL
== tree
->last
);
953 tree
->state
|= ROFF_PRELUDE_Dt
;
961 roff_Os(ROFFCALL_ARGS
)
964 if (ROFF_EXIT
== type
) {
965 return((*tree
->cb
.rofftail
)(tree
->arg
));
966 } else if (ROFF_BODY
& tree
->state
) {
967 assert( ! (ROFF_PRELUDE
& tree
->state
));
968 assert(ROFF_PRELUDE_Os
& tree
->state
);
969 return(roff_text(tok
, tree
, argv
, type
));
972 assert(ROFF_PRELUDE
& tree
->state
);
973 if ( ! (ROFF_PRELUDE_Dt
& tree
->state
) ||
974 ! (ROFF_PRELUDE_Dd
& tree
->state
)) {
975 roff_err(tree
, *argv
, "out-of-order `Os' in prelude");
979 /* TODO: extract OS. */
981 tree
->state
|= ROFF_PRELUDE_Os
;
982 tree
->state
&= ~ROFF_PRELUDE
;
983 tree
->state
|= ROFF_BODY
;
985 assert(NULL
== tree
->last
);
987 return((*tree
->cb
.roffhead
)(tree
->arg
));
993 roff_layout(ROFFCALL_ARGS
)
995 int i
, c
, argcp
[ROFF_MAXARG
];
996 char *argvp
[ROFF_MAXARG
];
998 if (ROFF_PRELUDE
& tree
->state
) {
999 roff_err(tree
, *argv
, "bad `%s' in prelude",
1002 } else if (ROFF_EXIT
== type
) {
1003 roffnode_free(tree
);
1004 return((*tree
->cb
.roffblkout
)(tree
->arg
, tok
));
1007 assert( ! (ROFF_CALLABLE
& tokens
[tok
].flags
));
1011 if ( ! roffparseopts(tree
, tok
, &argv
, argcp
, argvp
))
1013 if (NULL
== roffnode_new(tok
, tree
))
1017 * Layouts have two parts: the layout body and header. The
1018 * layout header is the trailing text of the line macro, while
1019 * the layout body is everything following until termination.
1022 if ( ! (*tree
->cb
.roffblkin
)(tree
->arg
, tok
, argcp
, argvp
))
1026 if ( ! (*tree
->cb
.roffin
)(tree
->arg
, tok
, argcp
, argvp
))
1030 * If there are no parsable parts, then write remaining tokens
1031 * into the layout header and exit.
1034 if ( ! (ROFF_PARSED
& tokens
[tok
].flags
)) {
1037 if ( ! (*tree
->cb
.roffdata
)(tree
->arg
, i
, *argv
++))
1041 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1045 * Parsable elements may be in the header (or be the header, for
1046 * that matter). Follow the regular parsing rules for these.
1051 if (ROFF_MAX
== (c
= rofffindcallable(*argv
))) {
1053 if ( ! (*tree
->cb
.roffdata
)
1054 (tree
->arg
, i
, *argv
++))
1060 if (NULL
== tokens
[c
].cb
) {
1061 roff_err(tree
, *argv
, "unsupported macro `%s'",
1066 if ( ! (*tokens
[c
].cb
)(c
, tree
, argv
, ROFF_ENTER
))
1073 * If there's trailing punctuation in the header, then write it
1074 * out now. Here we mimic the behaviour of a line-dominant text
1079 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1082 * Expensive. Scan to the end of line then work backwards until
1083 * a token isn't punctuation.
1091 if ( ! roffispunct(argv
[--i
]))
1092 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1094 while (i
>= 0 && roffispunct(argv
[i
]))
1102 if ( ! (*tree
->cb
.roffdata
)(tree
->arg
, 0, argv
[i
++]))
1105 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1111 roff_text(ROFFCALL_ARGS
)
1113 int i
, j
, first
, c
, argcp
[ROFF_MAXARG
];
1114 char *argvp
[ROFF_MAXARG
];
1116 if (ROFF_PRELUDE
& tree
->state
) {
1117 roff_err(tree
, *argv
, "`%s' disallowed in prelude",
1122 first
= (*argv
== tree
->cur
);
1125 if ( ! roffparseopts(tree
, tok
, &argv
, argcp
, argvp
))
1127 if ( ! (*tree
->cb
.roffin
)(tree
->arg
, tok
, argcp
, argvp
))
1130 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1132 if ( ! (ROFF_PARSED
& tokens
[tok
].flags
)) {
1135 if ( ! (*tree
->cb
.roffdata
)(tree
->arg
, i
, *argv
++))
1139 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1143 * Deal with punctuation. Ugly. Work ahead until we encounter
1144 * terminating punctuation. If we encounter it and all
1145 * subsequent tokens are punctuation, then stop processing (the
1146 * line-dominant macro will print these tokens after closure).
1151 if (ROFF_MAX
== (c
= rofffindcallable(*argv
))) {
1152 if ( ! roffispunct(*argv
)) {
1153 if ( ! (*tree
->cb
.roffdata
)
1154 (tree
->arg
, i
, *argv
++))
1160 /* See if only punctuation remains. */
1163 for (j
= 0; argv
[j
]; j
++)
1164 if ( ! roffispunct(argv
[j
]))
1168 if ( ! (*tree
->cb
.roffdata
)
1169 (tree
->arg
, 0, *argv
++))
1174 /* Only punctuation remains. */
1176 if ( ! (*tree
->cb
.roffout
)(tree
->arg
, tok
))
1182 * A sub-command has been found. Execute it and
1183 * discontinue parsing for arguments. If we're
1184 * line-scoped, then close out after it returns; if we
1185 * aren't, then close out before.
1188 if (NULL
== tokens
[c
].cb
) {
1189 roff_err(tree
, *argv
, "unsupported macro `%s'",
1194 if ( ! (ROFF_LSCOPE
& tokens
[tok
].flags
))
1195 if ( ! (*tree
->cb
.roffout
)(tree
->arg
, tok
))
1198 if ( ! (*tokens
[c
].cb
)(c
, tree
, argv
, ROFF_ENTER
))
1201 if (ROFF_LSCOPE
& tokens
[tok
].flags
)
1202 if ( ! (*tree
->cb
.roffout
)(tree
->arg
, tok
))
1209 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1214 * We're the line-dominant macro. Check if there's remaining
1215 * punctuation. If there is, then flush it out before exiting.
1223 if ( ! roffispunct(argv
[--i
]))
1226 while (i
>= 0 && roffispunct(argv
[i
]))
1234 if ( ! (*tree
->cb
.roffdata
)(tree
->arg
, 0, argv
[i
++]))
1243 roff_noop(ROFFCALL_ARGS
)
1252 roff_depr(ROFFCALL_ARGS
)
1255 roff_err(tree
, *argv
, "`%s' is deprecated", toknames
[tok
]);
1261 roff_warn(const struct rofftree
*tree
, const char *pos
, char *fmt
, ...)
1267 (void)vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
1270 (*tree
->cb
.roffmsg
)(tree
->arg
,
1271 ROFF_WARN
, tree
->cur
, pos
, buf
);
1276 roff_err(const struct rofftree
*tree
, const char *pos
, char *fmt
, ...)
1282 (void)vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
1285 (*tree
->cb
.roffmsg
)(tree
->arg
,
1286 ROFF_ERROR
, tree
->cur
, pos
, buf
);