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