]>
git.cameronkatri.com Git - mandoc.git/blob - roff.c
1 /* $Id: roff.c,v 1.41 2008/12/05 11:28:17 kristaps Exp $ */
3 * Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the
7 * above copyright notice and this permission notice appear in all
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
19 #include <sys/param.h>
20 #include <sys/types.h>
31 #include "libmdocml.h"
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 /* TODO: validate Dt macro arguments. */
44 /* FIXME: Bl -diag ignore callable children. */
58 #define ROFFCALL_ARGS \
59 int tok, struct rofftree *tree, \
60 char *argv[], enum roffd type
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. */
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. */
79 int tok
; /* Token id. */
80 struct roffnode
*parent
; /* Parent (or NULL). */
84 struct roffnode
*last
; /* Last parsed node. */
85 char *cur
; /* Line start. */
86 struct tm tm
; /* `Dd' results. */
87 char os
[64]; /* `Os' results. */
88 char title
[64]; /* `Dt' results. */
89 char section
[64]; /* `Dt' results. */
90 char volume
[64]; /* `Dt' results. */
92 #define ROFF_PRELUDE (1 << 1) /* In roff prelude. */
93 #define ROFF_PRELUDE_Os (1 << 2) /* `Os' is parsed. */
94 #define ROFF_PRELUDE_Dt (1 << 3) /* `Dt' is parsed. */
95 #define ROFF_PRELUDE_Dd (1 << 4) /* `Dd' is parsed. */
96 #define ROFF_BODY (1 << 5) /* In roff body. */
97 struct roffcb cb
; /* Callbacks. */
98 void *arg
; /* Callbacks' arg. */
101 static int roff_Dd(ROFFCALL_ARGS
);
102 static int roff_Dt(ROFFCALL_ARGS
);
103 static int roff_Os(ROFFCALL_ARGS
);
104 static int roff_Ns(ROFFCALL_ARGS
);
105 static int roff_Sm(ROFFCALL_ARGS
);
106 static int roff_layout(ROFFCALL_ARGS
);
107 static int roff_text(ROFFCALL_ARGS
);
108 static int roff_noop(ROFFCALL_ARGS
);
109 static int roff_depr(ROFFCALL_ARGS
);
110 static int roff_ordered(ROFFCALL_ARGS
);
111 static struct roffnode
*roffnode_new(int, struct rofftree
*);
112 static void roffnode_free(struct rofftree
*);
113 static void roff_warn(const struct rofftree
*,
114 const char *, char *, ...);
115 static void roff_err(const struct rofftree
*,
116 const char *, char *, ...);
117 static int roffpurgepunct(struct rofftree
*, char **);
118 static int roffscan(int, const int *);
119 static int rofffindtok(const char *);
120 static int rofffindarg(const char *);
121 static int rofffindcallable(const char *);
122 static int roffargs(const struct rofftree
*,
123 int, char *, char **);
124 static int roffargok(int, int);
125 static int roffnextopt(const struct rofftree
*,
126 int, char ***, char **);
127 static int roffparseopts(struct rofftree
*, int,
128 char ***, int *, char **);
129 static int roffcall(struct rofftree
*, int, char **);
130 static int roffparse(struct rofftree
*, char *);
131 static int textparse(struct rofftree
*, char *);
132 static int roffdata(struct rofftree
*, int, char *);
133 static int roffspecial(struct rofftree
*, int, char **);
136 extern size_t strlcat(char *, const char *, size_t);
137 extern size_t strlcpy(char *, const char *, size_t);
138 extern int vsnprintf(char *, size_t,
139 const char *, va_list);
140 extern char *strptime(const char *, const char *,
145 static const int roffarg_An
[] = { ROFF_Split
, ROFF_Nosplit
, ROFF_ARGMAX
};
146 static const int roffarg_Bd
[] = { ROFF_Ragged
, ROFF_Unfilled
, ROFF_Literal
,
147 ROFF_File
, ROFF_Offset
, ROFF_Filled
, ROFF_Compact
, ROFF_ARGMAX
};
148 static const int roffarg_Bk
[] = { ROFF_Words
, ROFF_ARGMAX
};
149 static const int roffarg_Ex
[] = { ROFF_Std
, ROFF_ARGMAX
};
150 static const int roffarg_Rv
[] = { ROFF_Std
, ROFF_ARGMAX
};
151 static const int roffarg_Bl
[] = { ROFF_Bullet
, ROFF_Dash
, ROFF_Hyphen
,
152 ROFF_Item
, ROFF_Enum
, ROFF_Tag
, ROFF_Diag
, ROFF_Hang
, ROFF_Ohang
,
153 ROFF_Inset
, ROFF_Column
, ROFF_Offset
, ROFF_Width
, ROFF_Compact
,
155 static const int roffarg_St
[] = { ROFF_p1003_1_88
, ROFF_p1003_1_90
,
156 ROFF_p1003_1_96
, ROFF_p1003_1_2001
, ROFF_p1003_1_2004
, ROFF_p1003_1
,
157 ROFF_p1003_1b
, ROFF_p1003_1b_93
, ROFF_p1003_1c_95
, ROFF_p1003_1g_2000
,
158 ROFF_p1003_2_92
, ROFF_p1387_2_95
, ROFF_p1003_2
, ROFF_p1387_2
,
159 ROFF_isoC_90
, ROFF_isoC_amd1
, ROFF_isoC_tcor1
, ROFF_isoC_tcor2
,
160 ROFF_isoC_99
, ROFF_ansiC
, ROFF_ansiC_89
, ROFF_ansiC_99
, ROFF_ieee754
,
161 ROFF_iso8802_3
, ROFF_xpg3
, ROFF_xpg4
, ROFF_xpg4_2
, ROFF_xpg4_3
,
162 ROFF_xbd5
, ROFF_xcu5
, ROFF_xsh5
, ROFF_xns5
, ROFF_xns5_2d2_0
,
163 ROFF_xcurses4_2
, ROFF_susv2
, ROFF_susv3
, ROFF_svid4
, ROFF_ARGMAX
};
165 static const int roffchild_Bl
[] = { ROFF_It
, ROFF_El
, ROFF_MAX
};
166 static const int roffchild_Fo
[] = { ROFF_Fa
, ROFF_Fc
, ROFF_MAX
};
167 static const int roffchild_Rs
[] = { ROFF_Re
, ROFF__A
, ROFF__B
, ROFF__D
,
168 ROFF__I
, ROFF__J
, ROFF__N
, ROFF__O
, ROFF__P
, ROFF__R
, ROFF__T
, ROFF__V
,
171 static const int roffparent_El
[] = { ROFF_Bl
, ROFF_It
, ROFF_MAX
};
172 static const int roffparent_Fc
[] = { ROFF_Fo
, ROFF_Fa
, ROFF_MAX
};
173 static const int roffparent_Oc
[] = { ROFF_Oo
, ROFF_MAX
};
174 static const int roffparent_It
[] = { ROFF_Bl
, ROFF_It
, ROFF_MAX
};
175 static const int roffparent_Re
[] = { ROFF_Rs
, ROFF_MAX
};
177 static const struct rofftok tokens
[ROFF_MAX
] = {
178 { roff_noop
, NULL
, NULL
, NULL
, 0, ROFF_COMMENT
, 0 }, /* \" */
179 { roff_Dd
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Dd */
180 { roff_Dt
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Dt */
181 { roff_Os
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Os */
182 { roff_layout
, NULL
, NULL
, NULL
, ROFF_Sh
, ROFF_LAYOUT
, 0 }, /* Sh */
183 { roff_layout
, NULL
, NULL
, NULL
, ROFF_Ss
, ROFF_LAYOUT
, 0 }, /* Ss */
184 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Pp */ /* XXX 0 args */
185 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_LSCOPE
}, /* D1 */
186 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_LSCOPE
}, /* Dl */
187 { roff_layout
, roffarg_Bd
, NULL
, NULL
, 0, ROFF_LAYOUT
, 0 }, /* Bd */
188 { roff_noop
, NULL
, NULL
, NULL
, ROFF_Bd
, ROFF_LAYOUT
, 0 }, /* Ed */
189 { roff_layout
, roffarg_Bl
, NULL
, roffchild_Bl
, 0, ROFF_LAYOUT
, 0 }, /* Bl */
190 { roff_noop
, NULL
, roffparent_El
, NULL
, ROFF_Bl
, ROFF_LAYOUT
, 0 }, /* El */
191 { roff_layout
, NULL
, roffparent_It
, NULL
, ROFF_It
, ROFF_LAYOUT
, ROFF_PARSED
| ROFF_SHALLOW
}, /* It */
192 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ad */ /* FIXME */
193 { roff_text
, roffarg_An
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* An */
194 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ar */
195 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Cd */ /* XXX man.4 only */
196 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Cm */
197 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Dv */ /* XXX needs arg */
198 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Er */ /* XXX needs arg */
199 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ev */ /* XXX needs arg */
200 { roff_text
, roffarg_Ex
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Ex */
201 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Fa */ /* XXX needs arg */
202 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Fd */
203 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Fl */
204 {roff_ordered
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, /*XXX*/ -1 }, /* Fn */
205 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Ft */
206 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ic */ /* XXX needs arg */
207 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* In */
208 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Li */
209 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Nd */
210 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Nm */ /* FIXME */
211 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
| ROFF_LSCOPE
}, /* Op */
212 { roff_depr
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Ot */
213 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Pa */
214 { roff_text
, roffarg_Rv
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Rv */
215 { roff_text
, roffarg_St
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* St */
216 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Va */
217 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Vt */ /* XXX needs arg */
218 {roff_ordered
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, /*XXX*/ -1 }, /* Xr */
219 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* %A */
220 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* %B */
221 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* %D */
222 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* %I */
223 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* %J */
224 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* %N */
225 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* %O */
226 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* %P */
227 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* %R */
228 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* %T */
229 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* %V */
230 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ac */
231 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ao */
232 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
| ROFF_LSCOPE
}, /* Aq */
233 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* At */ /* XXX at most 2 args */
234 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Bc */
235 { roff_layout
, NULL
, NULL
, NULL
, 0, ROFF_LAYOUT
, 0 }, /* Bf */ /* FIXME */
236 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Bo */
237 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
| ROFF_LSCOPE
}, /* Bq */
238 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Bsx */
239 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Bx */
240 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_SPECIAL
, 0 }, /* Db */
241 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Dc */
242 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Do */
243 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
| ROFF_LSCOPE
}, /* Dq */
244 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ec */
245 { roff_noop
, NULL
, NULL
, NULL
, ROFF_Bf
, ROFF_LAYOUT
, 0 }, /* Ef */
246 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Em */ /* XXX needs arg */
247 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Eo */
248 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Fx */
249 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Ms */
250 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* No */
251 { roff_Ns
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ns */
252 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Nx */
253 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Ox */
254 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Pc */
255 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Pf */
256 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Po */
257 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
| ROFF_LSCOPE
}, /* Pq */
258 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Qc */
259 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Ql */
260 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Qo */
261 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
| ROFF_LSCOPE
}, /* Qq */
262 { roff_noop
, NULL
, roffparent_Re
, NULL
, ROFF_Rs
, ROFF_LAYOUT
, 0 }, /* Re */
263 { roff_layout
, NULL
, NULL
, roffchild_Rs
, 0, ROFF_LAYOUT
, 0 }, /* Rs */
264 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Sc */
265 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* So */
266 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
| ROFF_LSCOPE
}, /* Sq */
267 { roff_Sm
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Sm */
268 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Sx */
269 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Sy */
270 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Tn */
271 { roff_text
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
}, /* Ux */
272 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Xc */
273 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Xo */
274 { roff_layout
, NULL
, NULL
, roffchild_Fo
, 0, ROFF_LAYOUT
, 0 }, /* Fo */
275 { roff_noop
, NULL
, roffparent_Fc
, NULL
, ROFF_Fo
, ROFF_LAYOUT
, 0 }, /* Fc */
276 { roff_layout
, NULL
, NULL
, NULL
, 0, ROFF_LAYOUT
, 0 }, /* Oo */
277 { roff_noop
, NULL
, roffparent_Oc
, NULL
, ROFF_Oo
, ROFF_LAYOUT
, 0 }, /* Oc */
278 { roff_layout
, roffarg_Bk
, NULL
, NULL
, 0, ROFF_LAYOUT
, 0 }, /* Bk */
279 { roff_noop
, NULL
, NULL
, NULL
, ROFF_Bk
, ROFF_LAYOUT
, 0 }, /* Ek */
280 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Bt */
281 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Hf */
282 { roff_depr
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Fr */
283 { NULL
, NULL
, NULL
, NULL
, 0, ROFF_TEXT
, 0 }, /* Ud */
286 #define ROFF_VALUE (1 << 0)
288 static const int tokenargs
[ROFF_ARGMAX
] = {
290 0, ROFF_VALUE
, ROFF_VALUE
, 0,
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",
318 "Xr", "\%A", "\%B", "\%D",
320 "\%I", "\%J", "\%N", "\%O",
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",
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",
362 const char *const *toknames
= toknamesp
;
363 const char *const *tokargnames
= tokargnamesp
;
367 roff_free(struct rofftree
*tree
, int flush
)
379 if (ROFF_PRELUDE
& tree
->state
) {
380 roff_err(tree
, NULL
, "prelude never finished");
384 for (n
= tree
->last
; n
; n
= n
->parent
) {
385 if (0 != tokens
[n
->tok
].ctx
)
387 roff_err(tree
, NULL
, "closing explicit scope `%s'",
394 if ( ! (*tokens
[t
].cb
)(t
, tree
, NULL
, ROFF_EXIT
))
398 if ( ! (*tree
->cb
.rofftail
)(tree
->arg
))
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_err(tree
, buf
, "blank line");
444 } else if ('.' != *buf
)
445 return(textparse(tree
, buf
));
447 return(roffparse(tree
, buf
));
452 textparse(struct rofftree
*tree
, char *buf
)
456 /* TODO: literal parsing. */
458 if ( ! (ROFF_BODY
& tree
->state
)) {
459 roff_err(tree
, buf
, "data not in body");
465 while (*buf
&& isspace(*buf
))
473 while (*buf
&& ! isspace(*buf
))
478 if ( ! roffdata(tree
, 1, bufp
))
483 if ( ! roffdata(tree
, 1, bufp
))
493 roffargs(const struct rofftree
*tree
,
494 int tok
, char *buf
, char **argv
)
499 assert(tok
>= 0 && tok
< ROFF_MAX
);
505 * This is an ugly little loop. It parses a line into
506 * space-delimited tokens. If a quote mark is encountered, a
507 * token is alloted the entire quoted text. If whitespace is
508 * escaped, it's included in the prior alloted token.
512 for (i
= 0; *buf
&& i
< ROFF_MAXLINEARG
; i
++) {
515 while (*buf
&& '\"' != *buf
)
518 roff_err(tree
, argv
[i
], "unclosed "
527 if ( ! isspace(*buf
)) {
531 if (*(buf
- 1) == '\\') {
541 while (*buf
&& isspace(*buf
))
546 if (ROFF_MAXLINEARG
== i
&& *buf
) {
547 roff_err(tree
, p
, "too many arguments for `%s'", toknames
558 roffscan(int tok
, const int *tokv
)
564 for ( ; ROFF_MAX
!= *tokv
; tokv
++)
573 roffparse(struct rofftree
*tree
, char *buf
)
577 char *argv
[ROFF_MAXLINEARG
];
580 if (0 != *buf
&& 0 != *(buf
+ 1) && 0 != *(buf
+ 2))
581 if (0 == strncmp(buf
, ".\\\"", 3))
584 if (ROFF_MAX
== (tok
= rofffindtok(buf
+ 1))) {
585 roff_err(tree
, buf
+ 1, "bogus line macro");
587 } else if (NULL
== tokens
[tok
].cb
) {
588 roff_err(tree
, buf
+ 1, "unsupported macro `%s'",
593 assert(ROFF___
!= tok
);
594 if ( ! roffargs(tree
, tok
, buf
, argv
))
597 argvp
= (char **)argv
;
600 * Prelude macros break some assumptions, so branch now.
603 if (ROFF_PRELUDE
& tree
->state
) {
604 assert(NULL
== tree
->last
);
605 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
608 assert(ROFF_BODY
& tree
->state
);
611 * First check that our possible parents and parent's possible
612 * children are satisfied.
615 if (tree
->last
&& ! roffscan
616 (tree
->last
->tok
, tokens
[tok
].parents
)) {
617 roff_err(tree
, *argvp
, "`%s' has invalid parent `%s'",
619 toknames
[tree
->last
->tok
]);
623 if (tree
->last
&& ! roffscan
624 (tok
, tokens
[tree
->last
->tok
].children
)) {
625 roff_err(tree
, *argvp
, "`%s' is invalid child of `%s'",
627 toknames
[tree
->last
->tok
]);
632 * Branch if we're not a layout token.
635 if (ROFF_LAYOUT
!= tokens
[tok
].type
)
636 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
637 if (0 == tokens
[tok
].ctx
)
638 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
641 * First consider implicit-end tags, like as follows:
644 * In this, we want to close the scope of the NAME section. If
645 * there's an intermediary implicit-end tag, such as
649 * then it must be closed as well.
652 if (tok
== tokens
[tok
].ctx
) {
654 * First search up to the point where we must close.
655 * If one doesn't exist, then we can open a new scope.
658 for (n
= tree
->last
; n
; n
= n
->parent
) {
659 assert(0 == tokens
[n
->tok
].ctx
||
660 n
->tok
== tokens
[n
->tok
].ctx
);
663 if (ROFF_SHALLOW
& tokens
[tok
].flags
) {
667 if (tokens
[n
->tok
].ctx
== n
->tok
)
669 roff_err(tree
, *argv
, "`%s' breaks `%s' scope",
670 toknames
[tok
], toknames
[n
->tok
]);
675 * Create a new scope, as no previous one exists to
680 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
683 * Close out all intermediary scoped blocks, then hang
684 * the current scope from our predecessor's parent.
689 if ( ! (*tokens
[t
].cb
)(t
, tree
, NULL
, ROFF_EXIT
))
693 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
697 * Now consider explicit-end tags, where we want to close back
698 * to a specific tag. Example:
702 * In this, the `El' tag closes out the scope of `Bl'.
705 assert(tok
!= tokens
[tok
].ctx
&& 0 != tokens
[tok
].ctx
);
708 for (n
= tree
->last
; n
; n
= n
->parent
)
709 if (n
->tok
!= tokens
[tok
].ctx
) {
710 if (n
->tok
== tokens
[n
->tok
].ctx
)
712 roff_err(tree
, *argv
, "`%s' breaks `%s' scope",
713 toknames
[tok
], toknames
[n
->tok
]);
720 roff_err(tree
, *argv
, "`%s' has no starting tag `%s'",
722 toknames
[tokens
[tok
].ctx
]);
729 if ( ! (*tokens
[t
].cb
)(t
, tree
, NULL
, ROFF_EXIT
))
731 } while (t
!= tokens
[tok
].ctx
);
738 rofffindarg(const char *name
)
742 /* FIXME: use a table, this is slow but ok for now. */
745 for (i
= 0; i
< ROFF_ARGMAX
; i
++)
747 if (0 == strcmp(name
, tokargnames
[i
]))
755 rofffindtok(const char *buf
)
760 for (i
= 0; *buf
&& ! isspace(*buf
) && i
< 3; i
++, buf
++)
768 /* FIXME: use a table, this is slow but ok for now. */
771 for (i
= 0; i
< ROFF_MAX
; i
++)
773 if (0 == strcmp(toknames
[i
], token
))
781 roffispunct(const char *p
)
823 rofffindcallable(const char *name
)
827 if (ROFF_MAX
== (c
= rofffindtok(name
)))
829 assert(c
>= 0 && c
< ROFF_MAX
);
830 return(ROFF_CALLABLE
& tokens
[c
].flags
? c
: ROFF_MAX
);
834 static struct roffnode
*
835 roffnode_new(int tokid
, struct rofftree
*tree
)
839 if (NULL
== (p
= malloc(sizeof(struct roffnode
))))
843 p
->parent
= tree
->last
;
851 roffargok(int tokid
, int argid
)
855 if (NULL
== (c
= tokens
[tokid
].args
))
858 for ( ; ROFF_ARGMAX
!= *c
; c
++)
867 roffnode_free(struct rofftree
*tree
)
874 tree
->last
= tree
->last
->parent
;
880 roffspecial(struct rofftree
*tree
, int tok
, char **ordp
)
883 return((*tree
->cb
.roffspecial
)(tree
->arg
, tok
,
889 roffcall(struct rofftree
*tree
, int tok
, char **argv
)
892 if (NULL
== tokens
[tok
].cb
) {
893 roff_err(tree
, *argv
, "unsupported macro `%s'",
897 if ( ! (*tokens
[tok
].cb
)(tok
, tree
, argv
, ROFF_ENTER
))
904 roffnextopt(const struct rofftree
*tree
, int tok
,
905 char ***in
, char **val
)
914 if (NULL
== (arg
= *argv
))
919 if (ROFF_ARGMAX
== (v
= rofffindarg(arg
+ 1))) {
920 roff_warn(tree
, arg
, "argument-like parameter `%s' to "
921 "`%s'", arg
, toknames
[tok
]);
925 if ( ! roffargok(tok
, v
)) {
926 roff_warn(tree
, arg
, "invalid argument parameter `%s' to "
927 "`%s'", tokargnames
[v
], toknames
[tok
]);
931 if ( ! (ROFF_VALUE
& tokenargs
[v
]))
937 roff_err(tree
, arg
, "empty value of `%s' for `%s'",
938 tokargnames
[v
], toknames
[tok
]);
947 roffpurgepunct(struct rofftree
*tree
, char **argv
)
955 if ( ! roffispunct(argv
[--i
]))
957 while (i
>= 0 && roffispunct(argv
[i
]))
963 if ( ! roffdata(tree
, 0, argv
[i
++]))
970 roffparseopts(struct rofftree
*tree
, int tok
,
971 char ***args
, int *argc
, char **argv
)
978 while (-1 != (c
= roffnextopt(tree
, tok
, args
, &v
))) {
979 if (ROFF_ARGMAX
== c
)
988 argc
[i
] = ROFF_ARGMAX
;
995 roffdata(struct rofftree
*tree
, int space
, char *buf
)
1000 return((*tree
->cb
.roffdata
)(tree
->arg
,
1001 space
!= 0, tree
->cur
, buf
));
1007 roff_Dd(ROFFCALL_ARGS
)
1012 if (ROFF_BODY
& tree
->state
) {
1013 assert( ! (ROFF_PRELUDE
& tree
->state
));
1014 assert(ROFF_PRELUDE_Dd
& tree
->state
);
1015 return(roff_text(tok
, tree
, argv
, type
));
1018 assert(ROFF_PRELUDE
& tree
->state
);
1019 assert( ! (ROFF_BODY
& tree
->state
));
1021 if (ROFF_PRELUDE_Dd
& tree
->state
) {
1022 roff_err(tree
, *argv
, "repeated `Dd' in prelude");
1024 } else if (ROFF_PRELUDE_Dt
& tree
->state
) {
1025 roff_err(tree
, *argv
, "out-of-order `Dd' in prelude");
1029 assert(NULL
== tree
->last
);
1033 if (0 == strcmp(*argv
, "$Mdocdate: December 5 2008 $")) {
1035 if (NULL
== localtime_r(&t
, &tree
->tm
))
1036 err(1, "localtime_r");
1037 tree
->state
|= ROFF_PRELUDE_Dd
;
1041 /* Build this from Mdocdate or raw date. */
1046 if (0 != strcmp(*argv
, "$Mdocdate:")) {
1048 if (strlcat(buf
, *argv
++, sizeof(buf
))
1051 roff_err(tree
, p
, "bad `Dd' date");
1054 if (strptime(buf
, "%b%d,%Y", &tree
->tm
)) {
1055 tree
->state
|= ROFF_PRELUDE_Dd
;
1058 roff_err(tree
, *argv
, "bad `Dd' date");
1063 while (*argv
&& **argv
!= '$') {
1064 if (strlcat(buf
, *argv
++, sizeof(buf
))
1066 roff_err(tree
, p
, "bad `Dd' Mdocdate");
1069 if (strlcat(buf
, " ", sizeof(buf
))
1071 roff_err(tree
, p
, "bad `Dd' Mdocdate");
1075 if (NULL
== *argv
) {
1076 roff_err(tree
, p
, "bad `Dd' Mdocdate");
1080 if (NULL
== strptime(buf
, "%b %d %Y", &tree
->tm
)) {
1081 roff_err(tree
, *argv
, "bad `Dd' Mdocdate");
1085 tree
->state
|= ROFF_PRELUDE_Dd
;
1092 roff_Dt(ROFFCALL_ARGS
)
1095 if (ROFF_BODY
& tree
->state
) {
1096 assert( ! (ROFF_PRELUDE
& tree
->state
));
1097 assert(ROFF_PRELUDE_Dt
& tree
->state
);
1098 return(roff_text(tok
, tree
, argv
, type
));
1101 assert(ROFF_PRELUDE
& tree
->state
);
1102 assert( ! (ROFF_BODY
& tree
->state
));
1104 if ( ! (ROFF_PRELUDE_Dd
& tree
->state
)) {
1105 roff_err(tree
, *argv
, "out-of-order `Dt' in prelude");
1107 } else if (ROFF_PRELUDE_Dt
& tree
->state
) {
1108 roff_err(tree
, *argv
, "repeated `Dt' in prelude");
1113 if (NULL
== *argv
) {
1114 roff_err(tree
, *argv
, "`Dt' needs document title");
1116 } else if (strlcpy(tree
->title
, *argv
, sizeof(tree
->title
))
1117 >= sizeof(tree
->title
)) {
1118 roff_err(tree
, *argv
, "`Dt' document title too long");
1123 if (NULL
== *argv
) {
1124 roff_err(tree
, *argv
, "`Dt' needs section");
1126 } else if (strlcpy(tree
->section
, *argv
, sizeof(tree
->section
))
1127 >= sizeof(tree
->section
)) {
1128 roff_err(tree
, *argv
, "`Dt' section too long");
1133 if (NULL
== *argv
) {
1134 tree
->volume
[0] = 0;
1135 } else if (strlcpy(tree
->volume
, *argv
, sizeof(tree
->volume
))
1136 >= sizeof(tree
->volume
)) {
1137 roff_err(tree
, *argv
, "`Dt' volume too long");
1141 assert(NULL
== tree
->last
);
1142 tree
->state
|= ROFF_PRELUDE_Dt
;
1150 roff_Sm(ROFFCALL_ARGS
)
1156 if (NULL
== (morep
[0] = *argv
++)) {
1157 roff_err(tree
, p
, "`Sm' expects an argument");
1159 } else if (0 != strcmp(morep
[0], "on") &&
1160 0 != strcmp(morep
[0], "off")) {
1161 roff_err(tree
, p
, "`Sm' has invalid argument");
1166 roff_warn(tree
, *argv
, "`Sm' shouldn't have arguments");
1168 if ( ! roffspecial(tree
, tok
, morep
))
1172 if ( ! roffdata(tree
, 1, *argv
++))
1181 roff_Ns(ROFFCALL_ARGS
)
1186 first
= (*argv
++ == tree
->cur
);
1189 if ( ! roffspecial(tree
, tok
, morep
))
1193 if (ROFF_MAX
!= (c
= rofffindcallable(*argv
))) {
1194 if ( ! roffcall(tree
, c
, argv
))
1199 if ( ! roffispunct(*argv
)) {
1200 if ( ! roffdata(tree
, 1, *argv
++))
1205 for (j
= 0; argv
[j
]; j
++)
1206 if ( ! roffispunct(argv
[j
]))
1210 if ( ! roffdata(tree
, 0, *argv
++))
1221 return(roffpurgepunct(tree
, argv
));
1227 roff_Os(ROFFCALL_ARGS
)
1231 if (ROFF_BODY
& tree
->state
) {
1232 assert( ! (ROFF_PRELUDE
& tree
->state
));
1233 assert(ROFF_PRELUDE_Os
& tree
->state
);
1234 return(roff_text(tok
, tree
, argv
, type
));
1237 assert(ROFF_PRELUDE
& tree
->state
);
1238 if ( ! (ROFF_PRELUDE_Dt
& tree
->state
) ||
1239 ! (ROFF_PRELUDE_Dd
& tree
->state
)) {
1240 roff_err(tree
, *argv
, "out-of-order `Os' in prelude");
1249 if (strlcat(tree
->os
, *argv
++, sizeof(tree
->os
))
1252 roff_err(tree
, p
, "`Os' value too long");
1256 if (0 == tree
->os
[0])
1257 if (strlcpy(tree
->os
, "LOCAL", sizeof(tree
->os
))
1258 >= sizeof(tree
->os
)) {
1259 roff_err(tree
, p
, "`Os' value too long");
1263 tree
->state
|= ROFF_PRELUDE_Os
;
1264 tree
->state
&= ~ROFF_PRELUDE
;
1265 tree
->state
|= ROFF_BODY
;
1267 assert(NULL
== tree
->last
);
1269 return((*tree
->cb
.roffhead
)(tree
->arg
, &tree
->tm
,
1270 tree
->os
, tree
->title
, tree
->section
,
1277 roff_layout(ROFFCALL_ARGS
)
1279 int i
, c
, argcp
[ROFF_MAXLINEARG
];
1280 char *argvp
[ROFF_MAXLINEARG
];
1282 if (ROFF_PRELUDE
& tree
->state
) {
1283 roff_err(tree
, *argv
, "bad `%s' in prelude",
1286 } else if (ROFF_EXIT
== type
) {
1287 roffnode_free(tree
);
1288 if ( ! (*tree
->cb
.roffblkbodyout
)(tree
->arg
, tok
))
1290 return((*tree
->cb
.roffblkout
)(tree
->arg
, tok
));
1293 assert( ! (ROFF_CALLABLE
& tokens
[tok
].flags
));
1297 if ( ! roffparseopts(tree
, tok
, &argv
, argcp
, argvp
))
1299 if (NULL
== roffnode_new(tok
, tree
))
1303 * Layouts have two parts: the layout body and header. The
1304 * layout header is the trailing text of the line macro, while
1305 * the layout body is everything following until termination.
1308 if ( ! (*tree
->cb
.roffblkin
)(tree
->arg
, tok
, argcp
, argvp
))
1311 return((*tree
->cb
.roffblkbodyin
)
1312 (tree
->arg
, tok
, argcp
, argvp
));
1314 if ( ! (*tree
->cb
.roffblkheadin
)(tree
->arg
, tok
, argcp
, argvp
))
1318 * If there are no parsable parts, then write remaining tokens
1319 * into the layout header and exit.
1322 if ( ! (ROFF_PARSED
& tokens
[tok
].flags
)) {
1325 if ( ! roffdata(tree
, i
++, *argv
++))
1328 if ( ! (*tree
->cb
.roffblkheadout
)(tree
->arg
, tok
))
1330 return((*tree
->cb
.roffblkbodyin
)
1331 (tree
->arg
, tok
, argcp
, argvp
));
1335 * Parsable elements may be in the header (or be the header, for
1336 * that matter). Follow the regular parsing rules for these.
1341 if (ROFF_MAX
== (c
= rofffindcallable(*argv
))) {
1343 if ( ! roffdata(tree
, i
++, *argv
++))
1347 if ( ! roffcall(tree
, c
, argv
))
1353 * If there's trailing punctuation in the header, then write it
1354 * out now. Here we mimic the behaviour of a line-dominant text
1358 if (NULL
== *argv
) {
1359 if ( ! (*tree
->cb
.roffblkheadout
)(tree
->arg
, tok
))
1361 return((*tree
->cb
.roffblkbodyin
)
1362 (tree
->arg
, tok
, argcp
, argvp
));
1366 * Expensive. Scan to the end of line then work backwards until
1367 * a token isn't punctuation.
1370 if ( ! roffpurgepunct(tree
, argv
))
1373 if ( ! (*tree
->cb
.roffblkheadout
)(tree
->arg
, tok
))
1375 return((*tree
->cb
.roffblkbodyin
)
1376 (tree
->arg
, tok
, argcp
, argvp
));
1382 roff_ordered(ROFFCALL_ARGS
)
1385 char *ordp
[ROFF_MAXLINEARG
];
1387 if (ROFF_PRELUDE
& tree
->state
) {
1388 roff_err(tree
, *argv
, "`%s' disallowed in prelude",
1393 first
= (*argv
== tree
->cur
);
1396 if (NULL
== *argv
) {
1398 /* FIXME: satisfies number of args? */
1401 return(roffspecial(tree
, tok
, ordp
));
1405 while (*argv
&& i
< ROFF_MAXLINEARG
) {
1406 if (ROFF_MAX
!= (c
= rofffindcallable(*argv
)))
1407 return(roffcall(tree
, c
, argv
));
1408 if (roffispunct(*argv
))
1411 ordp
[i
++] = *argv
++;
1416 /* FIXME: too many or too few args? */
1418 if (i
== ROFF_MAXLINEARG
&& *argv
) {
1419 roff_err(tree
, *argv
, "too many args", toknames
[tok
]);
1423 /* FIXME: error if there's stuff after the punctuation. */
1425 if ( ! roffspecial(tree
, tok
, ordp
))
1428 if ( ! first
|| NULL
== *argv
)
1431 return(roffpurgepunct(tree
, argv
));
1437 roff_text(ROFFCALL_ARGS
)
1439 int i
, j
, first
, c
, argcp
[ROFF_MAXLINEARG
];
1440 char *argvp
[ROFF_MAXLINEARG
];
1442 if (ROFF_PRELUDE
& tree
->state
) {
1443 roff_err(tree
, *argv
, "`%s' disallowed in prelude",
1448 first
= (*argv
== tree
->cur
);
1451 if ( ! roffparseopts(tree
, tok
, &argv
, argcp
, argvp
))
1453 if ( ! (*tree
->cb
.roffin
)(tree
->arg
, tok
, argcp
, argvp
))
1456 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1458 if ( ! (ROFF_PARSED
& tokens
[tok
].flags
)) {
1461 if ( ! roffdata(tree
, i
++, *argv
++))
1464 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1468 * Deal with punctuation. Ugly. Work ahead until we encounter
1469 * terminating punctuation. If we encounter it and all
1470 * subsequent tokens are punctuation, then stop processing (the
1471 * line-dominant macro will print these tokens after closure).
1476 if (ROFF_MAX
!= (c
= rofffindcallable(*argv
))) {
1477 if ( ! (ROFF_LSCOPE
& tokens
[tok
].flags
))
1478 if ( ! (*tree
->cb
.roffout
)(tree
->arg
, tok
))
1481 if ( ! roffcall(tree
, c
, argv
))
1484 if (ROFF_LSCOPE
& tokens
[tok
].flags
)
1485 if ( ! (*tree
->cb
.roffout
)(tree
->arg
, tok
))
1491 if ( ! roffispunct(*argv
)) {
1492 if ( ! roffdata(tree
, i
++, *argv
++))
1498 for (j
= 0; argv
[j
]; j
++)
1499 if ( ! roffispunct(argv
[j
]))
1503 if ( ! roffdata(tree
, 0, *argv
++))
1508 if ( ! (*tree
->cb
.roffout
)(tree
->arg
, tok
))
1514 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1518 return(roffpurgepunct(tree
, argv
));
1524 roff_noop(ROFFCALL_ARGS
)
1533 roff_depr(ROFFCALL_ARGS
)
1536 roff_err(tree
, *argv
, "`%s' is deprecated", toknames
[tok
]);
1542 roff_warn(const struct rofftree
*tree
, const char *pos
, char *fmt
, ...)
1548 (void)vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
1551 (*tree
->cb
.roffmsg
)(tree
->arg
,
1552 ROFF_WARN
, tree
->cur
, pos
, buf
);
1557 roff_err(const struct rofftree
*tree
, const char *pos
, char *fmt
, ...)
1563 (void)vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
1566 (*tree
->cb
.roffmsg
)(tree
->arg
,
1567 ROFF_ERROR
, tree
->cur
, pos
, buf
);