]>
git.cameronkatri.com Git - mandoc.git/blob - roff.c
1 /* $Id: roff.c,v 1.37 2008/12/04 16:19:52 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. */
57 #define ROFFCALL_ARGS \
58 int tok, struct rofftree *tree, \
59 char *argv[], enum roffd type
64 int (*cb
)(ROFFCALL_ARGS
); /* Callback. */
65 const int *args
; /* Args (or NULL). */
66 const int *parents
; /* Limit to parents. */
67 const int *children
; /* Limit to kids. */
68 int ctx
; /* Blk-close node. */
69 enum rofftype type
; /* Type of macro. */
71 #define ROFF_PARSED (1 << 0) /* "Parsed". */
72 #define ROFF_CALLABLE (1 << 1) /* "Callable". */
73 #define ROFF_SHALLOW (1 << 2) /* Nesting block. */
74 #define ROFF_LSCOPE (1 << 3) /* Line scope. */
79 #define ROFF_VALUE (1 << 0) /* Has a value. */
83 int tok
; /* Token id. */
84 struct roffnode
*parent
; /* Parent (or NULL). */
88 struct roffnode
*last
; /* Last parsed node. */
89 char *cur
; /* Line start. */
90 struct tm tm
; /* `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. */
96 #define ROFF_PRELUDE (1 << 1) /* In roff prelude. */
97 #define ROFF_PRELUDE_Os (1 << 2) /* `Os' is parsed. */
98 #define ROFF_PRELUDE_Dt (1 << 3) /* `Dt' is parsed. */
99 #define ROFF_PRELUDE_Dd (1 << 4) /* `Dd' is parsed. */
100 #define ROFF_BODY (1 << 5) /* In roff body. */
101 struct roffcb cb
; /* Callbacks. */
102 void *arg
; /* Callbacks' arg. */
105 static int roff_Dd(ROFFCALL_ARGS
);
106 static int roff_Dt(ROFFCALL_ARGS
);
107 static int roff_Os(ROFFCALL_ARGS
);
108 static int roff_Ns(ROFFCALL_ARGS
);
109 static int roff_Sm(ROFFCALL_ARGS
);
110 static int roff_layout(ROFFCALL_ARGS
);
111 static int roff_text(ROFFCALL_ARGS
);
112 static int roff_noop(ROFFCALL_ARGS
);
113 static int roff_depr(ROFFCALL_ARGS
);
114 static struct roffnode
*roffnode_new(int, struct rofftree
*);
115 static void roffnode_free(struct rofftree
*);
116 static void roff_warn(const struct rofftree
*,
117 const char *, char *, ...);
118 static void roff_err(const struct rofftree
*,
119 const char *, char *, ...);
120 static int roffpurgepunct(struct rofftree
*, char **);
121 static int roffscan(int, const int *);
122 static int rofffindtok(const char *);
123 static int rofffindarg(const char *);
124 static int rofffindcallable(const char *);
125 static int roffargs(const struct rofftree
*,
126 int, char *, char **);
127 static int roffargok(int, int);
128 static int roffnextopt(const struct rofftree
*,
129 int, char ***, char **);
130 static int roffparseopts(struct rofftree
*, int,
131 char ***, int *, char **);
132 static int roffcall(struct rofftree
*, int, char **);
133 static int roffparse(struct rofftree
*, char *);
134 static int textparse(struct rofftree
*, char *);
135 static int roffdata(struct rofftree
*, int, char *);
138 extern size_t strlcat(char *, const char *, size_t);
139 extern 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 *,
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
,
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
};
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
,
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
};
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 */
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 for (i
= 0; *buf
&& i
< ROFF_MAXLINEARG
; i
++) {
508 while (*buf
&& '\"' != *buf
)
511 roff_err(tree
, argv
[i
], "unclosed "
519 while (*buf
&& ! isspace(*buf
))
525 while (*buf
&& isspace(*buf
))
530 if (ROFF_MAXLINEARG
== i
&& *buf
) {
531 roff_err(tree
, p
, "too many arguments for `%s'", toknames
542 roffscan(int tok
, const int *tokv
)
548 for ( ; ROFF_MAX
!= *tokv
; tokv
++)
557 roffparse(struct rofftree
*tree
, char *buf
)
561 char *argv
[ROFF_MAXLINEARG
];
564 if (0 != *buf
&& 0 != *(buf
+ 1) && 0 != *(buf
+ 2))
565 if (0 == strncmp(buf
, ".\\\"", 3))
568 if (ROFF_MAX
== (tok
= rofffindtok(buf
+ 1))) {
569 roff_err(tree
, buf
+ 1, "bogus line macro");
571 } else if (NULL
== tokens
[tok
].cb
) {
572 roff_err(tree
, buf
+ 1, "unsupported macro `%s'",
577 assert(ROFF___
!= tok
);
578 if ( ! roffargs(tree
, tok
, buf
, argv
))
581 argvp
= (char **)argv
;
584 * Prelude macros break some assumptions, so branch now.
587 if (ROFF_PRELUDE
& tree
->state
) {
588 assert(NULL
== tree
->last
);
589 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
592 assert(ROFF_BODY
& tree
->state
);
595 * First check that our possible parents and parent's possible
596 * children are satisfied.
599 if (tree
->last
&& ! roffscan
600 (tree
->last
->tok
, tokens
[tok
].parents
)) {
601 roff_err(tree
, *argvp
, "`%s' has invalid parent `%s'",
603 toknames
[tree
->last
->tok
]);
607 if (tree
->last
&& ! roffscan
608 (tok
, tokens
[tree
->last
->tok
].children
)) {
609 roff_err(tree
, *argvp
, "`%s' is invalid child of `%s'",
611 toknames
[tree
->last
->tok
]);
616 * Branch if we're not a layout token.
619 if (ROFF_LAYOUT
!= tokens
[tok
].type
)
620 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
621 if (0 == tokens
[tok
].ctx
)
622 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
625 * First consider implicit-end tags, like as follows:
628 * In this, we want to close the scope of the NAME section. If
629 * there's an intermediary implicit-end tag, such as
633 * then it must be closed as well.
636 if (tok
== tokens
[tok
].ctx
) {
638 * First search up to the point where we must close.
639 * If one doesn't exist, then we can open a new scope.
642 for (n
= tree
->last
; n
; n
= n
->parent
) {
643 assert(0 == tokens
[n
->tok
].ctx
||
644 n
->tok
== tokens
[n
->tok
].ctx
);
647 if (ROFF_SHALLOW
& tokens
[tok
].flags
) {
651 if (tokens
[n
->tok
].ctx
== n
->tok
)
653 roff_err(tree
, *argv
, "`%s' breaks `%s' scope",
654 toknames
[tok
], toknames
[n
->tok
]);
659 * Create a new scope, as no previous one exists to
664 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
667 * Close out all intermediary scoped blocks, then hang
668 * the current scope from our predecessor's parent.
673 if ( ! (*tokens
[t
].cb
)(t
, tree
, NULL
, ROFF_EXIT
))
677 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
681 * Now consider explicit-end tags, where we want to close back
682 * to a specific tag. Example:
686 * In this, the `El' tag closes out the scope of `Bl'.
689 assert(tok
!= tokens
[tok
].ctx
&& 0 != tokens
[tok
].ctx
);
692 for (n
= tree
->last
; n
; n
= n
->parent
)
693 if (n
->tok
!= tokens
[tok
].ctx
) {
694 if (n
->tok
== tokens
[n
->tok
].ctx
)
696 roff_err(tree
, *argv
, "`%s' breaks `%s' scope",
697 toknames
[tok
], toknames
[n
->tok
]);
704 roff_err(tree
, *argv
, "`%s' has no starting tag `%s'",
706 toknames
[tokens
[tok
].ctx
]);
713 if ( ! (*tokens
[t
].cb
)(t
, tree
, NULL
, ROFF_EXIT
))
715 } while (t
!= tokens
[tok
].ctx
);
722 rofffindarg(const char *name
)
726 /* FIXME: use a table, this is slow but ok for now. */
729 for (i
= 0; i
< ROFF_ARGMAX
; i
++)
731 if (0 == strcmp(name
, tokargnames
[i
]))
739 rofffindtok(const char *buf
)
744 for (i
= 0; *buf
&& ! isspace(*buf
) && i
< 3; i
++, buf
++)
752 /* FIXME: use a table, this is slow but ok for now. */
755 for (i
= 0; i
< ROFF_MAX
; i
++)
757 if (0 == strcmp(toknames
[i
], token
))
765 roffispunct(const char *p
)
807 rofffindcallable(const char *name
)
811 if (ROFF_MAX
== (c
= rofffindtok(name
)))
813 assert(c
>= 0 && c
< ROFF_MAX
);
814 return(ROFF_CALLABLE
& tokens
[c
].flags
? c
: ROFF_MAX
);
818 static struct roffnode
*
819 roffnode_new(int tokid
, struct rofftree
*tree
)
823 if (NULL
== (p
= malloc(sizeof(struct roffnode
))))
827 p
->parent
= tree
->last
;
835 roffargok(int tokid
, int argid
)
839 if (NULL
== (c
= tokens
[tokid
].args
))
842 for ( ; ROFF_ARGMAX
!= *c
; c
++)
851 roffnode_free(struct rofftree
*tree
)
858 tree
->last
= tree
->last
->parent
;
864 roffcall(struct rofftree
*tree
, int tok
, char **argv
)
867 if (NULL
== tokens
[tok
].cb
) {
868 roff_err(tree
, *argv
, "unsupported macro `%s'",
872 if ( ! (*tokens
[tok
].cb
)(tok
, tree
, argv
, ROFF_ENTER
))
879 roffnextopt(const struct rofftree
*tree
, int tok
,
880 char ***in
, char **val
)
889 if (NULL
== (arg
= *argv
))
894 if (ROFF_ARGMAX
== (v
= rofffindarg(arg
+ 1))) {
895 roff_warn(tree
, arg
, "argument-like parameter `%s' to "
896 "`%s'", arg
, toknames
[tok
]);
900 if ( ! roffargok(tok
, v
)) {
901 roff_warn(tree
, arg
, "invalid argument parameter `%s' to "
902 "`%s'", tokargnames
[v
], toknames
[tok
]);
906 if ( ! (ROFF_VALUE
& tokenargs
[v
]))
912 roff_err(tree
, arg
, "empty value of `%s' for `%s'",
913 tokargnames
[v
], toknames
[tok
]);
922 roffpurgepunct(struct rofftree
*tree
, char **argv
)
930 if ( ! roffispunct(argv
[--i
]))
932 while (i
>= 0 && roffispunct(argv
[i
]))
938 if ( ! roffdata(tree
, 0, argv
[i
++]))
945 roffparseopts(struct rofftree
*tree
, int tok
,
946 char ***args
, int *argc
, char **argv
)
953 while (-1 != (c
= roffnextopt(tree
, tok
, args
, &v
))) {
954 if (ROFF_ARGMAX
== c
)
963 argc
[i
] = ROFF_ARGMAX
;
970 roffdata(struct rofftree
*tree
, int space
, char *buf
)
974 if (-1 == (tok
= rofftok_scan(buf
))) {
975 roff_err(tree
, buf
, "invalid character sequence");
977 } else if (ROFFTok_MAX
!= tok
)
978 return((*tree
->cb
.rofftoken
)
979 (tree
->arg
, space
!= 0, tok
));
981 return((*tree
->cb
.roffdata
)(tree
->arg
,
982 space
!= 0, tree
->cur
, buf
));
988 roff_Dd(ROFFCALL_ARGS
)
993 if (ROFF_BODY
& tree
->state
) {
994 assert( ! (ROFF_PRELUDE
& tree
->state
));
995 assert(ROFF_PRELUDE_Dd
& tree
->state
);
996 return(roff_text(tok
, tree
, argv
, type
));
999 assert(ROFF_PRELUDE
& tree
->state
);
1000 assert( ! (ROFF_BODY
& tree
->state
));
1002 if (ROFF_PRELUDE_Dd
& tree
->state
) {
1003 roff_err(tree
, *argv
, "repeated `Dd' in prelude");
1005 } else if (ROFF_PRELUDE_Dt
& tree
->state
) {
1006 roff_err(tree
, *argv
, "out-of-order `Dd' in prelude");
1010 assert(NULL
== tree
->last
);
1014 if (0 == strcmp(*argv
, "$Mdocdate: December 4 2008 $")) {
1016 if (NULL
== localtime_r(&t
, &tree
->tm
))
1017 err(1, "localtime_r");
1018 tree
->state
|= ROFF_PRELUDE_Dd
;
1022 /* Build this from Mdocdate or raw date. */
1027 if (0 != strcmp(*argv
, "$Mdocdate:")) {
1029 if (strlcat(buf
, *argv
++, sizeof(buf
))
1032 roff_err(tree
, p
, "bad `Dd' date");
1035 if (strptime(buf
, "%b%d,%Y", &tree
->tm
)) {
1036 tree
->state
|= ROFF_PRELUDE_Dd
;
1039 roff_err(tree
, *argv
, "bad `Dd' date");
1044 while (*argv
&& **argv
!= '$') {
1045 if (strlcat(buf
, *argv
++, sizeof(buf
))
1047 roff_err(tree
, p
, "bad `Dd' Mdocdate");
1050 if (strlcat(buf
, " ", sizeof(buf
))
1052 roff_err(tree
, p
, "bad `Dd' Mdocdate");
1056 if (NULL
== *argv
) {
1057 roff_err(tree
, p
, "bad `Dd' Mdocdate");
1061 if (NULL
== strptime(buf
, "%b %d %Y", &tree
->tm
)) {
1062 roff_err(tree
, *argv
, "bad `Dd' Mdocdate");
1066 tree
->state
|= ROFF_PRELUDE_Dd
;
1073 roff_Dt(ROFFCALL_ARGS
)
1076 if (ROFF_BODY
& tree
->state
) {
1077 assert( ! (ROFF_PRELUDE
& tree
->state
));
1078 assert(ROFF_PRELUDE_Dt
& tree
->state
);
1079 return(roff_text(tok
, tree
, argv
, type
));
1082 assert(ROFF_PRELUDE
& tree
->state
);
1083 assert( ! (ROFF_BODY
& tree
->state
));
1085 if ( ! (ROFF_PRELUDE_Dd
& tree
->state
)) {
1086 roff_err(tree
, *argv
, "out-of-order `Dt' in prelude");
1088 } else if (ROFF_PRELUDE_Dt
& tree
->state
) {
1089 roff_err(tree
, *argv
, "repeated `Dt' in prelude");
1094 if (NULL
== *argv
) {
1095 roff_err(tree
, *argv
, "`Dt' needs document title");
1097 } else if (strlcpy(tree
->title
, *argv
, sizeof(tree
->title
))
1098 >= sizeof(tree
->title
)) {
1099 roff_err(tree
, *argv
, "`Dt' document title too long");
1104 if (NULL
== *argv
) {
1105 roff_err(tree
, *argv
, "`Dt' needs section");
1107 } else if (strlcpy(tree
->section
, *argv
, sizeof(tree
->section
))
1108 >= sizeof(tree
->section
)) {
1109 roff_err(tree
, *argv
, "`Dt' section too long");
1114 if (NULL
== *argv
) {
1115 tree
->volume
[0] = 0;
1116 } else if (strlcpy(tree
->volume
, *argv
, sizeof(tree
->volume
))
1117 >= sizeof(tree
->volume
)) {
1118 roff_err(tree
, *argv
, "`Dt' volume too long");
1122 assert(NULL
== tree
->last
);
1123 tree
->state
|= ROFF_PRELUDE_Dt
;
1131 roff_Sm(ROFFCALL_ARGS
)
1134 char *argvp
[1], *morep
[1], *p
;
1138 argcp
[0] = ROFF_ARGMAX
;
1140 if (NULL
== (morep
[0] = *argv
++)) {
1141 roff_err(tree
, p
, "`Sm' expects an argument");
1143 } else if (0 != strcmp(morep
[0], "on") &&
1144 0 != strcmp(morep
[0], "off")) {
1145 roff_err(tree
, p
, "`Sm' has invalid argument");
1150 roff_warn(tree
, *argv
, "`Sm' shouldn't have arguments");
1152 if ( ! (*tree
->cb
.roffspecial
)(tree
->arg
,
1153 tok
, argcp
, argvp
, morep
))
1157 if ( ! roffdata(tree
, 1, *argv
++))
1166 roff_Ns(ROFFCALL_ARGS
)
1170 char *argvp
[1], *morep
[1];
1172 first
= (*argv
++ == tree
->cur
);
1174 argcp
[0] = ROFF_ARGMAX
;
1175 argvp
[0] = morep
[0] = NULL
;
1177 if ( ! (*tree
->cb
.roffspecial
)(tree
->arg
,
1178 tok
, argcp
, argvp
, morep
))
1182 if (ROFF_MAX
!= (c
= rofffindcallable(*argv
))) {
1183 if ( ! roffcall(tree
, c
, argv
))
1188 if ( ! roffispunct(*argv
)) {
1189 if ( ! roffdata(tree
, 1, *argv
++))
1194 for (j
= 0; argv
[j
]; j
++)
1195 if ( ! roffispunct(argv
[j
]))
1199 if ( ! roffdata(tree
, 0, *argv
++))
1210 return(roffpurgepunct(tree
, argv
));
1216 roff_Os(ROFFCALL_ARGS
)
1220 if (ROFF_BODY
& tree
->state
) {
1221 assert( ! (ROFF_PRELUDE
& tree
->state
));
1222 assert(ROFF_PRELUDE_Os
& tree
->state
);
1223 return(roff_text(tok
, tree
, argv
, type
));
1226 assert(ROFF_PRELUDE
& tree
->state
);
1227 if ( ! (ROFF_PRELUDE_Dt
& tree
->state
) ||
1228 ! (ROFF_PRELUDE_Dd
& tree
->state
)) {
1229 roff_err(tree
, *argv
, "out-of-order `Os' in prelude");
1238 if (strlcat(tree
->os
, *argv
++, sizeof(tree
->os
))
1241 roff_err(tree
, p
, "`Os' value too long");
1245 if (0 == tree
->os
[0])
1246 if (strlcpy(tree
->os
, "LOCAL", sizeof(tree
->os
))
1247 >= sizeof(tree
->os
)) {
1248 roff_err(tree
, p
, "`Os' value too long");
1252 tree
->state
|= ROFF_PRELUDE_Os
;
1253 tree
->state
&= ~ROFF_PRELUDE
;
1254 tree
->state
|= ROFF_BODY
;
1256 assert(NULL
== tree
->last
);
1258 return((*tree
->cb
.roffhead
)(tree
->arg
, &tree
->tm
,
1259 tree
->os
, tree
->title
, tree
->section
,
1266 roff_layout(ROFFCALL_ARGS
)
1268 int i
, c
, argcp
[ROFF_MAXLINEARG
];
1269 char *argvp
[ROFF_MAXLINEARG
];
1271 if (ROFF_PRELUDE
& tree
->state
) {
1272 roff_err(tree
, *argv
, "bad `%s' in prelude",
1275 } else if (ROFF_EXIT
== type
) {
1276 roffnode_free(tree
);
1277 if ( ! (*tree
->cb
.roffblkbodyout
)(tree
->arg
, tok
))
1279 return((*tree
->cb
.roffblkout
)(tree
->arg
, tok
));
1282 assert( ! (ROFF_CALLABLE
& tokens
[tok
].flags
));
1286 if ( ! roffparseopts(tree
, tok
, &argv
, argcp
, argvp
))
1288 if (NULL
== roffnode_new(tok
, tree
))
1292 * Layouts have two parts: the layout body and header. The
1293 * layout header is the trailing text of the line macro, while
1294 * the layout body is everything following until termination.
1297 if ( ! (*tree
->cb
.roffblkin
)(tree
->arg
, tok
, argcp
, argvp
))
1300 return((*tree
->cb
.roffblkbodyin
)
1301 (tree
->arg
, tok
, argcp
, argvp
));
1303 if ( ! (*tree
->cb
.roffblkheadin
)(tree
->arg
, tok
, argcp
, argvp
))
1307 * If there are no parsable parts, then write remaining tokens
1308 * into the layout header and exit.
1311 if ( ! (ROFF_PARSED
& tokens
[tok
].flags
)) {
1314 if ( ! roffdata(tree
, i
++, *argv
++))
1317 if ( ! (*tree
->cb
.roffblkheadout
)(tree
->arg
, tok
))
1319 return((*tree
->cb
.roffblkbodyin
)
1320 (tree
->arg
, tok
, argcp
, argvp
));
1324 * Parsable elements may be in the header (or be the header, for
1325 * that matter). Follow the regular parsing rules for these.
1330 if (ROFF_MAX
== (c
= rofffindcallable(*argv
))) {
1332 if ( ! roffdata(tree
, i
++, *argv
++))
1336 if ( ! roffcall(tree
, c
, argv
))
1342 * If there's trailing punctuation in the header, then write it
1343 * out now. Here we mimic the behaviour of a line-dominant text
1347 if (NULL
== *argv
) {
1348 if ( ! (*tree
->cb
.roffblkheadout
)(tree
->arg
, tok
))
1350 return((*tree
->cb
.roffblkbodyin
)
1351 (tree
->arg
, tok
, argcp
, argvp
));
1355 * Expensive. Scan to the end of line then work backwards until
1356 * a token isn't punctuation.
1359 if ( ! roffpurgepunct(tree
, argv
))
1362 if ( ! (*tree
->cb
.roffblkheadout
)(tree
->arg
, tok
))
1364 return((*tree
->cb
.roffblkbodyin
)
1365 (tree
->arg
, tok
, argcp
, argvp
));
1371 roff_text(ROFFCALL_ARGS
)
1373 int i
, j
, first
, c
, argcp
[ROFF_MAXLINEARG
];
1374 char *argvp
[ROFF_MAXLINEARG
];
1376 if (ROFF_PRELUDE
& tree
->state
) {
1377 roff_err(tree
, *argv
, "`%s' disallowed in prelude",
1382 first
= (*argv
== tree
->cur
);
1385 if ( ! roffparseopts(tree
, tok
, &argv
, argcp
, argvp
))
1387 if ( ! (*tree
->cb
.roffin
)(tree
->arg
, tok
, argcp
, argvp
))
1390 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1392 if ( ! (ROFF_PARSED
& tokens
[tok
].flags
)) {
1395 if ( ! roffdata(tree
, i
++, *argv
++))
1398 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1402 * Deal with punctuation. Ugly. Work ahead until we encounter
1403 * terminating punctuation. If we encounter it and all
1404 * subsequent tokens are punctuation, then stop processing (the
1405 * line-dominant macro will print these tokens after closure).
1410 if (ROFF_MAX
!= (c
= rofffindcallable(*argv
))) {
1411 if ( ! (ROFF_LSCOPE
& tokens
[tok
].flags
))
1412 if ( ! (*tree
->cb
.roffout
)(tree
->arg
, tok
))
1415 if ( ! roffcall(tree
, c
, argv
))
1418 if (ROFF_LSCOPE
& tokens
[tok
].flags
)
1419 if ( ! (*tree
->cb
.roffout
)(tree
->arg
, tok
))
1425 if ( ! roffispunct(*argv
)) {
1426 if ( ! roffdata(tree
, i
++, *argv
++))
1432 for (j
= 0; argv
[j
]; j
++)
1433 if ( ! roffispunct(argv
[j
]))
1437 if ( ! roffdata(tree
, 0, *argv
++))
1442 if ( ! (*tree
->cb
.roffout
)(tree
->arg
, tok
))
1448 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1452 return(roffpurgepunct(tree
, argv
));
1458 roff_noop(ROFFCALL_ARGS
)
1467 roff_depr(ROFFCALL_ARGS
)
1470 roff_err(tree
, *argv
, "`%s' is deprecated", toknames
[tok
]);
1476 roff_warn(const struct rofftree
*tree
, const char *pos
, char *fmt
, ...)
1482 (void)vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
1485 (*tree
->cb
.roffmsg
)(tree
->arg
,
1486 ROFF_WARN
, tree
->cur
, pos
, buf
);
1491 roff_err(const struct rofftree
*tree
, const char *pos
, char *fmt
, ...)
1497 (void)vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
1500 (*tree
->cb
.roffmsg
)(tree
->arg
,
1501 ROFF_ERROR
, tree
->cur
, pos
, buf
);