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