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