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