6356 Update mdocml to 1.13.3
[unleashed.git] / usr / src / cmd / mandoc / mdoc_validate.c
blobeb531e82898753d0a611df49aa4b1b61ed0a16be
1 /* $Id: mdoc_validate.c,v 1.283 2015/02/23 13:55:55 schwarze Exp $ */
2 /*
3 * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2010-2015 Ingo Schwarze <schwarze@openbsd.org>
5 * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org>
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.
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.
19 #include "config.h"
21 #include <sys/types.h>
22 #ifndef OSNAME
23 #include <sys/utsname.h>
24 #endif
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>
34 #include "mdoc.h"
35 #include "mandoc.h"
36 #include "mandoc_aux.h"
37 #include "libmdoc.h"
38 #include "libmandoc.h"
40 /* FIXME: .Bl -diag can't have non-text children in HEAD. */
42 #define PRE_ARGS struct mdoc *mdoc, struct mdoc_node *n
43 #define POST_ARGS struct mdoc *mdoc
45 enum check_ineq {
46 CHECK_LT,
47 CHECK_GT,
48 CHECK_EQ
51 typedef void (*v_pre)(PRE_ARGS);
52 typedef void (*v_post)(POST_ARGS);
54 struct valids {
55 v_pre pre;
56 v_post post;
59 static void check_text(struct mdoc *, int, int, char *);
60 static void check_argv(struct mdoc *,
61 struct mdoc_node *, struct mdoc_argv *);
62 static void check_args(struct mdoc *, struct mdoc_node *);
63 static int child_an(const struct mdoc_node *);
64 static enum mdoc_sec a2sec(const char *);
65 static size_t macro2len(enum mdoct);
66 static void rewrite_macro2len(char **);
68 static void post_an(POST_ARGS);
69 static void post_at(POST_ARGS);
70 static void post_bf(POST_ARGS);
71 static void post_bk(POST_ARGS);
72 static void post_bl(POST_ARGS);
73 static void post_bl_block(POST_ARGS);
74 static void post_bl_block_tag(POST_ARGS);
75 static void post_bl_head(POST_ARGS);
76 static void post_bx(POST_ARGS);
77 static void post_d1(POST_ARGS);
78 static void post_defaults(POST_ARGS);
79 static void post_dd(POST_ARGS);
80 static void post_dt(POST_ARGS);
81 static void post_en(POST_ARGS);
82 static void post_es(POST_ARGS);
83 static void post_eoln(POST_ARGS);
84 static void post_ex(POST_ARGS);
85 static void post_fa(POST_ARGS);
86 static void post_fn(POST_ARGS);
87 static void post_fname(POST_ARGS);
88 static void post_fo(POST_ARGS);
89 static void post_hyph(POST_ARGS);
90 static void post_ignpar(POST_ARGS);
91 static void post_it(POST_ARGS);
92 static void post_lb(POST_ARGS);
93 static void post_literal(POST_ARGS);
94 static void post_nd(POST_ARGS);
95 static void post_nm(POST_ARGS);
96 static void post_ns(POST_ARGS);
97 static void post_os(POST_ARGS);
98 static void post_par(POST_ARGS);
99 static void post_root(POST_ARGS);
100 static void post_rs(POST_ARGS);
101 static void post_sh(POST_ARGS);
102 static void post_sh_head(POST_ARGS);
103 static void post_sh_name(POST_ARGS);
104 static void post_sh_see_also(POST_ARGS);
105 static void post_sh_authors(POST_ARGS);
106 static void post_sm(POST_ARGS);
107 static void post_st(POST_ARGS);
108 static void post_vt(POST_ARGS);
110 static void pre_an(PRE_ARGS);
111 static void pre_bd(PRE_ARGS);
112 static void pre_bl(PRE_ARGS);
113 static void pre_dd(PRE_ARGS);
114 static void pre_display(PRE_ARGS);
115 static void pre_dt(PRE_ARGS);
116 static void pre_literal(PRE_ARGS);
117 static void pre_obsolete(PRE_ARGS);
118 static void pre_os(PRE_ARGS);
119 static void pre_par(PRE_ARGS);
120 static void pre_std(PRE_ARGS);
122 static const struct valids mdoc_valids[MDOC_MAX] = {
123 { NULL, NULL }, /* Ap */
124 { pre_dd, post_dd }, /* Dd */
125 { pre_dt, post_dt }, /* Dt */
126 { pre_os, post_os }, /* Os */
127 { NULL, post_sh }, /* Sh */
128 { NULL, post_ignpar }, /* Ss */
129 { pre_par, post_par }, /* Pp */
130 { pre_display, post_d1 }, /* D1 */
131 { pre_literal, post_literal }, /* Dl */
132 { pre_bd, post_literal }, /* Bd */
133 { NULL, NULL }, /* Ed */
134 { pre_bl, post_bl }, /* Bl */
135 { NULL, NULL }, /* El */
136 { pre_par, post_it }, /* It */
137 { NULL, NULL }, /* Ad */
138 { pre_an, post_an }, /* An */
139 { NULL, post_defaults }, /* Ar */
140 { NULL, NULL }, /* Cd */
141 { NULL, NULL }, /* Cm */
142 { NULL, NULL }, /* Dv */
143 { NULL, NULL }, /* Er */
144 { NULL, NULL }, /* Ev */
145 { pre_std, post_ex }, /* Ex */
146 { NULL, post_fa }, /* Fa */
147 { NULL, NULL }, /* Fd */
148 { NULL, NULL }, /* Fl */
149 { NULL, post_fn }, /* Fn */
150 { NULL, NULL }, /* Ft */
151 { NULL, NULL }, /* Ic */
152 { NULL, NULL }, /* In */
153 { NULL, post_defaults }, /* Li */
154 { NULL, post_nd }, /* Nd */
155 { NULL, post_nm }, /* Nm */
156 { NULL, NULL }, /* Op */
157 { pre_obsolete, NULL }, /* Ot */
158 { NULL, post_defaults }, /* Pa */
159 { pre_std, NULL }, /* Rv */
160 { NULL, post_st }, /* St */
161 { NULL, NULL }, /* Va */
162 { NULL, post_vt }, /* Vt */
163 { NULL, NULL }, /* Xr */
164 { NULL, NULL }, /* %A */
165 { NULL, post_hyph }, /* %B */ /* FIXME: can be used outside Rs/Re. */
166 { NULL, NULL }, /* %D */
167 { NULL, NULL }, /* %I */
168 { NULL, NULL }, /* %J */
169 { NULL, post_hyph }, /* %N */
170 { NULL, post_hyph }, /* %O */
171 { NULL, NULL }, /* %P */
172 { NULL, post_hyph }, /* %R */
173 { NULL, post_hyph }, /* %T */ /* FIXME: can be used outside Rs/Re. */
174 { NULL, NULL }, /* %V */
175 { NULL, NULL }, /* Ac */
176 { NULL, NULL }, /* Ao */
177 { NULL, NULL }, /* Aq */
178 { NULL, post_at }, /* At */
179 { NULL, NULL }, /* Bc */
180 { NULL, post_bf }, /* Bf */
181 { NULL, NULL }, /* Bo */
182 { NULL, NULL }, /* Bq */
183 { NULL, NULL }, /* Bsx */
184 { NULL, post_bx }, /* Bx */
185 { pre_obsolete, NULL }, /* Db */
186 { NULL, NULL }, /* Dc */
187 { NULL, NULL }, /* Do */
188 { NULL, NULL }, /* Dq */
189 { NULL, NULL }, /* Ec */
190 { NULL, NULL }, /* Ef */
191 { NULL, NULL }, /* Em */
192 { NULL, NULL }, /* Eo */
193 { NULL, NULL }, /* Fx */
194 { NULL, NULL }, /* Ms */
195 { NULL, NULL }, /* No */
196 { NULL, post_ns }, /* Ns */
197 { NULL, NULL }, /* Nx */
198 { NULL, NULL }, /* Ox */
199 { NULL, NULL }, /* Pc */
200 { NULL, NULL }, /* Pf */
201 { NULL, NULL }, /* Po */
202 { NULL, NULL }, /* Pq */
203 { NULL, NULL }, /* Qc */
204 { NULL, NULL }, /* Ql */
205 { NULL, NULL }, /* Qo */
206 { NULL, NULL }, /* Qq */
207 { NULL, NULL }, /* Re */
208 { NULL, post_rs }, /* Rs */
209 { NULL, NULL }, /* Sc */
210 { NULL, NULL }, /* So */
211 { NULL, NULL }, /* Sq */
212 { NULL, post_sm }, /* Sm */
213 { NULL, post_hyph }, /* Sx */
214 { NULL, NULL }, /* Sy */
215 { NULL, NULL }, /* Tn */
216 { NULL, NULL }, /* Ux */
217 { NULL, NULL }, /* Xc */
218 { NULL, NULL }, /* Xo */
219 { NULL, post_fo }, /* Fo */
220 { NULL, NULL }, /* Fc */
221 { NULL, NULL }, /* Oo */
222 { NULL, NULL }, /* Oc */
223 { NULL, post_bk }, /* Bk */
224 { NULL, NULL }, /* Ek */
225 { NULL, post_eoln }, /* Bt */
226 { NULL, NULL }, /* Hf */
227 { pre_obsolete, NULL }, /* Fr */
228 { NULL, post_eoln }, /* Ud */
229 { NULL, post_lb }, /* Lb */
230 { pre_par, post_par }, /* Lp */
231 { NULL, NULL }, /* Lk */
232 { NULL, post_defaults }, /* Mt */
233 { NULL, NULL }, /* Brq */
234 { NULL, NULL }, /* Bro */
235 { NULL, NULL }, /* Brc */
236 { NULL, NULL }, /* %C */
237 { pre_obsolete, post_es }, /* Es */
238 { pre_obsolete, post_en }, /* En */
239 { NULL, NULL }, /* Dx */
240 { NULL, NULL }, /* %Q */
241 { NULL, post_par }, /* br */
242 { NULL, post_par }, /* sp */
243 { NULL, NULL }, /* %U */
244 { NULL, NULL }, /* Ta */
245 { NULL, NULL }, /* ll */
248 #define RSORD_MAX 14 /* Number of `Rs' blocks. */
250 static const enum mdoct rsord[RSORD_MAX] = {
251 MDOC__A,
252 MDOC__T,
253 MDOC__B,
254 MDOC__I,
255 MDOC__J,
256 MDOC__R,
257 MDOC__N,
258 MDOC__V,
259 MDOC__U,
260 MDOC__P,
261 MDOC__Q,
262 MDOC__C,
263 MDOC__D,
264 MDOC__O
267 static const char * const secnames[SEC__MAX] = {
268 NULL,
269 "NAME",
270 "LIBRARY",
271 "SYNOPSIS",
272 "DESCRIPTION",
273 "CONTEXT",
274 "IMPLEMENTATION NOTES",
275 "RETURN VALUES",
276 "ENVIRONMENT",
277 "FILES",
278 "EXIT STATUS",
279 "EXAMPLES",
280 "DIAGNOSTICS",
281 "COMPATIBILITY",
282 "ERRORS",
283 "SEE ALSO",
284 "STANDARDS",
285 "HISTORY",
286 "AUTHORS",
287 "CAVEATS",
288 "BUGS",
289 "SECURITY CONSIDERATIONS",
290 NULL
294 void
295 mdoc_valid_pre(struct mdoc *mdoc, struct mdoc_node *n)
297 v_pre p;
299 switch (n->type) {
300 case MDOC_TEXT:
301 if (n->sec != SEC_SYNOPSIS || n->parent->tok != MDOC_Fd)
302 check_text(mdoc, n->line, n->pos, n->string);
303 /* FALLTHROUGH */
304 case MDOC_TBL:
305 /* FALLTHROUGH */
306 case MDOC_EQN:
307 /* FALLTHROUGH */
308 case MDOC_ROOT:
309 return;
310 default:
311 break;
314 check_args(mdoc, n);
315 p = mdoc_valids[n->tok].pre;
316 if (*p)
317 (*p)(mdoc, n);
320 void
321 mdoc_valid_post(struct mdoc *mdoc)
323 struct mdoc_node *n;
324 v_post p;
326 n = mdoc->last;
327 if (n->flags & MDOC_VALID)
328 return;
329 n->flags |= MDOC_VALID | MDOC_ENDED;
331 switch (n->type) {
332 case MDOC_TEXT:
333 /* FALLTHROUGH */
334 case MDOC_EQN:
335 /* FALLTHROUGH */
336 case MDOC_TBL:
337 break;
338 case MDOC_ROOT:
339 post_root(mdoc);
340 break;
341 default:
344 * Closing delimiters are not special at the
345 * beginning of a block, opening delimiters
346 * are not special at the end.
349 if (n->child != NULL)
350 n->child->flags &= ~MDOC_DELIMC;
351 if (n->last != NULL)
352 n->last->flags &= ~MDOC_DELIMO;
354 /* Call the macro's postprocessor. */
356 p = mdoc_valids[n->tok].post;
357 if (*p)
358 (*p)(mdoc);
359 break;
363 static void
364 check_args(struct mdoc *mdoc, struct mdoc_node *n)
366 int i;
368 if (NULL == n->args)
369 return;
371 assert(n->args->argc);
372 for (i = 0; i < (int)n->args->argc; i++)
373 check_argv(mdoc, n, &n->args->argv[i]);
376 static void
377 check_argv(struct mdoc *mdoc, struct mdoc_node *n, struct mdoc_argv *v)
379 int i;
381 for (i = 0; i < (int)v->sz; i++)
382 check_text(mdoc, v->line, v->pos, v->value[i]);
385 static void
386 check_text(struct mdoc *mdoc, int ln, int pos, char *p)
388 char *cp;
390 if (MDOC_LITERAL & mdoc->flags)
391 return;
393 for (cp = p; NULL != (p = strchr(p, '\t')); p++)
394 mandoc_msg(MANDOCERR_FI_TAB, mdoc->parse,
395 ln, pos + (int)(p - cp), NULL);
398 static void
399 pre_display(PRE_ARGS)
401 struct mdoc_node *node;
403 if (MDOC_BLOCK != n->type)
404 return;
406 for (node = mdoc->last->parent; node; node = node->parent)
407 if (MDOC_BLOCK == node->type)
408 if (MDOC_Bd == node->tok)
409 break;
411 if (node)
412 mandoc_vmsg(MANDOCERR_BD_NEST,
413 mdoc->parse, n->line, n->pos,
414 "%s in Bd", mdoc_macronames[n->tok]);
417 static void
418 pre_bl(PRE_ARGS)
420 struct mdoc_argv *argv, *wa;
421 int i;
422 enum mdocargt mdoclt;
423 enum mdoc_list lt;
425 if (n->type != MDOC_BLOCK)
426 return;
429 * First figure out which kind of list to use: bind ourselves to
430 * the first mentioned list type and warn about any remaining
431 * ones. If we find no list type, we default to LIST_item.
434 wa = (n->args == NULL) ? NULL : n->args->argv;
435 mdoclt = MDOC_ARG_MAX;
436 for (i = 0; n->args && i < (int)n->args->argc; i++) {
437 argv = n->args->argv + i;
438 lt = LIST__NONE;
439 switch (argv->arg) {
440 /* Set list types. */
441 case MDOC_Bullet:
442 lt = LIST_bullet;
443 break;
444 case MDOC_Dash:
445 lt = LIST_dash;
446 break;
447 case MDOC_Enum:
448 lt = LIST_enum;
449 break;
450 case MDOC_Hyphen:
451 lt = LIST_hyphen;
452 break;
453 case MDOC_Item:
454 lt = LIST_item;
455 break;
456 case MDOC_Tag:
457 lt = LIST_tag;
458 break;
459 case MDOC_Diag:
460 lt = LIST_diag;
461 break;
462 case MDOC_Hang:
463 lt = LIST_hang;
464 break;
465 case MDOC_Ohang:
466 lt = LIST_ohang;
467 break;
468 case MDOC_Inset:
469 lt = LIST_inset;
470 break;
471 case MDOC_Column:
472 lt = LIST_column;
473 break;
474 /* Set list arguments. */
475 case MDOC_Compact:
476 if (n->norm->Bl.comp)
477 mandoc_msg(MANDOCERR_ARG_REP,
478 mdoc->parse, argv->line,
479 argv->pos, "Bl -compact");
480 n->norm->Bl.comp = 1;
481 break;
482 case MDOC_Width:
483 wa = argv;
484 if (0 == argv->sz) {
485 mandoc_msg(MANDOCERR_ARG_EMPTY,
486 mdoc->parse, argv->line,
487 argv->pos, "Bl -width");
488 n->norm->Bl.width = "0n";
489 break;
491 if (NULL != n->norm->Bl.width)
492 mandoc_vmsg(MANDOCERR_ARG_REP,
493 mdoc->parse, argv->line,
494 argv->pos, "Bl -width %s",
495 argv->value[0]);
496 rewrite_macro2len(argv->value);
497 n->norm->Bl.width = argv->value[0];
498 break;
499 case MDOC_Offset:
500 if (0 == argv->sz) {
501 mandoc_msg(MANDOCERR_ARG_EMPTY,
502 mdoc->parse, argv->line,
503 argv->pos, "Bl -offset");
504 break;
506 if (NULL != n->norm->Bl.offs)
507 mandoc_vmsg(MANDOCERR_ARG_REP,
508 mdoc->parse, argv->line,
509 argv->pos, "Bl -offset %s",
510 argv->value[0]);
511 rewrite_macro2len(argv->value);
512 n->norm->Bl.offs = argv->value[0];
513 break;
514 default:
515 continue;
517 if (LIST__NONE == lt)
518 continue;
519 mdoclt = argv->arg;
521 /* Check: multiple list types. */
523 if (LIST__NONE != n->norm->Bl.type) {
524 mandoc_vmsg(MANDOCERR_BL_REP,
525 mdoc->parse, n->line, n->pos,
526 "Bl -%s", mdoc_argnames[argv->arg]);
527 continue;
530 /* The list type should come first. */
532 if (n->norm->Bl.width ||
533 n->norm->Bl.offs ||
534 n->norm->Bl.comp)
535 mandoc_vmsg(MANDOCERR_BL_LATETYPE,
536 mdoc->parse, n->line, n->pos, "Bl -%s",
537 mdoc_argnames[n->args->argv[0].arg]);
539 n->norm->Bl.type = lt;
540 if (LIST_column == lt) {
541 n->norm->Bl.ncols = argv->sz;
542 n->norm->Bl.cols = (void *)argv->value;
546 /* Allow lists to default to LIST_item. */
548 if (LIST__NONE == n->norm->Bl.type) {
549 mandoc_msg(MANDOCERR_BL_NOTYPE, mdoc->parse,
550 n->line, n->pos, "Bl");
551 n->norm->Bl.type = LIST_item;
555 * Validate the width field. Some list types don't need width
556 * types and should be warned about them. Others should have it
557 * and must also be warned. Yet others have a default and need
558 * no warning.
561 switch (n->norm->Bl.type) {
562 case LIST_tag:
563 if (NULL == n->norm->Bl.width)
564 mandoc_msg(MANDOCERR_BL_NOWIDTH, mdoc->parse,
565 n->line, n->pos, "Bl -tag");
566 break;
567 case LIST_column:
568 /* FALLTHROUGH */
569 case LIST_diag:
570 /* FALLTHROUGH */
571 case LIST_ohang:
572 /* FALLTHROUGH */
573 case LIST_inset:
574 /* FALLTHROUGH */
575 case LIST_item:
576 if (n->norm->Bl.width)
577 mandoc_vmsg(MANDOCERR_BL_SKIPW, mdoc->parse,
578 wa->line, wa->pos, "Bl -%s",
579 mdoc_argnames[mdoclt]);
580 break;
581 case LIST_bullet:
582 /* FALLTHROUGH */
583 case LIST_dash:
584 /* FALLTHROUGH */
585 case LIST_hyphen:
586 if (NULL == n->norm->Bl.width)
587 n->norm->Bl.width = "2n";
588 break;
589 case LIST_enum:
590 if (NULL == n->norm->Bl.width)
591 n->norm->Bl.width = "3n";
592 break;
593 default:
594 break;
596 pre_par(mdoc, n);
599 static void
600 pre_bd(PRE_ARGS)
602 struct mdoc_argv *argv;
603 int i;
604 enum mdoc_disp dt;
606 pre_literal(mdoc, n);
608 if (n->type != MDOC_BLOCK)
609 return;
611 for (i = 0; n->args && i < (int)n->args->argc; i++) {
612 argv = n->args->argv + i;
613 dt = DISP__NONE;
615 switch (argv->arg) {
616 case MDOC_Centred:
617 dt = DISP_centered;
618 break;
619 case MDOC_Ragged:
620 dt = DISP_ragged;
621 break;
622 case MDOC_Unfilled:
623 dt = DISP_unfilled;
624 break;
625 case MDOC_Filled:
626 dt = DISP_filled;
627 break;
628 case MDOC_Literal:
629 dt = DISP_literal;
630 break;
631 case MDOC_File:
632 mandoc_msg(MANDOCERR_BD_FILE, mdoc->parse,
633 n->line, n->pos, NULL);
634 break;
635 case MDOC_Offset:
636 if (0 == argv->sz) {
637 mandoc_msg(MANDOCERR_ARG_EMPTY,
638 mdoc->parse, argv->line,
639 argv->pos, "Bd -offset");
640 break;
642 if (NULL != n->norm->Bd.offs)
643 mandoc_vmsg(MANDOCERR_ARG_REP,
644 mdoc->parse, argv->line,
645 argv->pos, "Bd -offset %s",
646 argv->value[0]);
647 rewrite_macro2len(argv->value);
648 n->norm->Bd.offs = argv->value[0];
649 break;
650 case MDOC_Compact:
651 if (n->norm->Bd.comp)
652 mandoc_msg(MANDOCERR_ARG_REP,
653 mdoc->parse, argv->line,
654 argv->pos, "Bd -compact");
655 n->norm->Bd.comp = 1;
656 break;
657 default:
658 abort();
659 /* NOTREACHED */
661 if (DISP__NONE == dt)
662 continue;
664 if (DISP__NONE == n->norm->Bd.type)
665 n->norm->Bd.type = dt;
666 else
667 mandoc_vmsg(MANDOCERR_BD_REP,
668 mdoc->parse, n->line, n->pos,
669 "Bd -%s", mdoc_argnames[argv->arg]);
672 if (DISP__NONE == n->norm->Bd.type) {
673 mandoc_msg(MANDOCERR_BD_NOTYPE, mdoc->parse,
674 n->line, n->pos, "Bd");
675 n->norm->Bd.type = DISP_ragged;
677 pre_par(mdoc, n);
680 static void
681 pre_an(PRE_ARGS)
683 struct mdoc_argv *argv;
684 size_t i;
686 if (n->args == NULL)
687 return;
689 for (i = 1; i < n->args->argc; i++) {
690 argv = n->args->argv + i;
691 mandoc_vmsg(MANDOCERR_AN_REP,
692 mdoc->parse, argv->line, argv->pos,
693 "An -%s", mdoc_argnames[argv->arg]);
696 argv = n->args->argv;
697 if (argv->arg == MDOC_Split)
698 n->norm->An.auth = AUTH_split;
699 else if (argv->arg == MDOC_Nosplit)
700 n->norm->An.auth = AUTH_nosplit;
701 else
702 abort();
705 static void
706 pre_std(PRE_ARGS)
709 if (n->args && 1 == n->args->argc)
710 if (MDOC_Std == n->args->argv[0].arg)
711 return;
713 mandoc_msg(MANDOCERR_ARG_STD, mdoc->parse,
714 n->line, n->pos, mdoc_macronames[n->tok]);
717 static void
718 pre_obsolete(PRE_ARGS)
721 if (MDOC_ELEM == n->type || MDOC_BLOCK == n->type)
722 mandoc_msg(MANDOCERR_MACRO_OBS, mdoc->parse,
723 n->line, n->pos, mdoc_macronames[n->tok]);
726 static void
727 pre_dt(PRE_ARGS)
730 if (mdoc->meta.title != NULL)
731 mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
732 n->line, n->pos, "Dt");
733 else if (mdoc->meta.os != NULL)
734 mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
735 n->line, n->pos, "Dt after Os");
738 static void
739 pre_os(PRE_ARGS)
742 if (mdoc->meta.os != NULL)
743 mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
744 n->line, n->pos, "Os");
745 else if (mdoc->flags & MDOC_PBODY)
746 mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse,
747 n->line, n->pos, "Os");
750 static void
751 pre_dd(PRE_ARGS)
754 if (mdoc->meta.date != NULL)
755 mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
756 n->line, n->pos, "Dd");
757 else if (mdoc->flags & MDOC_PBODY)
758 mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse,
759 n->line, n->pos, "Dd");
760 else if (mdoc->meta.title != NULL)
761 mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
762 n->line, n->pos, "Dd after Dt");
763 else if (mdoc->meta.os != NULL)
764 mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
765 n->line, n->pos, "Dd after Os");
768 static void
769 post_bf(POST_ARGS)
771 struct mdoc_node *np, *nch;
772 enum mdocargt arg;
775 * Unlike other data pointers, these are "housed" by the HEAD
776 * element, which contains the goods.
779 np = mdoc->last;
780 if (MDOC_HEAD != np->type)
781 return;
783 assert(MDOC_BLOCK == np->parent->type);
784 assert(MDOC_Bf == np->parent->tok);
786 /* Check the number of arguments. */
788 nch = np->child;
789 if (NULL == np->parent->args) {
790 if (NULL == nch) {
791 mandoc_msg(MANDOCERR_BF_NOFONT, mdoc->parse,
792 np->line, np->pos, "Bf");
793 return;
795 nch = nch->next;
797 if (NULL != nch)
798 mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
799 nch->line, nch->pos, "Bf ... %s", nch->string);
801 /* Extract argument into data. */
803 if (np->parent->args) {
804 arg = np->parent->args->argv[0].arg;
805 if (MDOC_Emphasis == arg)
806 np->norm->Bf.font = FONT_Em;
807 else if (MDOC_Literal == arg)
808 np->norm->Bf.font = FONT_Li;
809 else if (MDOC_Symbolic == arg)
810 np->norm->Bf.font = FONT_Sy;
811 else
812 abort();
813 return;
816 /* Extract parameter into data. */
818 if (0 == strcmp(np->child->string, "Em"))
819 np->norm->Bf.font = FONT_Em;
820 else if (0 == strcmp(np->child->string, "Li"))
821 np->norm->Bf.font = FONT_Li;
822 else if (0 == strcmp(np->child->string, "Sy"))
823 np->norm->Bf.font = FONT_Sy;
824 else
825 mandoc_vmsg(MANDOCERR_BF_BADFONT, mdoc->parse,
826 np->child->line, np->child->pos,
827 "Bf %s", np->child->string);
830 static void
831 post_lb(POST_ARGS)
833 struct mdoc_node *n;
834 const char *stdlibname;
835 char *libname;
837 n = mdoc->last->child;
838 assert(MDOC_TEXT == n->type);
840 if (NULL == (stdlibname = mdoc_a2lib(n->string)))
841 mandoc_asprintf(&libname,
842 "library \\(Lq%s\\(Rq", n->string);
843 else
844 libname = mandoc_strdup(stdlibname);
846 free(n->string);
847 n->string = libname;
850 static void
851 post_eoln(POST_ARGS)
853 const struct mdoc_node *n;
855 n = mdoc->last;
856 if (n->child)
857 mandoc_vmsg(MANDOCERR_ARG_SKIP,
858 mdoc->parse, n->line, n->pos,
859 "%s %s", mdoc_macronames[n->tok],
860 n->child->string);
863 static void
864 post_fname(POST_ARGS)
866 const struct mdoc_node *n;
867 const char *cp;
868 size_t pos;
870 n = mdoc->last->child;
871 pos = strcspn(n->string, "()");
872 cp = n->string + pos;
873 if ( ! (cp[0] == '\0' || (cp[0] == '(' && cp[1] == '*')))
874 mandoc_msg(MANDOCERR_FN_PAREN, mdoc->parse,
875 n->line, n->pos + pos, n->string);
878 static void
879 post_fn(POST_ARGS)
882 post_fname(mdoc);
883 post_fa(mdoc);
886 static void
887 post_fo(POST_ARGS)
889 const struct mdoc_node *n;
891 n = mdoc->last;
893 if (n->type != MDOC_HEAD)
894 return;
896 if (n->child == NULL) {
897 mandoc_msg(MANDOCERR_FO_NOHEAD, mdoc->parse,
898 n->line, n->pos, "Fo");
899 return;
901 if (n->child != n->last) {
902 mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
903 n->child->next->line, n->child->next->pos,
904 "Fo ... %s", n->child->next->string);
905 while (n->child != n->last)
906 mdoc_node_delete(mdoc, n->last);
909 post_fname(mdoc);
912 static void
913 post_fa(POST_ARGS)
915 const struct mdoc_node *n;
916 const char *cp;
918 for (n = mdoc->last->child; n != NULL; n = n->next) {
919 for (cp = n->string; *cp != '\0'; cp++) {
920 /* Ignore callbacks and alterations. */
921 if (*cp == '(' || *cp == '{')
922 break;
923 if (*cp != ',')
924 continue;
925 mandoc_msg(MANDOCERR_FA_COMMA, mdoc->parse,
926 n->line, n->pos + (cp - n->string),
927 n->string);
928 break;
933 static void
934 post_vt(POST_ARGS)
936 const struct mdoc_node *n;
939 * The Vt macro comes in both ELEM and BLOCK form, both of which
940 * have different syntaxes (yet more context-sensitive
941 * behaviour). ELEM types must have a child, which is already
942 * guaranteed by the in_line parsing routine; BLOCK types,
943 * specifically the BODY, should only have TEXT children.
946 if (MDOC_BODY != mdoc->last->type)
947 return;
949 for (n = mdoc->last->child; n; n = n->next)
950 if (MDOC_TEXT != n->type)
951 mandoc_msg(MANDOCERR_VT_CHILD, mdoc->parse,
952 n->line, n->pos, mdoc_macronames[n->tok]);
955 static void
956 post_nm(POST_ARGS)
958 struct mdoc_node *n;
960 n = mdoc->last;
962 if (n->last != NULL &&
963 (n->last->tok == MDOC_Pp ||
964 n->last->tok == MDOC_Lp))
965 mdoc_node_relink(mdoc, n->last);
967 if (NULL != mdoc->meta.name)
968 return;
970 mdoc_deroff(&mdoc->meta.name, n);
972 if (NULL == mdoc->meta.name)
973 mandoc_msg(MANDOCERR_NM_NONAME, mdoc->parse,
974 n->line, n->pos, "Nm");
977 static void
978 post_nd(POST_ARGS)
980 struct mdoc_node *n;
982 n = mdoc->last;
984 if (n->type != MDOC_BODY)
985 return;
987 if (n->child == NULL)
988 mandoc_msg(MANDOCERR_ND_EMPTY, mdoc->parse,
989 n->line, n->pos, "Nd");
991 post_hyph(mdoc);
994 static void
995 post_d1(POST_ARGS)
997 struct mdoc_node *n;
999 n = mdoc->last;
1001 if (n->type != MDOC_BODY)
1002 return;
1004 if (n->child == NULL)
1005 mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse,
1006 n->line, n->pos, "D1");
1008 post_hyph(mdoc);
1011 static void
1012 post_literal(POST_ARGS)
1014 struct mdoc_node *n;
1016 n = mdoc->last;
1018 if (n->type != MDOC_BODY)
1019 return;
1021 if (n->child == NULL)
1022 mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse,
1023 n->line, n->pos, mdoc_macronames[n->tok]);
1025 if (n->tok == MDOC_Bd &&
1026 n->norm->Bd.type != DISP_literal &&
1027 n->norm->Bd.type != DISP_unfilled)
1028 return;
1030 mdoc->flags &= ~MDOC_LITERAL;
1033 static void
1034 post_defaults(POST_ARGS)
1036 struct mdoc_node *nn;
1039 * The `Ar' defaults to "file ..." if no value is provided as an
1040 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
1041 * gets an empty string.
1044 if (mdoc->last->child)
1045 return;
1047 nn = mdoc->last;
1048 mdoc->next = MDOC_NEXT_CHILD;
1050 switch (nn->tok) {
1051 case MDOC_Ar:
1052 mdoc_word_alloc(mdoc, nn->line, nn->pos, "file");
1053 mdoc_word_alloc(mdoc, nn->line, nn->pos, "...");
1054 break;
1055 case MDOC_Pa:
1056 /* FALLTHROUGH */
1057 case MDOC_Mt:
1058 mdoc_word_alloc(mdoc, nn->line, nn->pos, "~");
1059 break;
1060 default:
1061 abort();
1062 /* NOTREACHED */
1064 mdoc->last = nn;
1067 static void
1068 post_at(POST_ARGS)
1070 struct mdoc_node *n;
1071 const char *std_att;
1072 char *att;
1074 n = mdoc->last;
1075 if (n->child == NULL) {
1076 mdoc->next = MDOC_NEXT_CHILD;
1077 mdoc_word_alloc(mdoc, n->line, n->pos, "AT&T UNIX");
1078 mdoc->last = n;
1079 return;
1083 * If we have a child, look it up in the standard keys. If a
1084 * key exist, use that instead of the child; if it doesn't,
1085 * prefix "AT&T UNIX " to the existing data.
1088 n = n->child;
1089 assert(MDOC_TEXT == n->type);
1090 if (NULL == (std_att = mdoc_a2att(n->string))) {
1091 mandoc_vmsg(MANDOCERR_AT_BAD, mdoc->parse,
1092 n->line, n->pos, "At %s", n->string);
1093 mandoc_asprintf(&att, "AT&T UNIX %s", n->string);
1094 } else
1095 att = mandoc_strdup(std_att);
1097 free(n->string);
1098 n->string = att;
1101 static void
1102 post_an(POST_ARGS)
1104 struct mdoc_node *np, *nch;
1106 np = mdoc->last;
1107 nch = np->child;
1108 if (np->norm->An.auth == AUTH__NONE) {
1109 if (nch == NULL)
1110 mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse,
1111 np->line, np->pos, "An");
1112 } else if (nch != NULL)
1113 mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
1114 nch->line, nch->pos, "An ... %s", nch->string);
1117 static void
1118 post_en(POST_ARGS)
1121 if (MDOC_BLOCK == mdoc->last->type)
1122 mdoc->last->norm->Es = mdoc->last_es;
1125 static void
1126 post_es(POST_ARGS)
1129 mdoc->last_es = mdoc->last;
1132 static void
1133 post_it(POST_ARGS)
1135 int i, cols;
1136 enum mdoc_list lt;
1137 struct mdoc_node *nbl, *nit, *nch;
1139 nit = mdoc->last;
1140 if (nit->type != MDOC_BLOCK)
1141 return;
1143 nbl = nit->parent->parent;
1144 lt = nbl->norm->Bl.type;
1146 switch (lt) {
1147 case LIST_tag:
1148 /* FALLTHROUGH */
1149 case LIST_hang:
1150 /* FALLTHROUGH */
1151 case LIST_ohang:
1152 /* FALLTHROUGH */
1153 case LIST_inset:
1154 /* FALLTHROUGH */
1155 case LIST_diag:
1156 if (nit->head->child == NULL)
1157 mandoc_vmsg(MANDOCERR_IT_NOHEAD,
1158 mdoc->parse, nit->line, nit->pos,
1159 "Bl -%s It",
1160 mdoc_argnames[nbl->args->argv[0].arg]);
1161 break;
1162 case LIST_bullet:
1163 /* FALLTHROUGH */
1164 case LIST_dash:
1165 /* FALLTHROUGH */
1166 case LIST_enum:
1167 /* FALLTHROUGH */
1168 case LIST_hyphen:
1169 if (nit->body == NULL || nit->body->child == NULL)
1170 mandoc_vmsg(MANDOCERR_IT_NOBODY,
1171 mdoc->parse, nit->line, nit->pos,
1172 "Bl -%s It",
1173 mdoc_argnames[nbl->args->argv[0].arg]);
1174 /* FALLTHROUGH */
1175 case LIST_item:
1176 if (nit->head->child != NULL)
1177 mandoc_vmsg(MANDOCERR_ARG_SKIP,
1178 mdoc->parse, nit->line, nit->pos,
1179 "It %s", nit->head->child->string);
1180 break;
1181 case LIST_column:
1182 cols = (int)nbl->norm->Bl.ncols;
1184 assert(nit->head->child == NULL);
1186 for (i = 0, nch = nit->child; nch; nch = nch->next)
1187 if (nch->type == MDOC_BODY)
1188 i++;
1190 if (i < cols || i > cols + 1)
1191 mandoc_vmsg(MANDOCERR_BL_COL,
1192 mdoc->parse, nit->line, nit->pos,
1193 "%d columns, %d cells", cols, i);
1194 break;
1195 default:
1196 abort();
1200 static void
1201 post_bl_block(POST_ARGS)
1203 struct mdoc_node *n, *ni, *nc;
1206 * These are fairly complicated, so we've broken them into two
1207 * functions. post_bl_block_tag() is called when a -tag is
1208 * specified, but no -width (it must be guessed). The second
1209 * when a -width is specified (macro indicators must be
1210 * rewritten into real lengths).
1213 n = mdoc->last;
1215 if (LIST_tag == n->norm->Bl.type &&
1216 NULL == n->norm->Bl.width) {
1217 post_bl_block_tag(mdoc);
1218 assert(n->norm->Bl.width);
1221 for (ni = n->body->child; ni; ni = ni->next) {
1222 if (NULL == ni->body)
1223 continue;
1224 nc = ni->body->last;
1225 while (NULL != nc) {
1226 switch (nc->tok) {
1227 case MDOC_Pp:
1228 /* FALLTHROUGH */
1229 case MDOC_Lp:
1230 /* FALLTHROUGH */
1231 case MDOC_br:
1232 break;
1233 default:
1234 nc = NULL;
1235 continue;
1237 if (NULL == ni->next) {
1238 mandoc_msg(MANDOCERR_PAR_MOVE,
1239 mdoc->parse, nc->line, nc->pos,
1240 mdoc_macronames[nc->tok]);
1241 mdoc_node_relink(mdoc, nc);
1242 } else if (0 == n->norm->Bl.comp &&
1243 LIST_column != n->norm->Bl.type) {
1244 mandoc_vmsg(MANDOCERR_PAR_SKIP,
1245 mdoc->parse, nc->line, nc->pos,
1246 "%s before It",
1247 mdoc_macronames[nc->tok]);
1248 mdoc_node_delete(mdoc, nc);
1249 } else
1250 break;
1251 nc = ni->body->last;
1257 * If the argument of -offset or -width is a macro,
1258 * replace it with the associated default width.
1260 void
1261 rewrite_macro2len(char **arg)
1263 size_t width;
1264 enum mdoct tok;
1266 if (*arg == NULL)
1267 return;
1268 else if ( ! strcmp(*arg, "Ds"))
1269 width = 6;
1270 else if ((tok = mdoc_hash_find(*arg)) == MDOC_MAX)
1271 return;
1272 else
1273 width = macro2len(tok);
1275 free(*arg);
1276 mandoc_asprintf(arg, "%zun", width);
1279 static void
1280 post_bl_block_tag(POST_ARGS)
1282 struct mdoc_node *n, *nn;
1283 size_t sz, ssz;
1284 int i;
1285 char buf[24];
1288 * Calculate the -width for a `Bl -tag' list if it hasn't been
1289 * provided. Uses the first head macro. NOTE AGAIN: this is
1290 * ONLY if the -width argument has NOT been provided. See
1291 * rewrite_macro2len() for converting the -width string.
1294 sz = 10;
1295 n = mdoc->last;
1297 for (nn = n->body->child; nn; nn = nn->next) {
1298 if (MDOC_It != nn->tok)
1299 continue;
1301 assert(MDOC_BLOCK == nn->type);
1302 nn = nn->head->child;
1304 if (nn == NULL)
1305 break;
1307 if (MDOC_TEXT == nn->type) {
1308 sz = strlen(nn->string) + 1;
1309 break;
1312 if (0 != (ssz = macro2len(nn->tok)))
1313 sz = ssz;
1315 break;
1318 /* Defaults to ten ens. */
1320 (void)snprintf(buf, sizeof(buf), "%un", (unsigned int)sz);
1323 * We have to dynamically add this to the macro's argument list.
1324 * We're guaranteed that a MDOC_Width doesn't already exist.
1327 assert(n->args);
1328 i = (int)(n->args->argc)++;
1330 n->args->argv = mandoc_reallocarray(n->args->argv,
1331 n->args->argc, sizeof(struct mdoc_argv));
1333 n->args->argv[i].arg = MDOC_Width;
1334 n->args->argv[i].line = n->line;
1335 n->args->argv[i].pos = n->pos;
1336 n->args->argv[i].sz = 1;
1337 n->args->argv[i].value = mandoc_malloc(sizeof(char *));
1338 n->args->argv[i].value[0] = mandoc_strdup(buf);
1340 /* Set our width! */
1341 n->norm->Bl.width = n->args->argv[i].value[0];
1344 static void
1345 post_bl_head(POST_ARGS)
1347 struct mdoc_node *nbl, *nh, *nch, *nnext;
1348 struct mdoc_argv *argv;
1349 int i, j;
1351 nh = mdoc->last;
1353 if (nh->norm->Bl.type != LIST_column) {
1354 if ((nch = nh->child) == NULL)
1355 return;
1356 mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
1357 nch->line, nch->pos, "Bl ... %s", nch->string);
1358 while (nch != NULL) {
1359 mdoc_node_delete(mdoc, nch);
1360 nch = nh->child;
1362 return;
1366 * Append old-style lists, where the column width specifiers
1367 * trail as macro parameters, to the new-style ("normal-form")
1368 * lists where they're argument values following -column.
1371 if (nh->child == NULL)
1372 return;
1374 nbl = nh->parent;
1375 for (j = 0; j < (int)nbl->args->argc; j++)
1376 if (nbl->args->argv[j].arg == MDOC_Column)
1377 break;
1379 assert(j < (int)nbl->args->argc);
1382 * Accommodate for new-style groff column syntax. Shuffle the
1383 * child nodes, all of which must be TEXT, as arguments for the
1384 * column field. Then, delete the head children.
1387 argv = nbl->args->argv + j;
1388 i = argv->sz;
1389 argv->sz += nh->nchild;
1390 argv->value = mandoc_reallocarray(argv->value,
1391 argv->sz, sizeof(char *));
1393 nh->norm->Bl.ncols = argv->sz;
1394 nh->norm->Bl.cols = (void *)argv->value;
1396 for (nch = nh->child; nch != NULL; nch = nnext) {
1397 argv->value[i++] = nch->string;
1398 nch->string = NULL;
1399 nnext = nch->next;
1400 mdoc_node_delete(NULL, nch);
1402 nh->nchild = 0;
1403 nh->child = NULL;
1406 static void
1407 post_bl(POST_ARGS)
1409 struct mdoc_node *nparent, *nprev; /* of the Bl block */
1410 struct mdoc_node *nblock, *nbody; /* of the Bl */
1411 struct mdoc_node *nchild, *nnext; /* of the Bl body */
1413 nbody = mdoc->last;
1414 switch (nbody->type) {
1415 case MDOC_BLOCK:
1416 post_bl_block(mdoc);
1417 return;
1418 case MDOC_HEAD:
1419 post_bl_head(mdoc);
1420 return;
1421 case MDOC_BODY:
1422 break;
1423 default:
1424 return;
1427 nchild = nbody->child;
1428 if (nchild == NULL) {
1429 mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse,
1430 nbody->line, nbody->pos, "Bl");
1431 return;
1433 while (nchild != NULL) {
1434 if (nchild->tok == MDOC_It ||
1435 (nchild->tok == MDOC_Sm &&
1436 nchild->next != NULL &&
1437 nchild->next->tok == MDOC_It)) {
1438 nchild = nchild->next;
1439 continue;
1442 mandoc_msg(MANDOCERR_BL_MOVE, mdoc->parse,
1443 nchild->line, nchild->pos,
1444 mdoc_macronames[nchild->tok]);
1447 * Move the node out of the Bl block.
1448 * First, collect all required node pointers.
1451 nblock = nbody->parent;
1452 nprev = nblock->prev;
1453 nparent = nblock->parent;
1454 nnext = nchild->next;
1457 * Unlink this child.
1460 assert(NULL == nchild->prev);
1461 if (0 == --nbody->nchild) {
1462 nbody->child = NULL;
1463 nbody->last = NULL;
1464 assert(NULL == nnext);
1465 } else {
1466 nbody->child = nnext;
1467 nnext->prev = NULL;
1471 * Relink this child.
1474 nchild->parent = nparent;
1475 nchild->prev = nprev;
1476 nchild->next = nblock;
1478 nblock->prev = nchild;
1479 nparent->nchild++;
1480 if (NULL == nprev)
1481 nparent->child = nchild;
1482 else
1483 nprev->next = nchild;
1485 nchild = nnext;
1489 static void
1490 post_bk(POST_ARGS)
1492 struct mdoc_node *n;
1494 n = mdoc->last;
1496 if (n->type == MDOC_BLOCK && n->body->child == NULL) {
1497 mandoc_msg(MANDOCERR_BLK_EMPTY,
1498 mdoc->parse, n->line, n->pos, "Bk");
1499 mdoc_node_delete(mdoc, n);
1503 static void
1504 post_sm(struct mdoc *mdoc)
1506 struct mdoc_node *nch;
1508 nch = mdoc->last->child;
1510 if (nch == NULL) {
1511 mdoc->flags ^= MDOC_SMOFF;
1512 return;
1515 assert(nch->type == MDOC_TEXT);
1517 if ( ! strcmp(nch->string, "on")) {
1518 mdoc->flags &= ~MDOC_SMOFF;
1519 return;
1521 if ( ! strcmp(nch->string, "off")) {
1522 mdoc->flags |= MDOC_SMOFF;
1523 return;
1526 mandoc_vmsg(MANDOCERR_SM_BAD,
1527 mdoc->parse, nch->line, nch->pos,
1528 "%s %s", mdoc_macronames[mdoc->last->tok], nch->string);
1529 mdoc_node_relink(mdoc, nch);
1530 return;
1533 static void
1534 post_root(POST_ARGS)
1536 struct mdoc_node *n;
1538 /* Add missing prologue data. */
1540 if (mdoc->meta.date == NULL)
1541 mdoc->meta.date = mdoc->quick ?
1542 mandoc_strdup("") :
1543 mandoc_normdate(mdoc->parse, NULL, 0, 0);
1545 if (mdoc->meta.title == NULL) {
1546 mandoc_msg(MANDOCERR_DT_NOTITLE,
1547 mdoc->parse, 0, 0, "EOF");
1548 mdoc->meta.title = mandoc_strdup("UNTITLED");
1551 if (mdoc->meta.vol == NULL)
1552 mdoc->meta.vol = mandoc_strdup("LOCAL");
1554 if (mdoc->meta.os == NULL) {
1555 mandoc_msg(MANDOCERR_OS_MISSING,
1556 mdoc->parse, 0, 0, NULL);
1557 mdoc->meta.os = mandoc_strdup("");
1560 /* Check that we begin with a proper `Sh'. */
1562 n = mdoc->first->child;
1563 while (n != NULL && mdoc_macros[n->tok].flags & MDOC_PROLOGUE)
1564 n = n->next;
1566 if (n == NULL)
1567 mandoc_msg(MANDOCERR_DOC_EMPTY, mdoc->parse, 0, 0, NULL);
1568 else if (n->tok != MDOC_Sh)
1569 mandoc_msg(MANDOCERR_SEC_BEFORE, mdoc->parse,
1570 n->line, n->pos, mdoc_macronames[n->tok]);
1573 static void
1574 post_st(POST_ARGS)
1576 struct mdoc_node *n, *nch;
1577 const char *p;
1579 n = mdoc->last;
1580 nch = n->child;
1582 assert(MDOC_TEXT == nch->type);
1584 if (NULL == (p = mdoc_a2st(nch->string))) {
1585 mandoc_vmsg(MANDOCERR_ST_BAD, mdoc->parse,
1586 nch->line, nch->pos, "St %s", nch->string);
1587 mdoc_node_delete(mdoc, n);
1588 } else {
1589 free(nch->string);
1590 nch->string = mandoc_strdup(p);
1594 static void
1595 post_rs(POST_ARGS)
1597 struct mdoc_node *np, *nch, *next, *prev;
1598 int i, j;
1600 np = mdoc->last;
1602 if (np->type != MDOC_BODY)
1603 return;
1605 if (np->child == NULL) {
1606 mandoc_msg(MANDOCERR_RS_EMPTY, mdoc->parse,
1607 np->line, np->pos, "Rs");
1608 return;
1612 * The full `Rs' block needs special handling to order the
1613 * sub-elements according to `rsord'. Pick through each element
1614 * and correctly order it. This is an insertion sort.
1617 next = NULL;
1618 for (nch = np->child->next; nch != NULL; nch = next) {
1619 /* Determine order number of this child. */
1620 for (i = 0; i < RSORD_MAX; i++)
1621 if (rsord[i] == nch->tok)
1622 break;
1624 if (i == RSORD_MAX) {
1625 mandoc_msg(MANDOCERR_RS_BAD,
1626 mdoc->parse, nch->line, nch->pos,
1627 mdoc_macronames[nch->tok]);
1628 i = -1;
1629 } else if (nch->tok == MDOC__J || nch->tok == MDOC__B)
1630 np->norm->Rs.quote_T++;
1633 * Remove this child from the chain. This somewhat
1634 * repeats mdoc_node_unlink(), but since we're
1635 * just re-ordering, there's no need for the
1636 * full unlink process.
1639 if ((next = nch->next) != NULL)
1640 next->prev = nch->prev;
1642 if ((prev = nch->prev) != NULL)
1643 prev->next = nch->next;
1645 nch->prev = nch->next = NULL;
1648 * Scan back until we reach a node that's
1649 * to be ordered before this child.
1652 for ( ; prev ; prev = prev->prev) {
1653 /* Determine order of `prev'. */
1654 for (j = 0; j < RSORD_MAX; j++)
1655 if (rsord[j] == prev->tok)
1656 break;
1657 if (j == RSORD_MAX)
1658 j = -1;
1660 if (j <= i)
1661 break;
1665 * Set this child back into its correct place
1666 * in front of the `prev' node.
1669 nch->prev = prev;
1671 if (prev == NULL) {
1672 np->child->prev = nch;
1673 nch->next = np->child;
1674 np->child = nch;
1675 } else {
1676 if (prev->next)
1677 prev->next->prev = nch;
1678 nch->next = prev->next;
1679 prev->next = nch;
1685 * For some arguments of some macros,
1686 * convert all breakable hyphens into ASCII_HYPH.
1688 static void
1689 post_hyph(POST_ARGS)
1691 struct mdoc_node *nch;
1692 char *cp;
1694 for (nch = mdoc->last->child; nch != NULL; nch = nch->next) {
1695 if (nch->type != MDOC_TEXT)
1696 continue;
1697 cp = nch->string;
1698 if (*cp == '\0')
1699 continue;
1700 while (*(++cp) != '\0')
1701 if (*cp == '-' &&
1702 isalpha((unsigned char)cp[-1]) &&
1703 isalpha((unsigned char)cp[1]))
1704 *cp = ASCII_HYPH;
1708 static void
1709 post_ns(POST_ARGS)
1712 if (MDOC_LINE & mdoc->last->flags)
1713 mandoc_msg(MANDOCERR_NS_SKIP, mdoc->parse,
1714 mdoc->last->line, mdoc->last->pos, NULL);
1717 static void
1718 post_sh(POST_ARGS)
1721 post_ignpar(mdoc);
1723 switch (mdoc->last->type) {
1724 case MDOC_HEAD:
1725 post_sh_head(mdoc);
1726 break;
1727 case MDOC_BODY:
1728 switch (mdoc->lastsec) {
1729 case SEC_NAME:
1730 post_sh_name(mdoc);
1731 break;
1732 case SEC_SEE_ALSO:
1733 post_sh_see_also(mdoc);
1734 break;
1735 case SEC_AUTHORS:
1736 post_sh_authors(mdoc);
1737 break;
1738 default:
1739 break;
1741 break;
1742 default:
1743 break;
1747 static void
1748 post_sh_name(POST_ARGS)
1750 struct mdoc_node *n;
1751 int hasnm, hasnd;
1753 hasnm = hasnd = 0;
1755 for (n = mdoc->last->child; n != NULL; n = n->next) {
1756 switch (n->tok) {
1757 case MDOC_Nm:
1758 hasnm = 1;
1759 break;
1760 case MDOC_Nd:
1761 hasnd = 1;
1762 if (n->next != NULL)
1763 mandoc_msg(MANDOCERR_NAMESEC_ND,
1764 mdoc->parse, n->line, n->pos, NULL);
1765 break;
1766 case MDOC_MAX:
1767 if (hasnm)
1768 break;
1769 /* FALLTHROUGH */
1770 default:
1771 mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
1772 n->line, n->pos, mdoc_macronames[n->tok]);
1773 break;
1777 if ( ! hasnm)
1778 mandoc_msg(MANDOCERR_NAMESEC_NONM, mdoc->parse,
1779 mdoc->last->line, mdoc->last->pos, NULL);
1780 if ( ! hasnd)
1781 mandoc_msg(MANDOCERR_NAMESEC_NOND, mdoc->parse,
1782 mdoc->last->line, mdoc->last->pos, NULL);
1785 static void
1786 post_sh_see_also(POST_ARGS)
1788 const struct mdoc_node *n;
1789 const char *name, *sec;
1790 const char *lastname, *lastsec, *lastpunct;
1791 int cmp;
1793 n = mdoc->last->child;
1794 lastname = lastsec = lastpunct = NULL;
1795 while (n != NULL) {
1796 if (n->tok != MDOC_Xr || n->nchild < 2)
1797 break;
1799 /* Process one .Xr node. */
1801 name = n->child->string;
1802 sec = n->child->next->string;
1803 if (lastsec != NULL) {
1804 if (lastpunct[0] != ',' || lastpunct[1] != '\0')
1805 mandoc_vmsg(MANDOCERR_XR_PUNCT,
1806 mdoc->parse, n->line, n->pos,
1807 "%s before %s(%s)", lastpunct,
1808 name, sec);
1809 cmp = strcmp(lastsec, sec);
1810 if (cmp > 0)
1811 mandoc_vmsg(MANDOCERR_XR_ORDER,
1812 mdoc->parse, n->line, n->pos,
1813 "%s(%s) after %s(%s)", name,
1814 sec, lastname, lastsec);
1815 else if (cmp == 0 &&
1816 strcasecmp(lastname, name) > 0)
1817 mandoc_vmsg(MANDOCERR_XR_ORDER,
1818 mdoc->parse, n->line, n->pos,
1819 "%s after %s", name, lastname);
1821 lastname = name;
1822 lastsec = sec;
1824 /* Process the following node. */
1826 n = n->next;
1827 if (n == NULL)
1828 break;
1829 if (n->tok == MDOC_Xr) {
1830 lastpunct = "none";
1831 continue;
1833 if (n->type != MDOC_TEXT)
1834 break;
1835 for (name = n->string; *name != '\0'; name++)
1836 if (isalpha((const unsigned char)*name))
1837 return;
1838 lastpunct = n->string;
1839 if (n->next == NULL)
1840 mandoc_vmsg(MANDOCERR_XR_PUNCT, mdoc->parse,
1841 n->line, n->pos, "%s after %s(%s)",
1842 lastpunct, lastname, lastsec);
1843 n = n->next;
1847 static int
1848 child_an(const struct mdoc_node *n)
1851 for (n = n->child; n != NULL; n = n->next)
1852 if ((n->tok == MDOC_An && n->nchild) || child_an(n))
1853 return(1);
1854 return(0);
1857 static void
1858 post_sh_authors(POST_ARGS)
1861 if ( ! child_an(mdoc->last))
1862 mandoc_msg(MANDOCERR_AN_MISSING, mdoc->parse,
1863 mdoc->last->line, mdoc->last->pos, NULL);
1866 static void
1867 post_sh_head(POST_ARGS)
1869 struct mdoc_node *n;
1870 const char *goodsec;
1871 char *secname;
1872 enum mdoc_sec sec;
1875 * Process a new section. Sections are either "named" or
1876 * "custom". Custom sections are user-defined, while named ones
1877 * follow a conventional order and may only appear in certain
1878 * manual sections.
1881 secname = NULL;
1882 sec = SEC_CUSTOM;
1883 mdoc_deroff(&secname, mdoc->last);
1884 sec = NULL == secname ? SEC_CUSTOM : a2sec(secname);
1886 /* The NAME should be first. */
1888 if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed)
1889 mandoc_vmsg(MANDOCERR_NAMESEC_FIRST, mdoc->parse,
1890 mdoc->last->line, mdoc->last->pos,
1891 "Sh %s", secname);
1893 /* The SYNOPSIS gets special attention in other areas. */
1895 if (SEC_SYNOPSIS == sec) {
1896 roff_setreg(mdoc->roff, "nS", 1, '=');
1897 mdoc->flags |= MDOC_SYNOPSIS;
1898 } else {
1899 roff_setreg(mdoc->roff, "nS", 0, '=');
1900 mdoc->flags &= ~MDOC_SYNOPSIS;
1903 /* Mark our last section. */
1905 mdoc->lastsec = sec;
1908 * Set the section attribute for the current HEAD, for its
1909 * parent BLOCK, and for the HEAD children; the latter can
1910 * only be TEXT nodes, so no recursion is needed.
1911 * For other blocks and elements, including .Sh BODY, this is
1912 * done when allocating the node data structures, but for .Sh
1913 * BLOCK and HEAD, the section is still unknown at that time.
1916 mdoc->last->parent->sec = sec;
1917 mdoc->last->sec = sec;
1918 for (n = mdoc->last->child; n; n = n->next)
1919 n->sec = sec;
1921 /* We don't care about custom sections after this. */
1923 if (SEC_CUSTOM == sec) {
1924 free(secname);
1925 return;
1929 * Check whether our non-custom section is being repeated or is
1930 * out of order.
1933 if (sec == mdoc->lastnamed)
1934 mandoc_vmsg(MANDOCERR_SEC_REP, mdoc->parse,
1935 mdoc->last->line, mdoc->last->pos,
1936 "Sh %s", secname);
1938 if (sec < mdoc->lastnamed)
1939 mandoc_vmsg(MANDOCERR_SEC_ORDER, mdoc->parse,
1940 mdoc->last->line, mdoc->last->pos,
1941 "Sh %s", secname);
1943 /* Mark the last named section. */
1945 mdoc->lastnamed = sec;
1947 /* Check particular section/manual conventions. */
1949 if (mdoc->meta.msec == NULL) {
1950 free(secname);
1951 return;
1954 goodsec = NULL;
1955 switch (sec) {
1956 case SEC_ERRORS:
1957 if (*mdoc->meta.msec == '4')
1958 break;
1959 goodsec = "2, 3, 4, 9";
1960 /* FALLTHROUGH */
1961 case SEC_RETURN_VALUES:
1962 /* FALLTHROUGH */
1963 case SEC_LIBRARY:
1964 if (*mdoc->meta.msec == '2')
1965 break;
1966 if (*mdoc->meta.msec == '3')
1967 break;
1968 if (NULL == goodsec)
1969 goodsec = "2, 3, 9";
1970 /* FALLTHROUGH */
1971 case SEC_CONTEXT:
1972 if (*mdoc->meta.msec == '9')
1973 break;
1974 if (NULL == goodsec)
1975 goodsec = "9";
1976 mandoc_vmsg(MANDOCERR_SEC_MSEC, mdoc->parse,
1977 mdoc->last->line, mdoc->last->pos,
1978 "Sh %s for %s only", secname, goodsec);
1979 break;
1980 default:
1981 break;
1983 free(secname);
1986 static void
1987 post_ignpar(POST_ARGS)
1989 struct mdoc_node *np;
1991 switch (mdoc->last->type) {
1992 case MDOC_HEAD:
1993 post_hyph(mdoc);
1994 return;
1995 case MDOC_BODY:
1996 break;
1997 default:
1998 return;
2001 if (NULL != (np = mdoc->last->child))
2002 if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
2003 mandoc_vmsg(MANDOCERR_PAR_SKIP,
2004 mdoc->parse, np->line, np->pos,
2005 "%s after %s", mdoc_macronames[np->tok],
2006 mdoc_macronames[mdoc->last->tok]);
2007 mdoc_node_delete(mdoc, np);
2010 if (NULL != (np = mdoc->last->last))
2011 if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
2012 mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2013 np->line, np->pos, "%s at the end of %s",
2014 mdoc_macronames[np->tok],
2015 mdoc_macronames[mdoc->last->tok]);
2016 mdoc_node_delete(mdoc, np);
2020 static void
2021 pre_par(PRE_ARGS)
2024 if (NULL == mdoc->last)
2025 return;
2026 if (MDOC_ELEM != n->type && MDOC_BLOCK != n->type)
2027 return;
2030 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
2031 * block: `Lp', `Pp', or non-compact `Bd' or `Bl'.
2034 if (MDOC_Pp != mdoc->last->tok &&
2035 MDOC_Lp != mdoc->last->tok &&
2036 MDOC_br != mdoc->last->tok)
2037 return;
2038 if (MDOC_Bl == n->tok && n->norm->Bl.comp)
2039 return;
2040 if (MDOC_Bd == n->tok && n->norm->Bd.comp)
2041 return;
2042 if (MDOC_It == n->tok && n->parent->norm->Bl.comp)
2043 return;
2045 mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2046 mdoc->last->line, mdoc->last->pos,
2047 "%s before %s", mdoc_macronames[mdoc->last->tok],
2048 mdoc_macronames[n->tok]);
2049 mdoc_node_delete(mdoc, mdoc->last);
2052 static void
2053 post_par(POST_ARGS)
2055 struct mdoc_node *np;
2057 np = mdoc->last;
2059 if (np->tok == MDOC_sp) {
2060 if (np->nchild > 1)
2061 mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
2062 np->child->next->line, np->child->next->pos,
2063 "sp ... %s", np->child->next->string);
2064 } else if (np->child != NULL)
2065 mandoc_vmsg(MANDOCERR_ARG_SKIP,
2066 mdoc->parse, np->line, np->pos, "%s %s",
2067 mdoc_macronames[np->tok], np->child->string);
2069 if (NULL == (np = mdoc->last->prev)) {
2070 np = mdoc->last->parent;
2071 if (MDOC_Sh != np->tok && MDOC_Ss != np->tok)
2072 return;
2073 } else if (MDOC_Pp != np->tok && MDOC_Lp != np->tok &&
2074 (MDOC_br != mdoc->last->tok ||
2075 (MDOC_sp != np->tok && MDOC_br != np->tok)))
2076 return;
2078 mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2079 mdoc->last->line, mdoc->last->pos,
2080 "%s after %s", mdoc_macronames[mdoc->last->tok],
2081 mdoc_macronames[np->tok]);
2082 mdoc_node_delete(mdoc, mdoc->last);
2085 static void
2086 pre_literal(PRE_ARGS)
2089 pre_display(mdoc, n);
2091 if (MDOC_BODY != n->type)
2092 return;
2095 * The `Dl' (note "el" not "one") and `Bd -literal' and `Bd
2096 * -unfilled' macros set MDOC_LITERAL on entrance to the body.
2099 switch (n->tok) {
2100 case MDOC_Dl:
2101 mdoc->flags |= MDOC_LITERAL;
2102 break;
2103 case MDOC_Bd:
2104 if (DISP_literal == n->norm->Bd.type)
2105 mdoc->flags |= MDOC_LITERAL;
2106 if (DISP_unfilled == n->norm->Bd.type)
2107 mdoc->flags |= MDOC_LITERAL;
2108 break;
2109 default:
2110 abort();
2111 /* NOTREACHED */
2115 static void
2116 post_dd(POST_ARGS)
2118 struct mdoc_node *n;
2119 char *datestr;
2121 if (mdoc->meta.date)
2122 free(mdoc->meta.date);
2124 n = mdoc->last;
2125 if (NULL == n->child || '\0' == n->child->string[0]) {
2126 mdoc->meta.date = mdoc->quick ? mandoc_strdup("") :
2127 mandoc_normdate(mdoc->parse, NULL, n->line, n->pos);
2128 goto out;
2131 datestr = NULL;
2132 mdoc_deroff(&datestr, n);
2133 if (mdoc->quick)
2134 mdoc->meta.date = datestr;
2135 else {
2136 mdoc->meta.date = mandoc_normdate(mdoc->parse,
2137 datestr, n->line, n->pos);
2138 free(datestr);
2140 out:
2141 mdoc_node_delete(mdoc, n);
2144 static void
2145 post_dt(POST_ARGS)
2147 struct mdoc_node *nn, *n;
2148 const char *cp;
2149 char *p;
2151 n = mdoc->last;
2153 free(mdoc->meta.title);
2154 free(mdoc->meta.msec);
2155 free(mdoc->meta.vol);
2156 free(mdoc->meta.arch);
2158 mdoc->meta.title = NULL;
2159 mdoc->meta.msec = NULL;
2160 mdoc->meta.vol = NULL;
2161 mdoc->meta.arch = NULL;
2163 /* Mandatory first argument: title. */
2165 nn = n->child;
2166 if (nn == NULL || *nn->string == '\0') {
2167 mandoc_msg(MANDOCERR_DT_NOTITLE,
2168 mdoc->parse, n->line, n->pos, "Dt");
2169 mdoc->meta.title = mandoc_strdup("UNTITLED");
2170 } else {
2171 mdoc->meta.title = mandoc_strdup(nn->string);
2173 /* Check that all characters are uppercase. */
2175 for (p = nn->string; *p != '\0'; p++)
2176 if (islower((unsigned char)*p)) {
2177 mandoc_vmsg(MANDOCERR_TITLE_CASE,
2178 mdoc->parse, nn->line,
2179 nn->pos + (p - nn->string),
2180 "Dt %s", nn->string);
2181 break;
2185 /* Mandatory second argument: section. */
2187 if (nn != NULL)
2188 nn = nn->next;
2190 if (nn == NULL) {
2191 mandoc_vmsg(MANDOCERR_MSEC_MISSING,
2192 mdoc->parse, n->line, n->pos,
2193 "Dt %s", mdoc->meta.title);
2194 mdoc->meta.vol = mandoc_strdup("LOCAL");
2195 goto out; /* msec and arch remain NULL. */
2198 mdoc->meta.msec = mandoc_strdup(nn->string);
2200 /* Infer volume title from section number. */
2202 cp = mandoc_a2msec(nn->string);
2203 if (cp == NULL) {
2204 mandoc_vmsg(MANDOCERR_MSEC_BAD, mdoc->parse,
2205 nn->line, nn->pos, "Dt ... %s", nn->string);
2206 mdoc->meta.vol = mandoc_strdup(nn->string);
2207 } else
2208 mdoc->meta.vol = mandoc_strdup(cp);
2210 /* Optional third argument: architecture. */
2212 if ((nn = nn->next) == NULL)
2213 goto out;
2215 for (p = nn->string; *p != '\0'; p++)
2216 *p = tolower((unsigned char)*p);
2217 mdoc->meta.arch = mandoc_strdup(nn->string);
2219 /* Ignore fourth and later arguments. */
2221 if ((nn = nn->next) != NULL)
2222 mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
2223 nn->line, nn->pos, "Dt ... %s", nn->string);
2225 out:
2226 mdoc_node_delete(mdoc, n);
2229 static void
2230 post_bx(POST_ARGS)
2232 struct mdoc_node *n;
2235 * Make `Bx's second argument always start with an uppercase
2236 * letter. Groff checks if it's an "accepted" term, but we just
2237 * uppercase blindly.
2240 n = mdoc->last->child;
2241 if (n && NULL != (n = n->next))
2242 *n->string = (char)toupper((unsigned char)*n->string);
2245 static void
2246 post_os(POST_ARGS)
2248 #ifndef OSNAME
2249 struct utsname utsname;
2250 static char *defbuf;
2251 #endif
2252 struct mdoc_node *n;
2254 n = mdoc->last;
2257 * Set the operating system by way of the `Os' macro.
2258 * The order of precedence is:
2259 * 1. the argument of the `Os' macro, unless empty
2260 * 2. the -Ios=foo command line argument, if provided
2261 * 3. -DOSNAME="\"foo\"", if provided during compilation
2262 * 4. "sysname release" from uname(3)
2265 free(mdoc->meta.os);
2266 mdoc->meta.os = NULL;
2267 mdoc_deroff(&mdoc->meta.os, n);
2268 if (mdoc->meta.os)
2269 goto out;
2271 if (mdoc->defos) {
2272 mdoc->meta.os = mandoc_strdup(mdoc->defos);
2273 goto out;
2276 #ifdef OSNAME
2277 mdoc->meta.os = mandoc_strdup(OSNAME);
2278 #else /*!OSNAME */
2279 if (NULL == defbuf) {
2280 if (-1 == uname(&utsname)) {
2281 mandoc_msg(MANDOCERR_OS_UNAME, mdoc->parse,
2282 n->line, n->pos, "Os");
2283 defbuf = mandoc_strdup("UNKNOWN");
2284 } else
2285 mandoc_asprintf(&defbuf, "%s %s",
2286 utsname.sysname, utsname.release);
2288 mdoc->meta.os = mandoc_strdup(defbuf);
2289 #endif /*!OSNAME*/
2291 out:
2292 mdoc_node_delete(mdoc, n);
2296 * If no argument is provided,
2297 * fill in the name of the current manual page.
2299 static void
2300 post_ex(POST_ARGS)
2302 struct mdoc_node *n;
2304 n = mdoc->last;
2306 if (n->child)
2307 return;
2309 if (mdoc->meta.name == NULL) {
2310 mandoc_msg(MANDOCERR_EX_NONAME, mdoc->parse,
2311 n->line, n->pos, "Ex");
2312 return;
2315 mdoc->next = MDOC_NEXT_CHILD;
2316 mdoc_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name);
2317 mdoc->last = n;
2320 static enum mdoc_sec
2321 a2sec(const char *p)
2323 int i;
2325 for (i = 0; i < (int)SEC__MAX; i++)
2326 if (secnames[i] && 0 == strcmp(p, secnames[i]))
2327 return((enum mdoc_sec)i);
2329 return(SEC_CUSTOM);
2332 static size_t
2333 macro2len(enum mdoct macro)
2336 switch (macro) {
2337 case MDOC_Ad:
2338 return(12);
2339 case MDOC_Ao:
2340 return(12);
2341 case MDOC_An:
2342 return(12);
2343 case MDOC_Aq:
2344 return(12);
2345 case MDOC_Ar:
2346 return(12);
2347 case MDOC_Bo:
2348 return(12);
2349 case MDOC_Bq:
2350 return(12);
2351 case MDOC_Cd:
2352 return(12);
2353 case MDOC_Cm:
2354 return(10);
2355 case MDOC_Do:
2356 return(10);
2357 case MDOC_Dq:
2358 return(12);
2359 case MDOC_Dv:
2360 return(12);
2361 case MDOC_Eo:
2362 return(12);
2363 case MDOC_Em:
2364 return(10);
2365 case MDOC_Er:
2366 return(17);
2367 case MDOC_Ev:
2368 return(15);
2369 case MDOC_Fa:
2370 return(12);
2371 case MDOC_Fl:
2372 return(10);
2373 case MDOC_Fo:
2374 return(16);
2375 case MDOC_Fn:
2376 return(16);
2377 case MDOC_Ic:
2378 return(10);
2379 case MDOC_Li:
2380 return(16);
2381 case MDOC_Ms:
2382 return(6);
2383 case MDOC_Nm:
2384 return(10);
2385 case MDOC_No:
2386 return(12);
2387 case MDOC_Oo:
2388 return(10);
2389 case MDOC_Op:
2390 return(14);
2391 case MDOC_Pa:
2392 return(32);
2393 case MDOC_Pf:
2394 return(12);
2395 case MDOC_Po:
2396 return(12);
2397 case MDOC_Pq:
2398 return(12);
2399 case MDOC_Ql:
2400 return(16);
2401 case MDOC_Qo:
2402 return(12);
2403 case MDOC_So:
2404 return(12);
2405 case MDOC_Sq:
2406 return(12);
2407 case MDOC_Sy:
2408 return(6);
2409 case MDOC_Sx:
2410 return(16);
2411 case MDOC_Tn:
2412 return(10);
2413 case MDOC_Va:
2414 return(12);
2415 case MDOC_Vt:
2416 return(12);
2417 case MDOC_Xr:
2418 return(10);
2419 default:
2420 break;
2422 return(0);