Commit | Line | Data |
---|---|---|
7888c61d | 1 | /* $Id: mdoc_validate.c,v 1.198 2013/12/15 21:23:52 schwarze Exp $ */ |
80387638 | 2 | /* |
f88b6c16 | 3 | * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv> |
7888c61d | 4 | * Copyright (c) 2010, 2011, 2012, 2013 Ingo Schwarze <schwarze@openbsd.org> |
80387638 SW |
5 | * |
6 | * Permission to use, copy, modify, and distribute this software for any | |
7 | * purpose with or without fee is hereby granted, provided that the above | |
8 | * copyright notice and this permission notice appear in all copies. | |
9 | * | |
10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
17 | */ | |
18 | #ifdef HAVE_CONFIG_H | |
19 | #include "config.h" | |
20 | #endif | |
21 | ||
7888c61d | 22 | #ifndef OSNAME |
80387638 SW |
23 | #include <sys/utsname.h> |
24 | #endif | |
25 | ||
26 | #include <sys/types.h> | |
27 | ||
28 | #include <assert.h> | |
29 | #include <ctype.h> | |
30 | #include <limits.h> | |
31 | #include <stdio.h> | |
32 | #include <stdlib.h> | |
33 | #include <string.h> | |
34 | #include <time.h> | |
35 | ||
60e1e752 | 36 | #include "mdoc.h" |
80387638 SW |
37 | #include "mandoc.h" |
38 | #include "libmdoc.h" | |
39 | #include "libmandoc.h" | |
40 | ||
41 | /* FIXME: .Bl -diag can't have non-text children in HEAD. */ | |
42 | ||
43 | #define PRE_ARGS struct mdoc *mdoc, struct mdoc_node *n | |
44 | #define POST_ARGS struct mdoc *mdoc | |
45 | ||
46 | #define NUMSIZ 32 | |
47 | #define DATESIZE 32 | |
48 | ||
49 | enum check_ineq { | |
50 | CHECK_LT, | |
51 | CHECK_GT, | |
52 | CHECK_EQ | |
53 | }; | |
54 | ||
55 | enum check_lvl { | |
56 | CHECK_WARN, | |
57 | CHECK_ERROR, | |
58 | }; | |
59 | ||
60 | typedef int (*v_pre)(PRE_ARGS); | |
61 | typedef int (*v_post)(POST_ARGS); | |
62 | ||
63 | struct valids { | |
64 | v_pre *pre; | |
65 | v_post *post; | |
66 | }; | |
67 | ||
68 | static int check_count(struct mdoc *, enum mdoc_type, | |
69 | enum check_lvl, enum check_ineq, int); | |
70 | static int check_parent(PRE_ARGS, enum mdoct, enum mdoc_type); | |
71 | static void check_text(struct mdoc *, int, int, char *); | |
72 | static void check_argv(struct mdoc *, | |
73 | struct mdoc_node *, struct mdoc_argv *); | |
74 | static void check_args(struct mdoc *, struct mdoc_node *); | |
36342e81 | 75 | static int concat(char *, const struct mdoc_node *, size_t); |
60e1e752 SW |
76 | static enum mdoc_sec a2sec(const char *); |
77 | static size_t macro2len(enum mdoct); | |
80387638 SW |
78 | |
79 | static int ebool(POST_ARGS); | |
80 | static int berr_ge1(POST_ARGS); | |
81 | static int bwarn_ge1(POST_ARGS); | |
80387638 SW |
82 | static int ewarn_eq0(POST_ARGS); |
83 | static int ewarn_eq1(POST_ARGS); | |
84 | static int ewarn_ge1(POST_ARGS); | |
85 | static int ewarn_le1(POST_ARGS); | |
86 | static int hwarn_eq0(POST_ARGS); | |
87 | static int hwarn_eq1(POST_ARGS); | |
88 | static int hwarn_ge1(POST_ARGS); | |
89 | static int hwarn_le1(POST_ARGS); | |
90 | ||
91 | static int post_an(POST_ARGS); | |
92 | static int post_at(POST_ARGS); | |
93 | static int post_bf(POST_ARGS); | |
94 | static int post_bl(POST_ARGS); | |
95 | static int post_bl_block(POST_ARGS); | |
96 | static int post_bl_block_width(POST_ARGS); | |
97 | static int post_bl_block_tag(POST_ARGS); | |
98 | static int post_bl_head(POST_ARGS); | |
60e1e752 | 99 | static int post_bx(POST_ARGS); |
7888c61d | 100 | static int post_defaults(POST_ARGS); |
80387638 SW |
101 | static int post_dd(POST_ARGS); |
102 | static int post_dt(POST_ARGS); | |
80387638 | 103 | static int post_eoln(POST_ARGS); |
7888c61d FF |
104 | static int post_hyph(POST_ARGS); |
105 | static int post_ignpar(POST_ARGS); | |
80387638 SW |
106 | static int post_it(POST_ARGS); |
107 | static int post_lb(POST_ARGS); | |
7888c61d | 108 | static int post_literal(POST_ARGS); |
80387638 | 109 | static int post_nm(POST_ARGS); |
60e1e752 | 110 | static int post_ns(POST_ARGS); |
80387638 | 111 | static int post_os(POST_ARGS); |
f88b6c16 | 112 | static int post_par(POST_ARGS); |
80387638 SW |
113 | static int post_prol(POST_ARGS); |
114 | static int post_root(POST_ARGS); | |
115 | static int post_rs(POST_ARGS); | |
116 | static int post_sh(POST_ARGS); | |
117 | static int post_sh_body(POST_ARGS); | |
118 | static int post_sh_head(POST_ARGS); | |
119 | static int post_st(POST_ARGS); | |
120 | static int post_std(POST_ARGS); | |
121 | static int post_vt(POST_ARGS); | |
122 | static int pre_an(PRE_ARGS); | |
123 | static int pre_bd(PRE_ARGS); | |
124 | static int pre_bl(PRE_ARGS); | |
125 | static int pre_dd(PRE_ARGS); | |
126 | static int pre_display(PRE_ARGS); | |
127 | static int pre_dt(PRE_ARGS); | |
128 | static int pre_it(PRE_ARGS); | |
129 | static int pre_literal(PRE_ARGS); | |
130 | static int pre_os(PRE_ARGS); | |
131 | static int pre_par(PRE_ARGS); | |
132 | static int pre_sh(PRE_ARGS); | |
133 | static int pre_ss(PRE_ARGS); | |
134 | static int pre_std(PRE_ARGS); | |
135 | ||
136 | static v_post posts_an[] = { post_an, NULL }; | |
137 | static v_post posts_at[] = { post_at, post_defaults, NULL }; | |
138 | static v_post posts_bd[] = { post_literal, hwarn_eq0, bwarn_ge1, NULL }; | |
139 | static v_post posts_bf[] = { hwarn_le1, post_bf, NULL }; | |
140 | static v_post posts_bk[] = { hwarn_eq0, bwarn_ge1, NULL }; | |
141 | static v_post posts_bl[] = { bwarn_ge1, post_bl, NULL }; | |
60e1e752 | 142 | static v_post posts_bx[] = { post_bx, NULL }; |
80387638 SW |
143 | static v_post posts_bool[] = { ebool, NULL }; |
144 | static v_post posts_eoln[] = { post_eoln, NULL }; | |
145 | static v_post posts_defaults[] = { post_defaults, NULL }; | |
7888c61d | 146 | static v_post posts_d1[] = { bwarn_ge1, post_hyph, NULL }; |
60e1e752 | 147 | static v_post posts_dd[] = { post_dd, post_prol, NULL }; |
80387638 SW |
148 | static v_post posts_dl[] = { post_literal, bwarn_ge1, NULL }; |
149 | static v_post posts_dt[] = { post_dt, post_prol, NULL }; | |
150 | static v_post posts_fo[] = { hwarn_eq1, bwarn_ge1, NULL }; | |
7888c61d FF |
151 | static v_post posts_hyph[] = { post_hyph, NULL }; |
152 | static v_post posts_hyphtext[] = { ewarn_ge1, post_hyph, NULL }; | |
80387638 SW |
153 | static v_post posts_it[] = { post_it, NULL }; |
154 | static v_post posts_lb[] = { post_lb, NULL }; | |
7888c61d | 155 | static v_post posts_nd[] = { berr_ge1, post_hyph, NULL }; |
80387638 SW |
156 | static v_post posts_nm[] = { post_nm, NULL }; |
157 | static v_post posts_notext[] = { ewarn_eq0, NULL }; | |
60e1e752 | 158 | static v_post posts_ns[] = { post_ns, NULL }; |
80387638 | 159 | static v_post posts_os[] = { post_os, post_prol, NULL }; |
f88b6c16 | 160 | static v_post posts_pp[] = { post_par, ewarn_eq0, NULL }; |
80387638 | 161 | static v_post posts_rs[] = { post_rs, NULL }; |
7888c61d | 162 | static v_post posts_sh[] = { post_ignpar,hwarn_ge1,post_sh,post_hyph,NULL }; |
f88b6c16 | 163 | static v_post posts_sp[] = { post_par, ewarn_le1, NULL }; |
7888c61d | 164 | static v_post posts_ss[] = { post_ignpar, hwarn_ge1, post_hyph, NULL }; |
80387638 SW |
165 | static v_post posts_st[] = { post_st, NULL }; |
166 | static v_post posts_std[] = { post_std, NULL }; | |
60e1e752 | 167 | static v_post posts_text[] = { ewarn_ge1, NULL }; |
80387638 SW |
168 | static v_post posts_text1[] = { ewarn_eq1, NULL }; |
169 | static v_post posts_vt[] = { post_vt, NULL }; | |
80387638 SW |
170 | static v_pre pres_an[] = { pre_an, NULL }; |
171 | static v_pre pres_bd[] = { pre_display, pre_bd, pre_literal, pre_par, NULL }; | |
172 | static v_pre pres_bl[] = { pre_bl, pre_par, NULL }; | |
173 | static v_pre pres_d1[] = { pre_display, NULL }; | |
174 | static v_pre pres_dl[] = { pre_literal, pre_display, NULL }; | |
175 | static v_pre pres_dd[] = { pre_dd, NULL }; | |
176 | static v_pre pres_dt[] = { pre_dt, NULL }; | |
80387638 SW |
177 | static v_pre pres_it[] = { pre_it, pre_par, NULL }; |
178 | static v_pre pres_os[] = { pre_os, NULL }; | |
179 | static v_pre pres_pp[] = { pre_par, NULL }; | |
180 | static v_pre pres_sh[] = { pre_sh, NULL }; | |
181 | static v_pre pres_ss[] = { pre_ss, NULL }; | |
182 | static v_pre pres_std[] = { pre_std, NULL }; | |
183 | ||
60e1e752 | 184 | static const struct valids mdoc_valids[MDOC_MAX] = { |
80387638 SW |
185 | { NULL, NULL }, /* Ap */ |
186 | { pres_dd, posts_dd }, /* Dd */ | |
187 | { pres_dt, posts_dt }, /* Dt */ | |
188 | { pres_os, posts_os }, /* Os */ | |
189 | { pres_sh, posts_sh }, /* Sh */ | |
190 | { pres_ss, posts_ss }, /* Ss */ | |
f88b6c16 | 191 | { pres_pp, posts_pp }, /* Pp */ |
7888c61d | 192 | { pres_d1, posts_d1 }, /* D1 */ |
80387638 SW |
193 | { pres_dl, posts_dl }, /* Dl */ |
194 | { pres_bd, posts_bd }, /* Bd */ | |
195 | { NULL, NULL }, /* Ed */ | |
196 | { pres_bl, posts_bl }, /* Bl */ | |
197 | { NULL, NULL }, /* El */ | |
198 | { pres_it, posts_it }, /* It */ | |
60e1e752 | 199 | { NULL, NULL }, /* Ad */ |
80387638 SW |
200 | { pres_an, posts_an }, /* An */ |
201 | { NULL, posts_defaults }, /* Ar */ | |
60e1e752 | 202 | { NULL, NULL }, /* Cd */ |
80387638 SW |
203 | { NULL, NULL }, /* Cm */ |
204 | { NULL, NULL }, /* Dv */ | |
7888c61d | 205 | { NULL, NULL }, /* Er */ |
80387638 SW |
206 | { NULL, NULL }, /* Ev */ |
207 | { pres_std, posts_std }, /* Ex */ | |
208 | { NULL, NULL }, /* Fa */ | |
7888c61d | 209 | { NULL, posts_text }, /* Fd */ |
80387638 | 210 | { NULL, NULL }, /* Fl */ |
60e1e752 SW |
211 | { NULL, NULL }, /* Fn */ |
212 | { NULL, NULL }, /* Ft */ | |
213 | { NULL, NULL }, /* Ic */ | |
80387638 SW |
214 | { NULL, posts_text1 }, /* In */ |
215 | { NULL, posts_defaults }, /* Li */ | |
216 | { NULL, posts_nd }, /* Nd */ | |
217 | { NULL, posts_nm }, /* Nm */ | |
218 | { NULL, NULL }, /* Op */ | |
219 | { NULL, NULL }, /* Ot */ | |
220 | { NULL, posts_defaults }, /* Pa */ | |
221 | { pres_std, posts_std }, /* Rv */ | |
222 | { NULL, posts_st }, /* St */ | |
223 | { NULL, NULL }, /* Va */ | |
224 | { NULL, posts_vt }, /* Vt */ | |
60e1e752 | 225 | { NULL, posts_text }, /* Xr */ |
80387638 | 226 | { NULL, posts_text }, /* %A */ |
7888c61d | 227 | { NULL, posts_hyphtext }, /* %B */ /* FIXME: can be used outside Rs/Re. */ |
60e1e752 | 228 | { NULL, posts_text }, /* %D */ |
80387638 SW |
229 | { NULL, posts_text }, /* %I */ |
230 | { NULL, posts_text }, /* %J */ | |
7888c61d FF |
231 | { NULL, posts_hyphtext }, /* %N */ |
232 | { NULL, posts_hyphtext }, /* %O */ | |
80387638 | 233 | { NULL, posts_text }, /* %P */ |
7888c61d FF |
234 | { NULL, posts_hyphtext }, /* %R */ |
235 | { NULL, posts_hyphtext }, /* %T */ /* FIXME: can be used outside Rs/Re. */ | |
80387638 SW |
236 | { NULL, posts_text }, /* %V */ |
237 | { NULL, NULL }, /* Ac */ | |
238 | { NULL, NULL }, /* Ao */ | |
239 | { NULL, NULL }, /* Aq */ | |
240 | { NULL, posts_at }, /* At */ | |
241 | { NULL, NULL }, /* Bc */ | |
242 | { NULL, posts_bf }, /* Bf */ | |
243 | { NULL, NULL }, /* Bo */ | |
244 | { NULL, NULL }, /* Bq */ | |
245 | { NULL, NULL }, /* Bsx */ | |
60e1e752 | 246 | { NULL, posts_bx }, /* Bx */ |
80387638 SW |
247 | { NULL, posts_bool }, /* Db */ |
248 | { NULL, NULL }, /* Dc */ | |
249 | { NULL, NULL }, /* Do */ | |
250 | { NULL, NULL }, /* Dq */ | |
251 | { NULL, NULL }, /* Ec */ | |
252 | { NULL, NULL }, /* Ef */ | |
253 | { NULL, NULL }, /* Em */ | |
254 | { NULL, NULL }, /* Eo */ | |
255 | { NULL, NULL }, /* Fx */ | |
60e1e752 | 256 | { NULL, NULL }, /* Ms */ |
80387638 | 257 | { NULL, posts_notext }, /* No */ |
60e1e752 | 258 | { NULL, posts_ns }, /* Ns */ |
80387638 SW |
259 | { NULL, NULL }, /* Nx */ |
260 | { NULL, NULL }, /* Ox */ | |
261 | { NULL, NULL }, /* Pc */ | |
262 | { NULL, posts_text1 }, /* Pf */ | |
263 | { NULL, NULL }, /* Po */ | |
264 | { NULL, NULL }, /* Pq */ | |
265 | { NULL, NULL }, /* Qc */ | |
266 | { NULL, NULL }, /* Ql */ | |
267 | { NULL, NULL }, /* Qo */ | |
268 | { NULL, NULL }, /* Qq */ | |
269 | { NULL, NULL }, /* Re */ | |
270 | { NULL, posts_rs }, /* Rs */ | |
271 | { NULL, NULL }, /* Sc */ | |
272 | { NULL, NULL }, /* So */ | |
273 | { NULL, NULL }, /* Sq */ | |
274 | { NULL, posts_bool }, /* Sm */ | |
7888c61d | 275 | { NULL, posts_hyph }, /* Sx */ |
60e1e752 SW |
276 | { NULL, NULL }, /* Sy */ |
277 | { NULL, NULL }, /* Tn */ | |
80387638 SW |
278 | { NULL, NULL }, /* Ux */ |
279 | { NULL, NULL }, /* Xc */ | |
280 | { NULL, NULL }, /* Xo */ | |
281 | { NULL, posts_fo }, /* Fo */ | |
282 | { NULL, NULL }, /* Fc */ | |
283 | { NULL, NULL }, /* Oo */ | |
284 | { NULL, NULL }, /* Oc */ | |
285 | { NULL, posts_bk }, /* Bk */ | |
286 | { NULL, NULL }, /* Ek */ | |
287 | { NULL, posts_eoln }, /* Bt */ | |
288 | { NULL, NULL }, /* Hf */ | |
289 | { NULL, NULL }, /* Fr */ | |
290 | { NULL, posts_eoln }, /* Ud */ | |
291 | { NULL, posts_lb }, /* Lb */ | |
f88b6c16 | 292 | { pres_pp, posts_pp }, /* Lp */ |
60e1e752 | 293 | { NULL, NULL }, /* Lk */ |
80387638 SW |
294 | { NULL, posts_defaults }, /* Mt */ |
295 | { NULL, NULL }, /* Brq */ | |
296 | { NULL, NULL }, /* Bro */ | |
297 | { NULL, NULL }, /* Brc */ | |
298 | { NULL, posts_text }, /* %C */ | |
299 | { NULL, NULL }, /* Es */ | |
300 | { NULL, NULL }, /* En */ | |
301 | { NULL, NULL }, /* Dx */ | |
302 | { NULL, posts_text }, /* %Q */ | |
f88b6c16 FF |
303 | { NULL, posts_pp }, /* br */ |
304 | { NULL, posts_sp }, /* sp */ | |
80387638 SW |
305 | { NULL, posts_text1 }, /* %U */ |
306 | { NULL, NULL }, /* Ta */ | |
307 | }; | |
308 | ||
309 | #define RSORD_MAX 14 /* Number of `Rs' blocks. */ | |
310 | ||
311 | static const enum mdoct rsord[RSORD_MAX] = { | |
312 | MDOC__A, | |
313 | MDOC__T, | |
314 | MDOC__B, | |
315 | MDOC__I, | |
316 | MDOC__J, | |
317 | MDOC__R, | |
318 | MDOC__N, | |
319 | MDOC__V, | |
f88b6c16 | 320 | MDOC__U, |
80387638 SW |
321 | MDOC__P, |
322 | MDOC__Q, | |
80387638 | 323 | MDOC__C, |
f88b6c16 FF |
324 | MDOC__D, |
325 | MDOC__O | |
80387638 SW |
326 | }; |
327 | ||
60e1e752 SW |
328 | static const char * const secnames[SEC__MAX] = { |
329 | NULL, | |
330 | "NAME", | |
331 | "LIBRARY", | |
332 | "SYNOPSIS", | |
333 | "DESCRIPTION", | |
334 | "IMPLEMENTATION NOTES", | |
335 | "RETURN VALUES", | |
336 | "ENVIRONMENT", | |
337 | "FILES", | |
338 | "EXIT STATUS", | |
339 | "EXAMPLES", | |
340 | "DIAGNOSTICS", | |
341 | "COMPATIBILITY", | |
342 | "ERRORS", | |
343 | "SEE ALSO", | |
344 | "STANDARDS", | |
345 | "HISTORY", | |
346 | "AUTHORS", | |
347 | "CAVEATS", | |
348 | "BUGS", | |
349 | "SECURITY CONSIDERATIONS", | |
350 | NULL | |
351 | }; | |
80387638 SW |
352 | |
353 | int | |
354 | mdoc_valid_pre(struct mdoc *mdoc, struct mdoc_node *n) | |
355 | { | |
356 | v_pre *p; | |
357 | int line, pos; | |
358 | char *tp; | |
359 | ||
360 | switch (n->type) { | |
361 | case (MDOC_TEXT): | |
362 | tp = n->string; | |
363 | line = n->line; | |
364 | pos = n->pos; | |
365 | check_text(mdoc, line, pos, tp); | |
366 | /* FALLTHROUGH */ | |
367 | case (MDOC_TBL): | |
368 | /* FALLTHROUGH */ | |
60e1e752 SW |
369 | case (MDOC_EQN): |
370 | /* FALLTHROUGH */ | |
80387638 SW |
371 | case (MDOC_ROOT): |
372 | return(1); | |
373 | default: | |
374 | break; | |
375 | } | |
376 | ||
377 | check_args(mdoc, n); | |
378 | ||
379 | if (NULL == mdoc_valids[n->tok].pre) | |
380 | return(1); | |
381 | for (p = mdoc_valids[n->tok].pre; *p; p++) | |
382 | if ( ! (*p)(mdoc, n)) | |
383 | return(0); | |
384 | return(1); | |
385 | } | |
386 | ||
387 | ||
388 | int | |
389 | mdoc_valid_post(struct mdoc *mdoc) | |
390 | { | |
391 | v_post *p; | |
392 | ||
393 | if (MDOC_VALID & mdoc->last->flags) | |
394 | return(1); | |
395 | mdoc->last->flags |= MDOC_VALID; | |
396 | ||
397 | switch (mdoc->last->type) { | |
398 | case (MDOC_TEXT): | |
399 | /* FALLTHROUGH */ | |
60e1e752 SW |
400 | case (MDOC_EQN): |
401 | /* FALLTHROUGH */ | |
80387638 SW |
402 | case (MDOC_TBL): |
403 | return(1); | |
404 | case (MDOC_ROOT): | |
405 | return(post_root(mdoc)); | |
406 | default: | |
407 | break; | |
408 | } | |
409 | ||
410 | if (NULL == mdoc_valids[mdoc->last->tok].post) | |
411 | return(1); | |
412 | for (p = mdoc_valids[mdoc->last->tok].post; *p; p++) | |
413 | if ( ! (*p)(mdoc)) | |
414 | return(0); | |
415 | ||
416 | return(1); | |
417 | } | |
418 | ||
419 | static int | |
f88b6c16 | 420 | check_count(struct mdoc *mdoc, enum mdoc_type type, |
80387638 SW |
421 | enum check_lvl lvl, enum check_ineq ineq, int val) |
422 | { | |
423 | const char *p; | |
424 | enum mandocerr t; | |
425 | ||
f88b6c16 | 426 | if (mdoc->last->type != type) |
80387638 SW |
427 | return(1); |
428 | ||
429 | switch (ineq) { | |
430 | case (CHECK_LT): | |
431 | p = "less than "; | |
f88b6c16 | 432 | if (mdoc->last->nchild < val) |
80387638 SW |
433 | return(1); |
434 | break; | |
435 | case (CHECK_GT): | |
436 | p = "more than "; | |
f88b6c16 | 437 | if (mdoc->last->nchild > val) |
80387638 SW |
438 | return(1); |
439 | break; | |
440 | case (CHECK_EQ): | |
441 | p = ""; | |
f88b6c16 | 442 | if (val == mdoc->last->nchild) |
80387638 SW |
443 | return(1); |
444 | break; | |
445 | default: | |
446 | abort(); | |
447 | /* NOTREACHED */ | |
448 | } | |
449 | ||
450 | t = lvl == CHECK_WARN ? MANDOCERR_ARGCWARN : MANDOCERR_ARGCOUNT; | |
f88b6c16 | 451 | mandoc_vmsg(t, mdoc->parse, mdoc->last->line, mdoc->last->pos, |
80387638 | 452 | "want %s%d children (have %d)", |
f88b6c16 | 453 | p, val, mdoc->last->nchild); |
60e1e752 | 454 | return(1); |
80387638 SW |
455 | } |
456 | ||
457 | static int | |
458 | berr_ge1(POST_ARGS) | |
459 | { | |
460 | ||
461 | return(check_count(mdoc, MDOC_BODY, CHECK_ERROR, CHECK_GT, 0)); | |
462 | } | |
463 | ||
464 | static int | |
465 | bwarn_ge1(POST_ARGS) | |
466 | { | |
467 | return(check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0)); | |
468 | } | |
469 | ||
80387638 SW |
470 | static int |
471 | ewarn_eq0(POST_ARGS) | |
472 | { | |
473 | return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0)); | |
474 | } | |
475 | ||
476 | static int | |
477 | ewarn_eq1(POST_ARGS) | |
478 | { | |
479 | return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1)); | |
480 | } | |
481 | ||
482 | static int | |
483 | ewarn_ge1(POST_ARGS) | |
484 | { | |
485 | return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0)); | |
486 | } | |
487 | ||
488 | static int | |
489 | ewarn_le1(POST_ARGS) | |
490 | { | |
491 | return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_LT, 2)); | |
492 | } | |
493 | ||
494 | static int | |
495 | hwarn_eq0(POST_ARGS) | |
496 | { | |
497 | return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0)); | |
498 | } | |
499 | ||
500 | static int | |
501 | hwarn_eq1(POST_ARGS) | |
502 | { | |
503 | return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 1)); | |
504 | } | |
505 | ||
506 | static int | |
507 | hwarn_ge1(POST_ARGS) | |
508 | { | |
509 | return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_GT, 0)); | |
510 | } | |
511 | ||
512 | static int | |
513 | hwarn_le1(POST_ARGS) | |
514 | { | |
515 | return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_LT, 2)); | |
516 | } | |
517 | ||
518 | static void | |
f88b6c16 | 519 | check_args(struct mdoc *mdoc, struct mdoc_node *n) |
80387638 SW |
520 | { |
521 | int i; | |
522 | ||
523 | if (NULL == n->args) | |
524 | return; | |
525 | ||
526 | assert(n->args->argc); | |
527 | for (i = 0; i < (int)n->args->argc; i++) | |
f88b6c16 | 528 | check_argv(mdoc, n, &n->args->argv[i]); |
80387638 SW |
529 | } |
530 | ||
531 | static void | |
f88b6c16 | 532 | check_argv(struct mdoc *mdoc, struct mdoc_node *n, struct mdoc_argv *v) |
80387638 SW |
533 | { |
534 | int i; | |
535 | ||
536 | for (i = 0; i < (int)v->sz; i++) | |
f88b6c16 | 537 | check_text(mdoc, v->line, v->pos, v->value[i]); |
80387638 SW |
538 | |
539 | /* FIXME: move to post_std(). */ | |
540 | ||
541 | if (MDOC_Std == v->arg) | |
f88b6c16 FF |
542 | if ( ! (v->sz || mdoc->meta.name)) |
543 | mdoc_nmsg(mdoc, n, MANDOCERR_NONAME); | |
80387638 SW |
544 | } |
545 | ||
546 | static void | |
f88b6c16 | 547 | check_text(struct mdoc *mdoc, int ln, int pos, char *p) |
80387638 | 548 | { |
36342e81 | 549 | char *cp; |
a4c7eb57 | 550 | |
f88b6c16 | 551 | if (MDOC_LITERAL & mdoc->flags) |
36342e81 | 552 | return; |
a4c7eb57 | 553 | |
36342e81 | 554 | for (cp = p; NULL != (p = strchr(p, '\t')); p++) |
f88b6c16 | 555 | mdoc_pmsg(mdoc, ln, pos + (int)(p - cp), MANDOCERR_BADTAB); |
80387638 SW |
556 | } |
557 | ||
558 | static int | |
559 | check_parent(PRE_ARGS, enum mdoct tok, enum mdoc_type t) | |
560 | { | |
561 | ||
562 | assert(n->parent); | |
563 | if ((MDOC_ROOT == t || tok == n->parent->tok) && | |
564 | (t == n->parent->type)) | |
565 | return(1); | |
566 | ||
60e1e752 SW |
567 | mandoc_vmsg(MANDOCERR_SYNTCHILD, mdoc->parse, n->line, |
568 | n->pos, "want parent %s", MDOC_ROOT == t ? | |
569 | "<root>" : mdoc_macronames[tok]); | |
80387638 SW |
570 | return(0); |
571 | } | |
572 | ||
573 | ||
574 | static int | |
575 | pre_display(PRE_ARGS) | |
576 | { | |
577 | struct mdoc_node *node; | |
578 | ||
579 | if (MDOC_BLOCK != n->type) | |
580 | return(1); | |
581 | ||
582 | for (node = mdoc->last->parent; node; node = node->parent) | |
583 | if (MDOC_BLOCK == node->type) | |
584 | if (MDOC_Bd == node->tok) | |
585 | break; | |
586 | ||
587 | if (node) | |
588 | mdoc_nmsg(mdoc, n, MANDOCERR_NESTEDDISP); | |
589 | ||
590 | return(1); | |
591 | } | |
592 | ||
593 | ||
594 | static int | |
595 | pre_bl(PRE_ARGS) | |
596 | { | |
597 | int i, comp, dup; | |
598 | const char *offs, *width; | |
599 | enum mdoc_list lt; | |
600 | struct mdoc_node *np; | |
601 | ||
602 | if (MDOC_BLOCK != n->type) { | |
603 | if (ENDBODY_NOT != n->end) { | |
604 | assert(n->pending); | |
605 | np = n->pending->parent; | |
606 | } else | |
607 | np = n->parent; | |
608 | ||
609 | assert(np); | |
610 | assert(MDOC_BLOCK == np->type); | |
611 | assert(MDOC_Bl == np->tok); | |
612 | return(1); | |
613 | } | |
614 | ||
615 | /* | |
616 | * First figure out which kind of list to use: bind ourselves to | |
617 | * the first mentioned list type and warn about any remaining | |
618 | * ones. If we find no list type, we default to LIST_item. | |
619 | */ | |
620 | ||
621 | /* LINTED */ | |
622 | for (i = 0; n->args && i < (int)n->args->argc; i++) { | |
623 | lt = LIST__NONE; | |
624 | dup = comp = 0; | |
625 | width = offs = NULL; | |
626 | switch (n->args->argv[i].arg) { | |
627 | /* Set list types. */ | |
628 | case (MDOC_Bullet): | |
629 | lt = LIST_bullet; | |
630 | break; | |
631 | case (MDOC_Dash): | |
632 | lt = LIST_dash; | |
633 | break; | |
634 | case (MDOC_Enum): | |
635 | lt = LIST_enum; | |
636 | break; | |
637 | case (MDOC_Hyphen): | |
638 | lt = LIST_hyphen; | |
639 | break; | |
640 | case (MDOC_Item): | |
641 | lt = LIST_item; | |
642 | break; | |
643 | case (MDOC_Tag): | |
644 | lt = LIST_tag; | |
645 | break; | |
646 | case (MDOC_Diag): | |
647 | lt = LIST_diag; | |
648 | break; | |
649 | case (MDOC_Hang): | |
650 | lt = LIST_hang; | |
651 | break; | |
652 | case (MDOC_Ohang): | |
653 | lt = LIST_ohang; | |
654 | break; | |
655 | case (MDOC_Inset): | |
656 | lt = LIST_inset; | |
657 | break; | |
658 | case (MDOC_Column): | |
659 | lt = LIST_column; | |
660 | break; | |
661 | /* Set list arguments. */ | |
662 | case (MDOC_Compact): | |
663 | dup = n->norm->Bl.comp; | |
664 | comp = 1; | |
665 | break; | |
666 | case (MDOC_Width): | |
36342e81 SW |
667 | /* NB: this can be empty! */ |
668 | if (n->args->argv[i].sz) { | |
669 | width = n->args->argv[i].value[0]; | |
670 | dup = (NULL != n->norm->Bl.width); | |
671 | break; | |
672 | } | |
673 | mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV); | |
80387638 SW |
674 | break; |
675 | case (MDOC_Offset): | |
676 | /* NB: this can be empty! */ | |
677 | if (n->args->argv[i].sz) { | |
678 | offs = n->args->argv[i].value[0]; | |
679 | dup = (NULL != n->norm->Bl.offs); | |
680 | break; | |
681 | } | |
682 | mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV); | |
683 | break; | |
684 | default: | |
685 | continue; | |
686 | } | |
687 | ||
688 | /* Check: duplicate auxiliary arguments. */ | |
689 | ||
690 | if (dup) | |
691 | mdoc_nmsg(mdoc, n, MANDOCERR_ARGVREP); | |
692 | ||
693 | if (comp && ! dup) | |
694 | n->norm->Bl.comp = comp; | |
695 | if (offs && ! dup) | |
696 | n->norm->Bl.offs = offs; | |
697 | if (width && ! dup) | |
698 | n->norm->Bl.width = width; | |
699 | ||
700 | /* Check: multiple list types. */ | |
701 | ||
702 | if (LIST__NONE != lt && n->norm->Bl.type != LIST__NONE) | |
703 | mdoc_nmsg(mdoc, n, MANDOCERR_LISTREP); | |
704 | ||
705 | /* Assign list type. */ | |
706 | ||
707 | if (LIST__NONE != lt && n->norm->Bl.type == LIST__NONE) { | |
708 | n->norm->Bl.type = lt; | |
709 | /* Set column information, too. */ | |
710 | if (LIST_column == lt) { | |
711 | n->norm->Bl.ncols = | |
712 | n->args->argv[i].sz; | |
36342e81 | 713 | n->norm->Bl.cols = (void *) |
80387638 SW |
714 | n->args->argv[i].value; |
715 | } | |
716 | } | |
717 | ||
718 | /* The list type should come first. */ | |
719 | ||
720 | if (n->norm->Bl.type == LIST__NONE) | |
721 | if (n->norm->Bl.width || | |
722 | n->norm->Bl.offs || | |
723 | n->norm->Bl.comp) | |
724 | mdoc_nmsg(mdoc, n, MANDOCERR_LISTFIRST); | |
725 | ||
726 | continue; | |
727 | } | |
728 | ||
729 | /* Allow lists to default to LIST_item. */ | |
730 | ||
731 | if (LIST__NONE == n->norm->Bl.type) { | |
732 | mdoc_nmsg(mdoc, n, MANDOCERR_LISTTYPE); | |
733 | n->norm->Bl.type = LIST_item; | |
734 | } | |
735 | ||
736 | /* | |
737 | * Validate the width field. Some list types don't need width | |
738 | * types and should be warned about them. Others should have it | |
f88b6c16 FF |
739 | * and must also be warned. Yet others have a default and need |
740 | * no warning. | |
80387638 SW |
741 | */ |
742 | ||
743 | switch (n->norm->Bl.type) { | |
744 | case (LIST_tag): | |
f88b6c16 FF |
745 | if (NULL == n->norm->Bl.width) |
746 | mdoc_nmsg(mdoc, n, MANDOCERR_NOWIDTHARG); | |
80387638 SW |
747 | break; |
748 | case (LIST_column): | |
749 | /* FALLTHROUGH */ | |
750 | case (LIST_diag): | |
751 | /* FALLTHROUGH */ | |
752 | case (LIST_ohang): | |
753 | /* FALLTHROUGH */ | |
754 | case (LIST_inset): | |
755 | /* FALLTHROUGH */ | |
756 | case (LIST_item): | |
757 | if (n->norm->Bl.width) | |
758 | mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV); | |
759 | break; | |
f88b6c16 FF |
760 | case (LIST_bullet): |
761 | /* FALLTHROUGH */ | |
762 | case (LIST_dash): | |
763 | /* FALLTHROUGH */ | |
764 | case (LIST_hyphen): | |
765 | if (NULL == n->norm->Bl.width) | |
766 | n->norm->Bl.width = "2n"; | |
767 | break; | |
768 | case (LIST_enum): | |
769 | if (NULL == n->norm->Bl.width) | |
770 | n->norm->Bl.width = "3n"; | |
771 | break; | |
80387638 SW |
772 | default: |
773 | break; | |
774 | } | |
775 | ||
776 | return(1); | |
777 | } | |
778 | ||
779 | ||
780 | static int | |
781 | pre_bd(PRE_ARGS) | |
782 | { | |
783 | int i, dup, comp; | |
784 | enum mdoc_disp dt; | |
785 | const char *offs; | |
786 | struct mdoc_node *np; | |
787 | ||
788 | if (MDOC_BLOCK != n->type) { | |
789 | if (ENDBODY_NOT != n->end) { | |
790 | assert(n->pending); | |
791 | np = n->pending->parent; | |
792 | } else | |
793 | np = n->parent; | |
794 | ||
795 | assert(np); | |
796 | assert(MDOC_BLOCK == np->type); | |
797 | assert(MDOC_Bd == np->tok); | |
798 | return(1); | |
799 | } | |
800 | ||
801 | /* LINTED */ | |
802 | for (i = 0; n->args && i < (int)n->args->argc; i++) { | |
803 | dt = DISP__NONE; | |
804 | dup = comp = 0; | |
805 | offs = NULL; | |
806 | ||
807 | switch (n->args->argv[i].arg) { | |
808 | case (MDOC_Centred): | |
809 | dt = DISP_centred; | |
810 | break; | |
811 | case (MDOC_Ragged): | |
812 | dt = DISP_ragged; | |
813 | break; | |
814 | case (MDOC_Unfilled): | |
815 | dt = DISP_unfilled; | |
816 | break; | |
817 | case (MDOC_Filled): | |
818 | dt = DISP_filled; | |
819 | break; | |
820 | case (MDOC_Literal): | |
821 | dt = DISP_literal; | |
822 | break; | |
823 | case (MDOC_File): | |
824 | mdoc_nmsg(mdoc, n, MANDOCERR_BADDISP); | |
825 | return(0); | |
826 | case (MDOC_Offset): | |
827 | /* NB: this can be empty! */ | |
828 | if (n->args->argv[i].sz) { | |
829 | offs = n->args->argv[i].value[0]; | |
830 | dup = (NULL != n->norm->Bd.offs); | |
831 | break; | |
832 | } | |
833 | mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV); | |
834 | break; | |
835 | case (MDOC_Compact): | |
836 | comp = 1; | |
837 | dup = n->norm->Bd.comp; | |
838 | break; | |
839 | default: | |
840 | abort(); | |
841 | /* NOTREACHED */ | |
842 | } | |
843 | ||
844 | /* Check whether we have duplicates. */ | |
845 | ||
846 | if (dup) | |
847 | mdoc_nmsg(mdoc, n, MANDOCERR_ARGVREP); | |
848 | ||
849 | /* Make our auxiliary assignments. */ | |
850 | ||
851 | if (offs && ! dup) | |
852 | n->norm->Bd.offs = offs; | |
853 | if (comp && ! dup) | |
854 | n->norm->Bd.comp = comp; | |
855 | ||
856 | /* Check whether a type has already been assigned. */ | |
857 | ||
858 | if (DISP__NONE != dt && n->norm->Bd.type != DISP__NONE) | |
859 | mdoc_nmsg(mdoc, n, MANDOCERR_DISPREP); | |
860 | ||
861 | /* Make our type assignment. */ | |
862 | ||
863 | if (DISP__NONE != dt && n->norm->Bd.type == DISP__NONE) | |
864 | n->norm->Bd.type = dt; | |
865 | } | |
866 | ||
867 | if (DISP__NONE == n->norm->Bd.type) { | |
868 | mdoc_nmsg(mdoc, n, MANDOCERR_DISPTYPE); | |
869 | n->norm->Bd.type = DISP_ragged; | |
870 | } | |
871 | ||
872 | return(1); | |
873 | } | |
874 | ||
875 | ||
876 | static int | |
877 | pre_ss(PRE_ARGS) | |
878 | { | |
879 | ||
880 | if (MDOC_BLOCK != n->type) | |
881 | return(1); | |
882 | return(check_parent(mdoc, n, MDOC_Sh, MDOC_BODY)); | |
883 | } | |
884 | ||
885 | ||
886 | static int | |
887 | pre_sh(PRE_ARGS) | |
888 | { | |
889 | ||
890 | if (MDOC_BLOCK != n->type) | |
891 | return(1); | |
80387638 SW |
892 | return(check_parent(mdoc, n, MDOC_MAX, MDOC_ROOT)); |
893 | } | |
894 | ||
895 | ||
896 | static int | |
897 | pre_it(PRE_ARGS) | |
898 | { | |
899 | ||
900 | if (MDOC_BLOCK != n->type) | |
901 | return(1); | |
902 | ||
903 | return(check_parent(mdoc, n, MDOC_Bl, MDOC_BODY)); | |
904 | } | |
905 | ||
906 | ||
907 | static int | |
908 | pre_an(PRE_ARGS) | |
909 | { | |
910 | int i; | |
911 | ||
912 | if (NULL == n->args) | |
913 | return(1); | |
914 | ||
915 | for (i = 1; i < (int)n->args->argc; i++) | |
916 | mdoc_pmsg(mdoc, n->args->argv[i].line, | |
917 | n->args->argv[i].pos, MANDOCERR_IGNARGV); | |
918 | ||
919 | if (MDOC_Split == n->args->argv[0].arg) | |
920 | n->norm->An.auth = AUTH_split; | |
921 | else if (MDOC_Nosplit == n->args->argv[0].arg) | |
922 | n->norm->An.auth = AUTH_nosplit; | |
923 | else | |
924 | abort(); | |
925 | ||
926 | return(1); | |
927 | } | |
928 | ||
929 | static int | |
930 | pre_std(PRE_ARGS) | |
931 | { | |
932 | ||
933 | if (n->args && 1 == n->args->argc) | |
934 | if (MDOC_Std == n->args->argv[0].arg) | |
935 | return(1); | |
936 | ||
937 | mdoc_nmsg(mdoc, n, MANDOCERR_NOARGV); | |
938 | return(1); | |
939 | } | |
940 | ||
941 | static int | |
942 | pre_dt(PRE_ARGS) | |
943 | { | |
944 | ||
60e1e752 | 945 | if (NULL == mdoc->meta.date || mdoc->meta.os) |
80387638 SW |
946 | mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO); |
947 | ||
948 | if (mdoc->meta.title) | |
949 | mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP); | |
950 | ||
951 | return(1); | |
952 | } | |
953 | ||
954 | static int | |
955 | pre_os(PRE_ARGS) | |
956 | { | |
957 | ||
60e1e752 | 958 | if (NULL == mdoc->meta.title || NULL == mdoc->meta.date) |
80387638 SW |
959 | mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO); |
960 | ||
961 | if (mdoc->meta.os) | |
962 | mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP); | |
963 | ||
964 | return(1); | |
965 | } | |
966 | ||
967 | static int | |
968 | pre_dd(PRE_ARGS) | |
969 | { | |
970 | ||
971 | if (mdoc->meta.title || mdoc->meta.os) | |
972 | mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO); | |
973 | ||
974 | if (mdoc->meta.date) | |
975 | mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP); | |
976 | ||
977 | return(1); | |
978 | } | |
979 | ||
980 | ||
981 | static int | |
982 | post_bf(POST_ARGS) | |
983 | { | |
984 | struct mdoc_node *np; | |
985 | enum mdocargt arg; | |
986 | ||
987 | /* | |
988 | * Unlike other data pointers, these are "housed" by the HEAD | |
989 | * element, which contains the goods. | |
990 | */ | |
991 | ||
992 | if (MDOC_HEAD != mdoc->last->type) { | |
993 | if (ENDBODY_NOT != mdoc->last->end) { | |
994 | assert(mdoc->last->pending); | |
995 | np = mdoc->last->pending->parent->head; | |
996 | } else if (MDOC_BLOCK != mdoc->last->type) { | |
997 | np = mdoc->last->parent->head; | |
998 | } else | |
999 | np = mdoc->last->head; | |
1000 | ||
1001 | assert(np); | |
1002 | assert(MDOC_HEAD == np->type); | |
1003 | assert(MDOC_Bf == np->tok); | |
1004 | return(1); | |
1005 | } | |
1006 | ||
1007 | np = mdoc->last; | |
1008 | assert(MDOC_BLOCK == np->parent->type); | |
1009 | assert(MDOC_Bf == np->parent->tok); | |
1010 | ||
1011 | /* | |
1012 | * Cannot have both argument and parameter. | |
1013 | * If neither is specified, let it through with a warning. | |
1014 | */ | |
1015 | ||
1016 | if (np->parent->args && np->child) { | |
1017 | mdoc_nmsg(mdoc, np, MANDOCERR_SYNTARGVCOUNT); | |
1018 | return(0); | |
1019 | } else if (NULL == np->parent->args && NULL == np->child) { | |
1020 | mdoc_nmsg(mdoc, np, MANDOCERR_FONTTYPE); | |
1021 | return(1); | |
1022 | } | |
1023 | ||
1024 | /* Extract argument into data. */ | |
1025 | ||
1026 | if (np->parent->args) { | |
1027 | arg = np->parent->args->argv[0].arg; | |
1028 | if (MDOC_Emphasis == arg) | |
1029 | np->norm->Bf.font = FONT_Em; | |
1030 | else if (MDOC_Literal == arg) | |
1031 | np->norm->Bf.font = FONT_Li; | |
1032 | else if (MDOC_Symbolic == arg) | |
1033 | np->norm->Bf.font = FONT_Sy; | |
1034 | else | |
1035 | abort(); | |
1036 | return(1); | |
1037 | } | |
1038 | ||
1039 | /* Extract parameter into data. */ | |
1040 | ||
1041 | if (0 == strcmp(np->child->string, "Em")) | |
1042 | np->norm->Bf.font = FONT_Em; | |
1043 | else if (0 == strcmp(np->child->string, "Li")) | |
1044 | np->norm->Bf.font = FONT_Li; | |
1045 | else if (0 == strcmp(np->child->string, "Sy")) | |
1046 | np->norm->Bf.font = FONT_Sy; | |
1047 | else | |
1048 | mdoc_nmsg(mdoc, np, MANDOCERR_FONTTYPE); | |
1049 | ||
1050 | return(1); | |
1051 | } | |
1052 | ||
1053 | static int | |
1054 | post_lb(POST_ARGS) | |
1055 | { | |
1056 | const char *p; | |
1057 | char *buf; | |
1058 | size_t sz; | |
1059 | ||
1060 | check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1); | |
1061 | ||
1062 | assert(mdoc->last->child); | |
1063 | assert(MDOC_TEXT == mdoc->last->child->type); | |
1064 | ||
1065 | p = mdoc_a2lib(mdoc->last->child->string); | |
1066 | ||
1067 | /* If lookup ok, replace with table value. */ | |
1068 | ||
1069 | if (p) { | |
1070 | free(mdoc->last->child->string); | |
1071 | mdoc->last->child->string = mandoc_strdup(p); | |
1072 | return(1); | |
1073 | } | |
1074 | ||
1075 | /* If not, use "library ``xxxx''. */ | |
1076 | ||
1077 | sz = strlen(mdoc->last->child->string) + | |
1078 | 2 + strlen("\\(lqlibrary\\(rq"); | |
1079 | buf = mandoc_malloc(sz); | |
1080 | snprintf(buf, sz, "library \\(lq%s\\(rq", | |
1081 | mdoc->last->child->string); | |
1082 | free(mdoc->last->child->string); | |
1083 | mdoc->last->child->string = buf; | |
1084 | return(1); | |
1085 | } | |
1086 | ||
1087 | static int | |
1088 | post_eoln(POST_ARGS) | |
1089 | { | |
1090 | ||
1091 | if (mdoc->last->child) | |
1092 | mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST); | |
1093 | return(1); | |
1094 | } | |
1095 | ||
1096 | ||
1097 | static int | |
1098 | post_vt(POST_ARGS) | |
1099 | { | |
1100 | const struct mdoc_node *n; | |
1101 | ||
1102 | /* | |
1103 | * The Vt macro comes in both ELEM and BLOCK form, both of which | |
1104 | * have different syntaxes (yet more context-sensitive | |
60e1e752 SW |
1105 | * behaviour). ELEM types must have a child, which is already |
1106 | * guaranteed by the in_line parsing routine; BLOCK types, | |
80387638 SW |
1107 | * specifically the BODY, should only have TEXT children. |
1108 | */ | |
1109 | ||
80387638 SW |
1110 | if (MDOC_BODY != mdoc->last->type) |
1111 | return(1); | |
1112 | ||
1113 | for (n = mdoc->last->child; n; n = n->next) | |
1114 | if (MDOC_TEXT != n->type) | |
1115 | mdoc_nmsg(mdoc, n, MANDOCERR_CHILD); | |
1116 | ||
1117 | return(1); | |
1118 | } | |
1119 | ||
1120 | ||
1121 | static int | |
1122 | post_nm(POST_ARGS) | |
1123 | { | |
1124 | char buf[BUFSIZ]; | |
36342e81 | 1125 | int c; |
80387638 | 1126 | |
f88b6c16 | 1127 | if (NULL != mdoc->meta.name) |
80387638 SW |
1128 | return(1); |
1129 | ||
f88b6c16 | 1130 | /* Try to use our children for setting the meta name. */ |
80387638 | 1131 | |
f88b6c16 FF |
1132 | if (NULL != mdoc->last->child) { |
1133 | buf[0] = '\0'; | |
1134 | c = concat(buf, mdoc->last->child, BUFSIZ); | |
1135 | } else | |
1136 | c = 0; | |
1137 | ||
1138 | switch (c) { | |
1139 | case (-1): | |
36342e81 | 1140 | mdoc_nmsg(mdoc, mdoc->last->child, MANDOCERR_MEM); |
80387638 | 1141 | return(0); |
f88b6c16 FF |
1142 | case (0): |
1143 | mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NONAME); | |
1144 | mdoc->meta.name = mandoc_strdup("UNKNOWN"); | |
1145 | break; | |
1146 | default: | |
1147 | mdoc->meta.name = mandoc_strdup(buf); | |
1148 | break; | |
36342e81 | 1149 | } |
80387638 SW |
1150 | return(1); |
1151 | } | |
1152 | ||
1153 | static int | |
1154 | post_literal(POST_ARGS) | |
1155 | { | |
1156 | ||
1157 | /* | |
1158 | * The `Dl' (note "el" not "one") and `Bd' macros unset the | |
1159 | * MDOC_LITERAL flag as they leave. Note that `Bd' only sets | |
1160 | * this in literal mode, but it doesn't hurt to just switch it | |
1161 | * off in general since displays can't be nested. | |
1162 | */ | |
1163 | ||
1164 | if (MDOC_BODY == mdoc->last->type) | |
1165 | mdoc->flags &= ~MDOC_LITERAL; | |
1166 | ||
1167 | return(1); | |
1168 | } | |
1169 | ||
1170 | static int | |
1171 | post_defaults(POST_ARGS) | |
1172 | { | |
1173 | struct mdoc_node *nn; | |
1174 | ||
1175 | /* | |
1176 | * The `Ar' defaults to "file ..." if no value is provided as an | |
1177 | * argument; the `Mt' and `Pa' macros use "~"; the `Li' just | |
1178 | * gets an empty string. | |
1179 | */ | |
1180 | ||
1181 | if (mdoc->last->child) | |
1182 | return(1); | |
1183 | ||
1184 | nn = mdoc->last; | |
1185 | mdoc->next = MDOC_NEXT_CHILD; | |
1186 | ||
1187 | switch (nn->tok) { | |
1188 | case (MDOC_Ar): | |
1189 | if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "file")) | |
1190 | return(0); | |
1191 | if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "...")) | |
1192 | return(0); | |
1193 | break; | |
1194 | case (MDOC_At): | |
1195 | if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "AT&T")) | |
1196 | return(0); | |
1197 | if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "UNIX")) | |
1198 | return(0); | |
1199 | break; | |
1200 | case (MDOC_Li): | |
1201 | if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "")) | |
1202 | return(0); | |
1203 | break; | |
1204 | case (MDOC_Pa): | |
1205 | /* FALLTHROUGH */ | |
1206 | case (MDOC_Mt): | |
1207 | if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "~")) | |
1208 | return(0); | |
1209 | break; | |
1210 | default: | |
1211 | abort(); | |
1212 | /* NOTREACHED */ | |
1213 | } | |
1214 | ||
1215 | mdoc->last = nn; | |
1216 | return(1); | |
1217 | } | |
1218 | ||
1219 | static int | |
1220 | post_at(POST_ARGS) | |
1221 | { | |
1222 | const char *p, *q; | |
1223 | char *buf; | |
1224 | size_t sz; | |
1225 | ||
1226 | /* | |
1227 | * If we have a child, look it up in the standard keys. If a | |
1228 | * key exist, use that instead of the child; if it doesn't, | |
1229 | * prefix "AT&T UNIX " to the existing data. | |
1230 | */ | |
1231 | ||
1232 | if (NULL == mdoc->last->child) | |
1233 | return(1); | |
1234 | ||
1235 | assert(MDOC_TEXT == mdoc->last->child->type); | |
1236 | p = mdoc_a2att(mdoc->last->child->string); | |
1237 | ||
1238 | if (p) { | |
1239 | free(mdoc->last->child->string); | |
1240 | mdoc->last->child->string = mandoc_strdup(p); | |
1241 | } else { | |
1242 | mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADATT); | |
1243 | p = "AT&T UNIX "; | |
1244 | q = mdoc->last->child->string; | |
1245 | sz = strlen(p) + strlen(q) + 1; | |
1246 | buf = mandoc_malloc(sz); | |
1247 | strlcpy(buf, p, sz); | |
1248 | strlcat(buf, q, sz); | |
1249 | free(mdoc->last->child->string); | |
1250 | mdoc->last->child->string = buf; | |
1251 | } | |
1252 | ||
1253 | return(1); | |
1254 | } | |
1255 | ||
1256 | static int | |
1257 | post_an(POST_ARGS) | |
1258 | { | |
1259 | struct mdoc_node *np; | |
1260 | ||
1261 | np = mdoc->last; | |
60e1e752 SW |
1262 | if (AUTH__NONE == np->norm->An.auth) { |
1263 | if (0 == np->child) | |
1264 | check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0); | |
1265 | } else if (np->child) | |
80387638 | 1266 | check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0); |
80387638 | 1267 | |
80387638 SW |
1268 | return(1); |
1269 | } | |
1270 | ||
1271 | ||
1272 | static int | |
1273 | post_it(POST_ARGS) | |
1274 | { | |
60e1e752 | 1275 | int i, cols; |
80387638 SW |
1276 | enum mdoc_list lt; |
1277 | struct mdoc_node *n, *c; | |
1278 | enum mandocerr er; | |
1279 | ||
1280 | if (MDOC_BLOCK != mdoc->last->type) | |
1281 | return(1); | |
1282 | ||
1283 | n = mdoc->last->parent->parent; | |
1284 | lt = n->norm->Bl.type; | |
1285 | ||
1286 | if (LIST__NONE == lt) { | |
1287 | mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_LISTTYPE); | |
1288 | return(1); | |
1289 | } | |
1290 | ||
1291 | switch (lt) { | |
1292 | case (LIST_tag): | |
1293 | if (mdoc->last->head->child) | |
1294 | break; | |
1295 | /* FIXME: give this a dummy value. */ | |
1296 | mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS); | |
1297 | break; | |
1298 | case (LIST_hang): | |
1299 | /* FALLTHROUGH */ | |
1300 | case (LIST_ohang): | |
1301 | /* FALLTHROUGH */ | |
1302 | case (LIST_inset): | |
1303 | /* FALLTHROUGH */ | |
1304 | case (LIST_diag): | |
1305 | if (NULL == mdoc->last->head->child) | |
1306 | mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS); | |
1307 | break; | |
1308 | case (LIST_bullet): | |
1309 | /* FALLTHROUGH */ | |
1310 | case (LIST_dash): | |
1311 | /* FALLTHROUGH */ | |
1312 | case (LIST_enum): | |
1313 | /* FALLTHROUGH */ | |
1314 | case (LIST_hyphen): | |
1315 | if (NULL == mdoc->last->body->child) | |
1316 | mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY); | |
1317 | /* FALLTHROUGH */ | |
1318 | case (LIST_item): | |
1319 | if (mdoc->last->head->child) | |
1320 | mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST); | |
1321 | break; | |
1322 | case (LIST_column): | |
1323 | cols = (int)n->norm->Bl.ncols; | |
1324 | ||
1325 | assert(NULL == mdoc->last->head->child); | |
1326 | ||
1327 | if (NULL == mdoc->last->body->child) | |
1328 | mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY); | |
1329 | ||
1330 | for (i = 0, c = mdoc->last->child; c; c = c->next) | |
1331 | if (MDOC_BODY == c->type) | |
1332 | i++; | |
1333 | ||
1334 | if (i < cols) | |
1335 | er = MANDOCERR_ARGCOUNT; | |
1336 | else if (i == cols || i == cols + 1) | |
1337 | break; | |
1338 | else | |
1339 | er = MANDOCERR_SYNTARGCOUNT; | |
1340 | ||
60e1e752 SW |
1341 | mandoc_vmsg(er, mdoc->parse, mdoc->last->line, |
1342 | mdoc->last->pos, | |
80387638 | 1343 | "columns == %d (have %d)", cols, i); |
60e1e752 | 1344 | return(MANDOCERR_ARGCOUNT == er); |
80387638 SW |
1345 | default: |
1346 | break; | |
1347 | } | |
1348 | ||
1349 | return(1); | |
1350 | } | |
1351 | ||
1352 | static int | |
1353 | post_bl_block(POST_ARGS) | |
1354 | { | |
f88b6c16 | 1355 | struct mdoc_node *n, *ni, *nc; |
80387638 SW |
1356 | |
1357 | /* | |
1358 | * These are fairly complicated, so we've broken them into two | |
1359 | * functions. post_bl_block_tag() is called when a -tag is | |
1360 | * specified, but no -width (it must be guessed). The second | |
1361 | * when a -width is specified (macro indicators must be | |
1362 | * rewritten into real lengths). | |
1363 | */ | |
1364 | ||
1365 | n = mdoc->last; | |
1366 | ||
1367 | if (LIST_tag == n->norm->Bl.type && | |
1368 | NULL == n->norm->Bl.width) { | |
1369 | if ( ! post_bl_block_tag(mdoc)) | |
1370 | return(0); | |
f88b6c16 | 1371 | assert(n->norm->Bl.width); |
80387638 SW |
1372 | } else if (NULL != n->norm->Bl.width) { |
1373 | if ( ! post_bl_block_width(mdoc)) | |
1374 | return(0); | |
f88b6c16 FF |
1375 | assert(n->norm->Bl.width); |
1376 | } | |
80387638 | 1377 | |
f88b6c16 FF |
1378 | for (ni = n->body->child; ni; ni = ni->next) { |
1379 | if (NULL == ni->body) | |
1380 | continue; | |
1381 | nc = ni->body->last; | |
1382 | while (NULL != nc) { | |
1383 | switch (nc->tok) { | |
1384 | case (MDOC_Pp): | |
1385 | /* FALLTHROUGH */ | |
1386 | case (MDOC_Lp): | |
1387 | /* FALLTHROUGH */ | |
1388 | case (MDOC_br): | |
1389 | break; | |
1390 | default: | |
1391 | nc = NULL; | |
1392 | continue; | |
1393 | } | |
1394 | if (NULL == ni->next) { | |
1395 | mdoc_nmsg(mdoc, nc, MANDOCERR_MOVEPAR); | |
1396 | if ( ! mdoc_node_relink(mdoc, nc)) | |
1397 | return(0); | |
1398 | } else if (0 == n->norm->Bl.comp && | |
1399 | LIST_column != n->norm->Bl.type) { | |
1400 | mdoc_nmsg(mdoc, nc, MANDOCERR_IGNPAR); | |
1401 | mdoc_node_delete(mdoc, nc); | |
1402 | } else | |
1403 | break; | |
1404 | nc = ni->body->last; | |
1405 | } | |
1406 | } | |
80387638 SW |
1407 | return(1); |
1408 | } | |
1409 | ||
1410 | static int | |
1411 | post_bl_block_width(POST_ARGS) | |
1412 | { | |
1413 | size_t width; | |
1414 | int i; | |
1415 | enum mdoct tok; | |
1416 | struct mdoc_node *n; | |
1417 | char buf[NUMSIZ]; | |
1418 | ||
1419 | n = mdoc->last; | |
1420 | ||
1421 | /* | |
1422 | * Calculate the real width of a list from the -width string, | |
1423 | * which may contain a macro (with a known default width), a | |
1424 | * literal string, or a scaling width. | |
1425 | * | |
1426 | * If the value to -width is a macro, then we re-write it to be | |
1427 | * the macro's width as set in share/tmac/mdoc/doc-common. | |
1428 | */ | |
1429 | ||
1430 | if (0 == strcmp(n->norm->Bl.width, "Ds")) | |
1431 | width = 6; | |
1432 | else if (MDOC_MAX == (tok = mdoc_hash_find(n->norm->Bl.width))) | |
1433 | return(1); | |
60e1e752 | 1434 | else if (0 == (width = macro2len(tok))) { |
80387638 SW |
1435 | mdoc_nmsg(mdoc, n, MANDOCERR_BADWIDTH); |
1436 | return(1); | |
1437 | } | |
1438 | ||
1439 | /* The value already exists: free and reallocate it. */ | |
1440 | ||
1441 | assert(n->args); | |
1442 | ||
1443 | for (i = 0; i < (int)n->args->argc; i++) | |
1444 | if (MDOC_Width == n->args->argv[i].arg) | |
1445 | break; | |
1446 | ||
1447 | assert(i < (int)n->args->argc); | |
1448 | ||
36342e81 | 1449 | snprintf(buf, NUMSIZ, "%un", (unsigned int)width); |
80387638 SW |
1450 | free(n->args->argv[i].value[0]); |
1451 | n->args->argv[i].value[0] = mandoc_strdup(buf); | |
1452 | ||
1453 | /* Set our width! */ | |
1454 | n->norm->Bl.width = n->args->argv[i].value[0]; | |
1455 | return(1); | |
1456 | } | |
1457 | ||
1458 | static int | |
1459 | post_bl_block_tag(POST_ARGS) | |
1460 | { | |
1461 | struct mdoc_node *n, *nn; | |
1462 | size_t sz, ssz; | |
1463 | int i; | |
1464 | char buf[NUMSIZ]; | |
1465 | ||
1466 | /* | |
1467 | * Calculate the -width for a `Bl -tag' list if it hasn't been | |
1468 | * provided. Uses the first head macro. NOTE AGAIN: this is | |
1469 | * ONLY if the -width argument has NOT been provided. See | |
1470 | * post_bl_block_width() for converting the -width string. | |
1471 | */ | |
1472 | ||
1473 | sz = 10; | |
1474 | n = mdoc->last; | |
1475 | ||
1476 | for (nn = n->body->child; nn; nn = nn->next) { | |
1477 | if (MDOC_It != nn->tok) | |
1478 | continue; | |
1479 | ||
1480 | assert(MDOC_BLOCK == nn->type); | |
1481 | nn = nn->head->child; | |
1482 | ||
1483 | if (nn == NULL) | |
1484 | break; | |
1485 | ||
1486 | if (MDOC_TEXT == nn->type) { | |
1487 | sz = strlen(nn->string) + 1; | |
1488 | break; | |
1489 | } | |
1490 | ||
60e1e752 | 1491 | if (0 != (ssz = macro2len(nn->tok))) |
80387638 SW |
1492 | sz = ssz; |
1493 | ||
1494 | break; | |
1495 | } | |
1496 | ||
1497 | /* Defaults to ten ens. */ | |
1498 | ||
36342e81 | 1499 | snprintf(buf, NUMSIZ, "%un", (unsigned int)sz); |
80387638 SW |
1500 | |
1501 | /* | |
1502 | * We have to dynamically add this to the macro's argument list. | |
1503 | * We're guaranteed that a MDOC_Width doesn't already exist. | |
1504 | */ | |
1505 | ||
1506 | assert(n->args); | |
1507 | i = (int)(n->args->argc)++; | |
1508 | ||
1509 | n->args->argv = mandoc_realloc(n->args->argv, | |
1510 | n->args->argc * sizeof(struct mdoc_argv)); | |
1511 | ||
1512 | n->args->argv[i].arg = MDOC_Width; | |
1513 | n->args->argv[i].line = n->line; | |
1514 | n->args->argv[i].pos = n->pos; | |
1515 | n->args->argv[i].sz = 1; | |
1516 | n->args->argv[i].value = mandoc_malloc(sizeof(char *)); | |
1517 | n->args->argv[i].value[0] = mandoc_strdup(buf); | |
1518 | ||
1519 | /* Set our width! */ | |
1520 | n->norm->Bl.width = n->args->argv[i].value[0]; | |
1521 | return(1); | |
1522 | } | |
1523 | ||
1524 | ||
1525 | static int | |
1526 | post_bl_head(POST_ARGS) | |
1527 | { | |
1528 | struct mdoc_node *np, *nn, *nnp; | |
1529 | int i, j; | |
1530 | ||
1531 | if (LIST_column != mdoc->last->norm->Bl.type) | |
1532 | /* FIXME: this should be ERROR class... */ | |
1533 | return(hwarn_eq0(mdoc)); | |
1534 | ||
1535 | /* | |
1536 | * Convert old-style lists, where the column width specifiers | |
1537 | * trail as macro parameters, to the new-style ("normal-form") | |
1538 | * lists where they're argument values following -column. | |
1539 | */ | |
1540 | ||
1541 | /* First, disallow both types and allow normal-form. */ | |
1542 | ||
1543 | /* | |
1544 | * TODO: technically, we can accept both and just merge the two | |
1545 | * lists, but I'll leave that for another day. | |
1546 | */ | |
1547 | ||
1548 | if (mdoc->last->norm->Bl.ncols && mdoc->last->nchild) { | |
1549 | mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_COLUMNS); | |
1550 | return(0); | |
1551 | } else if (NULL == mdoc->last->child) | |
1552 | return(1); | |
1553 | ||
1554 | np = mdoc->last->parent; | |
1555 | assert(np->args); | |
1556 | ||
1557 | for (j = 0; j < (int)np->args->argc; j++) | |
1558 | if (MDOC_Column == np->args->argv[j].arg) | |
1559 | break; | |
1560 | ||
1561 | assert(j < (int)np->args->argc); | |
1562 | assert(0 == np->args->argv[j].sz); | |
1563 | ||
1564 | /* | |
a4c7eb57 | 1565 | * Accommodate for new-style groff column syntax. Shuffle the |
80387638 SW |
1566 | * child nodes, all of which must be TEXT, as arguments for the |
1567 | * column field. Then, delete the head children. | |
1568 | */ | |
1569 | ||
1570 | np->args->argv[j].sz = (size_t)mdoc->last->nchild; | |
1571 | np->args->argv[j].value = mandoc_malloc | |
1572 | ((size_t)mdoc->last->nchild * sizeof(char *)); | |
1573 | ||
1574 | mdoc->last->norm->Bl.ncols = np->args->argv[j].sz; | |
36342e81 | 1575 | mdoc->last->norm->Bl.cols = (void *)np->args->argv[j].value; |
80387638 SW |
1576 | |
1577 | for (i = 0, nn = mdoc->last->child; nn; i++) { | |
1578 | np->args->argv[j].value[i] = nn->string; | |
1579 | nn->string = NULL; | |
1580 | nnp = nn; | |
1581 | nn = nn->next; | |
1582 | mdoc_node_delete(NULL, nnp); | |
1583 | } | |
1584 | ||
1585 | mdoc->last->nchild = 0; | |
1586 | mdoc->last->child = NULL; | |
1587 | ||
1588 | return(1); | |
1589 | } | |
1590 | ||
1591 | static int | |
1592 | post_bl(POST_ARGS) | |
1593 | { | |
7888c61d FF |
1594 | struct mdoc_node *nparent, *nprev; /* of the Bl block */ |
1595 | struct mdoc_node *nblock, *nbody; /* of the Bl */ | |
1596 | struct mdoc_node *nchild, *nnext; /* of the Bl body */ | |
80387638 | 1597 | |
7888c61d FF |
1598 | nbody = mdoc->last; |
1599 | switch (nbody->type) { | |
1600 | case (MDOC_BLOCK): | |
80387638 | 1601 | return(post_bl_block(mdoc)); |
7888c61d FF |
1602 | case (MDOC_HEAD): |
1603 | return(post_bl_head(mdoc)); | |
1604 | case (MDOC_BODY): | |
1605 | break; | |
1606 | default: | |
80387638 | 1607 | return(1); |
7888c61d | 1608 | } |
80387638 | 1609 | |
7888c61d FF |
1610 | nchild = nbody->child; |
1611 | while (NULL != nchild) { | |
1612 | if (MDOC_It == nchild->tok || MDOC_Sm == nchild->tok) { | |
1613 | nchild = nchild->next; | |
80387638 | 1614 | continue; |
80387638 SW |
1615 | } |
1616 | ||
7888c61d FF |
1617 | mdoc_nmsg(mdoc, nchild, MANDOCERR_CHILD); |
1618 | ||
1619 | /* | |
1620 | * Move the node out of the Bl block. | |
1621 | * First, collect all required node pointers. | |
1622 | */ | |
1623 | ||
1624 | nblock = nbody->parent; | |
1625 | nprev = nblock->prev; | |
1626 | nparent = nblock->parent; | |
1627 | nnext = nchild->next; | |
1628 | ||
1629 | /* | |
1630 | * Unlink this child. | |
1631 | */ | |
1632 | ||
1633 | assert(NULL == nchild->prev); | |
1634 | if (0 == --nbody->nchild) { | |
1635 | nbody->child = NULL; | |
1636 | nbody->last = NULL; | |
1637 | assert(NULL == nnext); | |
1638 | } else { | |
1639 | nbody->child = nnext; | |
1640 | nnext->prev = NULL; | |
1641 | } | |
1642 | ||
1643 | /* | |
1644 | * Relink this child. | |
1645 | */ | |
1646 | ||
1647 | nchild->parent = nparent; | |
1648 | nchild->prev = nprev; | |
1649 | nchild->next = nblock; | |
1650 | ||
1651 | nblock->prev = nchild; | |
1652 | nparent->nchild++; | |
1653 | if (NULL == nprev) | |
1654 | nparent->child = nchild; | |
1655 | else | |
1656 | nprev->next = nchild; | |
1657 | ||
1658 | nchild = nnext; | |
80387638 SW |
1659 | } |
1660 | ||
1661 | return(1); | |
1662 | } | |
1663 | ||
1664 | static int | |
1665 | ebool(struct mdoc *mdoc) | |
1666 | { | |
1667 | ||
1668 | if (NULL == mdoc->last->child) { | |
1669 | mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_MACROEMPTY); | |
1670 | mdoc_node_delete(mdoc, mdoc->last); | |
1671 | return(1); | |
1672 | } | |
1673 | check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1); | |
1674 | ||
1675 | assert(MDOC_TEXT == mdoc->last->child->type); | |
1676 | ||
7888c61d FF |
1677 | if (0 == strcmp(mdoc->last->child->string, "on")) { |
1678 | if (MDOC_Sm == mdoc->last->tok) | |
1679 | mdoc->flags &= ~MDOC_SMOFF; | |
80387638 | 1680 | return(1); |
7888c61d FF |
1681 | } |
1682 | if (0 == strcmp(mdoc->last->child->string, "off")) { | |
1683 | if (MDOC_Sm == mdoc->last->tok) | |
1684 | mdoc->flags |= MDOC_SMOFF; | |
80387638 | 1685 | return(1); |
7888c61d | 1686 | } |
80387638 SW |
1687 | |
1688 | mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADBOOL); | |
1689 | return(1); | |
1690 | } | |
1691 | ||
1692 | static int | |
1693 | post_root(POST_ARGS) | |
1694 | { | |
1695 | int erc; | |
1696 | struct mdoc_node *n; | |
1697 | ||
1698 | erc = 0; | |
1699 | ||
1700 | /* Check that we have a finished prologue. */ | |
1701 | ||
1702 | if ( ! (MDOC_PBODY & mdoc->flags)) { | |
1703 | erc++; | |
1704 | mdoc_nmsg(mdoc, mdoc->first, MANDOCERR_NODOCPROLOG); | |
1705 | } | |
1706 | ||
1707 | n = mdoc->first; | |
1708 | assert(n); | |
1709 | ||
1710 | /* Check that we begin with a proper `Sh'. */ | |
1711 | ||
1712 | if (NULL == n->child) { | |
1713 | erc++; | |
1714 | mdoc_nmsg(mdoc, n, MANDOCERR_NODOCBODY); | |
1715 | } else if (MDOC_BLOCK != n->child->type || | |
1716 | MDOC_Sh != n->child->tok) { | |
1717 | erc++; | |
1718 | /* Can this be lifted? See rxdebug.1 for example. */ | |
1719 | mdoc_nmsg(mdoc, n, MANDOCERR_NODOCBODY); | |
1720 | } | |
1721 | ||
1722 | return(erc ? 0 : 1); | |
1723 | } | |
1724 | ||
1725 | static int | |
1726 | post_st(POST_ARGS) | |
1727 | { | |
1728 | struct mdoc_node *ch; | |
1729 | const char *p; | |
1730 | ||
1731 | if (NULL == (ch = mdoc->last->child)) { | |
1732 | mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_MACROEMPTY); | |
1733 | mdoc_node_delete(mdoc, mdoc->last); | |
1734 | return(1); | |
1735 | } | |
1736 | ||
1737 | assert(MDOC_TEXT == ch->type); | |
1738 | ||
1739 | if (NULL == (p = mdoc_a2st(ch->string))) { | |
1740 | mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADSTANDARD); | |
1741 | mdoc_node_delete(mdoc, mdoc->last); | |
1742 | } else { | |
1743 | free(ch->string); | |
1744 | ch->string = mandoc_strdup(p); | |
1745 | } | |
1746 | ||
1747 | return(1); | |
1748 | } | |
1749 | ||
1750 | static int | |
1751 | post_rs(POST_ARGS) | |
1752 | { | |
1753 | struct mdoc_node *nn, *next, *prev; | |
1754 | int i, j; | |
1755 | ||
1756 | switch (mdoc->last->type) { | |
1757 | case (MDOC_HEAD): | |
1758 | check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0); | |
1759 | return(1); | |
1760 | case (MDOC_BODY): | |
1761 | if (mdoc->last->child) | |
1762 | break; | |
1763 | check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0); | |
1764 | return(1); | |
1765 | default: | |
1766 | return(1); | |
1767 | } | |
1768 | ||
1769 | /* | |
1770 | * Make sure only certain types of nodes are allowed within the | |
1771 | * the `Rs' body. Delete offending nodes and raise a warning. | |
1772 | * Do this before re-ordering for the sake of clarity. | |
1773 | */ | |
1774 | ||
1775 | next = NULL; | |
1776 | for (nn = mdoc->last->child; nn; nn = next) { | |
1777 | for (i = 0; i < RSORD_MAX; i++) | |
1778 | if (nn->tok == rsord[i]) | |
1779 | break; | |
1780 | ||
1781 | if (i < RSORD_MAX) { | |
60e1e752 SW |
1782 | if (MDOC__J == rsord[i] || MDOC__B == rsord[i]) |
1783 | mdoc->last->norm->Rs.quote_T++; | |
80387638 SW |
1784 | next = nn->next; |
1785 | continue; | |
1786 | } | |
1787 | ||
1788 | next = nn->next; | |
1789 | mdoc_nmsg(mdoc, nn, MANDOCERR_CHILD); | |
1790 | mdoc_node_delete(mdoc, nn); | |
1791 | } | |
1792 | ||
36342e81 SW |
1793 | /* |
1794 | * Nothing to sort if only invalid nodes were found | |
1795 | * inside the `Rs' body. | |
1796 | */ | |
1797 | ||
1798 | if (NULL == mdoc->last->child) | |
1799 | return(1); | |
1800 | ||
80387638 SW |
1801 | /* |
1802 | * The full `Rs' block needs special handling to order the | |
1803 | * sub-elements according to `rsord'. Pick through each element | |
1804 | * and correctly order it. This is a insertion sort. | |
1805 | */ | |
1806 | ||
1807 | next = NULL; | |
1808 | for (nn = mdoc->last->child->next; nn; nn = next) { | |
1809 | /* Determine order of `nn'. */ | |
1810 | for (i = 0; i < RSORD_MAX; i++) | |
1811 | if (rsord[i] == nn->tok) | |
1812 | break; | |
1813 | ||
1814 | /* | |
1815 | * Remove `nn' from the chain. This somewhat | |
1816 | * repeats mdoc_node_unlink(), but since we're | |
1817 | * just re-ordering, there's no need for the | |
1818 | * full unlink process. | |
1819 | */ | |
1820 | ||
1821 | if (NULL != (next = nn->next)) | |
1822 | next->prev = nn->prev; | |
1823 | ||
1824 | if (NULL != (prev = nn->prev)) | |
1825 | prev->next = nn->next; | |
1826 | ||
1827 | nn->prev = nn->next = NULL; | |
1828 | ||
1829 | /* | |
1830 | * Scan back until we reach a node that's | |
1831 | * ordered before `nn'. | |
1832 | */ | |
1833 | ||
1834 | for ( ; prev ; prev = prev->prev) { | |
1835 | /* Determine order of `prev'. */ | |
1836 | for (j = 0; j < RSORD_MAX; j++) | |
1837 | if (rsord[j] == prev->tok) | |
1838 | break; | |
1839 | ||
1840 | if (j <= i) | |
1841 | break; | |
1842 | } | |
1843 | ||
1844 | /* | |
1845 | * Set `nn' back into its correct place in front | |
1846 | * of the `prev' node. | |
1847 | */ | |
1848 | ||
1849 | nn->prev = prev; | |
1850 | ||
1851 | if (prev) { | |
1852 | if (prev->next) | |
1853 | prev->next->prev = nn; | |
1854 | nn->next = prev->next; | |
1855 | prev->next = nn; | |
1856 | } else { | |
1857 | mdoc->last->child->prev = nn; | |
1858 | nn->next = mdoc->last->child; | |
1859 | mdoc->last->child = nn; | |
1860 | } | |
1861 | } | |
1862 | ||
1863 | return(1); | |
1864 | } | |
1865 | ||
7888c61d FF |
1866 | /* |
1867 | * For some arguments of some macros, | |
1868 | * convert all breakable hyphens into ASCII_HYPH. | |
1869 | */ | |
1870 | static int | |
1871 | post_hyph(POST_ARGS) | |
1872 | { | |
1873 | struct mdoc_node *n, *nch; | |
1874 | char *cp; | |
1875 | ||
1876 | n = mdoc->last; | |
1877 | switch (n->type) { | |
1878 | case (MDOC_HEAD): | |
1879 | if (MDOC_Sh == n->tok || MDOC_Ss == n->tok) | |
1880 | break; | |
1881 | return(1); | |
1882 | case (MDOC_BODY): | |
1883 | if (MDOC_D1 == n->tok || MDOC_Nd == n->tok) | |
1884 | break; | |
1885 | return(1); | |
1886 | case (MDOC_ELEM): | |
1887 | break; | |
1888 | default: | |
1889 | return(1); | |
1890 | } | |
1891 | ||
1892 | for (nch = n->child; nch; nch = nch->next) { | |
1893 | if (MDOC_TEXT != nch->type) | |
1894 | continue; | |
1895 | cp = nch->string; | |
1896 | if (3 > strnlen(cp, 3)) | |
1897 | continue; | |
1898 | while ('\0' != *(++cp)) | |
1899 | if ('-' == *cp && | |
1900 | isalpha((unsigned char)cp[-1]) && | |
1901 | isalpha((unsigned char)cp[1])) | |
1902 | *cp = ASCII_HYPH; | |
1903 | } | |
1904 | return(1); | |
1905 | } | |
1906 | ||
60e1e752 SW |
1907 | static int |
1908 | post_ns(POST_ARGS) | |
1909 | { | |
1910 | ||
1911 | if (MDOC_LINE & mdoc->last->flags) | |
1912 | mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNNS); | |
1913 | return(1); | |
1914 | } | |
1915 | ||
80387638 SW |
1916 | static int |
1917 | post_sh(POST_ARGS) | |
1918 | { | |
1919 | ||
1920 | if (MDOC_HEAD == mdoc->last->type) | |
1921 | return(post_sh_head(mdoc)); | |
1922 | if (MDOC_BODY == mdoc->last->type) | |
1923 | return(post_sh_body(mdoc)); | |
1924 | ||
1925 | return(1); | |
1926 | } | |
1927 | ||
1928 | static int | |
1929 | post_sh_body(POST_ARGS) | |
1930 | { | |
1931 | struct mdoc_node *n; | |
1932 | ||
1933 | if (SEC_NAME != mdoc->lastsec) | |
1934 | return(1); | |
1935 | ||
1936 | /* | |
1937 | * Warn if the NAME section doesn't contain the `Nm' and `Nd' | |
1938 | * macros (can have multiple `Nm' and one `Nd'). Note that the | |
1939 | * children of the BODY declaration can also be "text". | |
1940 | */ | |
1941 | ||
1942 | if (NULL == (n = mdoc->last->child)) { | |
1943 | mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC); | |
1944 | return(1); | |
1945 | } | |
1946 | ||
1947 | for ( ; n && n->next; n = n->next) { | |
1948 | if (MDOC_ELEM == n->type && MDOC_Nm == n->tok) | |
1949 | continue; | |
1950 | if (MDOC_TEXT == n->type) | |
1951 | continue; | |
1952 | mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC); | |
1953 | } | |
1954 | ||
1955 | assert(n); | |
1956 | if (MDOC_BLOCK == n->type && MDOC_Nd == n->tok) | |
1957 | return(1); | |
1958 | ||
1959 | mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC); | |
1960 | return(1); | |
1961 | } | |
1962 | ||
1963 | static int | |
1964 | post_sh_head(POST_ARGS) | |
1965 | { | |
1966 | char buf[BUFSIZ]; | |
36342e81 | 1967 | struct mdoc_node *n; |
80387638 | 1968 | enum mdoc_sec sec; |
36342e81 | 1969 | int c; |
80387638 SW |
1970 | |
1971 | /* | |
1972 | * Process a new section. Sections are either "named" or | |
1973 | * "custom". Custom sections are user-defined, while named ones | |
1974 | * follow a conventional order and may only appear in certain | |
1975 | * manual sections. | |
1976 | */ | |
1977 | ||
36342e81 SW |
1978 | sec = SEC_CUSTOM; |
1979 | buf[0] = '\0'; | |
1980 | if (-1 == (c = concat(buf, mdoc->last->child, BUFSIZ))) { | |
1981 | mdoc_nmsg(mdoc, mdoc->last->child, MANDOCERR_MEM); | |
80387638 | 1982 | return(0); |
36342e81 SW |
1983 | } else if (1 == c) |
1984 | sec = a2sec(buf); | |
80387638 SW |
1985 | |
1986 | /* The NAME should be first. */ | |
1987 | ||
1988 | if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed) | |
1989 | mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NAMESECFIRST); | |
1990 | ||
1991 | /* The SYNOPSIS gets special attention in other areas. */ | |
1992 | ||
7888c61d FF |
1993 | if (SEC_SYNOPSIS == sec) { |
1994 | roff_setreg(mdoc->roff, "nS", 1, '='); | |
80387638 | 1995 | mdoc->flags |= MDOC_SYNOPSIS; |
7888c61d FF |
1996 | } else { |
1997 | roff_setreg(mdoc->roff, "nS", 0, '='); | |
80387638 | 1998 | mdoc->flags &= ~MDOC_SYNOPSIS; |
7888c61d | 1999 | } |
80387638 SW |
2000 | |
2001 | /* Mark our last section. */ | |
2002 | ||
2003 | mdoc->lastsec = sec; | |
2004 | ||
36342e81 SW |
2005 | /* |
2006 | * Set the section attribute for the current HEAD, for its | |
2007 | * parent BLOCK, and for the HEAD children; the latter can | |
2008 | * only be TEXT nodes, so no recursion is needed. | |
2009 | * For other blocks and elements, including .Sh BODY, this is | |
2010 | * done when allocating the node data structures, but for .Sh | |
2011 | * BLOCK and HEAD, the section is still unknown at that time. | |
2012 | */ | |
2013 | ||
2014 | mdoc->last->parent->sec = sec; | |
2015 | mdoc->last->sec = sec; | |
2016 | for (n = mdoc->last->child; n; n = n->next) | |
2017 | n->sec = sec; | |
2018 | ||
80387638 SW |
2019 | /* We don't care about custom sections after this. */ |
2020 | ||
2021 | if (SEC_CUSTOM == sec) | |
2022 | return(1); | |
2023 | ||
2024 | /* | |
2025 | * Check whether our non-custom section is being repeated or is | |
2026 | * out of order. | |
2027 | */ | |
2028 | ||
2029 | if (sec == mdoc->lastnamed) | |
2030 | mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECREP); | |
2031 | ||
2032 | if (sec < mdoc->lastnamed) | |
2033 | mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECOOO); | |
2034 | ||
2035 | /* Mark the last named section. */ | |
2036 | ||
2037 | mdoc->lastnamed = sec; | |
2038 | ||
2039 | /* Check particular section/manual conventions. */ | |
2040 | ||
2041 | assert(mdoc->meta.msec); | |
2042 | ||
2043 | switch (sec) { | |
2044 | case (SEC_RETURN_VALUES): | |
2045 | /* FALLTHROUGH */ | |
2046 | case (SEC_ERRORS): | |
2047 | /* FALLTHROUGH */ | |
2048 | case (SEC_LIBRARY): | |
2049 | if (*mdoc->meta.msec == '2') | |
2050 | break; | |
2051 | if (*mdoc->meta.msec == '3') | |
2052 | break; | |
2053 | if (*mdoc->meta.msec == '9') | |
2054 | break; | |
f88b6c16 FF |
2055 | mandoc_msg(MANDOCERR_SECMSEC, mdoc->parse, |
2056 | mdoc->last->line, mdoc->last->pos, buf); | |
80387638 SW |
2057 | break; |
2058 | default: | |
2059 | break; | |
2060 | } | |
2061 | ||
2062 | return(1); | |
2063 | } | |
2064 | ||
2065 | static int | |
2066 | post_ignpar(POST_ARGS) | |
2067 | { | |
2068 | struct mdoc_node *np; | |
2069 | ||
2070 | if (MDOC_BODY != mdoc->last->type) | |
2071 | return(1); | |
2072 | ||
2073 | if (NULL != (np = mdoc->last->child)) | |
2074 | if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) { | |
2075 | mdoc_nmsg(mdoc, np, MANDOCERR_IGNPAR); | |
2076 | mdoc_node_delete(mdoc, np); | |
2077 | } | |
2078 | ||
2079 | if (NULL != (np = mdoc->last->last)) | |
2080 | if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) { | |
2081 | mdoc_nmsg(mdoc, np, MANDOCERR_IGNPAR); | |
2082 | mdoc_node_delete(mdoc, np); | |
2083 | } | |
2084 | ||
2085 | return(1); | |
2086 | } | |
2087 | ||
2088 | static int | |
2089 | pre_par(PRE_ARGS) | |
2090 | { | |
2091 | ||
2092 | if (NULL == mdoc->last) | |
2093 | return(1); | |
2094 | if (MDOC_ELEM != n->type && MDOC_BLOCK != n->type) | |
2095 | return(1); | |
2096 | ||
2097 | /* | |
2098 | * Don't allow prior `Lp' or `Pp' prior to a paragraph-type | |
2099 | * block: `Lp', `Pp', or non-compact `Bd' or `Bl'. | |
2100 | */ | |
2101 | ||
f88b6c16 FF |
2102 | if (MDOC_Pp != mdoc->last->tok && |
2103 | MDOC_Lp != mdoc->last->tok && | |
2104 | MDOC_br != mdoc->last->tok) | |
80387638 SW |
2105 | return(1); |
2106 | if (MDOC_Bl == n->tok && n->norm->Bl.comp) | |
2107 | return(1); | |
2108 | if (MDOC_Bd == n->tok && n->norm->Bd.comp) | |
2109 | return(1); | |
2110 | if (MDOC_It == n->tok && n->parent->norm->Bl.comp) | |
2111 | return(1); | |
2112 | ||
2113 | mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNPAR); | |
2114 | mdoc_node_delete(mdoc, mdoc->last); | |
2115 | return(1); | |
2116 | } | |
2117 | ||
f88b6c16 FF |
2118 | static int |
2119 | post_par(POST_ARGS) | |
2120 | { | |
2121 | ||
2122 | if (MDOC_ELEM != mdoc->last->type && | |
2123 | MDOC_BLOCK != mdoc->last->type) | |
2124 | return(1); | |
2125 | ||
2126 | if (NULL == mdoc->last->prev) { | |
2127 | if (MDOC_Sh != mdoc->last->parent->tok && | |
2128 | MDOC_Ss != mdoc->last->parent->tok) | |
2129 | return(1); | |
2130 | } else { | |
2131 | if (MDOC_Pp != mdoc->last->prev->tok && | |
2132 | MDOC_Lp != mdoc->last->prev->tok && | |
2133 | (MDOC_br != mdoc->last->tok || | |
2134 | (MDOC_sp != mdoc->last->prev->tok && | |
2135 | MDOC_br != mdoc->last->prev->tok))) | |
2136 | return(1); | |
2137 | } | |
2138 | ||
2139 | mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNPAR); | |
2140 | mdoc_node_delete(mdoc, mdoc->last); | |
2141 | return(1); | |
2142 | } | |
2143 | ||
80387638 SW |
2144 | static int |
2145 | pre_literal(PRE_ARGS) | |
2146 | { | |
2147 | ||
2148 | if (MDOC_BODY != n->type) | |
2149 | return(1); | |
2150 | ||
2151 | /* | |
2152 | * The `Dl' (note "el" not "one") and `Bd -literal' and `Bd | |
2153 | * -unfilled' macros set MDOC_LITERAL on entrance to the body. | |
2154 | */ | |
2155 | ||
2156 | switch (n->tok) { | |
2157 | case (MDOC_Dl): | |
2158 | mdoc->flags |= MDOC_LITERAL; | |
2159 | break; | |
2160 | case (MDOC_Bd): | |
2161 | if (DISP_literal == n->norm->Bd.type) | |
2162 | mdoc->flags |= MDOC_LITERAL; | |
2163 | if (DISP_unfilled == n->norm->Bd.type) | |
2164 | mdoc->flags |= MDOC_LITERAL; | |
2165 | break; | |
2166 | default: | |
2167 | abort(); | |
2168 | /* NOTREACHED */ | |
2169 | } | |
2170 | ||
2171 | return(1); | |
2172 | } | |
2173 | ||
2174 | static int | |
2175 | post_dd(POST_ARGS) | |
2176 | { | |
2177 | char buf[DATESIZE]; | |
2178 | struct mdoc_node *n; | |
36342e81 | 2179 | int c; |
80387638 | 2180 | |
60e1e752 SW |
2181 | if (mdoc->meta.date) |
2182 | free(mdoc->meta.date); | |
80387638 | 2183 | |
60e1e752 SW |
2184 | n = mdoc->last; |
2185 | if (NULL == n->child || '\0' == n->child->string[0]) { | |
2186 | mdoc->meta.date = mandoc_normdate | |
2187 | (mdoc->parse, NULL, n->line, n->pos); | |
80387638 SW |
2188 | return(1); |
2189 | } | |
2190 | ||
36342e81 SW |
2191 | buf[0] = '\0'; |
2192 | if (-1 == (c = concat(buf, n->child, DATESIZE))) { | |
2193 | mdoc_nmsg(mdoc, n->child, MANDOCERR_MEM); | |
80387638 | 2194 | return(0); |
36342e81 | 2195 | } |
80387638 | 2196 | |
36342e81 | 2197 | assert(c); |
60e1e752 SW |
2198 | mdoc->meta.date = mandoc_normdate |
2199 | (mdoc->parse, buf, n->line, n->pos); | |
80387638 SW |
2200 | |
2201 | return(1); | |
2202 | } | |
2203 | ||
2204 | static int | |
2205 | post_dt(POST_ARGS) | |
2206 | { | |
2207 | struct mdoc_node *nn, *n; | |
2208 | const char *cp; | |
2209 | char *p; | |
2210 | ||
2211 | n = mdoc->last; | |
2212 | ||
2213 | if (mdoc->meta.title) | |
2214 | free(mdoc->meta.title); | |
2215 | if (mdoc->meta.vol) | |
2216 | free(mdoc->meta.vol); | |
2217 | if (mdoc->meta.arch) | |
2218 | free(mdoc->meta.arch); | |
2219 | ||
2220 | mdoc->meta.title = mdoc->meta.vol = mdoc->meta.arch = NULL; | |
2221 | ||
2222 | /* First make all characters uppercase. */ | |
2223 | ||
2224 | if (NULL != (nn = n->child)) | |
2225 | for (p = nn->string; *p; p++) { | |
36342e81 | 2226 | if (toupper((unsigned char)*p) == *p) |
80387638 SW |
2227 | continue; |
2228 | ||
2229 | /* | |
2230 | * FIXME: don't be lazy: have this make all | |
2231 | * characters be uppercase and just warn once. | |
2232 | */ | |
2233 | mdoc_nmsg(mdoc, nn, MANDOCERR_UPPERCASE); | |
2234 | break; | |
2235 | } | |
2236 | ||
2237 | /* Handles: `.Dt' | |
2238 | * --> title = unknown, volume = local, msec = 0, arch = NULL | |
2239 | */ | |
2240 | ||
2241 | if (NULL == (nn = n->child)) { | |
2242 | /* XXX: make these macro values. */ | |
2243 | /* FIXME: warn about missing values. */ | |
2244 | mdoc->meta.title = mandoc_strdup("UNKNOWN"); | |
2245 | mdoc->meta.vol = mandoc_strdup("LOCAL"); | |
2246 | mdoc->meta.msec = mandoc_strdup("1"); | |
2247 | return(1); | |
2248 | } | |
2249 | ||
2250 | /* Handles: `.Dt TITLE' | |
2251 | * --> title = TITLE, volume = local, msec = 0, arch = NULL | |
2252 | */ | |
2253 | ||
2254 | mdoc->meta.title = mandoc_strdup | |
2255 | ('\0' == nn->string[0] ? "UNKNOWN" : nn->string); | |
2256 | ||
2257 | if (NULL == (nn = nn->next)) { | |
2258 | /* FIXME: warn about missing msec. */ | |
2259 | /* XXX: make this a macro value. */ | |
2260 | mdoc->meta.vol = mandoc_strdup("LOCAL"); | |
2261 | mdoc->meta.msec = mandoc_strdup("1"); | |
2262 | return(1); | |
2263 | } | |
2264 | ||
2265 | /* Handles: `.Dt TITLE SEC' | |
2266 | * --> title = TITLE, volume = SEC is msec ? | |
2267 | * format(msec) : SEC, | |
2268 | * msec = SEC is msec ? atoi(msec) : 0, | |
2269 | * arch = NULL | |
2270 | */ | |
2271 | ||
36342e81 | 2272 | cp = mandoc_a2msec(nn->string); |
80387638 SW |
2273 | if (cp) { |
2274 | mdoc->meta.vol = mandoc_strdup(cp); | |
2275 | mdoc->meta.msec = mandoc_strdup(nn->string); | |
2276 | } else { | |
2277 | mdoc_nmsg(mdoc, n, MANDOCERR_BADMSEC); | |
2278 | mdoc->meta.vol = mandoc_strdup(nn->string); | |
2279 | mdoc->meta.msec = mandoc_strdup(nn->string); | |
2280 | } | |
2281 | ||
2282 | if (NULL == (nn = nn->next)) | |
2283 | return(1); | |
2284 | ||
2285 | /* Handles: `.Dt TITLE SEC VOL' | |
2286 | * --> title = TITLE, volume = VOL is vol ? | |
2287 | * format(VOL) : | |
2288 | * VOL is arch ? format(arch) : | |
2289 | * VOL | |
2290 | */ | |
2291 | ||
2292 | cp = mdoc_a2vol(nn->string); | |
2293 | if (cp) { | |
2294 | free(mdoc->meta.vol); | |
2295 | mdoc->meta.vol = mandoc_strdup(cp); | |
2296 | } else { | |
80387638 SW |
2297 | cp = mdoc_a2arch(nn->string); |
2298 | if (NULL == cp) { | |
f88b6c16 | 2299 | mdoc_nmsg(mdoc, nn, MANDOCERR_BADVOLARCH); |
80387638 SW |
2300 | free(mdoc->meta.vol); |
2301 | mdoc->meta.vol = mandoc_strdup(nn->string); | |
2302 | } else | |
2303 | mdoc->meta.arch = mandoc_strdup(cp); | |
2304 | } | |
2305 | ||
2306 | /* Ignore any subsequent parameters... */ | |
2307 | /* FIXME: warn about subsequent parameters. */ | |
2308 | ||
2309 | return(1); | |
2310 | } | |
2311 | ||
2312 | static int | |
2313 | post_prol(POST_ARGS) | |
2314 | { | |
2315 | /* | |
2316 | * Remove prologue macros from the document after they're | |
2317 | * processed. The final document uses mdoc_meta for these | |
2318 | * values and discards the originals. | |
2319 | */ | |
2320 | ||
2321 | mdoc_node_delete(mdoc, mdoc->last); | |
2322 | if (mdoc->meta.title && mdoc->meta.date && mdoc->meta.os) | |
2323 | mdoc->flags |= MDOC_PBODY; | |
2324 | ||
2325 | return(1); | |
2326 | } | |
2327 | ||
60e1e752 SW |
2328 | static int |
2329 | post_bx(POST_ARGS) | |
2330 | { | |
2331 | struct mdoc_node *n; | |
2332 | ||
2333 | /* | |
2334 | * Make `Bx's second argument always start with an uppercase | |
2335 | * letter. Groff checks if it's an "accepted" term, but we just | |
2336 | * uppercase blindly. | |
2337 | */ | |
2338 | ||
2339 | n = mdoc->last->child; | |
2340 | if (n && NULL != (n = n->next)) | |
2341 | *n->string = (char)toupper | |
2342 | ((unsigned char)*n->string); | |
2343 | ||
2344 | return(1); | |
2345 | } | |
2346 | ||
80387638 SW |
2347 | static int |
2348 | post_os(POST_ARGS) | |
2349 | { | |
2350 | struct mdoc_node *n; | |
2351 | char buf[BUFSIZ]; | |
36342e81 | 2352 | int c; |
80387638 SW |
2353 | #ifndef OSNAME |
2354 | struct utsname utsname; | |
2355 | #endif | |
2356 | ||
2357 | n = mdoc->last; | |
2358 | ||
2359 | /* | |
f88b6c16 FF |
2360 | * Set the operating system by way of the `Os' macro. |
2361 | * The order of precedence is: | |
2362 | * 1. the argument of the `Os' macro, unless empty | |
2363 | * 2. the -Ios=foo command line argument, if provided | |
2364 | * 3. -DOSNAME="\"foo\"", if provided during compilation | |
2365 | * 4. "sysname release" from uname(3) | |
80387638 SW |
2366 | */ |
2367 | ||
f88b6c16 | 2368 | free(mdoc->meta.os); |
80387638 | 2369 | |
36342e81 SW |
2370 | buf[0] = '\0'; |
2371 | if (-1 == (c = concat(buf, n->child, BUFSIZ))) { | |
2372 | mdoc_nmsg(mdoc, n->child, MANDOCERR_MEM); | |
80387638 | 2373 | return(0); |
36342e81 SW |
2374 | } |
2375 | ||
2376 | assert(c); | |
80387638 | 2377 | |
80387638 | 2378 | if ('\0' == buf[0]) { |
f88b6c16 FF |
2379 | if (mdoc->defos) { |
2380 | mdoc->meta.os = mandoc_strdup(mdoc->defos); | |
2381 | return(1); | |
2382 | } | |
80387638 SW |
2383 | #ifdef OSNAME |
2384 | if (strlcat(buf, OSNAME, BUFSIZ) >= BUFSIZ) { | |
2385 | mdoc_nmsg(mdoc, n, MANDOCERR_MEM); | |
2386 | return(0); | |
2387 | } | |
2388 | #else /*!OSNAME */ | |
60e1e752 | 2389 | if (-1 == uname(&utsname)) { |
80387638 SW |
2390 | mdoc_nmsg(mdoc, n, MANDOCERR_UNAME); |
2391 | mdoc->meta.os = mandoc_strdup("UNKNOWN"); | |
2392 | return(post_prol(mdoc)); | |
2393 | } | |
2394 | ||
2395 | if (strlcat(buf, utsname.sysname, BUFSIZ) >= BUFSIZ) { | |
2396 | mdoc_nmsg(mdoc, n, MANDOCERR_MEM); | |
2397 | return(0); | |
2398 | } | |
2399 | if (strlcat(buf, " ", BUFSIZ) >= BUFSIZ) { | |
2400 | mdoc_nmsg(mdoc, n, MANDOCERR_MEM); | |
2401 | return(0); | |
2402 | } | |
2403 | if (strlcat(buf, utsname.release, BUFSIZ) >= BUFSIZ) { | |
2404 | mdoc_nmsg(mdoc, n, MANDOCERR_MEM); | |
2405 | return(0); | |
2406 | } | |
2407 | #endif /*!OSNAME*/ | |
2408 | } | |
2409 | ||
2410 | mdoc->meta.os = mandoc_strdup(buf); | |
2411 | return(1); | |
2412 | } | |
2413 | ||
2414 | static int | |
2415 | post_std(POST_ARGS) | |
2416 | { | |
2417 | struct mdoc_node *nn, *n; | |
2418 | ||
2419 | n = mdoc->last; | |
2420 | ||
2421 | /* | |
2422 | * Macros accepting `-std' as an argument have the name of the | |
2423 | * current document (`Nm') filled in as the argument if it's not | |
2424 | * provided. | |
2425 | */ | |
2426 | ||
2427 | if (n->child) | |
2428 | return(1); | |
2429 | ||
2430 | if (NULL == mdoc->meta.name) | |
2431 | return(1); | |
2432 | ||
2433 | nn = n; | |
2434 | mdoc->next = MDOC_NEXT_CHILD; | |
2435 | ||
2436 | if ( ! mdoc_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name)) | |
2437 | return(0); | |
2438 | ||
2439 | mdoc->last = nn; | |
2440 | return(1); | |
2441 | } | |
2442 | ||
36342e81 SW |
2443 | /* |
2444 | * Concatenate a node, stopping at the first non-text. | |
2445 | * Concatenation is separated by a single whitespace. | |
2446 | * Returns -1 on fatal (string overrun) error, 0 if child nodes were | |
2447 | * encountered, 1 otherwise. | |
2448 | */ | |
80387638 | 2449 | static int |
36342e81 | 2450 | concat(char *p, const struct mdoc_node *n, size_t sz) |
80387638 SW |
2451 | { |
2452 | ||
36342e81 SW |
2453 | for ( ; NULL != n; n = n->next) { |
2454 | if (MDOC_TEXT != n->type) | |
80387638 | 2455 | return(0); |
36342e81 SW |
2456 | if ('\0' != p[0] && strlcat(p, " ", sz) >= sz) |
2457 | return(-1); | |
2458 | if (strlcat(p, n->string, sz) >= sz) | |
2459 | return(-1); | |
2460 | concat(p, n->child, sz); | |
80387638 SW |
2461 | } |
2462 | ||
2463 | return(1); | |
2464 | } | |
2465 | ||
60e1e752 SW |
2466 | static enum mdoc_sec |
2467 | a2sec(const char *p) | |
2468 | { | |
2469 | int i; | |
2470 | ||
2471 | for (i = 0; i < (int)SEC__MAX; i++) | |
2472 | if (secnames[i] && 0 == strcmp(p, secnames[i])) | |
2473 | return((enum mdoc_sec)i); | |
2474 | ||
2475 | return(SEC_CUSTOM); | |
2476 | } | |
2477 | ||
2478 | static size_t | |
2479 | macro2len(enum mdoct macro) | |
2480 | { | |
2481 | ||
2482 | switch (macro) { | |
2483 | case(MDOC_Ad): | |
2484 | return(12); | |
2485 | case(MDOC_Ao): | |
2486 | return(12); | |
2487 | case(MDOC_An): | |
2488 | return(12); | |
2489 | case(MDOC_Aq): | |
2490 | return(12); | |
2491 | case(MDOC_Ar): | |
2492 | return(12); | |
2493 | case(MDOC_Bo): | |
2494 | return(12); | |
2495 | case(MDOC_Bq): | |
2496 | return(12); | |
2497 | case(MDOC_Cd): | |
2498 | return(12); | |
2499 | case(MDOC_Cm): | |
2500 | return(10); | |
2501 | case(MDOC_Do): | |
2502 | return(10); | |
2503 | case(MDOC_Dq): | |
2504 | return(12); | |
2505 | case(MDOC_Dv): | |
2506 | return(12); | |
2507 | case(MDOC_Eo): | |
2508 | return(12); | |
2509 | case(MDOC_Em): | |
2510 | return(10); | |
2511 | case(MDOC_Er): | |
2512 | return(17); | |
2513 | case(MDOC_Ev): | |
2514 | return(15); | |
2515 | case(MDOC_Fa): | |
2516 | return(12); | |
2517 | case(MDOC_Fl): | |
2518 | return(10); | |
2519 | case(MDOC_Fo): | |
2520 | return(16); | |
2521 | case(MDOC_Fn): | |
2522 | return(16); | |
2523 | case(MDOC_Ic): | |
2524 | return(10); | |
2525 | case(MDOC_Li): | |
2526 | return(16); | |
2527 | case(MDOC_Ms): | |
2528 | return(6); | |
2529 | case(MDOC_Nm): | |
2530 | return(10); | |
2531 | case(MDOC_No): | |
2532 | return(12); | |
2533 | case(MDOC_Oo): | |
2534 | return(10); | |
2535 | case(MDOC_Op): | |
2536 | return(14); | |
2537 | case(MDOC_Pa): | |
2538 | return(32); | |
2539 | case(MDOC_Pf): | |
2540 | return(12); | |
2541 | case(MDOC_Po): | |
2542 | return(12); | |
2543 | case(MDOC_Pq): | |
2544 | return(12); | |
2545 | case(MDOC_Ql): | |
2546 | return(16); | |
2547 | case(MDOC_Qo): | |
2548 | return(12); | |
2549 | case(MDOC_So): | |
2550 | return(12); | |
2551 | case(MDOC_Sq): | |
2552 | return(12); | |
2553 | case(MDOC_Sy): | |
2554 | return(6); | |
2555 | case(MDOC_Sx): | |
2556 | return(16); | |
2557 | case(MDOC_Tn): | |
2558 | return(10); | |
2559 | case(MDOC_Va): | |
2560 | return(12); | |
2561 | case(MDOC_Vt): | |
2562 | return(12); | |
2563 | case(MDOC_Xr): | |
2564 | return(10); | |
2565 | default: | |
2566 | break; | |
2567 | }; | |
2568 | return(0); | |
2569 | } |