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