]> git.cameronkatri.com Git - mandoc.git/blob - roff.c
Added `Sm' functionality.
[mandoc.git] / roff.c
1 /* $Id: roff.c,v 1.33 2008/12/02 13:20:24 kristaps Exp $ */
2 /*
3 * Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
4 *
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
8 * copies.
9 *
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.
18 */
19 #include <sys/param.h>
20 #include <sys/types.h>
21
22 #include <assert.h>
23 #include <ctype.h>
24 #include <err.h>
25 #include <stdarg.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <time.h>
30
31 #include "libmdocml.h"
32 #include "private.h"
33
34 /* FIXME: First letters of quoted-text interpreted in rofffindtok. */
35 /* FIXME: `No' not implemented. */
36 /* TODO: warn if Pp occurs before/after Sh etc. (see mdoc.samples). */
37 /* TODO: warn about "X section only" macros. */
38 /* TODO: warn about empty lists. */
39 /* TODO: (warn) some sections need specific elements. */
40 /* TODO: (warn) NAME section has particular order. */
41 /* TODO: unify empty-content tags a la <br />. */
42 /* TODO: macros with a set number of arguments? */
43
44 #define ROFF_MAXARG 32
45
46 enum roffd {
47 ROFF_ENTER = 0,
48 ROFF_EXIT
49 };
50
51 enum rofftype {
52 ROFF_COMMENT,
53 ROFF_TEXT,
54 ROFF_LAYOUT,
55 ROFF_SPECIAL
56 };
57
58 #define ROFFCALL_ARGS \
59 int tok, struct rofftree *tree, \
60 char *argv[], enum roffd type
61
62 struct rofftree;
63
64 struct rofftok {
65 int (*cb)(ROFFCALL_ARGS); /* Callback. */
66 const int *args; /* Args (or NULL). */
67 const int *parents; /* Limit to parents. */
68 const int *children; /* Limit to kids. */
69 int ctx; /* Blk-close node. */
70 enum rofftype type; /* Type of macro. */
71 int flags;
72 #define ROFF_PARSED (1 << 0) /* "Parsed". */
73 #define ROFF_CALLABLE (1 << 1) /* "Callable". */
74 #define ROFF_SHALLOW (1 << 2) /* Nesting block. */
75 #define ROFF_LSCOPE (1 << 3) /* Line scope. */
76 };
77
78 struct roffarg {
79 int flags;
80 #define ROFF_VALUE (1 << 0) /* Has a value. */
81 };
82
83 struct roffnode {
84 int tok; /* Token id. */
85 struct roffnode *parent; /* Parent (or NULL). */
86 };
87
88 struct rofftree {
89 struct roffnode *last; /* Last parsed node. */
90 char *cur; /* Line start. */
91 struct tm tm; /* `Dd' results. */
92 char os[64]; /* `Os' results. */
93 char title[64]; /* `Dt' results. */
94 char section[64]; /* `Dt' results. */
95 char volume[64]; /* `Dt' results. */
96 int state;
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. */
102 struct roffcb cb; /* Callbacks. */
103 void *arg; /* Callbacks' arg. */
104 };
105
106 static int roff_Dd(ROFFCALL_ARGS);
107 static int roff_Dt(ROFFCALL_ARGS);
108 static int roff_Os(ROFFCALL_ARGS);
109 static int roff_Ns(ROFFCALL_ARGS);
110 static int roff_Sm(ROFFCALL_ARGS);
111 static int roff_layout(ROFFCALL_ARGS);
112 static int roff_text(ROFFCALL_ARGS);
113 static int roff_noop(ROFFCALL_ARGS);
114 static int roff_depr(ROFFCALL_ARGS);
115 static struct roffnode *roffnode_new(int, struct rofftree *);
116 static void roffnode_free(struct rofftree *);
117 static void roff_warn(const struct rofftree *,
118 const char *, char *, ...);
119 static void roff_err(const struct rofftree *,
120 const char *, char *, ...);
121 static int roffpurgepunct(struct rofftree *, char **);
122 static int roffscan(int, const int *);
123 static int rofffindtok(const char *);
124 static int rofffindarg(const char *);
125 static int rofffindcallable(const char *);
126 static int roffargs(const struct rofftree *,
127 int, char *, char **);
128 static int roffargok(int, int);
129 static int roffnextopt(const struct rofftree *,
130 int, char ***, char **);
131 static int roffparseopts(struct rofftree *, int,
132 char ***, int *, char **);
133 static int roffcall(struct rofftree *, int, char **);
134 static int roffparse(struct rofftree *, char *);
135 static int textparse(const struct rofftree *, char *);
136
137 #ifdef __linux__
138 static size_t strlcat(char *, const char *, size_t);
139 static size_t strlcpy(char *, const char *, size_t);
140 extern int vsnprintf(char *, size_t,
141 const char *, va_list);
142 extern char *strptime(const char *, const char *,
143 struct tm *);
144 #endif
145
146
147 static const int roffarg_An[] = { ROFF_Split, ROFF_Nosplit, ROFF_ARGMAX };
148 static const int roffarg_Bd[] = { ROFF_Ragged, ROFF_Unfilled, ROFF_Literal,
149 ROFF_File, ROFF_Offset, ROFF_Filled, ROFF_Compact, ROFF_ARGMAX };
150 static const int roffarg_Bk[] = { ROFF_Words, ROFF_ARGMAX };
151 static const int roffarg_Ex[] = { ROFF_Std, ROFF_ARGMAX };
152 static const int roffarg_Rv[] = { ROFF_Std, ROFF_ARGMAX };
153 static const int roffarg_Bl[] = { ROFF_Bullet, ROFF_Dash, ROFF_Hyphen,
154 ROFF_Item, ROFF_Enum, ROFF_Tag, ROFF_Diag, ROFF_Hang, ROFF_Ohang,
155 ROFF_Inset, ROFF_Column, ROFF_Offset, ROFF_Width, ROFF_Compact,
156 ROFF_ARGMAX };
157 static const int roffarg_St[] = { ROFF_p1003_1_88, ROFF_p1003_1_90,
158 ROFF_p1003_1_96, ROFF_p1003_1_2001, ROFF_p1003_1_2004, ROFF_p1003_1,
159 ROFF_p1003_1b, ROFF_p1003_1b_93, ROFF_p1003_1c_95, ROFF_p1003_1g_2000,
160 ROFF_p1003_2_92, ROFF_p1387_2_95, ROFF_p1003_2, ROFF_p1387_2,
161 ROFF_isoC_90, ROFF_isoC_amd1, ROFF_isoC_tcor1, ROFF_isoC_tcor2,
162 ROFF_isoC_99, ROFF_ansiC, ROFF_ansiC_89, ROFF_ansiC_99, ROFF_ieee754,
163 ROFF_iso8802_3, ROFF_xpg3, ROFF_xpg4, ROFF_xpg4_2, ROFF_xpg4_3,
164 ROFF_xbd5, ROFF_xcu5, ROFF_xsh5, ROFF_xns5, ROFF_xns5_2d2_0,
165 ROFF_xcurses4_2, ROFF_susv2, ROFF_susv3, ROFF_svid4, ROFF_ARGMAX };
166
167 static const int roffchild_Bl[] = { ROFF_It, ROFF_El, ROFF_MAX };
168 static const int roffchild_Fo[] = { ROFF_Fa, ROFF_Fc, ROFF_MAX };
169 static const int roffchild_Rs[] = { ROFF_Re, ROFF__A, ROFF__B, ROFF__D,
170 ROFF__I, ROFF__J, ROFF__N, ROFF__O, ROFF__P, ROFF__R, ROFF__T, ROFF__V,
171 ROFF_MAX };
172
173 static const int roffparent_El[] = { ROFF_Bl, ROFF_It, ROFF_MAX };
174 static const int roffparent_Fc[] = { ROFF_Fo, ROFF_Fa, ROFF_MAX };
175 static const int roffparent_Oc[] = { ROFF_Oo, ROFF_MAX };
176 static const int roffparent_It[] = { ROFF_Bl, ROFF_It, ROFF_MAX };
177 static const int roffparent_Re[] = { ROFF_Rs, ROFF_MAX };
178
179 static const struct rofftok tokens[ROFF_MAX] = {
180 { roff_noop, NULL, NULL, NULL, 0, ROFF_COMMENT, 0 }, /* \" */
181 { roff_Dd, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Dd */
182 { roff_Dt, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Dt */
183 { roff_Os, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Os */
184 { roff_layout, NULL, NULL, NULL, ROFF_Sh, ROFF_LAYOUT, 0 }, /* Sh */
185 { roff_layout, NULL, NULL, NULL, ROFF_Ss, ROFF_LAYOUT, 0 }, /* Ss */
186 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Pp */ /* XXX 0 args */
187 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_LSCOPE }, /* D1 */
188 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_LSCOPE }, /* Dl */
189 { roff_layout, roffarg_Bd, NULL, NULL, 0, ROFF_LAYOUT, 0 }, /* Bd */
190 { roff_noop, NULL, NULL, NULL, ROFF_Bd, ROFF_LAYOUT, 0 }, /* Ed */
191 { roff_layout, roffarg_Bl, NULL, roffchild_Bl, 0, ROFF_LAYOUT, 0 }, /* Bl */
192 { roff_noop, NULL, roffparent_El, NULL, ROFF_Bl, ROFF_LAYOUT, 0 }, /* El */
193 { roff_layout, NULL, roffparent_It, NULL, ROFF_It, ROFF_LAYOUT, ROFF_PARSED | ROFF_SHALLOW }, /* It */
194 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ad */ /* FIXME */
195 { roff_text, roffarg_An, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* An */
196 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ar */
197 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Cd */ /* XXX man.4 only */
198 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Cm */
199 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Dv */ /* XXX needs arg */
200 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Er */ /* XXX needs arg */
201 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ev */ /* XXX needs arg */
202 { roff_text, roffarg_Ex, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Ex */
203 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Fa */ /* XXX needs arg */
204 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Fd */
205 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Fl */
206 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Fn */ /* XXX needs arg */ /* FIXME */
207 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Ft */
208 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ic */ /* XXX needs arg */
209 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* In */
210 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Li */
211 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Nd */
212 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Nm */ /* FIXME */
213 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE | ROFF_LSCOPE }, /* Op */
214 { roff_depr, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Ot */
215 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Pa */
216 { roff_text, roffarg_Rv, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Rv */
217 { roff_text, roffarg_St, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* St */
218 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Va */
219 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Vt */ /* XXX needs arg */
220 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Xr */ /* XXX needs arg */
221 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* %A */
222 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE}, /* %B */
223 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* %D */
224 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE}, /* %I */
225 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE}, /* %J */
226 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* %N */
227 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* %O */
228 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* %P */
229 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* %R */
230 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* %T */
231 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* %V */
232 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ac */
233 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ao */
234 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE | ROFF_LSCOPE }, /* Aq */
235 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* At */ /* XXX at most 2 args */
236 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Bc */
237 { roff_layout, NULL, NULL, NULL, 0, ROFF_LAYOUT, 0 }, /* Bf */ /* FIXME */
238 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Bo */
239 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE | ROFF_LSCOPE }, /* Bq */
240 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Bsx */
241 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Bx */
242 { NULL, NULL, NULL, NULL, 0, ROFF_SPECIAL, 0 }, /* Db */
243 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Dc */
244 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Do */
245 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE | ROFF_LSCOPE }, /* Dq */
246 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ec */
247 { roff_noop, NULL, NULL, NULL, ROFF_Bf, ROFF_LAYOUT, 0 }, /* Ef */
248 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Em */ /* XXX needs arg */
249 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Eo */
250 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Fx */
251 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Ms */
252 { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* No */
253 { roff_Ns, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ns */
254 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Nx */
255 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Ox */
256 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Pc */
257 { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Pf */
258 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Po */
259 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE | ROFF_LSCOPE }, /* Pq */
260 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Qc */
261 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ql */
262 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Qo */
263 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE | ROFF_LSCOPE }, /* Qq */
264 { roff_noop, NULL, roffparent_Re, NULL, ROFF_Rs, ROFF_LAYOUT, 0 }, /* Re */
265 { roff_layout, NULL, NULL, roffchild_Rs, 0, ROFF_LAYOUT, 0 }, /* Rs */
266 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Sc */
267 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* So */
268 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE | ROFF_LSCOPE }, /* Sq */
269 { roff_Sm, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Sm */
270 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Sx */
271 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Sy */
272 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Tn */
273 { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Ux */
274 { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Xc */
275 { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Xo */
276 { roff_layout, NULL, NULL, roffchild_Fo, 0, ROFF_LAYOUT, 0 }, /* Fo */
277 { roff_noop, NULL, roffparent_Fc, NULL, ROFF_Fo, ROFF_LAYOUT, 0 }, /* Fc */
278 { roff_layout, NULL, NULL, NULL, 0, ROFF_LAYOUT, 0 }, /* Oo */
279 { roff_noop, NULL, roffparent_Oc, NULL, ROFF_Oo, ROFF_LAYOUT, 0 }, /* Oc */
280 { roff_layout, roffarg_Bk, NULL, NULL, 0, ROFF_LAYOUT, 0 }, /* Bk */
281 { roff_noop, NULL, NULL, NULL, ROFF_Bk, ROFF_LAYOUT, 0 }, /* Ek */
282 { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Bt */
283 { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Hf */
284 { roff_depr, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Fr */
285 { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Ud */
286 };
287
288 static const int tokenargs[ROFF_ARGMAX] = {
289 0, 0, 0, 0,
290 0, ROFF_VALUE, ROFF_VALUE, 0,
291 0, 0, 0, 0,
292 0, 0, 0, 0,
293 0, 0, ROFF_VALUE, 0,
294 0, 0, 0, 0,
295 0, 0, 0, 0,
296 0, 0, 0, 0,
297 0, 0, 0, 0,
298 0, 0, 0, 0,
299 0, 0, 0, 0,
300 0, 0, 0, 0,
301 0, 0, 0, 0,
302 0, 0, 0, 0,
303 0, 0, 0, 0,
304 };
305
306 const char *const toknamesp[ROFF_MAX] = {
307 "\\\"", "Dd", "Dt", "Os",
308 "Sh", "Ss", "Pp", "D1",
309 "Dl", "Bd", "Ed", "Bl",
310 "El", "It", "Ad", "An",
311 "Ar", "Cd", "Cm", "Dv",
312 "Er", "Ev", "Ex", "Fa",
313 "Fd", "Fl", "Fn", "Ft",
314 "Ic", "In", "Li", "Nd",
315 "Nm", "Op", "Ot", "Pa",
316 "Rv", "St", "Va", "Vt",
317 /* LINTED */
318 "Xr", "\%A", "\%B", "\%D",
319 /* LINTED */
320 "\%I", "\%J", "\%N", "\%O",
321 /* LINTED */
322 "\%P", "\%R", "\%T", "\%V",
323 "Ac", "Ao", "Aq", "At",
324 "Bc", "Bf", "Bo", "Bq",
325 "Bsx", "Bx", "Db", "Dc",
326 "Do", "Dq", "Ec", "Ef",
327 "Em", "Eo", "Fx", "Ms",
328 "No", "Ns", "Nx", "Ox",
329 "Pc", "Pf", "Po", "Pq",
330 "Qc", "Ql", "Qo", "Qq",
331 "Re", "Rs", "Sc", "So",
332 "Sq", "Sm", "Sx", "Sy",
333 "Tn", "Ux", "Xc", "Xo",
334 "Fo", "Fc", "Oo", "Oc",
335 "Bk", "Ek", "Bt", "Hf",
336 "Fr", "Ud",
337 };
338
339 const char *const tokargnamesp[ROFF_ARGMAX] = {
340 "split", "nosplit", "ragged",
341 "unfilled", "literal", "file",
342 "offset", "bullet", "dash",
343 "hyphen", "item", "enum",
344 "tag", "diag", "hang",
345 "ohang", "inset", "column",
346 "width", "compact", "std",
347 "p1003.1-88", "p1003.1-90", "p1003.1-96",
348 "p1003.1-2001", "p1003.1-2004", "p1003.1",
349 "p1003.1b", "p1003.1b-93", "p1003.1c-95",
350 "p1003.1g-2000", "p1003.2-92", "p1387.2-95",
351 "p1003.2", "p1387.2", "isoC-90",
352 "isoC-amd1", "isoC-tcor1", "isoC-tcor2",
353 "isoC-99", "ansiC", "ansiC-89",
354 "ansiC-99", "ieee754", "iso8802-3",
355 "xpg3", "xpg4", "xpg4.2",
356 "xpg4.3", "xbd5", "xcu5",
357 "xsh5", "xns5", "xns5.2d2.0",
358 "xcurses4.2", "susv2", "susv3",
359 "svid4", "filled", "words",
360 };
361
362 const char *const *toknames = toknamesp;
363 const char *const *tokargnames = tokargnamesp;
364
365
366 int
367 roff_free(struct rofftree *tree, int flush)
368 {
369 int error, t;
370 struct roffnode *n;
371
372 error = 0;
373
374 if ( ! flush)
375 goto end;
376
377 error = 1;
378
379 if (ROFF_PRELUDE & tree->state) {
380 roff_err(tree, NULL, "prelude never finished");
381 goto end;
382 }
383
384 for (n = tree->last; n; n = n->parent) {
385 if (0 != tokens[n->tok].ctx)
386 continue;
387 roff_err(tree, NULL, "closing explicit scope `%s'",
388 toknames[n->tok]);
389 goto end;
390 }
391
392 while (tree->last) {
393 t = tree->last->tok;
394 if ( ! (*tokens[t].cb)(t, tree, NULL, ROFF_EXIT))
395 goto end;
396 }
397
398 if ( ! (*tree->cb.rofftail)(tree->arg))
399 goto end;
400
401 error = 0;
402
403 end:
404
405 while (tree->last)
406 roffnode_free(tree);
407
408 free(tree);
409
410 return(error ? 0 : 1);
411 }
412
413
414 struct rofftree *
415 roff_alloc(const struct roffcb *cb, void *args)
416 {
417 struct rofftree *tree;
418
419 assert(args);
420 assert(cb);
421
422 if (NULL == (tree = calloc(1, sizeof(struct rofftree))))
423 err(1, "calloc");
424
425 tree->state = ROFF_PRELUDE;
426 tree->arg = args;
427
428 (void)memcpy(&tree->cb, cb, sizeof(struct roffcb));
429
430 return(tree);
431 }
432
433
434 int
435 roff_engine(struct rofftree *tree, char *buf)
436 {
437
438 tree->cur = buf;
439 assert(buf);
440
441 if (0 == *buf) {
442 roff_err(tree, buf, "blank line");
443 return(0);
444 } else if ('.' != *buf)
445 return(textparse(tree, buf));
446
447 return(roffparse(tree, buf));
448 }
449
450
451 static int
452 textparse(const struct rofftree *tree, char *buf)
453 {
454
455 if ( ! (ROFF_BODY & tree->state)) {
456 roff_err(tree, buf, "data not in body");
457 return(0);
458 }
459 return((*tree->cb.roffdata)(tree->arg, 1, buf));
460 }
461
462
463 static int
464 roffargs(const struct rofftree *tree,
465 int tok, char *buf, char **argv)
466 {
467 int i;
468 char *p;
469
470 assert(tok >= 0 && tok < ROFF_MAX);
471 assert('.' == *buf);
472
473 p = buf;
474
475 /* LINTED */
476 for (i = 0; *buf && i < ROFF_MAXARG; i++) {
477 if ('\"' == *buf) {
478 argv[i] = ++buf;
479 while (*buf && '\"' != *buf)
480 buf++;
481 if (0 == *buf) {
482 roff_err(tree, argv[i], "unclosed "
483 "quote in argument "
484 "list for `%s'",
485 toknames[tok]);
486 return(0);
487 }
488 } else {
489 argv[i] = buf++;
490 while (*buf && ! isspace(*buf))
491 buf++;
492 if (0 == *buf)
493 continue;
494 }
495 *buf++ = 0;
496 while (*buf && isspace(*buf))
497 buf++;
498 }
499
500 assert(i > 0);
501 if (ROFF_MAXARG == i && *buf) {
502 roff_err(tree, p, "too many arguments for `%s'", toknames
503 [tok]);
504 return(0);
505 }
506
507 argv[i] = NULL;
508 return(1);
509 }
510
511
512 static int
513 roffscan(int tok, const int *tokv)
514 {
515
516 if (NULL == tokv)
517 return(1);
518
519 for ( ; ROFF_MAX != *tokv; tokv++)
520 if (tok == *tokv)
521 return(1);
522
523 return(0);
524 }
525
526
527 static int
528 roffparse(struct rofftree *tree, char *buf)
529 {
530 int tok, t;
531 struct roffnode *n;
532 char *argv[ROFF_MAXARG];
533 char **argvp;
534
535 if (0 != *buf && 0 != *(buf + 1) && 0 != *(buf + 2))
536 if (0 == strncmp(buf, ".\\\"", 3))
537 return(1);
538
539 if (ROFF_MAX == (tok = rofffindtok(buf + 1))) {
540 roff_err(tree, buf + 1, "bogus line macro");
541 return(0);
542 } else if (NULL == tokens[tok].cb) {
543 roff_err(tree, buf + 1, "unsupported macro `%s'",
544 toknames[tok]);
545 return(0);
546 }
547
548 assert(ROFF___ != tok);
549 if ( ! roffargs(tree, tok, buf, argv))
550 return(0);
551
552 argvp = (char **)argv;
553
554 /*
555 * Prelude macros break some assumptions, so branch now.
556 */
557
558 if (ROFF_PRELUDE & tree->state) {
559 assert(NULL == tree->last);
560 return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER));
561 }
562
563 assert(ROFF_BODY & tree->state);
564
565 /*
566 * First check that our possible parents and parent's possible
567 * children are satisfied.
568 */
569
570 if (tree->last && ! roffscan
571 (tree->last->tok, tokens[tok].parents)) {
572 roff_err(tree, *argvp, "`%s' has invalid parent `%s'",
573 toknames[tok],
574 toknames[tree->last->tok]);
575 return(0);
576 }
577
578 if (tree->last && ! roffscan
579 (tok, tokens[tree->last->tok].children)) {
580 roff_err(tree, *argvp, "`%s' is invalid child of `%s'",
581 toknames[tok],
582 toknames[tree->last->tok]);
583 return(0);
584 }
585
586 /*
587 * Branch if we're not a layout token.
588 */
589
590 if (ROFF_LAYOUT != tokens[tok].type)
591 return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER));
592 if (0 == tokens[tok].ctx)
593 return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER));
594
595 /*
596 * First consider implicit-end tags, like as follows:
597 * .Sh SECTION 1
598 * .Sh SECTION 2
599 * In this, we want to close the scope of the NAME section. If
600 * there's an intermediary implicit-end tag, such as
601 * .Sh SECTION 1
602 * .Ss Subsection 1
603 * .Sh SECTION 2
604 * then it must be closed as well.
605 */
606
607 if (tok == tokens[tok].ctx) {
608 /*
609 * First search up to the point where we must close.
610 * If one doesn't exist, then we can open a new scope.
611 */
612
613 for (n = tree->last; n; n = n->parent) {
614 assert(0 == tokens[n->tok].ctx ||
615 n->tok == tokens[n->tok].ctx);
616 if (n->tok == tok)
617 break;
618 if (ROFF_SHALLOW & tokens[tok].flags) {
619 n = NULL;
620 break;
621 }
622 if (tokens[n->tok].ctx == n->tok)
623 continue;
624 roff_err(tree, *argv, "`%s' breaks `%s' scope",
625 toknames[tok], toknames[n->tok]);
626 return(0);
627 }
628
629 /*
630 * Create a new scope, as no previous one exists to
631 * close out.
632 */
633
634 if (NULL == n)
635 return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER));
636
637 /*
638 * Close out all intermediary scoped blocks, then hang
639 * the current scope from our predecessor's parent.
640 */
641
642 do {
643 t = tree->last->tok;
644 if ( ! (*tokens[t].cb)(t, tree, NULL, ROFF_EXIT))
645 return(0);
646 } while (t != tok);
647
648 return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER));
649 }
650
651 /*
652 * Now consider explicit-end tags, where we want to close back
653 * to a specific tag. Example:
654 * .Bl
655 * .It Item.
656 * .El
657 * In this, the `El' tag closes out the scope of `Bl'.
658 */
659
660 assert(tok != tokens[tok].ctx && 0 != tokens[tok].ctx);
661
662 /* LINTED */
663 for (n = tree->last; n; n = n->parent)
664 if (n->tok != tokens[tok].ctx) {
665 if (n->tok == tokens[n->tok].ctx)
666 continue;
667 roff_err(tree, *argv, "`%s' breaks `%s' scope",
668 toknames[tok], toknames[n->tok]);
669 return(0);
670 } else
671 break;
672
673
674 if (NULL == n) {
675 roff_err(tree, *argv, "`%s' has no starting tag `%s'",
676 toknames[tok],
677 toknames[tokens[tok].ctx]);
678 return(0);
679 }
680
681 /* LINTED */
682 do {
683 t = tree->last->tok;
684 if ( ! (*tokens[t].cb)(t, tree, NULL, ROFF_EXIT))
685 return(0);
686 } while (t != tokens[tok].ctx);
687
688 return(1);
689 }
690
691
692 static int
693 rofffindarg(const char *name)
694 {
695 size_t i;
696
697 /* FIXME: use a table, this is slow but ok for now. */
698
699 /* LINTED */
700 for (i = 0; i < ROFF_ARGMAX; i++)
701 /* LINTED */
702 if (0 == strcmp(name, tokargnames[i]))
703 return((int)i);
704
705 return(ROFF_ARGMAX);
706 }
707
708
709 static int
710 rofffindtok(const char *buf)
711 {
712 char token[4];
713 int i;
714
715 for (i = 0; *buf && ! isspace(*buf) && i < 3; i++, buf++)
716 token[i] = *buf;
717
718 if (i == 3)
719 return(ROFF_MAX);
720
721 token[i] = 0;
722
723 /* FIXME: use a table, this is slow but ok for now. */
724
725 /* LINTED */
726 for (i = 0; i < ROFF_MAX; i++)
727 /* LINTED */
728 if (0 == strcmp(toknames[i], token))
729 return((int)i);
730
731 return(ROFF_MAX);
732 }
733
734
735 static int
736 roffispunct(const char *p)
737 {
738
739 if (0 == *p)
740 return(0);
741 if (0 != *(p + 1))
742 return(0);
743
744 switch (*p) {
745 case('{'):
746 /* FALLTHROUGH */
747 case('.'):
748 /* FALLTHROUGH */
749 case(','):
750 /* FALLTHROUGH */
751 case(';'):
752 /* FALLTHROUGH */
753 case(':'):
754 /* FALLTHROUGH */
755 case('?'):
756 /* FALLTHROUGH */
757 case('!'):
758 /* FALLTHROUGH */
759 case('('):
760 /* FALLTHROUGH */
761 case(')'):
762 /* FALLTHROUGH */
763 case('['):
764 /* FALLTHROUGH */
765 case(']'):
766 /* FALLTHROUGH */
767 case('}'):
768 return(1);
769 default:
770 break;
771 }
772
773 return(0);
774 }
775
776
777 static int
778 rofffindcallable(const char *name)
779 {
780 int c;
781
782 if (ROFF_MAX == (c = rofffindtok(name)))
783 return(ROFF_MAX);
784 assert(c >= 0 && c < ROFF_MAX);
785 return(ROFF_CALLABLE & tokens[c].flags ? c : ROFF_MAX);
786 }
787
788
789 static struct roffnode *
790 roffnode_new(int tokid, struct rofftree *tree)
791 {
792 struct roffnode *p;
793
794 if (NULL == (p = malloc(sizeof(struct roffnode))))
795 err(1, "malloc");
796
797 p->tok = tokid;
798 p->parent = tree->last;
799 tree->last = p;
800
801 return(p);
802 }
803
804
805 static int
806 roffargok(int tokid, int argid)
807 {
808 const int *c;
809
810 if (NULL == (c = tokens[tokid].args))
811 return(0);
812
813 for ( ; ROFF_ARGMAX != *c; c++)
814 if (argid == *c)
815 return(1);
816
817 return(0);
818 }
819
820
821 static void
822 roffnode_free(struct rofftree *tree)
823 {
824 struct roffnode *p;
825
826 assert(tree->last);
827
828 p = tree->last;
829 tree->last = tree->last->parent;
830 free(p);
831 }
832
833
834 static int
835 roffcall(struct rofftree *tree, int tok, char **argv)
836 {
837
838 if (NULL == tokens[tok].cb) {
839 roff_err(tree, *argv, "unsupported macro `%s'",
840 toknames[tok]);
841 return(0);
842 }
843 if ( ! (*tokens[tok].cb)(tok, tree, argv, ROFF_ENTER))
844 return(0);
845 return(1);
846 }
847
848
849 static int
850 roffnextopt(const struct rofftree *tree, int tok,
851 char ***in, char **val)
852 {
853 char *arg, **argv;
854 int v;
855
856 *val = NULL;
857 argv = *in;
858 assert(argv);
859
860 if (NULL == (arg = *argv))
861 return(-1);
862 if ('-' != *arg)
863 return(-1);
864
865 if (ROFF_ARGMAX == (v = rofffindarg(arg + 1))) {
866 roff_warn(tree, arg, "argument-like parameter `%s' to "
867 "`%s'", arg, toknames[tok]);
868 return(-1);
869 }
870
871 if ( ! roffargok(tok, v)) {
872 roff_warn(tree, arg, "invalid argument parameter `%s' to "
873 "`%s'", tokargnames[v], toknames[tok]);
874 return(-1);
875 }
876
877 if ( ! (ROFF_VALUE & tokenargs[v]))
878 return(v);
879
880 *in = ++argv;
881
882 if (NULL == *argv) {
883 roff_err(tree, arg, "empty value of `%s' for `%s'",
884 tokargnames[v], toknames[tok]);
885 return(ROFF_ARGMAX);
886 }
887
888 return(v);
889 }
890
891
892 static int
893 roffpurgepunct(struct rofftree *tree, char **argv)
894 {
895 int i;
896
897 i = 0;
898 while (argv[i])
899 i++;
900 assert(i > 0);
901 if ( ! roffispunct(argv[--i]))
902 return(1);
903 while (i >= 0 && roffispunct(argv[i]))
904 i--;
905 i++;
906
907 /* LINTED */
908 while (argv[i])
909 if ( ! (*tree->cb.roffdata)(tree->arg, 0, argv[i++]))
910 return(0);
911 return(1);
912 }
913
914
915 static int
916 roffparseopts(struct rofftree *tree, int tok,
917 char ***args, int *argc, char **argv)
918 {
919 int i, c;
920 char *v;
921
922 i = 0;
923
924 while (-1 != (c = roffnextopt(tree, tok, args, &v))) {
925 if (ROFF_ARGMAX == c)
926 return(0);
927
928 argc[i] = c;
929 argv[i] = v;
930 i++;
931 *args = *args + 1;
932 }
933
934 argc[i] = ROFF_ARGMAX;
935 argv[i] = NULL;
936 return(1);
937 }
938
939
940 /* ARGSUSED */
941 static int
942 roff_Dd(ROFFCALL_ARGS)
943 {
944 time_t t;
945 char *p, buf[32];
946
947 if (ROFF_BODY & tree->state) {
948 assert( ! (ROFF_PRELUDE & tree->state));
949 assert(ROFF_PRELUDE_Dd & tree->state);
950 return(roff_text(tok, tree, argv, type));
951 }
952
953 assert(ROFF_PRELUDE & tree->state);
954 assert( ! (ROFF_BODY & tree->state));
955
956 if (ROFF_PRELUDE_Dd & tree->state) {
957 roff_err(tree, *argv, "repeated `Dd' in prelude");
958 return(0);
959 } else if (ROFF_PRELUDE_Dt & tree->state) {
960 roff_err(tree, *argv, "out-of-order `Dd' in prelude");
961 return(0);
962 }
963
964 assert(NULL == tree->last);
965
966 argv++;
967
968 if (0 == strcmp(*argv, "$Mdocdate: December 2 2008 $")) {
969 t = time(NULL);
970 if (NULL == localtime_r(&t, &tree->tm))
971 err(1, "localtime_r");
972 tree->state |= ROFF_PRELUDE_Dd;
973 return(1);
974 }
975
976 /* Build this from Mdocdate or raw date. */
977
978 buf[0] = 0;
979 p = *argv;
980
981 if (0 != strcmp(*argv, "$Mdocdate:")) {
982 while (*argv) {
983 if (strlcat(buf, *argv++, sizeof(buf))
984 < sizeof(buf))
985 continue;
986 roff_err(tree, p, "bad `Dd' date");
987 return(0);
988 }
989 if (strptime(buf, "%b%d,%Y", &tree->tm)) {
990 tree->state |= ROFF_PRELUDE_Dd;
991 return(1);
992 }
993 roff_err(tree, *argv, "bad `Dd' date");
994 return(0);
995 }
996
997 argv++;
998 while (*argv && **argv != '$') {
999 if (strlcat(buf, *argv++, sizeof(buf))
1000 >= sizeof(buf)) {
1001 roff_err(tree, p, "bad `Dd' Mdocdate");
1002 return(0);
1003 }
1004 if (strlcat(buf, " ", sizeof(buf))
1005 >= sizeof(buf)) {
1006 roff_err(tree, p, "bad `Dd' Mdocdate");
1007 return(0);
1008 }
1009 }
1010 if (NULL == *argv) {
1011 roff_err(tree, p, "bad `Dd' Mdocdate");
1012 return(0);
1013 }
1014
1015 if (NULL == strptime(buf, "%b %d %Y", &tree->tm)) {
1016 roff_err(tree, *argv, "bad `Dd' Mdocdate");
1017 return(0);
1018 }
1019
1020 tree->state |= ROFF_PRELUDE_Dd;
1021 return(1);
1022 }
1023
1024
1025 /* ARGSUSED */
1026 static int
1027 roff_Dt(ROFFCALL_ARGS)
1028 {
1029
1030 if (ROFF_BODY & tree->state) {
1031 assert( ! (ROFF_PRELUDE & tree->state));
1032 assert(ROFF_PRELUDE_Dt & tree->state);
1033 return(roff_text(tok, tree, argv, type));
1034 }
1035
1036 assert(ROFF_PRELUDE & tree->state);
1037 assert( ! (ROFF_BODY & tree->state));
1038
1039 if ( ! (ROFF_PRELUDE_Dd & tree->state)) {
1040 roff_err(tree, *argv, "out-of-order `Dt' in prelude");
1041 return(0);
1042 } else if (ROFF_PRELUDE_Dt & tree->state) {
1043 roff_err(tree, *argv, "repeated `Dt' in prelude");
1044 return(0);
1045 }
1046
1047 argv++;
1048 if (NULL == *argv) {
1049 roff_err(tree, *argv, "`Dt' needs document title");
1050 return(0);
1051 } else if (strlcpy(tree->title, *argv, sizeof(tree->title))
1052 >= sizeof(tree->title)) {
1053 roff_err(tree, *argv, "`Dt' document title too long");
1054 return(0);
1055 }
1056
1057 argv++;
1058 if (NULL == *argv) {
1059 roff_err(tree, *argv, "`Dt' needs section");
1060 return(0);
1061 } else if (strlcpy(tree->section, *argv, sizeof(tree->section))
1062 >= sizeof(tree->section)) {
1063 roff_err(tree, *argv, "`Dt' section too long");
1064 return(0);
1065 }
1066
1067 argv++;
1068 if (NULL == *argv) {
1069 tree->volume[0] = 0;
1070 } else if (strlcpy(tree->volume, *argv, sizeof(tree->volume))
1071 >= sizeof(tree->volume)) {
1072 roff_err(tree, *argv, "`Dt' volume too long");
1073 return(0);
1074 }
1075
1076 assert(NULL == tree->last);
1077 tree->state |= ROFF_PRELUDE_Dt;
1078
1079 return(1);
1080 }
1081
1082
1083 /* ARGSUSED */
1084 static int
1085 roff_Sm(ROFFCALL_ARGS)
1086 {
1087 int argcp[1];
1088 char *argvp[1], *morep[1], *p;
1089
1090 p = *argv++;
1091
1092 argcp[0] = ROFF_ARGMAX;
1093 argvp[0] = NULL;
1094 if (NULL == (morep[0] = *argv++)) {
1095 roff_err(tree, p, "`Sm' expects an argument");
1096 return(0);
1097 } else if (0 != strcmp(morep[0], "on") &&
1098 0 != strcmp(morep[0], "off")) {
1099 roff_err(tree, p, "`Sm' has invalid argument");
1100 return(0);
1101 }
1102
1103 if (*argv)
1104 roff_warn(tree, *argv, "`Sm' shouldn't have arguments");
1105
1106 if ( ! (*tree->cb.roffspecial)(tree->arg,
1107 tok, argcp, argvp, morep))
1108 return(0);
1109
1110 while (*argv) {
1111 if ((*tree->cb.roffdata)(tree->arg, 1, *argv++))
1112 continue;
1113 return(0);
1114 }
1115
1116 return(1);
1117 }
1118
1119
1120 /* ARGSUSED */
1121 static int
1122 roff_Ns(ROFFCALL_ARGS)
1123 {
1124 int j, c, first;
1125 int argcp[1];
1126 char *argvp[1], *morep[1];
1127
1128 first = (*argv++ == tree->cur);
1129
1130 argcp[0] = ROFF_ARGMAX;
1131 argvp[0] = morep[0] = NULL;
1132
1133 if ( ! (*tree->cb.roffspecial)(tree->arg,
1134 tok, argcp, argvp, morep))
1135 return(0);
1136
1137 while (*argv) {
1138 if (ROFF_MAX != (c = rofffindcallable(*argv))) {
1139 if ( ! roffcall(tree, c, argv))
1140 return(0);
1141 break;
1142 }
1143
1144 if ( ! roffispunct(*argv)) {
1145 if ((*tree->cb.roffdata)(tree->arg, 1, *argv++))
1146 continue;
1147 return(0);
1148 }
1149 for (j = 0; argv[j]; j++)
1150 if ( ! roffispunct(argv[j]))
1151 break;
1152
1153 if (argv[j]) {
1154 if ((*tree->cb.roffdata)(tree->arg, 0, *argv++))
1155 continue;
1156 return(0);
1157 }
1158
1159 break;
1160 }
1161
1162 if ( ! first)
1163 return(1);
1164
1165 return(roffpurgepunct(tree, argv));
1166 }
1167
1168
1169 /* ARGSUSED */
1170 static int
1171 roff_Os(ROFFCALL_ARGS)
1172 {
1173 char *p;
1174
1175 if (ROFF_BODY & tree->state) {
1176 assert( ! (ROFF_PRELUDE & tree->state));
1177 assert(ROFF_PRELUDE_Os & tree->state);
1178 return(roff_text(tok, tree, argv, type));
1179 }
1180
1181 assert(ROFF_PRELUDE & tree->state);
1182 if ( ! (ROFF_PRELUDE_Dt & tree->state) ||
1183 ! (ROFF_PRELUDE_Dd & tree->state)) {
1184 roff_err(tree, *argv, "out-of-order `Os' in prelude");
1185 return(0);
1186 }
1187
1188 tree->os[0] = 0;
1189
1190 p = *++argv;
1191
1192 while (*argv) {
1193 if (strlcat(tree->os, *argv++, sizeof(tree->os))
1194 < sizeof(tree->os))
1195 continue;
1196 roff_err(tree, p, "`Os' value too long");
1197 return(0);
1198 }
1199
1200 if (0 == tree->os[0])
1201 if (strlcpy(tree->os, "LOCAL", sizeof(tree->os))
1202 >= sizeof(tree->os)) {
1203 roff_err(tree, p, "`Os' value too long");
1204 return(0);
1205 }
1206
1207 tree->state |= ROFF_PRELUDE_Os;
1208 tree->state &= ~ROFF_PRELUDE;
1209 tree->state |= ROFF_BODY;
1210
1211 assert(NULL == tree->last);
1212
1213 return((*tree->cb.roffhead)(tree->arg));
1214 }
1215
1216
1217 /* ARGSUSED */
1218 static int
1219 roff_layout(ROFFCALL_ARGS)
1220 {
1221 int i, c, argcp[ROFF_MAXARG];
1222 char *argvp[ROFF_MAXARG];
1223
1224 if (ROFF_PRELUDE & tree->state) {
1225 roff_err(tree, *argv, "bad `%s' in prelude",
1226 toknames[tok]);
1227 return(0);
1228 } else if (ROFF_EXIT == type) {
1229 roffnode_free(tree);
1230 return((*tree->cb.roffblkout)(tree->arg, tok));
1231 }
1232
1233 assert( ! (ROFF_CALLABLE & tokens[tok].flags));
1234
1235 ++argv;
1236
1237 if ( ! roffparseopts(tree, tok, &argv, argcp, argvp))
1238 return(0);
1239 if (NULL == roffnode_new(tok, tree))
1240 return(0);
1241
1242 /*
1243 * Layouts have two parts: the layout body and header. The
1244 * layout header is the trailing text of the line macro, while
1245 * the layout body is everything following until termination.
1246 */
1247
1248 if ( ! (*tree->cb.roffblkin)(tree->arg, tok, argcp, argvp))
1249 return(0);
1250 if (NULL == *argv)
1251 return(1);
1252 if ( ! (*tree->cb.roffin)(tree->arg, tok, argcp, argvp))
1253 return(0);
1254
1255 /*
1256 * If there are no parsable parts, then write remaining tokens
1257 * into the layout header and exit.
1258 */
1259
1260 if ( ! (ROFF_PARSED & tokens[tok].flags)) {
1261 i = 0;
1262 while (*argv) {
1263 if ( ! (*tree->cb.roffdata)(tree->arg, i, *argv++))
1264 return(0);
1265 i = 1;
1266 }
1267 return((*tree->cb.roffout)(tree->arg, tok));
1268 }
1269
1270 /*
1271 * Parsable elements may be in the header (or be the header, for
1272 * that matter). Follow the regular parsing rules for these.
1273 */
1274
1275 i = 0;
1276 while (*argv) {
1277 if (ROFF_MAX == (c = rofffindcallable(*argv))) {
1278 assert(tree->arg);
1279 if ( ! (*tree->cb.roffdata)
1280 (tree->arg, i, *argv++))
1281 return(0);
1282 i = 1;
1283 continue;
1284 }
1285 if ( ! roffcall(tree, c, argv))
1286 return(0);
1287 break;
1288 }
1289
1290 /*
1291 * If there's trailing punctuation in the header, then write it
1292 * out now. Here we mimic the behaviour of a line-dominant text
1293 * macro.
1294 */
1295
1296 if (NULL == *argv)
1297 return((*tree->cb.roffout)(tree->arg, tok));
1298
1299 /*
1300 * Expensive. Scan to the end of line then work backwards until
1301 * a token isn't punctuation.
1302 */
1303
1304 if ( ! roffpurgepunct(tree, argv))
1305 return(0);
1306
1307 return((*tree->cb.roffout)(tree->arg, tok));
1308 }
1309
1310
1311 /* ARGSUSED */
1312 static int
1313 roff_text(ROFFCALL_ARGS)
1314 {
1315 int i, j, first, c, argcp[ROFF_MAXARG];
1316 char *argvp[ROFF_MAXARG];
1317
1318 if (ROFF_PRELUDE & tree->state) {
1319 roff_err(tree, *argv, "`%s' disallowed in prelude",
1320 toknames[tok]);
1321 return(0);
1322 }
1323
1324 first = (*argv == tree->cur);
1325 argv++;
1326
1327 if ( ! roffparseopts(tree, tok, &argv, argcp, argvp))
1328 return(0);
1329 if ( ! (*tree->cb.roffin)(tree->arg, tok, argcp, argvp))
1330 return(0);
1331 if (NULL == *argv)
1332 return((*tree->cb.roffout)(tree->arg, tok));
1333
1334 if ( ! (ROFF_PARSED & tokens[tok].flags)) {
1335 i = 0;
1336 while (*argv) {
1337 if ( ! (*tree->cb.roffdata)(tree->arg, i, *argv++))
1338 return(0);
1339 i = 1;
1340 }
1341 return((*tree->cb.roffout)(tree->arg, tok));
1342 }
1343
1344 /*
1345 * Deal with punctuation. Ugly. Work ahead until we encounter
1346 * terminating punctuation. If we encounter it and all
1347 * subsequent tokens are punctuation, then stop processing (the
1348 * line-dominant macro will print these tokens after closure).
1349 */
1350
1351 i = 0;
1352 while (*argv) {
1353 if (ROFF_MAX != (c = rofffindcallable(*argv))) {
1354 if ( ! (ROFF_LSCOPE & tokens[tok].flags))
1355 if ( ! (*tree->cb.roffout)(tree->arg, tok))
1356 return(0);
1357
1358 if ( ! roffcall(tree, c, argv))
1359 return(0);
1360
1361 if (ROFF_LSCOPE & tokens[tok].flags)
1362 if ( ! (*tree->cb.roffout)(tree->arg, tok))
1363 return(0);
1364
1365 break;
1366 }
1367
1368 if ( ! roffispunct(*argv)) {
1369 if ( ! (*tree->cb.roffdata)(tree->arg, i, *argv++))
1370 return(0);
1371 i = 1;
1372 continue;
1373 }
1374
1375 i = 1;
1376 for (j = 0; argv[j]; j++)
1377 if ( ! roffispunct(argv[j]))
1378 break;
1379
1380 if (argv[j]) {
1381 if ( ! (*tree->cb.roffdata)(tree->arg, 0, *argv++))
1382 return(0);
1383 continue;
1384 }
1385
1386 if ( ! (*tree->cb.roffout)(tree->arg, tok))
1387 return(0);
1388 break;
1389 }
1390
1391 if (NULL == *argv)
1392 return((*tree->cb.roffout)(tree->arg, tok));
1393 if ( ! first)
1394 return(1);
1395
1396 return(roffpurgepunct(tree, argv));
1397 }
1398
1399
1400 /* ARGSUSED */
1401 static int
1402 roff_noop(ROFFCALL_ARGS)
1403 {
1404
1405 return(1);
1406 }
1407
1408
1409 /* ARGSUSED */
1410 static int
1411 roff_depr(ROFFCALL_ARGS)
1412 {
1413
1414 roff_err(tree, *argv, "`%s' is deprecated", toknames[tok]);
1415 return(0);
1416 }
1417
1418
1419 static void
1420 roff_warn(const struct rofftree *tree, const char *pos, char *fmt, ...)
1421 {
1422 va_list ap;
1423 char buf[128];
1424
1425 va_start(ap, fmt);
1426 (void)vsnprintf(buf, sizeof(buf), fmt, ap);
1427 va_end(ap);
1428
1429 (*tree->cb.roffmsg)(tree->arg,
1430 ROFF_WARN, tree->cur, pos, buf);
1431 }
1432
1433
1434 static void
1435 roff_err(const struct rofftree *tree, const char *pos, char *fmt, ...)
1436 {
1437 va_list ap;
1438 char buf[128];
1439
1440 va_start(ap, fmt);
1441 (void)vsnprintf(buf, sizeof(buf), fmt, ap);
1442 va_end(ap);
1443
1444 (*tree->cb.roffmsg)(tree->arg,
1445 ROFF_ERROR, tree->cur, pos, buf);
1446 }
1447
1448
1449 #ifdef __linux
1450 /* $OpenBSD: strlcat.c,v 1.13 2005/08/08 08:05:37 espie Exp $ */
1451
1452 /*
1453 * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
1454 *
1455 * Permission to use, copy, modify, and distribute this software for any
1456 * purpose with or without fee is hereby granted, provided that the
1457 * above copyright notice and this permission notice appear in all
1458 * copies.
1459 *
1460 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
1461 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
1462 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
1463 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
1464 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
1465 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
1466 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
1467 * PERFORMANCE OF THIS SOFTWARE.
1468 */
1469 static size_t
1470 strlcat(char *dst, const char *src, size_t siz)
1471 {
1472 char *d = dst;
1473 const char *s = src;
1474 size_t n = siz;
1475 size_t dlen;
1476
1477 /* Find the end of dst and adjust bytes left but don't go past
1478 * end */
1479 while (n-- != 0 && *d != '\0')
1480 d++;
1481 dlen = d - dst;
1482 n = siz - dlen;
1483
1484 if (n == 0)
1485 return(dlen + strlen(s));
1486 while (*s != '\0') {
1487 if (n != 1) {
1488 *d++ = *s;
1489 n--;
1490 }
1491 s++;
1492 }
1493 *d = '\0';
1494
1495 return(dlen + (s - src)); /* count does not include NUL */
1496 }
1497
1498
1499 static size_t
1500 strlcpy(char *dst, const char *src, size_t siz)
1501 {
1502 char *d = dst;
1503 const char *s = src;
1504 size_t n = siz;
1505
1506 /* Copy as many bytes as will fit */
1507 if (n != 0) {
1508 while (--n != 0) {
1509 if ((*d++ = *s++) == '\0')
1510 break;
1511 }
1512 }
1513
1514 /* Not enough room in dst, add NUL and traverse rest of src */
1515 if (n == 0) {
1516 if (siz != 0)
1517 *d = '\0'; /* NUL-terminate dst */
1518 while (*s++)
1519 ;
1520 }
1521
1522 return(s - src - 1); /* count does not include NUL */
1523 }
1524 #endif /*__linux__*/