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