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