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