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