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