]>
git.cameronkatri.com Git - mandoc.git/blob - roff.c
1 /* $Id: roff.c,v 1.14 2008/11/28 11:21:12 kristaps Exp $ */
3 * Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the
7 * above copyright notice and this permission notice appear in all
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
28 #include "libmdocml.h"
31 /* FIXME: warn if Pp occurs before/after Sh etc. (see mdoc.samples). */
33 /* FIXME: warn about "X section only" macros. */
35 /* FIXME: warn about empty lists. */
37 /* FIXME: ; : } ) (etc.) after text macros? */
39 /* FIXME: NAME section needs specific elements. */
41 #define ROFF_MAXARG 32
55 #define ROFFCALL_ARGS \
56 int tok, struct rofftree *tree, \
57 const char *argv[], enum roffd type
62 int (*cb
)(ROFFCALL_ARGS
); /* Callback. */
63 const int *args
; /* Args (or NULL). */
67 enum rofftype type
; /* Type of macro. */
69 #define ROFF_PARSED (1 << 0) /* "Parsed". */
70 #define ROFF_CALLABLE (1 << 1) /* "Callable". */
71 #define ROFF_QUOTES (1 << 2) /* Quoted args. */
72 #define ROFF_SHALLOW (1 << 3) /* Nesting block. */
77 #define ROFF_VALUE (1 << 0) /* Has a value. */
81 int tok
; /* Token id. */
82 struct roffnode
*parent
; /* Parent (or NULL). */
83 size_t line
; /* Parsed at line. */
87 struct roffnode
*last
; /* Last parsed node. */
90 time_t date
; /* `Dd' results. */
91 char os
[64]; /* `Os' results. */
92 char title
[64]; /* `Dt' results. */
93 char section
[64]; /* `Dt' results. */
94 char volume
[64]; /* `Dt' results. */
97 #define ROFF_PRELUDE (1 << 1) /* In roff prelude. */
98 #define ROFF_PRELUDE_Os (1 << 2) /* `Os' is parsed. */
99 #define ROFF_PRELUDE_Dt (1 << 3) /* `Dt' is parsed. */
100 #define ROFF_PRELUDE_Dd (1 << 4) /* `Dd' is parsed. */
101 #define ROFF_BODY (1 << 5) /* In roff body. */
103 struct md_mbuf
*mbuf
; /* Output (or NULL). */
104 const struct md_args
*args
; /* Global args. */
105 const struct md_rbuf
*rbuf
; /* Input. */
106 const struct roffcb
*cb
;
109 static int roff_Dd(ROFFCALL_ARGS
);
110 static int roff_Dt(ROFFCALL_ARGS
);
111 static int roff_Os(ROFFCALL_ARGS
);
113 static int roff_layout(ROFFCALL_ARGS
);
114 static int roff_text(ROFFCALL_ARGS
);
115 static int roff_comment(ROFFCALL_ARGS
);
116 static int roff_close(ROFFCALL_ARGS
);
117 static int roff_special(ROFFCALL_ARGS
);
119 static struct roffnode
*roffnode_new(int, struct rofftree
*);
120 static void roffnode_free(int, 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, const char ***, char **);
136 static int roffparse(struct rofftree
*, char *, size_t);
137 static int textparse(const struct rofftree
*,
138 const char *, size_t);
141 static const int roffarg_An
[] = { ROFF_Split
, ROFF_Nosplit
,
143 static const int roffarg_Bd
[] = { ROFF_Ragged
, ROFF_Unfilled
,
144 ROFF_Literal
, ROFF_File
, ROFF_Offset
, ROFF_Filled
,
145 ROFF_Compact
, ROFF_ARGMAX
};
146 static const int roffarg_Bk
[] = { ROFF_Words
, ROFF_ARGMAX
};
147 static const int roffarg_Ex
[] = { ROFF_Std
, ROFF_ARGMAX
};
148 static const int roffarg_Rv
[] = { ROFF_Std
, ROFF_ARGMAX
};
149 static const int roffarg_Bl
[] = { ROFF_Bullet
, ROFF_Dash
,
150 ROFF_Hyphen
, ROFF_Item
, ROFF_Enum
, ROFF_Tag
, ROFF_Diag
,
151 ROFF_Hang
, ROFF_Ohang
, ROFF_Inset
, ROFF_Column
, ROFF_Offset
,
152 ROFF_Width
, ROFF_Compact
, ROFF_ARGMAX
};
153 static const int roffarg_St
[] = {
154 ROFF_p1003_1_88
, ROFF_p1003_1_90
, ROFF_p1003_1_96
,
155 ROFF_p1003_1_2001
, ROFF_p1003_1_2004
, ROFF_p1003_1
,
156 ROFF_p1003_1b
, ROFF_p1003_1b_93
, ROFF_p1003_1c_95
,
157 ROFF_p1003_1g_2000
, ROFF_p1003_2_92
, ROFF_p1387_2_95
,
158 ROFF_p1003_2
, ROFF_p1387_2
, ROFF_isoC_90
, ROFF_isoC_amd1
,
159 ROFF_isoC_tcor1
, ROFF_isoC_tcor2
, ROFF_isoC_99
, ROFF_ansiC
,
160 ROFF_ansiC_89
, ROFF_ansiC_99
, ROFF_ieee754
, ROFF_iso8802_3
,
161 ROFF_xpg3
, ROFF_xpg4
, ROFF_xpg4_2
, ROFF_xpg4_3
, ROFF_xbd5
,
162 ROFF_xcu5
, ROFF_xsh5
, ROFF_xns5
, ROFF_xns5_2d2_0
,
163 ROFF_xcurses4_2
, ROFF_susv2
, ROFF_susv3
, ROFF_svid4
,
166 static const int roffchild_Bl
[] = { ROFF_It
, ROFF_El
, ROFF_MAX
};
167 static const int roffchild_Fo
[] = { ROFF_Fa
, ROFF_Fc
, ROFF_MAX
};
168 static const int roffchild_Oo
[] = { ROFF_Op
, ROFF_Oc
, 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_Oc
, 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_comment
, 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
, ROFF_QUOTES
}, /* Os */
185 { roff_layout
, NULL
, NULL
, NULL
, ROFF_Sh
, ROFF_LAYOUT
, ROFF_PARSED
}, /* Sh */
186 { roff_layout
, NULL
, NULL
, NULL
, ROFF_Ss
, ROFF_LAYOUT
, ROFF_PARSED
}, /* Ss */
187 { roff_text
, NULL
, NULL
, NULL
, ROFF_Pp
, ROFF_TEXT
, 0 }, /* Pp */
188 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* D1 */
189 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Dl */
190 { roff_layout
, roffarg_Bd
, NULL
, NULL
, 0, ROFF_LAYOUT
, 0 }, /* Bd */
191 { roff_close
, NULL
, NULL
, NULL
, ROFF_Bd
, ROFF_LAYOUT
, 0 }, /* Ed */
192 { roff_layout
, roffarg_Bl
, NULL
, roffchild_Bl
, 0, ROFF_LAYOUT
, 0 }, /* Bl */
193 { roff_close
, NULL
, roffparent_El
, NULL
, ROFF_Bl
, ROFF_LAYOUT
, 0 }, /* El */
194 { roff_layout
, NULL
, roffparent_It
, NULL
, ROFF_It
, ROFF_LAYOUT
, ROFF_SHALLOW
}, /* It */
195 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ad */
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
, ROFF_QUOTES
}, /* 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
, ROFF_QUOTES
}, /* 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
}, /* Op */
215 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Ot */ /* XXX deprecated */
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
}, /* 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 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Bf */ /* FIXME */
239 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Bo */
240 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* 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 {roff_special
, 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
}, /* Dq */
247 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ec */
248 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Ef */ /* FIXME */
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 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Pf */
259 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_LAYOUT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Po */
260 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* 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_layout
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Qo */
264 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Qq */
265 { roff_close
, 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
}, /* Sq */
270 {roff_special
, 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_close
, NULL
, roffparent_Fc
, NULL
, ROFF_Fo
, ROFF_LAYOUT
, 0 }, /* Fc */
279 { roff_layout
, NULL
, NULL
, roffchild_Oo
, 0, ROFF_LAYOUT
, 0 }, /* Oo */
280 { roff_close
, NULL
, roffparent_Oc
, NULL
, ROFF_Oo
, ROFF_LAYOUT
, 0 }, /* Oc */
281 { roff_layout
, roffarg_Bk
, NULL
, NULL
, 0, ROFF_LAYOUT
, 0 }, /* Bk */
282 { roff_close
, NULL
, NULL
, NULL
, ROFF_Bk
, ROFF_LAYOUT
, 0 }, /* Ek */
285 /* Table of all known token arguments. */
286 static const int tokenargs
[ROFF_ARGMAX
] = {
288 0, ROFF_VALUE
, ROFF_VALUE
, 0,
304 const char *const toknamesp
[ROFF_MAX
] = {
305 "\\\"", "Dd", "Dt", "Os",
306 "Sh", "Ss", "Pp", "D1",
307 "Dl", "Bd", "Ed", "Bl",
308 "El", "It", "Ad", "An",
309 "Ar", "Cd", "Cm", "Dv",
310 "Er", "Ev", "Ex", "Fa",
311 "Fd", "Fl", "Fn", "Ft",
312 "Ic", "In", "Li", "Nd",
313 "Nm", "Op", "Ot", "Pa",
314 "Rv", "St", "Va", "Vt",
315 "Xr", "\%A", "\%B", "\%D",
316 "\%I", "\%J", "\%N", "\%O",
317 "\%P", "\%R", "\%T", "\%V",
318 "Ac", "Ao", "Aq", "At",
319 "Bc", "Bf", "Bo", "Bq",
320 "Bsx", "Bx", "Db", "Dc",
321 "Do", "Dq", "Ec", "Ef",
322 "Em", "Eo", "Fx", "Ms",
323 "No", "Ns", "Nx", "Ox",
324 "Pc", "Pf", "Po", "Pq",
325 "Qc", "Ql", "Qo", "Qq",
326 "Re", "Rs", "Sc", "So",
327 "Sq", "Sm", "Sx", "Sy",
328 "Tn", "Ux", "Xc", "Xo",
329 "Fo", "Fc", "Oo", "Oc",
333 const char *const tokargnamesp
[ROFF_ARGMAX
] = {
334 "split", "nosplit", "ragged",
335 "unfilled", "literal", "file",
336 "offset", "bullet", "dash",
337 "hyphen", "item", "enum",
338 "tag", "diag", "hang",
339 "ohang", "inset", "column",
340 "width", "compact", "std",
341 "p1003.1-88", "p1003.1-90", "p1003.1-96",
342 "p1003.1-2001", "p1003.1-2004", "p1003.1",
343 "p1003.1b", "p1003.1b-93", "p1003.1c-95",
344 "p1003.1g-2000", "p1003.2-92", "p1387.2-95",
345 "p1003.2", "p1387.2", "isoC-90",
346 "isoC-amd1", "isoC-tcor1", "isoC-tcor2",
347 "isoC-99", "ansiC", "ansiC-89",
348 "ansiC-99", "ieee754", "iso8802-3",
349 "xpg3", "xpg4", "xpg4.2",
350 "xpg4.3", "xbd5", "xcu5",
351 "xsh5", "xns5", "xns5.2d2.0",
352 "xcurses4.2", "susv2", "susv3",
353 "svid4", "filled", "words",
356 const char *const *toknames
= toknamesp
;
357 const char *const *tokargnames
= tokargnamesp
;
361 roff_free(struct rofftree
*tree
, int flush
)
371 if (tree
->last
->parent
) {
372 tok
= tree
->last
->tok
;
373 if (tokens
[tok
].ctx
== 0) {
374 warnx("%s: closing out explicit scope "
375 "of `%s' from line %zu",
382 if ( ! (*tokens
[tree
->last
->tok
].cb
)
383 (tree
->last
->tok
, tree
, NULL
, ROFF_EXIT
))
384 /* Disallow flushing. */
388 error
= tree
->mbuf
? 0 : 1;
390 if (tree
->mbuf
&& (ROFF_PRELUDE
& tree
->state
)) {
391 warnx("%s: prelude never finished",
397 return(error
? 0 : 1);
402 roff_alloc(const struct md_args
*args
, struct md_mbuf
*out
,
403 const struct md_rbuf
*in
, const struct roffcb
*cb
)
405 struct rofftree
*tree
;
407 if (NULL
== (tree
= calloc(1, sizeof(struct rofftree
))))
410 tree
->state
= ROFF_PRELUDE
;
421 roff_engine(struct rofftree
*tree
, char *buf
, size_t sz
)
427 roff_warn(tree
, buf
, "blank line");
429 } else if ('.' != *buf
)
430 return(textparse(tree
, buf
, sz
));
432 return(roffparse(tree
, buf
, sz
));
437 textparse(const struct rofftree
*tree
, const char *buf
, size_t sz
)
446 roffargs(const struct rofftree
*tree
,
447 int tok
, char *buf
, char **argv
)
452 assert(tok
>= 0 && tok
< ROFF_MAX
);
458 for (i
= 0; *buf
&& i
< ROFF_MAXARG
; i
++) {
461 while (*buf
&& '\"' != *buf
)
464 roff_err(tree
, argv
[i
], "unclosed "
472 while (*buf
&& ! isspace(*buf
))
478 while (*buf
&& isspace(*buf
))
483 if (ROFF_MAXARG
== i
&& *buf
) {
484 roff_err(tree
, p
, "too many arguments for `%s'", toknames
496 roffscan(int tok
, const int *tokv
)
502 for ( ; ROFF_MAX
!= *tokv
; tokv
++)
511 roffparse(struct rofftree
*tree
, char *buf
, size_t sz
)
515 char *argv
[ROFF_MAXARG
];
520 if (ROFF_MAX
== (tok
= rofffindtok(buf
+ 1))) {
521 roff_err(tree
, buf
+ 1, "bogus line macro");
523 } else if (NULL
== tokens
[tok
].cb
) {
524 roff_err(tree
, buf
+ 1, "unsupported macro `%s'",
527 } else if (ROFF_COMMENT
== tokens
[tok
].type
)
530 if ( ! roffargs(tree
, tok
, buf
, argv
))
533 argvp
= (const char **)argv
;
536 * Prelude macros break some assumptions, so branch now.
539 if (ROFF_PRELUDE
& tree
->state
) {
540 assert(NULL
== tree
->last
);
541 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
545 assert(ROFF_BODY
& tree
->state
);
548 * First check that our possible parents and parent's possible
549 * children are satisfied.
552 if ( ! roffscan(tree
->last
->tok
, tokens
[tok
].parents
)) {
553 roff_err(tree
, *argvp
, "`%s' has invalid parent `%s' "
554 "from line %zu", toknames
[tok
],
555 toknames
[tree
->rbuf
->line
],
560 if ( ! roffscan(tok
, tokens
[tree
->last
->tok
].children
)) {
561 roff_err(tree
, *argvp
, "`%s' is invalid child for "
562 "`%s' from line %zu", toknames
[tok
],
563 toknames
[tree
->rbuf
->line
],
569 * Branch if we're not a layout token.
572 if (ROFF_LAYOUT
!= tokens
[tok
].type
)
573 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
576 * Check our scope rules.
579 if (0 == tokens
[tok
].ctx
)
580 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
583 * First consider implicit-end tags, like as follows:
586 * In this, we want to close the scope of the NAME section. If
587 * there's an intermediary implicit-end tag, such as
591 * then it must be closed as well.
594 if (tok
== tokens
[tok
].ctx
) {
596 * First search up to the point where we must close.
597 * If one doesn't exist, then we can open a new scope.
600 for (n
= tree
->last
; n
; n
= n
->parent
) {
601 assert(0 == tokens
[n
->tok
].ctx
||
602 n
->tok
== tokens
[n
->tok
].ctx
);
605 if (ROFF_SHALLOW
& tokens
[tok
].flags
) {
612 * Create a new scope, as no previous one exists to
617 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
620 * Close out all intermediary scoped blocks, then hang
621 * the current scope from our predecessor's parent.
626 if ( ! (*tokens
[t
].cb
)(t
, tree
, NULL
, ROFF_EXIT
))
630 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
634 * Now consider explicit-end tags, where we want to close back
635 * to a specific tag. Example:
639 * In this, the `El' tag closes out the scope of `Bl'.
643 assert(tok
!= tokens
[tok
].ctx
&& 0 != tokens
[tok
].ctx
);
648 if ( ! (*tokens
[t
].cb
)(t
, tree
, NULL
, ROFF_EXIT
))
650 } while (t
!= tokens
[tok
].ctx
);
658 rofffindarg(const char *name
)
662 /* FIXME: use a table, this is slow but ok for now. */
665 for (i
= 0; i
< ROFF_ARGMAX
; i
++)
667 if (0 == strcmp(name
, tokargnames
[i
]))
675 rofffindtok(const char *buf
)
680 for (i
= 0; *buf
&& ! isspace(*buf
) && i
< 3; i
++, buf
++)
688 /* FIXME: use a table, this is slow but ok for now. */
691 for (i
= 0; i
< ROFF_MAX
; i
++)
693 if (0 == strcmp(toknames
[i
], token
))
701 rofffindcallable(const char *name
)
705 if (ROFF_MAX
== (c
= rofffindtok(name
)))
707 assert(c
>= 0 && c
< ROFF_MAX
);
708 return(ROFF_CALLABLE
& tokens
[c
].flags
? c
: ROFF_MAX
);
712 static struct roffnode
*
713 roffnode_new(int tokid
, struct rofftree
*tree
)
717 if (NULL
== (p
= malloc(sizeof(struct roffnode
))))
720 p
->line
= tree
->rbuf
->line
;
722 p
->parent
= tree
->last
;
730 roffargok(int tokid
, int argid
)
734 if (NULL
== (c
= tokens
[tokid
].args
))
737 for ( ; ROFF_ARGMAX
!= *c
; c
++)
746 roffnode_free(int tokid
, struct rofftree
*tree
)
751 assert(tree
->last
->tok
== tokid
);
754 tree
->last
= tree
->last
->parent
;
760 roffnextopt(const struct rofftree
*tree
, int tok
,
761 const char ***in
, char **val
)
763 const char *arg
, **argv
;
770 if (NULL
== (arg
= *argv
))
775 if (ROFF_ARGMAX
== (v
= rofffindarg(arg
+ 1))) {
776 roff_warn(tree
, arg
, "argument-like parameter `%s' to "
777 "`%s'", &arg
[1], toknames
[tok
]);
781 if ( ! roffargok(tok
, v
)) {
782 roff_warn(tree
, arg
, "invalid argument parameter `%s' to "
783 "`%s'", tokargnames
[v
], toknames
[tok
]);
787 if ( ! (ROFF_VALUE
& tokenargs
[v
]))
793 roff_err(tree
, arg
, "empty value of `%s' for `%s'",
794 tokargnames
[v
], toknames
[tok
]);
804 roff_Dd(ROFFCALL_ARGS
)
807 if (ROFF_BODY
& tree
->state
) {
808 assert( ! (ROFF_PRELUDE
& tree
->state
));
809 assert(ROFF_PRELUDE_Dd
& tree
->state
);
810 return(roff_text(tok
, tree
, argv
, type
));
813 assert(ROFF_PRELUDE
& tree
->state
);
814 assert( ! (ROFF_BODY
& tree
->state
));
816 if (ROFF_PRELUDE_Dd
& tree
->state
) {
817 roff_err(tree
, *argv
, "repeated `Dd' in prelude");
819 } else if (ROFF_PRELUDE_Dt
& tree
->state
) {
820 roff_err(tree
, *argv
, "out-of-order `Dd' in prelude");
824 /* TODO: parse date. */
826 assert(NULL
== tree
->last
);
827 tree
->state
|= ROFF_PRELUDE_Dd
;
835 roff_Dt(ROFFCALL_ARGS
)
838 if (ROFF_BODY
& tree
->state
) {
839 assert( ! (ROFF_PRELUDE
& tree
->state
));
840 assert(ROFF_PRELUDE_Dt
& tree
->state
);
841 return(roff_text(tok
, tree
, argv
, type
));
844 assert(ROFF_PRELUDE
& tree
->state
);
845 assert( ! (ROFF_BODY
& tree
->state
));
847 if ( ! (ROFF_PRELUDE_Dd
& tree
->state
)) {
848 roff_err(tree
, *argv
, "out-of-order `Dt' in prelude");
850 } else if (ROFF_PRELUDE_Dt
& tree
->state
) {
851 roff_err(tree
, *argv
, "repeated `Dt' in prelude");
855 /* TODO: parse date. */
857 assert(NULL
== tree
->last
);
858 tree
->state
|= ROFF_PRELUDE_Dt
;
866 roff_Os(ROFFCALL_ARGS
)
869 if (ROFF_EXIT
== type
) {
870 assert(ROFF_PRELUDE_Os
& tree
->state
);
871 return(roff_layout(tok
, tree
, argv
, type
));
872 } else if (ROFF_BODY
& tree
->state
) {
873 assert( ! (ROFF_PRELUDE
& tree
->state
));
874 assert(ROFF_PRELUDE_Os
& tree
->state
);
875 return(roff_text(tok
, tree
, argv
, type
));
878 assert(ROFF_PRELUDE
& tree
->state
);
879 if ( ! (ROFF_PRELUDE_Dt
& tree
->state
) ||
880 ! (ROFF_PRELUDE_Dd
& tree
->state
)) {
881 roff_err(tree
, *argv
, "out-of-order `Os' in prelude");
885 /* TODO: extract OS. */
887 tree
->state
|= ROFF_PRELUDE_Os
;
888 tree
->state
&= ~ROFF_PRELUDE
;
889 tree
->state
|= ROFF_BODY
;
891 assert(NULL
== tree
->last
);
893 return(roff_layout(tok
, tree
, argv
, type
));
899 roff_layout(ROFFCALL_ARGS
)
901 int i
, c
, argcp
[ROFF_MAXARG
];
902 char *v
, *argvp
[ROFF_MAXARG
];
904 if (ROFF_PRELUDE
& tree
->state
) {
905 roff_err(tree
, *argv
, "`%s' disallowed in prelude",
910 if (ROFF_EXIT
== type
) {
911 roffnode_free(tok
, tree
);
912 return((*tree
->cb
->roffblkout
)(tree
->args
, tok
));
920 while (-1 != (c
= roffnextopt(tree
, tok
, &argv
, &v
))) {
921 if (ROFF_ARGMAX
== c
)
930 argcp
[i
] = ROFF_ARGMAX
;
933 if (NULL
== roffnode_new(tok
, tree
))
936 if ( ! (*tree
->cb
->roffin
)(tree
->args
, tok
, argcp
, argvp
))
939 if ( ! (ROFF_PARSED
& tokens
[tok
].flags
)) {
941 if ( ! md_buf_putstring(tree
->mbuf
, *argv
++))
943 if ( ! md_buf_putchar(tree
->mbuf
, ' '))
947 if ( ! md_buf_putchar(tree
->mbuf
, '\n'))
950 if ( ! ((*tree
->cb
->roffout
)(tree
->args
, tok
)))
952 return((*tree
->cb
->roffblkin
)(tree
->args
, tok
));
956 if (ROFF_MAX
!= (c
= rofffindcallable(*argv
))) {
957 if (NULL
== tokens
[c
].cb
) {
958 roff_err(tree
, *argv
, "unsupported "
963 if ( ! (*tokens
[c
].cb
)(c
, tree
, argv
, ROFF_ENTER
))
968 if ( ! md_buf_putstring(tree
->mbuf
, *argv
++))
970 if ( ! md_buf_putchar(tree
->mbuf
, ' '))
974 if (NULL
== *argv
&& ! md_buf_putchar(tree
->mbuf
, '\n'))
977 if ( ! ((*tree
->cb
->roffout
)(tree
->args
, tok
)))
979 return((*tree
->cb
->roffblkin
)(tree
->args
, tok
));
985 roff_text(ROFFCALL_ARGS
)
987 int i
, c
, argcp
[ROFF_MAXARG
];
988 char *v
, *argvp
[ROFF_MAXARG
];
992 if (ROFF_PRELUDE
& tree
->state
) {
993 roff_err(tree
, *argv
, "`%s' disallowed in prelude",
1001 while (-1 != (c
= roffnextopt(tree
, tok
, &argv
, &v
))) {
1002 if (ROFF_ARGMAX
== c
)
1011 argcp
[i
] = ROFF_ARGMAX
;
1014 if ( ! (*tree
->cb
->roffin
)(tree
->args
, tok
, argcp
, argvp
))
1017 if ( ! (ROFF_PARSED
& tokens
[tok
].flags
)) {
1019 if ( ! md_buf_putstring(tree
->mbuf
, *argv
++))
1021 if ( ! md_buf_putchar(tree
->mbuf
, ' '))
1024 if ( ! md_buf_putchar(tree
->mbuf
, '\n'))
1026 return((*tree
->cb
->roffout
)(tree
->args
, tok
));
1030 if (ROFF_MAX
!= (c
= rofffindcallable(*argv
))) {
1031 if (NULL
== tokens
[c
].cb
) {
1032 roff_err(tree
, *argv
, "unsupported "
1037 if ( ! (*tokens
[c
].cb
)(c
, tree
,
1043 if ( ! md_buf_putstring(tree
->mbuf
, *argv
++))
1045 if ( ! md_buf_putchar(tree
->mbuf
, ' '))
1049 if (NULL
== *argv
&& ! md_buf_putchar(tree
->mbuf
, '\n'))
1052 return((*tree
->cb
->roffout
)(tree
->args
, tok
));
1058 roff_comment(ROFFCALL_ARGS
)
1067 roff_close(ROFFCALL_ARGS
)
1076 roff_special(ROFFCALL_ARGS
)
1079 return((*tree
->cb
->roffspecial
)(tree
->args
, tok
));
1084 roff_warn(const struct rofftree
*tree
, const char *pos
, char *fmt
, ...)
1090 (void)vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
1093 (*tree
->cb
->roffmsg
)(tree
->args
, ROFF_WARN
, tree
->cur
, pos
,
1094 tree
->rbuf
->name
, tree
->rbuf
->line
, buf
);
1099 roff_err(const struct rofftree
*tree
, const char *pos
, char *fmt
, ...)
1105 (void)vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
1108 (*tree
->cb
->roffmsg
)(tree
->args
, ROFF_ERROR
, tree
->cur
, pos
,
1109 tree
->rbuf
->name
, tree
->rbuf
->line
, buf
);