]>
git.cameronkatri.com Git - mandoc.git/blob - roff.c
1 /* $Id: roff.c,v 1.27 2008/12/01 15:32:36 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 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 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 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Ef */ /* FIXME */
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_LAYOUT
, 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_warn(tree
, NULL
, "prelude never finished");
387 for (n
= tree
->last
; n
->parent
; n
= n
->parent
) {
388 if (0 != tokens
[n
->tok
].ctx
)
390 roff_warn(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
));
560 assert(ROFF_BODY
& tree
->state
);
563 * First check that our possible parents and parent's possible
564 * children are satisfied.
567 if ( ! roffscan(tree
->last
->tok
, tokens
[tok
].parents
)) {
568 roff_err(tree
, *argvp
, "`%s' has invalid parent `%s'",
570 toknames
[tree
->last
->tok
]);
574 if ( ! roffscan(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
) {
620 * Create a new scope, as no previous one exists to
625 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
628 * Close out all intermediary scoped blocks, then hang
629 * the current scope from our predecessor's parent.
634 if ( ! (*tokens
[t
].cb
)(t
, tree
, NULL
, ROFF_EXIT
))
638 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
642 * Now consider explicit-end tags, where we want to close back
643 * to a specific tag. Example:
647 * In this, the `El' tag closes out the scope of `Bl'.
651 assert(tok
!= tokens
[tok
].ctx
&& 0 != tokens
[tok
].ctx
);
656 if ( ! (*tokens
[t
].cb
)(t
, tree
, NULL
, ROFF_EXIT
))
658 } while (t
!= tokens
[tok
].ctx
);
666 rofffindarg(const char *name
)
670 /* FIXME: use a table, this is slow but ok for now. */
673 for (i
= 0; i
< ROFF_ARGMAX
; i
++)
675 if (0 == strcmp(name
, tokargnames
[i
]))
683 rofffindtok(const char *buf
)
688 for (i
= 0; *buf
&& ! isspace(*buf
) && i
< 3; i
++, buf
++)
696 /* FIXME: use a table, this is slow but ok for now. */
699 for (i
= 0; i
< ROFF_MAX
; i
++)
701 if (0 == strcmp(toknames
[i
], token
))
709 roffispunct(const char *p
)
751 rofffindcallable(const char *name
)
755 if (ROFF_MAX
== (c
= rofffindtok(name
)))
757 assert(c
>= 0 && c
< ROFF_MAX
);
758 return(ROFF_CALLABLE
& tokens
[c
].flags
? c
: ROFF_MAX
);
762 static struct roffnode
*
763 roffnode_new(int tokid
, struct rofftree
*tree
)
767 if (NULL
== (p
= malloc(sizeof(struct roffnode
))))
771 p
->parent
= tree
->last
;
779 roffargok(int tokid
, int argid
)
783 if (NULL
== (c
= tokens
[tokid
].args
))
786 for ( ; ROFF_ARGMAX
!= *c
; c
++)
795 roffnode_free(struct rofftree
*tree
)
802 tree
->last
= tree
->last
->parent
;
808 roffnextopt(const struct rofftree
*tree
, int tok
,
809 char ***in
, char **val
)
818 if (NULL
== (arg
= *argv
))
823 if (ROFF_ARGMAX
== (v
= rofffindarg(arg
+ 1))) {
824 roff_warn(tree
, arg
, "argument-like parameter `%s' to "
825 "`%s'", &arg
[1], toknames
[tok
]);
829 if ( ! roffargok(tok
, v
)) {
830 roff_warn(tree
, arg
, "invalid argument parameter `%s' to "
831 "`%s'", tokargnames
[v
], toknames
[tok
]);
835 if ( ! (ROFF_VALUE
& tokenargs
[v
]))
841 roff_err(tree
, arg
, "empty value of `%s' for `%s'",
842 tokargnames
[v
], toknames
[tok
]);
851 roffparseopts(struct rofftree
*tree
, int tok
,
852 char ***args
, int *argc
, char **argv
)
859 while (-1 != (c
= roffnextopt(tree
, tok
, args
, &v
))) {
860 if (ROFF_ARGMAX
== c
)
869 argc
[i
] = ROFF_ARGMAX
;
877 roff_Dd(ROFFCALL_ARGS
)
880 if (ROFF_BODY
& tree
->state
) {
881 assert( ! (ROFF_PRELUDE
& tree
->state
));
882 assert(ROFF_PRELUDE_Dd
& tree
->state
);
883 return(roff_text(tok
, tree
, argv
, type
));
886 assert(ROFF_PRELUDE
& tree
->state
);
887 assert( ! (ROFF_BODY
& tree
->state
));
889 if (ROFF_PRELUDE_Dd
& tree
->state
) {
890 roff_err(tree
, *argv
, "repeated `Dd' in prelude");
892 } else if (ROFF_PRELUDE_Dt
& tree
->state
) {
893 roff_err(tree
, *argv
, "out-of-order `Dd' in prelude");
897 /* TODO: parse date. */
899 assert(NULL
== tree
->last
);
900 tree
->state
|= ROFF_PRELUDE_Dd
;
908 roff_Dt(ROFFCALL_ARGS
)
911 if (ROFF_BODY
& tree
->state
) {
912 assert( ! (ROFF_PRELUDE
& tree
->state
));
913 assert(ROFF_PRELUDE_Dt
& tree
->state
);
914 return(roff_text(tok
, tree
, argv
, type
));
917 assert(ROFF_PRELUDE
& tree
->state
);
918 assert( ! (ROFF_BODY
& tree
->state
));
920 if ( ! (ROFF_PRELUDE_Dd
& tree
->state
)) {
921 roff_err(tree
, *argv
, "out-of-order `Dt' in prelude");
923 } else if (ROFF_PRELUDE_Dt
& tree
->state
) {
924 roff_err(tree
, *argv
, "repeated `Dt' in prelude");
928 /* TODO: parse date. */
930 assert(NULL
== tree
->last
);
931 tree
->state
|= ROFF_PRELUDE_Dt
;
939 roff_Os(ROFFCALL_ARGS
)
942 if (ROFF_EXIT
== type
) {
944 return((*tree
->cb
.rofftail
)(tree
->arg
));
945 } else if (ROFF_BODY
& tree
->state
) {
946 assert( ! (ROFF_PRELUDE
& tree
->state
));
947 assert(ROFF_PRELUDE_Os
& tree
->state
);
948 return(roff_text(tok
, tree
, argv
, type
));
951 assert(ROFF_PRELUDE
& tree
->state
);
952 if ( ! (ROFF_PRELUDE_Dt
& tree
->state
) ||
953 ! (ROFF_PRELUDE_Dd
& tree
->state
)) {
954 roff_err(tree
, *argv
, "out-of-order `Os' in prelude");
958 /* TODO: extract OS. */
960 tree
->state
|= ROFF_PRELUDE_Os
;
961 tree
->state
&= ~ROFF_PRELUDE
;
962 tree
->state
|= ROFF_BODY
;
964 assert(NULL
== tree
->last
);
966 if (NULL
== roffnode_new(tok
, tree
))
969 return((*tree
->cb
.roffhead
)(tree
->arg
));
975 roff_layout(ROFFCALL_ARGS
)
977 int i
, c
, argcp
[ROFF_MAXARG
];
978 char *argvp
[ROFF_MAXARG
];
980 if (ROFF_PRELUDE
& tree
->state
) {
981 roff_err(tree
, *argv
, "bad `%s' in prelude",
984 } else if (ROFF_EXIT
== type
) {
986 return((*tree
->cb
.roffblkout
)(tree
->arg
, tok
));
989 assert( ! (ROFF_CALLABLE
& tokens
[tok
].flags
));
993 if ( ! roffparseopts(tree
, tok
, &argv
, argcp
, argvp
))
995 if (NULL
== roffnode_new(tok
, tree
))
999 * Layouts have two parts: the layout body and header. The
1000 * layout header is the trailing text of the line macro, while
1001 * the layout body is everything following until termination.
1004 if ( ! (*tree
->cb
.roffblkin
)(tree
->arg
, tok
, argcp
, argvp
))
1008 if ( ! (*tree
->cb
.roffin
)(tree
->arg
, tok
, argcp
, argvp
))
1012 * If there are no parsable parts, then write remaining tokens
1013 * into the layout header and exit.
1016 if ( ! (ROFF_PARSED
& tokens
[tok
].flags
)) {
1019 if ( ! (*tree
->cb
.roffdata
)(tree
->arg
, i
, *argv
++))
1023 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1027 * Parsable elements may be in the header (or be the header, for
1028 * that matter). Follow the regular parsing rules for these.
1033 if (ROFF_MAX
== (c
= rofffindcallable(*argv
))) {
1035 if ( ! (*tree
->cb
.roffdata
)
1036 (tree
->arg
, i
, *argv
++))
1042 if (NULL
== tokens
[c
].cb
) {
1043 roff_err(tree
, *argv
, "unsupported macro `%s'",
1048 if ( ! (*tokens
[c
].cb
)(c
, tree
, argv
, ROFF_ENTER
))
1055 * If there's trailing punctuation in the header, then write it
1056 * out now. Here we mimic the behaviour of a line-dominant text
1061 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1064 * Expensive. Scan to the end of line then work backwards until
1065 * a token isn't punctuation.
1073 if ( ! roffispunct(argv
[--i
]))
1074 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1076 while (i
>= 0 && roffispunct(argv
[i
]))
1084 if ( ! (*tree
->cb
.roffdata
)(tree
->arg
, 0, argv
[i
++]))
1087 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1093 roff_text(ROFFCALL_ARGS
)
1095 int i
, j
, first
, c
, argcp
[ROFF_MAXARG
];
1096 char *argvp
[ROFF_MAXARG
];
1098 if (ROFF_PRELUDE
& tree
->state
) {
1099 roff_err(tree
, *argv
, "`%s' disallowed in prelude",
1104 first
= (*argv
== tree
->cur
);
1107 if ( ! roffparseopts(tree
, tok
, &argv
, argcp
, argvp
))
1109 if ( ! (*tree
->cb
.roffin
)(tree
->arg
, tok
, argcp
, argvp
))
1112 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1114 if ( ! (ROFF_PARSED
& tokens
[tok
].flags
)) {
1117 if ( ! (*tree
->cb
.roffdata
)(tree
->arg
, i
, *argv
++))
1121 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1125 * Deal with punctuation. Ugly. Work ahead until we encounter
1126 * terminating punctuation. If we encounter it and all
1127 * subsequent tokens are punctuation, then stop processing (the
1128 * line-dominant macro will print these tokens after closure).
1133 if (ROFF_MAX
== (c
= rofffindcallable(*argv
))) {
1134 if ( ! roffispunct(*argv
)) {
1135 if ( ! (*tree
->cb
.roffdata
)
1136 (tree
->arg
, i
, *argv
++))
1142 /* See if only punctuation remains. */
1145 for (j
= 0; argv
[j
]; j
++)
1146 if ( ! roffispunct(argv
[j
]))
1150 if ( ! (*tree
->cb
.roffdata
)
1151 (tree
->arg
, 0, *argv
++))
1156 /* Only punctuation remains. */
1158 if ( ! (*tree
->cb
.roffout
)(tree
->arg
, tok
))
1164 * A sub-command has been found. Execute it and
1165 * discontinue parsing for arguments. If we're
1166 * line-scoped, then close out after it returns; if we
1167 * aren't, then close out before.
1170 if (NULL
== tokens
[c
].cb
) {
1171 roff_err(tree
, *argv
, "unsupported macro `%s'",
1176 if ( ! (ROFF_LSCOPE
& tokens
[tok
].flags
))
1177 if ( ! (*tree
->cb
.roffout
)(tree
->arg
, tok
))
1180 if ( ! (*tokens
[c
].cb
)(c
, tree
, argv
, ROFF_ENTER
))
1183 if (ROFF_LSCOPE
& tokens
[tok
].flags
)
1184 if ( ! (*tree
->cb
.roffout
)(tree
->arg
, tok
))
1191 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1196 * We're the line-dominant macro. Check if there's remaining
1197 * punctuation. If there is, then flush it out before exiting.
1205 if ( ! roffispunct(argv
[--i
]))
1208 while (i
>= 0 && roffispunct(argv
[i
]))
1216 if ( ! (*tree
->cb
.roffdata
)(tree
->arg
, 0, argv
[i
++]))
1225 roff_noop(ROFFCALL_ARGS
)
1234 roff_depr(ROFFCALL_ARGS
)
1237 roff_err(tree
, *argv
, "`%s' is deprecated", toknames
[tok
]);
1243 roff_warn(const struct rofftree
*tree
, const char *pos
, char *fmt
, ...)
1249 (void)vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
1252 (*tree
->cb
.roffmsg
)(tree
->arg
,
1253 ROFF_WARN
, tree
->cur
, pos
, buf
);
1258 roff_err(const struct rofftree
*tree
, const char *pos
, char *fmt
, ...)
1264 (void)vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
1267 (*tree
->cb
.roffmsg
)(tree
->arg
,
1268 ROFF_ERROR
, tree
->cur
, pos
, buf
);