]> git.cameronkatri.com Git - mandoc.git/blob - mdoc_validate.c
Bring .Pp/.Lp handling inside .Nm blocks closer to groff;
[mandoc.git] / mdoc_validate.c
1 /* $Id: mdoc_validate.c,v 1.266 2015/02/03 00:48:47 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 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 struct mdoc_node *n;
1051
1052 n = mdoc->last;
1053
1054 if (n->last != NULL &&
1055 (n->last->tok == MDOC_Pp ||
1056 n->last->tok == MDOC_Lp))
1057 mdoc_node_relink(mdoc, n->last);
1058
1059 if (NULL != mdoc->meta.name)
1060 return;
1061
1062 mdoc_deroff(&mdoc->meta.name, n);
1063
1064 if (NULL == mdoc->meta.name)
1065 mandoc_msg(MANDOCERR_NM_NONAME, mdoc->parse,
1066 n->line, n->pos, "Nm");
1067 }
1068
1069 static void
1070 post_nd(POST_ARGS)
1071 {
1072
1073 check_count(mdoc, MDOC_BODY, CHECK_ERROR, CHECK_GT, 0);
1074 post_hyph(mdoc);
1075 }
1076
1077 static void
1078 post_d1(POST_ARGS)
1079 {
1080
1081 bwarn_ge1(mdoc);
1082 post_hyph(mdoc);
1083 }
1084
1085 static void
1086 post_literal(POST_ARGS)
1087 {
1088
1089 if (mdoc->last->tok == MDOC_Bd)
1090 hwarn_eq0(mdoc);
1091 bwarn_ge1(mdoc);
1092
1093 /*
1094 * The `Dl' (note "el" not "one") and `Bd' macros unset the
1095 * MDOC_LITERAL flag as they leave. Note that `Bd' only sets
1096 * this in literal mode, but it doesn't hurt to just switch it
1097 * off in general since displays can't be nested.
1098 */
1099
1100 if (MDOC_BODY == mdoc->last->type)
1101 mdoc->flags &= ~MDOC_LITERAL;
1102 }
1103
1104 static void
1105 post_defaults(POST_ARGS)
1106 {
1107 struct mdoc_node *nn;
1108
1109 /*
1110 * The `Ar' defaults to "file ..." if no value is provided as an
1111 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
1112 * gets an empty string.
1113 */
1114
1115 if (mdoc->last->child)
1116 return;
1117
1118 nn = mdoc->last;
1119 mdoc->next = MDOC_NEXT_CHILD;
1120
1121 switch (nn->tok) {
1122 case MDOC_Ar:
1123 mdoc_word_alloc(mdoc, nn->line, nn->pos, "file");
1124 mdoc_word_alloc(mdoc, nn->line, nn->pos, "...");
1125 break;
1126 case MDOC_Pa:
1127 /* FALLTHROUGH */
1128 case MDOC_Mt:
1129 mdoc_word_alloc(mdoc, nn->line, nn->pos, "~");
1130 break;
1131 default:
1132 abort();
1133 /* NOTREACHED */
1134 }
1135 mdoc->last = nn;
1136 }
1137
1138 static void
1139 post_at(POST_ARGS)
1140 {
1141 struct mdoc_node *n;
1142 const char *std_att;
1143 char *att;
1144
1145 n = mdoc->last;
1146 if (n->child == NULL) {
1147 mdoc->next = MDOC_NEXT_CHILD;
1148 mdoc_word_alloc(mdoc, n->line, n->pos, "AT&T UNIX");
1149 mdoc->last = n;
1150 return;
1151 }
1152
1153 /*
1154 * If we have a child, look it up in the standard keys. If a
1155 * key exist, use that instead of the child; if it doesn't,
1156 * prefix "AT&T UNIX " to the existing data.
1157 */
1158
1159 n = n->child;
1160 assert(MDOC_TEXT == n->type);
1161 if (NULL == (std_att = mdoc_a2att(n->string))) {
1162 mandoc_vmsg(MANDOCERR_AT_BAD, mdoc->parse,
1163 n->line, n->pos, "At %s", n->string);
1164 mandoc_asprintf(&att, "AT&T UNIX %s", n->string);
1165 } else
1166 att = mandoc_strdup(std_att);
1167
1168 free(n->string);
1169 n->string = att;
1170 }
1171
1172 static void
1173 post_an(POST_ARGS)
1174 {
1175 struct mdoc_node *np;
1176
1177 np = mdoc->last;
1178 if (AUTH__NONE == np->norm->An.auth) {
1179 if (0 == np->child)
1180 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0);
1181 } else if (np->child)
1182 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0);
1183 }
1184
1185 static void
1186 post_en(POST_ARGS)
1187 {
1188
1189 if (MDOC_BLOCK == mdoc->last->type)
1190 mdoc->last->norm->Es = mdoc->last_es;
1191 }
1192
1193 static void
1194 post_es(POST_ARGS)
1195 {
1196
1197 mdoc->last_es = mdoc->last;
1198 }
1199
1200 static void
1201 post_it(POST_ARGS)
1202 {
1203 int i, cols;
1204 enum mdoc_list lt;
1205 struct mdoc_node *nbl, *nit, *nch;
1206
1207 nit = mdoc->last;
1208 if (nit->type != MDOC_BLOCK)
1209 return;
1210
1211 nbl = nit->parent->parent;
1212 lt = nbl->norm->Bl.type;
1213
1214 switch (lt) {
1215 case LIST_tag:
1216 /* FALLTHROUGH */
1217 case LIST_hang:
1218 /* FALLTHROUGH */
1219 case LIST_ohang:
1220 /* FALLTHROUGH */
1221 case LIST_inset:
1222 /* FALLTHROUGH */
1223 case LIST_diag:
1224 if (nit->head->child == NULL)
1225 mandoc_vmsg(MANDOCERR_IT_NOHEAD,
1226 mdoc->parse, nit->line, nit->pos,
1227 "Bl -%s It",
1228 mdoc_argnames[nbl->args->argv[0].arg]);
1229 break;
1230 case LIST_bullet:
1231 /* FALLTHROUGH */
1232 case LIST_dash:
1233 /* FALLTHROUGH */
1234 case LIST_enum:
1235 /* FALLTHROUGH */
1236 case LIST_hyphen:
1237 if (nit->body == NULL || nit->body->child == NULL)
1238 mandoc_vmsg(MANDOCERR_IT_NOBODY,
1239 mdoc->parse, nit->line, nit->pos,
1240 "Bl -%s It",
1241 mdoc_argnames[nbl->args->argv[0].arg]);
1242 /* FALLTHROUGH */
1243 case LIST_item:
1244 if (nit->head->child != NULL)
1245 mandoc_vmsg(MANDOCERR_ARG_SKIP,
1246 mdoc->parse, nit->line, nit->pos,
1247 "It %s", nit->head->child->string);
1248 break;
1249 case LIST_column:
1250 cols = (int)nbl->norm->Bl.ncols;
1251
1252 assert(nit->head->child == NULL);
1253
1254 for (i = 0, nch = nit->child; nch; nch = nch->next)
1255 if (nch->type == MDOC_BODY)
1256 i++;
1257
1258 if (i < cols || i > cols + 1)
1259 mandoc_vmsg(MANDOCERR_ARGCOUNT,
1260 mdoc->parse, nit->line, nit->pos,
1261 "columns == %d (have %d)", cols, i);
1262 break;
1263 default:
1264 abort();
1265 }
1266 }
1267
1268 static void
1269 post_bl_block(POST_ARGS)
1270 {
1271 struct mdoc_node *n, *ni, *nc;
1272
1273 /*
1274 * These are fairly complicated, so we've broken them into two
1275 * functions. post_bl_block_tag() is called when a -tag is
1276 * specified, but no -width (it must be guessed). The second
1277 * when a -width is specified (macro indicators must be
1278 * rewritten into real lengths).
1279 */
1280
1281 n = mdoc->last;
1282
1283 if (LIST_tag == n->norm->Bl.type &&
1284 NULL == n->norm->Bl.width) {
1285 post_bl_block_tag(mdoc);
1286 assert(n->norm->Bl.width);
1287 }
1288
1289 for (ni = n->body->child; ni; ni = ni->next) {
1290 if (NULL == ni->body)
1291 continue;
1292 nc = ni->body->last;
1293 while (NULL != nc) {
1294 switch (nc->tok) {
1295 case MDOC_Pp:
1296 /* FALLTHROUGH */
1297 case MDOC_Lp:
1298 /* FALLTHROUGH */
1299 case MDOC_br:
1300 break;
1301 default:
1302 nc = NULL;
1303 continue;
1304 }
1305 if (NULL == ni->next) {
1306 mandoc_msg(MANDOCERR_PAR_MOVE,
1307 mdoc->parse, nc->line, nc->pos,
1308 mdoc_macronames[nc->tok]);
1309 mdoc_node_relink(mdoc, nc);
1310 } else if (0 == n->norm->Bl.comp &&
1311 LIST_column != n->norm->Bl.type) {
1312 mandoc_vmsg(MANDOCERR_PAR_SKIP,
1313 mdoc->parse, nc->line, nc->pos,
1314 "%s before It",
1315 mdoc_macronames[nc->tok]);
1316 mdoc_node_delete(mdoc, nc);
1317 } else
1318 break;
1319 nc = ni->body->last;
1320 }
1321 }
1322 }
1323
1324 /*
1325 * If the argument of -offset or -width is a macro,
1326 * replace it with the associated default width.
1327 */
1328 void
1329 rewrite_macro2len(char **arg)
1330 {
1331 size_t width;
1332 enum mdoct tok;
1333
1334 if (*arg == NULL)
1335 return;
1336 else if ( ! strcmp(*arg, "Ds"))
1337 width = 6;
1338 else if ((tok = mdoc_hash_find(*arg)) == MDOC_MAX)
1339 return;
1340 else
1341 width = macro2len(tok);
1342
1343 free(*arg);
1344 mandoc_asprintf(arg, "%zun", width);
1345 }
1346
1347 static void
1348 post_bl_block_tag(POST_ARGS)
1349 {
1350 struct mdoc_node *n, *nn;
1351 size_t sz, ssz;
1352 int i;
1353 char buf[24];
1354
1355 /*
1356 * Calculate the -width for a `Bl -tag' list if it hasn't been
1357 * provided. Uses the first head macro. NOTE AGAIN: this is
1358 * ONLY if the -width argument has NOT been provided. See
1359 * rewrite_macro2len() for converting the -width string.
1360 */
1361
1362 sz = 10;
1363 n = mdoc->last;
1364
1365 for (nn = n->body->child; nn; nn = nn->next) {
1366 if (MDOC_It != nn->tok)
1367 continue;
1368
1369 assert(MDOC_BLOCK == nn->type);
1370 nn = nn->head->child;
1371
1372 if (nn == NULL)
1373 break;
1374
1375 if (MDOC_TEXT == nn->type) {
1376 sz = strlen(nn->string) + 1;
1377 break;
1378 }
1379
1380 if (0 != (ssz = macro2len(nn->tok)))
1381 sz = ssz;
1382
1383 break;
1384 }
1385
1386 /* Defaults to ten ens. */
1387
1388 (void)snprintf(buf, sizeof(buf), "%un", (unsigned int)sz);
1389
1390 /*
1391 * We have to dynamically add this to the macro's argument list.
1392 * We're guaranteed that a MDOC_Width doesn't already exist.
1393 */
1394
1395 assert(n->args);
1396 i = (int)(n->args->argc)++;
1397
1398 n->args->argv = mandoc_reallocarray(n->args->argv,
1399 n->args->argc, sizeof(struct mdoc_argv));
1400
1401 n->args->argv[i].arg = MDOC_Width;
1402 n->args->argv[i].line = n->line;
1403 n->args->argv[i].pos = n->pos;
1404 n->args->argv[i].sz = 1;
1405 n->args->argv[i].value = mandoc_malloc(sizeof(char *));
1406 n->args->argv[i].value[0] = mandoc_strdup(buf);
1407
1408 /* Set our width! */
1409 n->norm->Bl.width = n->args->argv[i].value[0];
1410 }
1411
1412 static void
1413 post_bl_head(POST_ARGS)
1414 {
1415 struct mdoc_node *np, *nn, *nnp;
1416 struct mdoc_argv *argv;
1417 int i, j;
1418
1419 if (LIST_column != mdoc->last->norm->Bl.type) {
1420 /* FIXME: this should be ERROR class... */
1421 hwarn_eq0(mdoc);
1422 return;
1423 }
1424
1425 /*
1426 * Append old-style lists, where the column width specifiers
1427 * trail as macro parameters, to the new-style ("normal-form")
1428 * lists where they're argument values following -column.
1429 */
1430
1431 if (mdoc->last->child == NULL)
1432 return;
1433
1434 np = mdoc->last->parent;
1435 assert(np->args);
1436
1437 for (j = 0; j < (int)np->args->argc; j++)
1438 if (MDOC_Column == np->args->argv[j].arg)
1439 break;
1440
1441 assert(j < (int)np->args->argc);
1442
1443 /*
1444 * Accommodate for new-style groff column syntax. Shuffle the
1445 * child nodes, all of which must be TEXT, as arguments for the
1446 * column field. Then, delete the head children.
1447 */
1448
1449 argv = np->args->argv + j;
1450 i = argv->sz;
1451 argv->sz += mdoc->last->nchild;
1452 argv->value = mandoc_reallocarray(argv->value,
1453 argv->sz, sizeof(char *));
1454
1455 mdoc->last->norm->Bl.ncols = argv->sz;
1456 mdoc->last->norm->Bl.cols = (void *)argv->value;
1457
1458 for (nn = mdoc->last->child; nn; i++) {
1459 argv->value[i] = nn->string;
1460 nn->string = NULL;
1461 nnp = nn;
1462 nn = nn->next;
1463 mdoc_node_delete(NULL, nnp);
1464 }
1465
1466 mdoc->last->nchild = 0;
1467 mdoc->last->child = NULL;
1468 }
1469
1470 static void
1471 post_bl(POST_ARGS)
1472 {
1473 struct mdoc_node *nparent, *nprev; /* of the Bl block */
1474 struct mdoc_node *nblock, *nbody; /* of the Bl */
1475 struct mdoc_node *nchild, *nnext; /* of the Bl body */
1476
1477 nbody = mdoc->last;
1478 switch (nbody->type) {
1479 case MDOC_BLOCK:
1480 post_bl_block(mdoc);
1481 return;
1482 case MDOC_HEAD:
1483 post_bl_head(mdoc);
1484 return;
1485 case MDOC_BODY:
1486 break;
1487 default:
1488 return;
1489 }
1490
1491 bwarn_ge1(mdoc);
1492
1493 nchild = nbody->child;
1494 while (NULL != nchild) {
1495 if (nchild->tok == MDOC_It ||
1496 (nchild->tok == MDOC_Sm &&
1497 nchild->next != NULL &&
1498 nchild->next->tok == MDOC_It)) {
1499 nchild = nchild->next;
1500 continue;
1501 }
1502
1503 mandoc_msg(MANDOCERR_BL_MOVE, mdoc->parse,
1504 nchild->line, nchild->pos,
1505 mdoc_macronames[nchild->tok]);
1506
1507 /*
1508 * Move the node out of the Bl block.
1509 * First, collect all required node pointers.
1510 */
1511
1512 nblock = nbody->parent;
1513 nprev = nblock->prev;
1514 nparent = nblock->parent;
1515 nnext = nchild->next;
1516
1517 /*
1518 * Unlink this child.
1519 */
1520
1521 assert(NULL == nchild->prev);
1522 if (0 == --nbody->nchild) {
1523 nbody->child = NULL;
1524 nbody->last = NULL;
1525 assert(NULL == nnext);
1526 } else {
1527 nbody->child = nnext;
1528 nnext->prev = NULL;
1529 }
1530
1531 /*
1532 * Relink this child.
1533 */
1534
1535 nchild->parent = nparent;
1536 nchild->prev = nprev;
1537 nchild->next = nblock;
1538
1539 nblock->prev = nchild;
1540 nparent->nchild++;
1541 if (NULL == nprev)
1542 nparent->child = nchild;
1543 else
1544 nprev->next = nchild;
1545
1546 nchild = nnext;
1547 }
1548 }
1549
1550 static void
1551 post_bk(POST_ARGS)
1552 {
1553
1554 hwarn_eq0(mdoc);
1555 bwarn_ge1(mdoc);
1556 }
1557
1558 static void
1559 post_sm(struct mdoc *mdoc)
1560 {
1561 struct mdoc_node *nch;
1562
1563 nch = mdoc->last->child;
1564
1565 if (nch == NULL) {
1566 mdoc->flags ^= MDOC_SMOFF;
1567 return;
1568 }
1569
1570 assert(nch->type == MDOC_TEXT);
1571
1572 if ( ! strcmp(nch->string, "on")) {
1573 mdoc->flags &= ~MDOC_SMOFF;
1574 return;
1575 }
1576 if ( ! strcmp(nch->string, "off")) {
1577 mdoc->flags |= MDOC_SMOFF;
1578 return;
1579 }
1580
1581 mandoc_vmsg(MANDOCERR_SM_BAD,
1582 mdoc->parse, nch->line, nch->pos,
1583 "%s %s", mdoc_macronames[mdoc->last->tok], nch->string);
1584 mdoc_node_relink(mdoc, nch);
1585 return;
1586 }
1587
1588 static void
1589 post_root(POST_ARGS)
1590 {
1591 struct mdoc_node *n;
1592
1593 /* Add missing prologue data. */
1594
1595 if (mdoc->meta.date == NULL)
1596 mdoc->meta.date = mdoc->quick ?
1597 mandoc_strdup("") :
1598 mandoc_normdate(mdoc->parse, NULL, 0, 0);
1599
1600 if (mdoc->meta.title == NULL) {
1601 mandoc_msg(MANDOCERR_DT_NOTITLE,
1602 mdoc->parse, 0, 0, "EOF");
1603 mdoc->meta.title = mandoc_strdup("UNTITLED");
1604 }
1605
1606 if (mdoc->meta.vol == NULL)
1607 mdoc->meta.vol = mandoc_strdup("LOCAL");
1608
1609 if (mdoc->meta.os == NULL) {
1610 mandoc_msg(MANDOCERR_OS_MISSING,
1611 mdoc->parse, 0, 0, NULL);
1612 mdoc->meta.os = mandoc_strdup("");
1613 }
1614
1615 /* Check that we begin with a proper `Sh'. */
1616
1617 n = mdoc->first->child;
1618 while (n != NULL && mdoc_macros[n->tok].flags & MDOC_PROLOGUE)
1619 n = n->next;
1620
1621 if (n == NULL)
1622 mandoc_msg(MANDOCERR_DOC_EMPTY, mdoc->parse, 0, 0, NULL);
1623 else if (n->tok != MDOC_Sh)
1624 mandoc_msg(MANDOCERR_SEC_BEFORE, mdoc->parse,
1625 n->line, n->pos, mdoc_macronames[n->tok]);
1626 }
1627
1628 static void
1629 post_st(POST_ARGS)
1630 {
1631 struct mdoc_node *n, *nch;
1632 const char *p;
1633
1634 n = mdoc->last;
1635 nch = n->child;
1636
1637 if (NULL == nch) {
1638 mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse,
1639 n->line, n->pos, mdoc_macronames[n->tok]);
1640 mdoc_node_delete(mdoc, n);
1641 return;
1642 }
1643
1644 assert(MDOC_TEXT == nch->type);
1645
1646 if (NULL == (p = mdoc_a2st(nch->string))) {
1647 mandoc_vmsg(MANDOCERR_ST_BAD, mdoc->parse,
1648 nch->line, nch->pos, "St %s", nch->string);
1649 mdoc_node_delete(mdoc, n);
1650 } else {
1651 free(nch->string);
1652 nch->string = mandoc_strdup(p);
1653 }
1654 }
1655
1656 static void
1657 post_rs(POST_ARGS)
1658 {
1659 struct mdoc_node *nn, *next, *prev;
1660 int i, j;
1661
1662 switch (mdoc->last->type) {
1663 case MDOC_HEAD:
1664 check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0);
1665 return;
1666 case MDOC_BODY:
1667 if (mdoc->last->child)
1668 break;
1669 check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0);
1670 return;
1671 default:
1672 return;
1673 }
1674
1675 /*
1676 * The full `Rs' block needs special handling to order the
1677 * sub-elements according to `rsord'. Pick through each element
1678 * and correctly order it. This is an insertion sort.
1679 */
1680
1681 next = NULL;
1682 for (nn = mdoc->last->child->next; nn; nn = next) {
1683 /* Determine order of `nn'. */
1684 for (i = 0; i < RSORD_MAX; i++)
1685 if (rsord[i] == nn->tok)
1686 break;
1687
1688 if (i == RSORD_MAX) {
1689 mandoc_msg(MANDOCERR_RS_BAD,
1690 mdoc->parse, nn->line, nn->pos,
1691 mdoc_macronames[nn->tok]);
1692 i = -1;
1693 } else if (MDOC__J == nn->tok || MDOC__B == nn->tok)
1694 mdoc->last->norm->Rs.quote_T++;
1695
1696 /*
1697 * Remove `nn' from the chain. This somewhat
1698 * repeats mdoc_node_unlink(), but since we're
1699 * just re-ordering, there's no need for the
1700 * full unlink process.
1701 */
1702
1703 if (NULL != (next = nn->next))
1704 next->prev = nn->prev;
1705
1706 if (NULL != (prev = nn->prev))
1707 prev->next = nn->next;
1708
1709 nn->prev = nn->next = NULL;
1710
1711 /*
1712 * Scan back until we reach a node that's
1713 * ordered before `nn'.
1714 */
1715
1716 for ( ; prev ; prev = prev->prev) {
1717 /* Determine order of `prev'. */
1718 for (j = 0; j < RSORD_MAX; j++)
1719 if (rsord[j] == prev->tok)
1720 break;
1721 if (j == RSORD_MAX)
1722 j = -1;
1723
1724 if (j <= i)
1725 break;
1726 }
1727
1728 /*
1729 * Set `nn' back into its correct place in front
1730 * of the `prev' node.
1731 */
1732
1733 nn->prev = prev;
1734
1735 if (prev) {
1736 if (prev->next)
1737 prev->next->prev = nn;
1738 nn->next = prev->next;
1739 prev->next = nn;
1740 } else {
1741 mdoc->last->child->prev = nn;
1742 nn->next = mdoc->last->child;
1743 mdoc->last->child = nn;
1744 }
1745 }
1746 }
1747
1748 /*
1749 * For some arguments of some macros,
1750 * convert all breakable hyphens into ASCII_HYPH.
1751 */
1752 static void
1753 post_hyph(POST_ARGS)
1754 {
1755 struct mdoc_node *n, *nch;
1756 char *cp;
1757
1758 n = mdoc->last;
1759 switch (n->type) {
1760 case MDOC_HEAD:
1761 if (MDOC_Sh == n->tok || MDOC_Ss == n->tok)
1762 break;
1763 return;
1764 case MDOC_BODY:
1765 if (MDOC_D1 == n->tok || MDOC_Nd == n->tok)
1766 break;
1767 return;
1768 case MDOC_ELEM:
1769 break;
1770 default:
1771 return;
1772 }
1773
1774 for (nch = n->child; nch; nch = nch->next) {
1775 if (MDOC_TEXT != nch->type)
1776 continue;
1777 cp = nch->string;
1778 if ('\0' == *cp)
1779 continue;
1780 while ('\0' != *(++cp))
1781 if ('-' == *cp &&
1782 isalpha((unsigned char)cp[-1]) &&
1783 isalpha((unsigned char)cp[1]))
1784 *cp = ASCII_HYPH;
1785 }
1786 }
1787
1788 static void
1789 post_hyphtext(POST_ARGS)
1790 {
1791
1792 ewarn_ge1(mdoc);
1793 post_hyph(mdoc);
1794 }
1795
1796 static void
1797 post_ns(POST_ARGS)
1798 {
1799
1800 if (MDOC_LINE & mdoc->last->flags)
1801 mandoc_msg(MANDOCERR_NS_SKIP, mdoc->parse,
1802 mdoc->last->line, mdoc->last->pos, NULL);
1803 }
1804
1805 static void
1806 post_sh(POST_ARGS)
1807 {
1808
1809 post_ignpar(mdoc);
1810
1811 switch (mdoc->last->type) {
1812 case MDOC_HEAD:
1813 post_sh_head(mdoc);
1814 break;
1815 case MDOC_BODY:
1816 switch (mdoc->lastsec) {
1817 case SEC_NAME:
1818 post_sh_name(mdoc);
1819 break;
1820 case SEC_SEE_ALSO:
1821 post_sh_see_also(mdoc);
1822 break;
1823 case SEC_AUTHORS:
1824 post_sh_authors(mdoc);
1825 break;
1826 default:
1827 break;
1828 }
1829 break;
1830 default:
1831 break;
1832 }
1833 }
1834
1835 static void
1836 post_sh_name(POST_ARGS)
1837 {
1838 struct mdoc_node *n;
1839
1840 /*
1841 * Warn if the NAME section doesn't contain the `Nm' and `Nd'
1842 * macros (can have multiple `Nm' and one `Nd'). Note that the
1843 * children of the BODY declaration can also be "text".
1844 */
1845
1846 if (NULL == (n = mdoc->last->child)) {
1847 mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
1848 mdoc->last->line, mdoc->last->pos, "empty");
1849 return;
1850 }
1851
1852 for ( ; n && n->next; n = n->next) {
1853 if (MDOC_ELEM == n->type && MDOC_Nm == n->tok)
1854 continue;
1855 if (MDOC_TEXT == n->type)
1856 continue;
1857 mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
1858 n->line, n->pos, mdoc_macronames[n->tok]);
1859 }
1860
1861 assert(n);
1862 if (MDOC_BLOCK == n->type && MDOC_Nd == n->tok)
1863 return;
1864
1865 mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
1866 n->line, n->pos, mdoc_macronames[n->tok]);
1867 }
1868
1869 static void
1870 post_sh_see_also(POST_ARGS)
1871 {
1872 const struct mdoc_node *n;
1873 const char *name, *sec;
1874 const char *lastname, *lastsec, *lastpunct;
1875 int cmp;
1876
1877 n = mdoc->last->child;
1878 lastname = lastsec = lastpunct = NULL;
1879 while (n != NULL) {
1880 if (n->tok != MDOC_Xr || n->nchild < 2)
1881 break;
1882
1883 /* Process one .Xr node. */
1884
1885 name = n->child->string;
1886 sec = n->child->next->string;
1887 if (lastsec != NULL) {
1888 if (lastpunct[0] != ',' || lastpunct[1] != '\0')
1889 mandoc_vmsg(MANDOCERR_XR_PUNCT,
1890 mdoc->parse, n->line, n->pos,
1891 "%s before %s(%s)", lastpunct,
1892 name, sec);
1893 cmp = strcmp(lastsec, sec);
1894 if (cmp > 0)
1895 mandoc_vmsg(MANDOCERR_XR_ORDER,
1896 mdoc->parse, n->line, n->pos,
1897 "%s(%s) after %s(%s)", name,
1898 sec, lastname, lastsec);
1899 else if (cmp == 0 &&
1900 strcasecmp(lastname, name) > 0)
1901 mandoc_vmsg(MANDOCERR_XR_ORDER,
1902 mdoc->parse, n->line, n->pos,
1903 "%s after %s", name, lastname);
1904 }
1905 lastname = name;
1906 lastsec = sec;
1907
1908 /* Process the following node. */
1909
1910 n = n->next;
1911 if (n == NULL)
1912 break;
1913 if (n->tok == MDOC_Xr) {
1914 lastpunct = "none";
1915 continue;
1916 }
1917 if (n->type != MDOC_TEXT)
1918 break;
1919 for (name = n->string; *name != '\0'; name++)
1920 if (isalpha((const unsigned char)*name))
1921 return;
1922 lastpunct = n->string;
1923 if (n->next == NULL)
1924 mandoc_vmsg(MANDOCERR_XR_PUNCT, mdoc->parse,
1925 n->line, n->pos, "%s after %s(%s)",
1926 lastpunct, lastname, lastsec);
1927 n = n->next;
1928 }
1929 }
1930
1931 static int
1932 child_an(const struct mdoc_node *n)
1933 {
1934
1935 for (n = n->child; n != NULL; n = n->next)
1936 if ((n->tok == MDOC_An && n->nchild) || child_an(n))
1937 return(1);
1938 return(0);
1939 }
1940
1941 static void
1942 post_sh_authors(POST_ARGS)
1943 {
1944
1945 if ( ! child_an(mdoc->last))
1946 mandoc_msg(MANDOCERR_AN_MISSING, mdoc->parse,
1947 mdoc->last->line, mdoc->last->pos, NULL);
1948 }
1949
1950 static void
1951 post_sh_head(POST_ARGS)
1952 {
1953 struct mdoc_node *n;
1954 const char *goodsec;
1955 char *secname;
1956 enum mdoc_sec sec;
1957
1958 /*
1959 * Process a new section. Sections are either "named" or
1960 * "custom". Custom sections are user-defined, while named ones
1961 * follow a conventional order and may only appear in certain
1962 * manual sections.
1963 */
1964
1965 secname = NULL;
1966 sec = SEC_CUSTOM;
1967 mdoc_deroff(&secname, mdoc->last);
1968 sec = NULL == secname ? SEC_CUSTOM : a2sec(secname);
1969
1970 /* The NAME should be first. */
1971
1972 if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed)
1973 mandoc_vmsg(MANDOCERR_NAMESEC_FIRST, mdoc->parse,
1974 mdoc->last->line, mdoc->last->pos,
1975 "Sh %s", secname);
1976
1977 /* The SYNOPSIS gets special attention in other areas. */
1978
1979 if (SEC_SYNOPSIS == sec) {
1980 roff_setreg(mdoc->roff, "nS", 1, '=');
1981 mdoc->flags |= MDOC_SYNOPSIS;
1982 } else {
1983 roff_setreg(mdoc->roff, "nS", 0, '=');
1984 mdoc->flags &= ~MDOC_SYNOPSIS;
1985 }
1986
1987 /* Mark our last section. */
1988
1989 mdoc->lastsec = sec;
1990
1991 /*
1992 * Set the section attribute for the current HEAD, for its
1993 * parent BLOCK, and for the HEAD children; the latter can
1994 * only be TEXT nodes, so no recursion is needed.
1995 * For other blocks and elements, including .Sh BODY, this is
1996 * done when allocating the node data structures, but for .Sh
1997 * BLOCK and HEAD, the section is still unknown at that time.
1998 */
1999
2000 mdoc->last->parent->sec = sec;
2001 mdoc->last->sec = sec;
2002 for (n = mdoc->last->child; n; n = n->next)
2003 n->sec = sec;
2004
2005 /* We don't care about custom sections after this. */
2006
2007 if (SEC_CUSTOM == sec) {
2008 free(secname);
2009 return;
2010 }
2011
2012 /*
2013 * Check whether our non-custom section is being repeated or is
2014 * out of order.
2015 */
2016
2017 if (sec == mdoc->lastnamed)
2018 mandoc_vmsg(MANDOCERR_SEC_REP, mdoc->parse,
2019 mdoc->last->line, mdoc->last->pos,
2020 "Sh %s", secname);
2021
2022 if (sec < mdoc->lastnamed)
2023 mandoc_vmsg(MANDOCERR_SEC_ORDER, mdoc->parse,
2024 mdoc->last->line, mdoc->last->pos,
2025 "Sh %s", secname);
2026
2027 /* Mark the last named section. */
2028
2029 mdoc->lastnamed = sec;
2030
2031 /* Check particular section/manual conventions. */
2032
2033 if (mdoc->meta.msec == NULL) {
2034 free(secname);
2035 return;
2036 }
2037
2038 goodsec = NULL;
2039 switch (sec) {
2040 case SEC_ERRORS:
2041 if (*mdoc->meta.msec == '4')
2042 break;
2043 goodsec = "2, 3, 4, 9";
2044 /* FALLTHROUGH */
2045 case SEC_RETURN_VALUES:
2046 /* FALLTHROUGH */
2047 case SEC_LIBRARY:
2048 if (*mdoc->meta.msec == '2')
2049 break;
2050 if (*mdoc->meta.msec == '3')
2051 break;
2052 if (NULL == goodsec)
2053 goodsec = "2, 3, 9";
2054 /* FALLTHROUGH */
2055 case SEC_CONTEXT:
2056 if (*mdoc->meta.msec == '9')
2057 break;
2058 if (NULL == goodsec)
2059 goodsec = "9";
2060 mandoc_vmsg(MANDOCERR_SEC_MSEC, mdoc->parse,
2061 mdoc->last->line, mdoc->last->pos,
2062 "Sh %s for %s only", secname, goodsec);
2063 break;
2064 default:
2065 break;
2066 }
2067 free(secname);
2068 }
2069
2070 static void
2071 post_ignpar(POST_ARGS)
2072 {
2073 struct mdoc_node *np;
2074
2075 check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_GT, 0);
2076 post_hyph(mdoc);
2077
2078 if (MDOC_BODY != mdoc->last->type)
2079 return;
2080
2081 if (NULL != (np = mdoc->last->child))
2082 if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
2083 mandoc_vmsg(MANDOCERR_PAR_SKIP,
2084 mdoc->parse, np->line, np->pos,
2085 "%s after %s", mdoc_macronames[np->tok],
2086 mdoc_macronames[mdoc->last->tok]);
2087 mdoc_node_delete(mdoc, np);
2088 }
2089
2090 if (NULL != (np = mdoc->last->last))
2091 if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
2092 mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2093 np->line, np->pos, "%s at the end of %s",
2094 mdoc_macronames[np->tok],
2095 mdoc_macronames[mdoc->last->tok]);
2096 mdoc_node_delete(mdoc, np);
2097 }
2098 }
2099
2100 static void
2101 pre_par(PRE_ARGS)
2102 {
2103
2104 if (NULL == mdoc->last)
2105 return;
2106 if (MDOC_ELEM != n->type && MDOC_BLOCK != n->type)
2107 return;
2108
2109 /*
2110 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
2111 * block: `Lp', `Pp', or non-compact `Bd' or `Bl'.
2112 */
2113
2114 if (MDOC_Pp != mdoc->last->tok &&
2115 MDOC_Lp != mdoc->last->tok &&
2116 MDOC_br != mdoc->last->tok)
2117 return;
2118 if (MDOC_Bl == n->tok && n->norm->Bl.comp)
2119 return;
2120 if (MDOC_Bd == n->tok && n->norm->Bd.comp)
2121 return;
2122 if (MDOC_It == n->tok && n->parent->norm->Bl.comp)
2123 return;
2124
2125 mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2126 mdoc->last->line, mdoc->last->pos,
2127 "%s before %s", mdoc_macronames[mdoc->last->tok],
2128 mdoc_macronames[n->tok]);
2129 mdoc_node_delete(mdoc, mdoc->last);
2130 }
2131
2132 static void
2133 post_par(POST_ARGS)
2134 {
2135 struct mdoc_node *np;
2136
2137 if (mdoc->last->tok == MDOC_sp)
2138 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_LT, 2);
2139 else
2140 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0);
2141
2142 if (MDOC_ELEM != mdoc->last->type &&
2143 MDOC_BLOCK != mdoc->last->type)
2144 return;
2145
2146 if (NULL == (np = mdoc->last->prev)) {
2147 np = mdoc->last->parent;
2148 if (MDOC_Sh != np->tok && MDOC_Ss != np->tok)
2149 return;
2150 } else if (MDOC_Pp != np->tok && MDOC_Lp != np->tok &&
2151 (MDOC_br != mdoc->last->tok ||
2152 (MDOC_sp != np->tok && MDOC_br != np->tok)))
2153 return;
2154
2155 mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2156 mdoc->last->line, mdoc->last->pos,
2157 "%s after %s", mdoc_macronames[mdoc->last->tok],
2158 mdoc_macronames[np->tok]);
2159 mdoc_node_delete(mdoc, mdoc->last);
2160 }
2161
2162 static void
2163 pre_literal(PRE_ARGS)
2164 {
2165
2166 pre_display(mdoc, n);
2167
2168 if (MDOC_BODY != n->type)
2169 return;
2170
2171 /*
2172 * The `Dl' (note "el" not "one") and `Bd -literal' and `Bd
2173 * -unfilled' macros set MDOC_LITERAL on entrance to the body.
2174 */
2175
2176 switch (n->tok) {
2177 case MDOC_Dl:
2178 mdoc->flags |= MDOC_LITERAL;
2179 break;
2180 case MDOC_Bd:
2181 if (DISP_literal == n->norm->Bd.type)
2182 mdoc->flags |= MDOC_LITERAL;
2183 if (DISP_unfilled == n->norm->Bd.type)
2184 mdoc->flags |= MDOC_LITERAL;
2185 break;
2186 default:
2187 abort();
2188 /* NOTREACHED */
2189 }
2190 }
2191
2192 static void
2193 post_dd(POST_ARGS)
2194 {
2195 struct mdoc_node *n;
2196 char *datestr;
2197
2198 if (mdoc->meta.date)
2199 free(mdoc->meta.date);
2200
2201 n = mdoc->last;
2202 if (NULL == n->child || '\0' == n->child->string[0]) {
2203 mdoc->meta.date = mdoc->quick ? mandoc_strdup("") :
2204 mandoc_normdate(mdoc->parse, NULL, n->line, n->pos);
2205 goto out;
2206 }
2207
2208 datestr = NULL;
2209 mdoc_deroff(&datestr, n);
2210 if (mdoc->quick)
2211 mdoc->meta.date = datestr;
2212 else {
2213 mdoc->meta.date = mandoc_normdate(mdoc->parse,
2214 datestr, n->line, n->pos);
2215 free(datestr);
2216 }
2217 out:
2218 mdoc_node_delete(mdoc, n);
2219 }
2220
2221 static void
2222 post_dt(POST_ARGS)
2223 {
2224 struct mdoc_node *nn, *n;
2225 const char *cp;
2226 char *p;
2227
2228 n = mdoc->last;
2229
2230 free(mdoc->meta.title);
2231 free(mdoc->meta.msec);
2232 free(mdoc->meta.vol);
2233 free(mdoc->meta.arch);
2234
2235 mdoc->meta.title = NULL;
2236 mdoc->meta.msec = NULL;
2237 mdoc->meta.vol = NULL;
2238 mdoc->meta.arch = NULL;
2239
2240 /* First check that all characters are uppercase. */
2241
2242 if (NULL != (nn = n->child))
2243 for (p = nn->string; *p; p++) {
2244 if (toupper((unsigned char)*p) == *p)
2245 continue;
2246 mandoc_vmsg(MANDOCERR_TITLE_CASE,
2247 mdoc->parse, nn->line,
2248 nn->pos + (p - nn->string),
2249 "Dt %s", nn->string);
2250 break;
2251 }
2252
2253 /* No argument: msec and arch remain NULL. */
2254
2255 if (NULL == (nn = n->child)) {
2256 mandoc_msg(MANDOCERR_DT_NOTITLE,
2257 mdoc->parse, n->line, n->pos, "Dt");
2258 mdoc->meta.title = mandoc_strdup("UNTITLED");
2259 mdoc->meta.vol = mandoc_strdup("LOCAL");
2260 goto out;
2261 }
2262
2263 /* One argument: msec and arch remain NULL. */
2264
2265 mdoc->meta.title = mandoc_strdup(
2266 '\0' == nn->string[0] ? "UNTITLED" : nn->string);
2267
2268 if (NULL == (nn = nn->next)) {
2269 mandoc_vmsg(MANDOCERR_MSEC_MISSING,
2270 mdoc->parse, n->line, n->pos,
2271 "Dt %s", mdoc->meta.title);
2272 mdoc->meta.vol = mandoc_strdup("LOCAL");
2273 goto out;
2274 }
2275
2276 /* Handles: `.Dt TITLE SEC'
2277 * title = TITLE,
2278 * volume = SEC is msec ? format(msec) : SEC,
2279 * msec = SEC is msec ? atoi(msec) : 0,
2280 * arch = NULL
2281 */
2282
2283 cp = mandoc_a2msec(nn->string);
2284 if (cp) {
2285 mdoc->meta.vol = mandoc_strdup(cp);
2286 mdoc->meta.msec = mandoc_strdup(nn->string);
2287 } else {
2288 mandoc_vmsg(MANDOCERR_MSEC_BAD, mdoc->parse,
2289 nn->line, nn->pos, "Dt ... %s", nn->string);
2290 mdoc->meta.vol = mandoc_strdup(nn->string);
2291 mdoc->meta.msec = mandoc_strdup(nn->string);
2292 }
2293
2294 /* Handle an optional architecture */
2295
2296 if ((nn = nn->next) != NULL) {
2297 for (p = nn->string; *p; p++)
2298 *p = tolower((unsigned char)*p);
2299 mdoc->meta.arch = mandoc_strdup(nn->string);
2300 }
2301
2302 /* Ignore any subsequent parameters... */
2303 /* FIXME: warn about subsequent parameters. */
2304 out:
2305 mdoc_node_delete(mdoc, n);
2306 }
2307
2308 static void
2309 post_bx(POST_ARGS)
2310 {
2311 struct mdoc_node *n;
2312
2313 /*
2314 * Make `Bx's second argument always start with an uppercase
2315 * letter. Groff checks if it's an "accepted" term, but we just
2316 * uppercase blindly.
2317 */
2318
2319 n = mdoc->last->child;
2320 if (n && NULL != (n = n->next))
2321 *n->string = (char)toupper((unsigned char)*n->string);
2322 }
2323
2324 static void
2325 post_os(POST_ARGS)
2326 {
2327 #ifndef OSNAME
2328 struct utsname utsname;
2329 static char *defbuf;
2330 #endif
2331 struct mdoc_node *n;
2332
2333 n = mdoc->last;
2334
2335 /*
2336 * Set the operating system by way of the `Os' macro.
2337 * The order of precedence is:
2338 * 1. the argument of the `Os' macro, unless empty
2339 * 2. the -Ios=foo command line argument, if provided
2340 * 3. -DOSNAME="\"foo\"", if provided during compilation
2341 * 4. "sysname release" from uname(3)
2342 */
2343
2344 free(mdoc->meta.os);
2345 mdoc->meta.os = NULL;
2346 mdoc_deroff(&mdoc->meta.os, n);
2347 if (mdoc->meta.os)
2348 goto out;
2349
2350 if (mdoc->defos) {
2351 mdoc->meta.os = mandoc_strdup(mdoc->defos);
2352 goto out;
2353 }
2354
2355 #ifdef OSNAME
2356 mdoc->meta.os = mandoc_strdup(OSNAME);
2357 #else /*!OSNAME */
2358 if (NULL == defbuf) {
2359 if (-1 == uname(&utsname)) {
2360 mandoc_msg(MANDOCERR_OS_UNAME, mdoc->parse,
2361 n->line, n->pos, "Os");
2362 defbuf = mandoc_strdup("UNKNOWN");
2363 } else
2364 mandoc_asprintf(&defbuf, "%s %s",
2365 utsname.sysname, utsname.release);
2366 }
2367 mdoc->meta.os = mandoc_strdup(defbuf);
2368 #endif /*!OSNAME*/
2369
2370 out:
2371 mdoc_node_delete(mdoc, n);
2372 }
2373
2374 /*
2375 * If no argument is provided,
2376 * fill in the name of the current manual page.
2377 */
2378 static void
2379 post_ex(POST_ARGS)
2380 {
2381 struct mdoc_node *n;
2382
2383 n = mdoc->last;
2384
2385 if (n->child)
2386 return;
2387
2388 if (mdoc->meta.name == NULL) {
2389 mandoc_msg(MANDOCERR_EX_NONAME, mdoc->parse,
2390 n->line, n->pos, "Ex");
2391 return;
2392 }
2393
2394 mdoc->next = MDOC_NEXT_CHILD;
2395 mdoc_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name);
2396 mdoc->last = n;
2397 }
2398
2399 static enum mdoc_sec
2400 a2sec(const char *p)
2401 {
2402 int i;
2403
2404 for (i = 0; i < (int)SEC__MAX; i++)
2405 if (secnames[i] && 0 == strcmp(p, secnames[i]))
2406 return((enum mdoc_sec)i);
2407
2408 return(SEC_CUSTOM);
2409 }
2410
2411 static size_t
2412 macro2len(enum mdoct macro)
2413 {
2414
2415 switch (macro) {
2416 case MDOC_Ad:
2417 return(12);
2418 case MDOC_Ao:
2419 return(12);
2420 case MDOC_An:
2421 return(12);
2422 case MDOC_Aq:
2423 return(12);
2424 case MDOC_Ar:
2425 return(12);
2426 case MDOC_Bo:
2427 return(12);
2428 case MDOC_Bq:
2429 return(12);
2430 case MDOC_Cd:
2431 return(12);
2432 case MDOC_Cm:
2433 return(10);
2434 case MDOC_Do:
2435 return(10);
2436 case MDOC_Dq:
2437 return(12);
2438 case MDOC_Dv:
2439 return(12);
2440 case MDOC_Eo:
2441 return(12);
2442 case MDOC_Em:
2443 return(10);
2444 case MDOC_Er:
2445 return(17);
2446 case MDOC_Ev:
2447 return(15);
2448 case MDOC_Fa:
2449 return(12);
2450 case MDOC_Fl:
2451 return(10);
2452 case MDOC_Fo:
2453 return(16);
2454 case MDOC_Fn:
2455 return(16);
2456 case MDOC_Ic:
2457 return(10);
2458 case MDOC_Li:
2459 return(16);
2460 case MDOC_Ms:
2461 return(6);
2462 case MDOC_Nm:
2463 return(10);
2464 case MDOC_No:
2465 return(12);
2466 case MDOC_Oo:
2467 return(10);
2468 case MDOC_Op:
2469 return(14);
2470 case MDOC_Pa:
2471 return(32);
2472 case MDOC_Pf:
2473 return(12);
2474 case MDOC_Po:
2475 return(12);
2476 case MDOC_Pq:
2477 return(12);
2478 case MDOC_Ql:
2479 return(16);
2480 case MDOC_Qo:
2481 return(12);
2482 case MDOC_So:
2483 return(12);
2484 case MDOC_Sq:
2485 return(12);
2486 case MDOC_Sy:
2487 return(6);
2488 case MDOC_Sx:
2489 return(16);
2490 case MDOC_Tn:
2491 return(10);
2492 case MDOC_Va:
2493 return(12);
2494 case MDOC_Vt:
2495 return(12);
2496 case MDOC_Xr:
2497 return(10);
2498 default:
2499 break;
2500 };
2501 return(0);
2502 }