tools/llvm: Do not build with symbols
[minix3.git] / external / bsd / mdocml / dist / mdoc_validate.c
blob458f7cbde80236c966a3c696391b812efdf15da1
1 /* $Vendor-Id: mdoc_validate.c,v 1.182 2012/03/23 05:50:25 kristaps Exp $ */
2 /*
3 * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org>
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
22 #ifndef OSNAME
23 #include <sys/utsname.h>
24 #endif
26 #include <sys/types.h>
28 #include <assert.h>
29 #include <ctype.h>
30 #include <limits.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <time.h>
36 #include "mdoc.h"
37 #include "mandoc.h"
38 #include "libmdoc.h"
39 #include "libmandoc.h"
41 /* FIXME: .Bl -diag can't have non-text children in HEAD. */
43 #define PRE_ARGS struct mdoc *mdoc, struct mdoc_node *n
44 #define POST_ARGS struct mdoc *mdoc
46 #define NUMSIZ 32
47 #define DATESIZE 32
49 enum check_ineq {
50 CHECK_LT,
51 CHECK_GT,
52 CHECK_EQ
55 enum check_lvl {
56 CHECK_WARN,
57 CHECK_ERROR,
60 typedef int (*v_pre)(PRE_ARGS);
61 typedef int (*v_post)(POST_ARGS);
63 struct valids {
64 v_pre *pre;
65 v_post *post;
68 static int check_count(struct mdoc *, enum mdoc_type,
69 enum check_lvl, enum check_ineq, int);
70 static int check_parent(PRE_ARGS, enum mdoct, enum mdoc_type);
71 static void check_text(struct mdoc *, int, int, char *);
72 static void check_argv(struct mdoc *,
73 struct mdoc_node *, struct mdoc_argv *);
74 static void check_args(struct mdoc *, struct mdoc_node *);
75 static int concat(char *, const struct mdoc_node *, size_t);
76 static enum mdoc_sec a2sec(const char *);
77 static size_t macro2len(enum mdoct);
79 static int ebool(POST_ARGS);
80 static int berr_ge1(POST_ARGS);
81 static int bwarn_ge1(POST_ARGS);
82 static int ewarn_eq0(POST_ARGS);
83 static int ewarn_eq1(POST_ARGS);
84 static int ewarn_ge1(POST_ARGS);
85 static int ewarn_le1(POST_ARGS);
86 static int hwarn_eq0(POST_ARGS);
87 static int hwarn_eq1(POST_ARGS);
88 static int hwarn_ge1(POST_ARGS);
89 static int hwarn_le1(POST_ARGS);
91 static int post_an(POST_ARGS);
92 static int post_at(POST_ARGS);
93 static int post_bf(POST_ARGS);
94 static int post_bl(POST_ARGS);
95 static int post_bl_block(POST_ARGS);
96 static int post_bl_block_width(POST_ARGS);
97 static int post_bl_block_tag(POST_ARGS);
98 static int post_bl_head(POST_ARGS);
99 static int post_bx(POST_ARGS);
100 static int post_dd(POST_ARGS);
101 static int post_dt(POST_ARGS);
102 static int post_defaults(POST_ARGS);
103 static int post_literal(POST_ARGS);
104 static int post_eoln(POST_ARGS);
105 static int post_it(POST_ARGS);
106 static int post_lb(POST_ARGS);
107 static int post_nm(POST_ARGS);
108 static int post_ns(POST_ARGS);
109 static int post_os(POST_ARGS);
110 static int post_ignpar(POST_ARGS);
111 static int post_prol(POST_ARGS);
112 static int post_root(POST_ARGS);
113 static int post_rs(POST_ARGS);
114 static int post_sh(POST_ARGS);
115 static int post_sh_body(POST_ARGS);
116 static int post_sh_head(POST_ARGS);
117 static int post_st(POST_ARGS);
118 static int post_std(POST_ARGS);
119 static int post_vt(POST_ARGS);
120 static int pre_an(PRE_ARGS);
121 static int pre_bd(PRE_ARGS);
122 static int pre_bl(PRE_ARGS);
123 static int pre_dd(PRE_ARGS);
124 static int pre_display(PRE_ARGS);
125 static int pre_dt(PRE_ARGS);
126 static int pre_it(PRE_ARGS);
127 static int pre_literal(PRE_ARGS);
128 static int pre_os(PRE_ARGS);
129 static int pre_par(PRE_ARGS);
130 static int pre_sh(PRE_ARGS);
131 static int pre_ss(PRE_ARGS);
132 static int pre_std(PRE_ARGS);
134 static v_post posts_an[] = { post_an, NULL };
135 static v_post posts_at[] = { post_at, post_defaults, NULL };
136 static v_post posts_bd[] = { post_literal, hwarn_eq0, bwarn_ge1, NULL };
137 static v_post posts_bf[] = { hwarn_le1, post_bf, NULL };
138 static v_post posts_bk[] = { hwarn_eq0, bwarn_ge1, NULL };
139 static v_post posts_bl[] = { bwarn_ge1, post_bl, NULL };
140 static v_post posts_bx[] = { post_bx, NULL };
141 static v_post posts_bool[] = { ebool, NULL };
142 static v_post posts_eoln[] = { post_eoln, NULL };
143 static v_post posts_defaults[] = { post_defaults, NULL };
144 static v_post posts_dd[] = { post_dd, post_prol, NULL };
145 static v_post posts_dl[] = { post_literal, bwarn_ge1, NULL };
146 static v_post posts_dt[] = { post_dt, post_prol, NULL };
147 static v_post posts_fo[] = { hwarn_eq1, bwarn_ge1, NULL };
148 static v_post posts_it[] = { post_it, NULL };
149 static v_post posts_lb[] = { post_lb, NULL };
150 static v_post posts_nd[] = { berr_ge1, NULL };
151 static v_post posts_nm[] = { post_nm, NULL };
152 static v_post posts_notext[] = { ewarn_eq0, NULL };
153 static v_post posts_ns[] = { post_ns, NULL };
154 static v_post posts_os[] = { post_os, post_prol, NULL };
155 static v_post posts_rs[] = { post_rs, NULL };
156 static v_post posts_sh[] = { post_ignpar, hwarn_ge1, post_sh, NULL };
157 static v_post posts_sp[] = { ewarn_le1, NULL };
158 static v_post posts_ss[] = { post_ignpar, hwarn_ge1, NULL };
159 static v_post posts_st[] = { post_st, NULL };
160 static v_post posts_std[] = { post_std, NULL };
161 static v_post posts_text[] = { ewarn_ge1, NULL };
162 static v_post posts_text1[] = { ewarn_eq1, NULL };
163 static v_post posts_vt[] = { post_vt, NULL };
164 static v_post posts_wline[] = { bwarn_ge1, NULL };
165 static v_pre pres_an[] = { pre_an, NULL };
166 static v_pre pres_bd[] = { pre_display, pre_bd, pre_literal, pre_par, NULL };
167 static v_pre pres_bl[] = { pre_bl, pre_par, NULL };
168 static v_pre pres_d1[] = { pre_display, NULL };
169 static v_pre pres_dl[] = { pre_literal, pre_display, NULL };
170 static v_pre pres_dd[] = { pre_dd, NULL };
171 static v_pre pres_dt[] = { pre_dt, NULL };
172 static v_pre pres_er[] = { NULL, NULL };
173 static v_pre pres_fd[] = { NULL, NULL };
174 static v_pre pres_it[] = { pre_it, pre_par, NULL };
175 static v_pre pres_os[] = { pre_os, NULL };
176 static v_pre pres_pp[] = { pre_par, NULL };
177 static v_pre pres_sh[] = { pre_sh, NULL };
178 static v_pre pres_ss[] = { pre_ss, NULL };
179 static v_pre pres_std[] = { pre_std, NULL };
181 static const struct valids mdoc_valids[MDOC_MAX] = {
182 { NULL, NULL }, /* Ap */
183 { pres_dd, posts_dd }, /* Dd */
184 { pres_dt, posts_dt }, /* Dt */
185 { pres_os, posts_os }, /* Os */
186 { pres_sh, posts_sh }, /* Sh */
187 { pres_ss, posts_ss }, /* Ss */
188 { pres_pp, posts_notext }, /* Pp */
189 { pres_d1, posts_wline }, /* D1 */
190 { pres_dl, posts_dl }, /* Dl */
191 { pres_bd, posts_bd }, /* Bd */
192 { NULL, NULL }, /* Ed */
193 { pres_bl, posts_bl }, /* Bl */
194 { NULL, NULL }, /* El */
195 { pres_it, posts_it }, /* It */
196 { NULL, NULL }, /* Ad */
197 { pres_an, posts_an }, /* An */
198 { NULL, posts_defaults }, /* Ar */
199 { NULL, NULL }, /* Cd */
200 { NULL, NULL }, /* Cm */
201 { NULL, NULL }, /* Dv */
202 { pres_er, NULL }, /* Er */
203 { NULL, NULL }, /* Ev */
204 { pres_std, posts_std }, /* Ex */
205 { NULL, NULL }, /* Fa */
206 { pres_fd, posts_text }, /* Fd */
207 { NULL, NULL }, /* Fl */
208 { NULL, NULL }, /* Fn */
209 { NULL, NULL }, /* Ft */
210 { NULL, NULL }, /* Ic */
211 { NULL, posts_text1 }, /* In */
212 { NULL, posts_defaults }, /* Li */
213 { NULL, posts_nd }, /* Nd */
214 { NULL, posts_nm }, /* Nm */
215 { NULL, NULL }, /* Op */
216 { NULL, NULL }, /* Ot */
217 { NULL, posts_defaults }, /* Pa */
218 { pres_std, posts_std }, /* Rv */
219 { NULL, posts_st }, /* St */
220 { NULL, NULL }, /* Va */
221 { NULL, posts_vt }, /* Vt */
222 { NULL, posts_text }, /* Xr */
223 { NULL, posts_text }, /* %A */
224 { NULL, posts_text }, /* %B */ /* FIXME: can be used outside Rs/Re. */
225 { NULL, posts_text }, /* %D */
226 { NULL, posts_text }, /* %I */
227 { NULL, posts_text }, /* %J */
228 { NULL, posts_text }, /* %N */
229 { NULL, posts_text }, /* %O */
230 { NULL, posts_text }, /* %P */
231 { NULL, posts_text }, /* %R */
232 { NULL, posts_text }, /* %T */ /* FIXME: can be used outside Rs/Re. */
233 { NULL, posts_text }, /* %V */
234 { NULL, NULL }, /* Ac */
235 { NULL, NULL }, /* Ao */
236 { NULL, NULL }, /* Aq */
237 { NULL, posts_at }, /* At */
238 { NULL, NULL }, /* Bc */
239 { NULL, posts_bf }, /* Bf */
240 { NULL, NULL }, /* Bo */
241 { NULL, NULL }, /* Bq */
242 { NULL, NULL }, /* Bsx */
243 { NULL, posts_bx }, /* Bx */
244 { NULL, posts_bool }, /* Db */
245 { NULL, NULL }, /* Dc */
246 { NULL, NULL }, /* Do */
247 { NULL, NULL }, /* Dq */
248 { NULL, NULL }, /* Ec */
249 { NULL, NULL }, /* Ef */
250 { NULL, NULL }, /* Em */
251 { NULL, NULL }, /* Eo */
252 { NULL, NULL }, /* Fx */
253 { NULL, NULL }, /* Ms */
254 { NULL, posts_notext }, /* No */
255 { NULL, posts_ns }, /* Ns */
256 { NULL, NULL }, /* Nx */
257 { NULL, NULL }, /* Ox */
258 { NULL, NULL }, /* Pc */
259 { NULL, posts_text1 }, /* Pf */
260 { NULL, NULL }, /* Po */
261 { NULL, NULL }, /* Pq */
262 { NULL, NULL }, /* Qc */
263 { NULL, NULL }, /* Ql */
264 { NULL, NULL }, /* Qo */
265 { NULL, NULL }, /* Qq */
266 { NULL, NULL }, /* Re */
267 { NULL, posts_rs }, /* Rs */
268 { NULL, NULL }, /* Sc */
269 { NULL, NULL }, /* So */
270 { NULL, NULL }, /* Sq */
271 { NULL, posts_bool }, /* Sm */
272 { NULL, NULL }, /* Sx */
273 { NULL, NULL }, /* Sy */
274 { NULL, NULL }, /* Tn */
275 { NULL, NULL }, /* Ux */
276 { NULL, NULL }, /* Xc */
277 { NULL, NULL }, /* Xo */
278 { NULL, posts_fo }, /* Fo */
279 { NULL, NULL }, /* Fc */
280 { NULL, NULL }, /* Oo */
281 { NULL, NULL }, /* Oc */
282 { NULL, posts_bk }, /* Bk */
283 { NULL, NULL }, /* Ek */
284 { NULL, posts_eoln }, /* Bt */
285 { NULL, NULL }, /* Hf */
286 { NULL, NULL }, /* Fr */
287 { NULL, posts_eoln }, /* Ud */
288 { NULL, posts_lb }, /* Lb */
289 { NULL, posts_notext }, /* Lp */
290 { NULL, NULL }, /* Lk */
291 { NULL, posts_defaults }, /* Mt */
292 { NULL, NULL }, /* Brq */
293 { NULL, NULL }, /* Bro */
294 { NULL, NULL }, /* Brc */
295 { NULL, posts_text }, /* %C */
296 { NULL, NULL }, /* Es */
297 { NULL, NULL }, /* En */
298 { NULL, NULL }, /* Dx */
299 { NULL, posts_text }, /* %Q */
300 { NULL, posts_notext }, /* br */
301 { pres_pp, posts_sp }, /* sp */
302 { NULL, posts_text1 }, /* %U */
303 { NULL, NULL }, /* Ta */
306 #define RSORD_MAX 14 /* Number of `Rs' blocks. */
308 static const enum mdoct rsord[RSORD_MAX] = {
309 MDOC__A,
310 MDOC__T,
311 MDOC__B,
312 MDOC__I,
313 MDOC__J,
314 MDOC__R,
315 MDOC__N,
316 MDOC__V,
317 MDOC__P,
318 MDOC__Q,
319 MDOC__D,
320 MDOC__O,
321 MDOC__C,
322 MDOC__U
325 static const char * const secnames[SEC__MAX] = {
326 NULL,
327 "NAME",
328 "LIBRARY",
329 "SYNOPSIS",
330 "DESCRIPTION",
331 "IMPLEMENTATION NOTES",
332 "RETURN VALUES",
333 "ENVIRONMENT",
334 "FILES",
335 "EXIT STATUS",
336 "EXAMPLES",
337 "DIAGNOSTICS",
338 "COMPATIBILITY",
339 "ERRORS",
340 "SEE ALSO",
341 "STANDARDS",
342 "HISTORY",
343 "AUTHORS",
344 "CAVEATS",
345 "BUGS",
346 "SECURITY CONSIDERATIONS",
347 NULL
351 mdoc_valid_pre(struct mdoc *mdoc, struct mdoc_node *n)
353 v_pre *p;
354 int line, pos;
355 char *tp;
357 switch (n->type) {
358 case (MDOC_TEXT):
359 tp = n->string;
360 line = n->line;
361 pos = n->pos;
362 check_text(mdoc, line, pos, tp);
363 /* FALLTHROUGH */
364 case (MDOC_TBL):
365 /* FALLTHROUGH */
366 case (MDOC_EQN):
367 /* FALLTHROUGH */
368 case (MDOC_ROOT):
369 return(1);
370 default:
371 break;
374 check_args(mdoc, n);
376 if (NULL == mdoc_valids[n->tok].pre)
377 return(1);
378 for (p = mdoc_valids[n->tok].pre; *p; p++)
379 if ( ! (*p)(mdoc, n))
380 return(0);
381 return(1);
386 mdoc_valid_post(struct mdoc *mdoc)
388 v_post *p;
390 if (MDOC_VALID & mdoc->last->flags)
391 return(1);
392 mdoc->last->flags |= MDOC_VALID;
394 switch (mdoc->last->type) {
395 case (MDOC_TEXT):
396 /* FALLTHROUGH */
397 case (MDOC_EQN):
398 /* FALLTHROUGH */
399 case (MDOC_TBL):
400 return(1);
401 case (MDOC_ROOT):
402 return(post_root(mdoc));
403 default:
404 break;
407 if (NULL == mdoc_valids[mdoc->last->tok].post)
408 return(1);
409 for (p = mdoc_valids[mdoc->last->tok].post; *p; p++)
410 if ( ! (*p)(mdoc))
411 return(0);
413 return(1);
416 static int
417 check_count(struct mdoc *m, enum mdoc_type type,
418 enum check_lvl lvl, enum check_ineq ineq, int val)
420 const char *p;
421 enum mandocerr t;
423 if (m->last->type != type)
424 return(1);
426 switch (ineq) {
427 case (CHECK_LT):
428 p = "less than ";
429 if (m->last->nchild < val)
430 return(1);
431 break;
432 case (CHECK_GT):
433 p = "more than ";
434 if (m->last->nchild > val)
435 return(1);
436 break;
437 case (CHECK_EQ):
438 p = "";
439 if (val == m->last->nchild)
440 return(1);
441 break;
442 default:
443 abort();
444 /* NOTREACHED */
447 t = lvl == CHECK_WARN ? MANDOCERR_ARGCWARN : MANDOCERR_ARGCOUNT;
448 mandoc_vmsg(t, m->parse, m->last->line, m->last->pos,
449 "want %s%d children (have %d)",
450 p, val, m->last->nchild);
451 return(1);
454 static int
455 berr_ge1(POST_ARGS)
458 return(check_count(mdoc, MDOC_BODY, CHECK_ERROR, CHECK_GT, 0));
461 static int
462 bwarn_ge1(POST_ARGS)
464 return(check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0));
467 static int
468 ewarn_eq0(POST_ARGS)
470 return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0));
473 static int
474 ewarn_eq1(POST_ARGS)
476 return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1));
479 static int
480 ewarn_ge1(POST_ARGS)
482 return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0));
485 static int
486 ewarn_le1(POST_ARGS)
488 return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_LT, 2));
491 static int
492 hwarn_eq0(POST_ARGS)
494 return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0));
497 static int
498 hwarn_eq1(POST_ARGS)
500 return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 1));
503 static int
504 hwarn_ge1(POST_ARGS)
506 return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_GT, 0));
509 static int
510 hwarn_le1(POST_ARGS)
512 return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_LT, 2));
515 static void
516 check_args(struct mdoc *m, struct mdoc_node *n)
518 int i;
520 if (NULL == n->args)
521 return;
523 assert(n->args->argc);
524 for (i = 0; i < (int)n->args->argc; i++)
525 check_argv(m, n, &n->args->argv[i]);
528 static void
529 check_argv(struct mdoc *m, struct mdoc_node *n, struct mdoc_argv *v)
531 int i;
533 for (i = 0; i < (int)v->sz; i++)
534 check_text(m, v->line, v->pos, v->value[i]);
536 /* FIXME: move to post_std(). */
538 if (MDOC_Std == v->arg)
539 if ( ! (v->sz || m->meta.name))
540 mdoc_nmsg(m, n, MANDOCERR_NONAME);
543 static void
544 check_text(struct mdoc *m, int ln, int pos, char *p)
546 char *cp;
548 if (MDOC_LITERAL & m->flags)
549 return;
551 for (cp = p; NULL != (p = strchr(p, '\t')); p++)
552 mdoc_pmsg(m, ln, pos + (int)(p - cp), MANDOCERR_BADTAB);
555 static int
556 check_parent(PRE_ARGS, enum mdoct tok, enum mdoc_type t)
559 assert(n->parent);
560 if ((MDOC_ROOT == t || tok == n->parent->tok) &&
561 (t == n->parent->type))
562 return(1);
564 mandoc_vmsg(MANDOCERR_SYNTCHILD, mdoc->parse, n->line,
565 n->pos, "want parent %s", MDOC_ROOT == t ?
566 "<root>" : mdoc_macronames[tok]);
567 return(0);
571 static int
572 pre_display(PRE_ARGS)
574 struct mdoc_node *node;
576 if (MDOC_BLOCK != n->type)
577 return(1);
579 for (node = mdoc->last->parent; node; node = node->parent)
580 if (MDOC_BLOCK == node->type)
581 if (MDOC_Bd == node->tok)
582 break;
584 if (node)
585 mdoc_nmsg(mdoc, n, MANDOCERR_NESTEDDISP);
587 return(1);
591 static int
592 pre_bl(PRE_ARGS)
594 int i, comp, dup;
595 const char *offs, *width;
596 enum mdoc_list lt;
597 struct mdoc_node *np;
599 if (MDOC_BLOCK != n->type) {
600 if (ENDBODY_NOT != n->end) {
601 assert(n->pending);
602 np = n->pending->parent;
603 } else
604 np = n->parent;
606 assert(np);
607 assert(MDOC_BLOCK == np->type);
608 assert(MDOC_Bl == np->tok);
609 return(1);
613 * First figure out which kind of list to use: bind ourselves to
614 * the first mentioned list type and warn about any remaining
615 * ones. If we find no list type, we default to LIST_item.
618 /* LINTED */
619 for (i = 0; n->args && i < (int)n->args->argc; i++) {
620 lt = LIST__NONE;
621 dup = comp = 0;
622 width = offs = NULL;
623 switch (n->args->argv[i].arg) {
624 /* Set list types. */
625 case (MDOC_Bullet):
626 lt = LIST_bullet;
627 break;
628 case (MDOC_Dash):
629 lt = LIST_dash;
630 break;
631 case (MDOC_Enum):
632 lt = LIST_enum;
633 break;
634 case (MDOC_Hyphen):
635 lt = LIST_hyphen;
636 break;
637 case (MDOC_Item):
638 lt = LIST_item;
639 break;
640 case (MDOC_Tag):
641 lt = LIST_tag;
642 break;
643 case (MDOC_Diag):
644 lt = LIST_diag;
645 break;
646 case (MDOC_Hang):
647 lt = LIST_hang;
648 break;
649 case (MDOC_Ohang):
650 lt = LIST_ohang;
651 break;
652 case (MDOC_Inset):
653 lt = LIST_inset;
654 break;
655 case (MDOC_Column):
656 lt = LIST_column;
657 break;
658 /* Set list arguments. */
659 case (MDOC_Compact):
660 dup = n->norm->Bl.comp;
661 comp = 1;
662 break;
663 case (MDOC_Width):
664 /* NB: this can be empty! */
665 if (n->args->argv[i].sz) {
666 width = n->args->argv[i].value[0];
667 dup = (NULL != n->norm->Bl.width);
668 break;
670 mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
671 break;
672 case (MDOC_Offset):
673 /* NB: this can be empty! */
674 if (n->args->argv[i].sz) {
675 offs = n->args->argv[i].value[0];
676 dup = (NULL != n->norm->Bl.offs);
677 break;
679 mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
680 break;
681 default:
682 continue;
685 /* Check: duplicate auxiliary arguments. */
687 if (dup)
688 mdoc_nmsg(mdoc, n, MANDOCERR_ARGVREP);
690 if (comp && ! dup)
691 n->norm->Bl.comp = comp;
692 if (offs && ! dup)
693 n->norm->Bl.offs = offs;
694 if (width && ! dup)
695 n->norm->Bl.width = width;
697 /* Check: multiple list types. */
699 if (LIST__NONE != lt && n->norm->Bl.type != LIST__NONE)
700 mdoc_nmsg(mdoc, n, MANDOCERR_LISTREP);
702 /* Assign list type. */
704 if (LIST__NONE != lt && n->norm->Bl.type == LIST__NONE) {
705 n->norm->Bl.type = lt;
706 /* Set column information, too. */
707 if (LIST_column == lt) {
708 n->norm->Bl.ncols =
709 n->args->argv[i].sz;
710 n->norm->Bl.cols = (void *)
711 n->args->argv[i].value;
715 /* The list type should come first. */
717 if (n->norm->Bl.type == LIST__NONE)
718 if (n->norm->Bl.width ||
719 n->norm->Bl.offs ||
720 n->norm->Bl.comp)
721 mdoc_nmsg(mdoc, n, MANDOCERR_LISTFIRST);
723 continue;
726 /* Allow lists to default to LIST_item. */
728 if (LIST__NONE == n->norm->Bl.type) {
729 mdoc_nmsg(mdoc, n, MANDOCERR_LISTTYPE);
730 n->norm->Bl.type = LIST_item;
734 * Validate the width field. Some list types don't need width
735 * types and should be warned about them. Others should have it
736 * and must also be warned.
739 switch (n->norm->Bl.type) {
740 case (LIST_tag):
741 if (n->norm->Bl.width)
742 break;
743 mdoc_nmsg(mdoc, n, MANDOCERR_NOWIDTHARG);
744 break;
745 case (LIST_column):
746 /* FALLTHROUGH */
747 case (LIST_diag):
748 /* FALLTHROUGH */
749 case (LIST_ohang):
750 /* FALLTHROUGH */
751 case (LIST_inset):
752 /* FALLTHROUGH */
753 case (LIST_item):
754 if (n->norm->Bl.width)
755 mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
756 break;
757 default:
758 break;
761 return(1);
765 static int
766 pre_bd(PRE_ARGS)
768 int i, dup, comp;
769 enum mdoc_disp dt;
770 const char *offs;
771 struct mdoc_node *np;
773 if (MDOC_BLOCK != n->type) {
774 if (ENDBODY_NOT != n->end) {
775 assert(n->pending);
776 np = n->pending->parent;
777 } else
778 np = n->parent;
780 assert(np);
781 assert(MDOC_BLOCK == np->type);
782 assert(MDOC_Bd == np->tok);
783 return(1);
786 /* LINTED */
787 for (i = 0; n->args && i < (int)n->args->argc; i++) {
788 dt = DISP__NONE;
789 dup = comp = 0;
790 offs = NULL;
792 switch (n->args->argv[i].arg) {
793 case (MDOC_Centred):
794 dt = DISP_centred;
795 break;
796 case (MDOC_Ragged):
797 dt = DISP_ragged;
798 break;
799 case (MDOC_Unfilled):
800 dt = DISP_unfilled;
801 break;
802 case (MDOC_Filled):
803 dt = DISP_filled;
804 break;
805 case (MDOC_Literal):
806 dt = DISP_literal;
807 break;
808 case (MDOC_File):
809 mdoc_nmsg(mdoc, n, MANDOCERR_BADDISP);
810 return(0);
811 case (MDOC_Offset):
812 /* NB: this can be empty! */
813 if (n->args->argv[i].sz) {
814 offs = n->args->argv[i].value[0];
815 dup = (NULL != n->norm->Bd.offs);
816 break;
818 mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
819 break;
820 case (MDOC_Compact):
821 comp = 1;
822 dup = n->norm->Bd.comp;
823 break;
824 default:
825 abort();
826 /* NOTREACHED */
829 /* Check whether we have duplicates. */
831 if (dup)
832 mdoc_nmsg(mdoc, n, MANDOCERR_ARGVREP);
834 /* Make our auxiliary assignments. */
836 if (offs && ! dup)
837 n->norm->Bd.offs = offs;
838 if (comp && ! dup)
839 n->norm->Bd.comp = comp;
841 /* Check whether a type has already been assigned. */
843 if (DISP__NONE != dt && n->norm->Bd.type != DISP__NONE)
844 mdoc_nmsg(mdoc, n, MANDOCERR_DISPREP);
846 /* Make our type assignment. */
848 if (DISP__NONE != dt && n->norm->Bd.type == DISP__NONE)
849 n->norm->Bd.type = dt;
852 if (DISP__NONE == n->norm->Bd.type) {
853 mdoc_nmsg(mdoc, n, MANDOCERR_DISPTYPE);
854 n->norm->Bd.type = DISP_ragged;
857 return(1);
861 static int
862 pre_ss(PRE_ARGS)
865 if (MDOC_BLOCK != n->type)
866 return(1);
867 return(check_parent(mdoc, n, MDOC_Sh, MDOC_BODY));
871 static int
872 pre_sh(PRE_ARGS)
875 if (MDOC_BLOCK != n->type)
876 return(1);
878 roff_regunset(mdoc->roff, REG_nS);
879 return(check_parent(mdoc, n, MDOC_MAX, MDOC_ROOT));
883 static int
884 pre_it(PRE_ARGS)
887 if (MDOC_BLOCK != n->type)
888 return(1);
890 return(check_parent(mdoc, n, MDOC_Bl, MDOC_BODY));
894 static int
895 pre_an(PRE_ARGS)
897 int i;
899 if (NULL == n->args)
900 return(1);
902 for (i = 1; i < (int)n->args->argc; i++)
903 mdoc_pmsg(mdoc, n->args->argv[i].line,
904 n->args->argv[i].pos, MANDOCERR_IGNARGV);
906 if (MDOC_Split == n->args->argv[0].arg)
907 n->norm->An.auth = AUTH_split;
908 else if (MDOC_Nosplit == n->args->argv[0].arg)
909 n->norm->An.auth = AUTH_nosplit;
910 else
911 abort();
913 return(1);
916 static int
917 pre_std(PRE_ARGS)
920 if (n->args && 1 == n->args->argc)
921 if (MDOC_Std == n->args->argv[0].arg)
922 return(1);
924 mdoc_nmsg(mdoc, n, MANDOCERR_NOARGV);
925 return(1);
928 static int
929 pre_dt(PRE_ARGS)
932 if (NULL == mdoc->meta.date || mdoc->meta.os)
933 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
935 if (mdoc->meta.title)
936 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
938 return(1);
941 static int
942 pre_os(PRE_ARGS)
945 if (NULL == mdoc->meta.title || NULL == mdoc->meta.date)
946 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
948 if (mdoc->meta.os)
949 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
951 return(1);
954 static int
955 pre_dd(PRE_ARGS)
958 if (mdoc->meta.title || mdoc->meta.os)
959 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
961 if (mdoc->meta.date)
962 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
964 return(1);
968 static int
969 post_bf(POST_ARGS)
971 struct mdoc_node *np;
972 enum mdocargt arg;
975 * Unlike other data pointers, these are "housed" by the HEAD
976 * element, which contains the goods.
979 if (MDOC_HEAD != mdoc->last->type) {
980 if (ENDBODY_NOT != mdoc->last->end) {
981 assert(mdoc->last->pending);
982 np = mdoc->last->pending->parent->head;
983 } else if (MDOC_BLOCK != mdoc->last->type) {
984 np = mdoc->last->parent->head;
985 } else
986 np = mdoc->last->head;
988 assert(np);
989 assert(MDOC_HEAD == np->type);
990 assert(MDOC_Bf == np->tok);
991 return(1);
994 np = mdoc->last;
995 assert(MDOC_BLOCK == np->parent->type);
996 assert(MDOC_Bf == np->parent->tok);
999 * Cannot have both argument and parameter.
1000 * If neither is specified, let it through with a warning.
1003 if (np->parent->args && np->child) {
1004 mdoc_nmsg(mdoc, np, MANDOCERR_SYNTARGVCOUNT);
1005 return(0);
1006 } else if (NULL == np->parent->args && NULL == np->child) {
1007 mdoc_nmsg(mdoc, np, MANDOCERR_FONTTYPE);
1008 return(1);
1011 /* Extract argument into data. */
1013 if (np->parent->args) {
1014 arg = np->parent->args->argv[0].arg;
1015 if (MDOC_Emphasis == arg)
1016 np->norm->Bf.font = FONT_Em;
1017 else if (MDOC_Literal == arg)
1018 np->norm->Bf.font = FONT_Li;
1019 else if (MDOC_Symbolic == arg)
1020 np->norm->Bf.font = FONT_Sy;
1021 else
1022 abort();
1023 return(1);
1026 /* Extract parameter into data. */
1028 if (0 == strcmp(np->child->string, "Em"))
1029 np->norm->Bf.font = FONT_Em;
1030 else if (0 == strcmp(np->child->string, "Li"))
1031 np->norm->Bf.font = FONT_Li;
1032 else if (0 == strcmp(np->child->string, "Sy"))
1033 np->norm->Bf.font = FONT_Sy;
1034 else
1035 mdoc_nmsg(mdoc, np, MANDOCERR_FONTTYPE);
1037 return(1);
1040 static int
1041 post_lb(POST_ARGS)
1043 const char *p;
1044 char *buf;
1045 size_t sz;
1047 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1);
1049 assert(mdoc->last->child);
1050 assert(MDOC_TEXT == mdoc->last->child->type);
1052 p = mdoc_a2lib(mdoc->last->child->string);
1054 /* If lookup ok, replace with table value. */
1056 if (p) {
1057 free(mdoc->last->child->string);
1058 mdoc->last->child->string = mandoc_strdup(p);
1059 return(1);
1062 /* If not, use "library ``xxxx''. */
1064 sz = strlen(mdoc->last->child->string) +
1065 2 + strlen("\\(lqlibrary\\(rq");
1066 buf = mandoc_malloc(sz);
1067 snprintf(buf, sz, "library \\(lq%s\\(rq",
1068 mdoc->last->child->string);
1069 free(mdoc->last->child->string);
1070 mdoc->last->child->string = buf;
1071 return(1);
1074 static int
1075 post_eoln(POST_ARGS)
1078 if (mdoc->last->child)
1079 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST);
1080 return(1);
1084 static int
1085 post_vt(POST_ARGS)
1087 const struct mdoc_node *n;
1090 * The Vt macro comes in both ELEM and BLOCK form, both of which
1091 * have different syntaxes (yet more context-sensitive
1092 * behaviour). ELEM types must have a child, which is already
1093 * guaranteed by the in_line parsing routine; BLOCK types,
1094 * specifically the BODY, should only have TEXT children.
1097 if (MDOC_BODY != mdoc->last->type)
1098 return(1);
1100 for (n = mdoc->last->child; n; n = n->next)
1101 if (MDOC_TEXT != n->type)
1102 mdoc_nmsg(mdoc, n, MANDOCERR_CHILD);
1104 return(1);
1108 static int
1109 post_nm(POST_ARGS)
1111 char buf[BUFSIZ];
1112 int c;
1114 /* If no child specified, make sure we have the meta name. */
1116 if (NULL == mdoc->last->child && NULL == mdoc->meta.name) {
1117 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NONAME);
1118 return(1);
1119 } else if (mdoc->meta.name)
1120 return(1);
1122 /* If no meta name, set it from the child. */
1124 buf[0] = '\0';
1125 if (-1 == (c = concat(buf, mdoc->last->child, BUFSIZ))) {
1126 mdoc_nmsg(mdoc, mdoc->last->child, MANDOCERR_MEM);
1127 return(0);
1130 assert(c);
1131 mdoc->meta.name = mandoc_strdup(buf);
1132 return(1);
1135 static int
1136 post_literal(POST_ARGS)
1140 * The `Dl' (note "el" not "one") and `Bd' macros unset the
1141 * MDOC_LITERAL flag as they leave. Note that `Bd' only sets
1142 * this in literal mode, but it doesn't hurt to just switch it
1143 * off in general since displays can't be nested.
1146 if (MDOC_BODY == mdoc->last->type)
1147 mdoc->flags &= ~MDOC_LITERAL;
1149 return(1);
1152 static int
1153 post_defaults(POST_ARGS)
1155 struct mdoc_node *nn;
1158 * The `Ar' defaults to "file ..." if no value is provided as an
1159 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
1160 * gets an empty string.
1163 if (mdoc->last->child)
1164 return(1);
1166 nn = mdoc->last;
1167 mdoc->next = MDOC_NEXT_CHILD;
1169 switch (nn->tok) {
1170 case (MDOC_Ar):
1171 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "file"))
1172 return(0);
1173 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "..."))
1174 return(0);
1175 break;
1176 case (MDOC_At):
1177 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "AT&T"))
1178 return(0);
1179 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "UNIX"))
1180 return(0);
1181 break;
1182 case (MDOC_Li):
1183 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, ""))
1184 return(0);
1185 break;
1186 case (MDOC_Pa):
1187 /* FALLTHROUGH */
1188 case (MDOC_Mt):
1189 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "~"))
1190 return(0);
1191 break;
1192 default:
1193 abort();
1194 /* NOTREACHED */
1197 mdoc->last = nn;
1198 return(1);
1201 static int
1202 post_at(POST_ARGS)
1204 const char *p, *q;
1205 char *buf;
1206 size_t sz;
1209 * If we have a child, look it up in the standard keys. If a
1210 * key exist, use that instead of the child; if it doesn't,
1211 * prefix "AT&T UNIX " to the existing data.
1214 if (NULL == mdoc->last->child)
1215 return(1);
1217 assert(MDOC_TEXT == mdoc->last->child->type);
1218 p = mdoc_a2att(mdoc->last->child->string);
1220 if (p) {
1221 free(mdoc->last->child->string);
1222 mdoc->last->child->string = mandoc_strdup(p);
1223 } else {
1224 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADATT);
1225 p = "AT&T UNIX ";
1226 q = mdoc->last->child->string;
1227 sz = strlen(p) + strlen(q) + 1;
1228 buf = mandoc_malloc(sz);
1229 strlcpy(buf, p, sz);
1230 strlcat(buf, q, sz);
1231 free(mdoc->last->child->string);
1232 mdoc->last->child->string = buf;
1235 return(1);
1238 static int
1239 post_an(POST_ARGS)
1241 struct mdoc_node *np;
1243 np = mdoc->last;
1244 if (AUTH__NONE == np->norm->An.auth) {
1245 if (0 == np->child)
1246 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0);
1247 } else if (np->child)
1248 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0);
1250 return(1);
1254 static int
1255 post_it(POST_ARGS)
1257 int i, cols;
1258 enum mdoc_list lt;
1259 struct mdoc_node *n, *c;
1260 enum mandocerr er;
1262 if (MDOC_BLOCK != mdoc->last->type)
1263 return(1);
1265 n = mdoc->last->parent->parent;
1266 lt = n->norm->Bl.type;
1268 if (LIST__NONE == lt) {
1269 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_LISTTYPE);
1270 return(1);
1273 switch (lt) {
1274 case (LIST_tag):
1275 if (mdoc->last->head->child)
1276 break;
1277 /* FIXME: give this a dummy value. */
1278 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS);
1279 break;
1280 case (LIST_hang):
1281 /* FALLTHROUGH */
1282 case (LIST_ohang):
1283 /* FALLTHROUGH */
1284 case (LIST_inset):
1285 /* FALLTHROUGH */
1286 case (LIST_diag):
1287 if (NULL == mdoc->last->head->child)
1288 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS);
1289 break;
1290 case (LIST_bullet):
1291 /* FALLTHROUGH */
1292 case (LIST_dash):
1293 /* FALLTHROUGH */
1294 case (LIST_enum):
1295 /* FALLTHROUGH */
1296 case (LIST_hyphen):
1297 if (NULL == mdoc->last->body->child)
1298 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY);
1299 /* FALLTHROUGH */
1300 case (LIST_item):
1301 if (mdoc->last->head->child)
1302 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST);
1303 break;
1304 case (LIST_column):
1305 cols = (int)n->norm->Bl.ncols;
1307 assert(NULL == mdoc->last->head->child);
1309 if (NULL == mdoc->last->body->child)
1310 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY);
1312 for (i = 0, c = mdoc->last->child; c; c = c->next)
1313 if (MDOC_BODY == c->type)
1314 i++;
1316 if (i < cols)
1317 er = MANDOCERR_ARGCOUNT;
1318 else if (i == cols || i == cols + 1)
1319 break;
1320 else
1321 er = MANDOCERR_SYNTARGCOUNT;
1323 mandoc_vmsg(er, mdoc->parse, mdoc->last->line,
1324 mdoc->last->pos,
1325 "columns == %d (have %d)", cols, i);
1326 return(MANDOCERR_ARGCOUNT == er);
1327 default:
1328 break;
1331 return(1);
1334 static int
1335 post_bl_block(POST_ARGS)
1337 struct mdoc_node *n;
1340 * These are fairly complicated, so we've broken them into two
1341 * functions. post_bl_block_tag() is called when a -tag is
1342 * specified, but no -width (it must be guessed). The second
1343 * when a -width is specified (macro indicators must be
1344 * rewritten into real lengths).
1347 n = mdoc->last;
1349 if (LIST_tag == n->norm->Bl.type &&
1350 NULL == n->norm->Bl.width) {
1351 if ( ! post_bl_block_tag(mdoc))
1352 return(0);
1353 } else if (NULL != n->norm->Bl.width) {
1354 if ( ! post_bl_block_width(mdoc))
1355 return(0);
1356 } else
1357 return(1);
1359 assert(n->norm->Bl.width);
1360 return(1);
1363 static int
1364 post_bl_block_width(POST_ARGS)
1366 size_t width;
1367 int i;
1368 enum mdoct tok;
1369 struct mdoc_node *n;
1370 char buf[NUMSIZ];
1372 n = mdoc->last;
1375 * Calculate the real width of a list from the -width string,
1376 * which may contain a macro (with a known default width), a
1377 * literal string, or a scaling width.
1379 * If the value to -width is a macro, then we re-write it to be
1380 * the macro's width as set in share/tmac/mdoc/doc-common.
1383 if (0 == strcmp(n->norm->Bl.width, "Ds"))
1384 width = 6;
1385 else if (MDOC_MAX == (tok = mdoc_hash_find(n->norm->Bl.width)))
1386 return(1);
1387 else if (0 == (width = macro2len(tok))) {
1388 mdoc_nmsg(mdoc, n, MANDOCERR_BADWIDTH);
1389 return(1);
1392 /* The value already exists: free and reallocate it. */
1394 assert(n->args);
1396 for (i = 0; i < (int)n->args->argc; i++)
1397 if (MDOC_Width == n->args->argv[i].arg)
1398 break;
1400 assert(i < (int)n->args->argc);
1402 snprintf(buf, NUMSIZ, "%un", (unsigned int)width);
1403 free(n->args->argv[i].value[0]);
1404 n->args->argv[i].value[0] = mandoc_strdup(buf);
1406 /* Set our width! */
1407 n->norm->Bl.width = n->args->argv[i].value[0];
1408 return(1);
1411 static int
1412 post_bl_block_tag(POST_ARGS)
1414 struct mdoc_node *n, *nn;
1415 size_t sz, ssz;
1416 int i;
1417 char buf[NUMSIZ];
1420 * Calculate the -width for a `Bl -tag' list if it hasn't been
1421 * provided. Uses the first head macro. NOTE AGAIN: this is
1422 * ONLY if the -width argument has NOT been provided. See
1423 * post_bl_block_width() for converting the -width string.
1426 sz = 10;
1427 n = mdoc->last;
1429 for (nn = n->body->child; nn; nn = nn->next) {
1430 if (MDOC_It != nn->tok)
1431 continue;
1433 assert(MDOC_BLOCK == nn->type);
1434 nn = nn->head->child;
1436 if (nn == NULL)
1437 break;
1439 if (MDOC_TEXT == nn->type) {
1440 sz = strlen(nn->string) + 1;
1441 break;
1444 if (0 != (ssz = macro2len(nn->tok)))
1445 sz = ssz;
1447 break;
1450 /* Defaults to ten ens. */
1452 snprintf(buf, NUMSIZ, "%un", (unsigned int)sz);
1455 * We have to dynamically add this to the macro's argument list.
1456 * We're guaranteed that a MDOC_Width doesn't already exist.
1459 assert(n->args);
1460 i = (int)(n->args->argc)++;
1462 n->args->argv = mandoc_realloc(n->args->argv,
1463 n->args->argc * sizeof(struct mdoc_argv));
1465 n->args->argv[i].arg = MDOC_Width;
1466 n->args->argv[i].line = n->line;
1467 n->args->argv[i].pos = n->pos;
1468 n->args->argv[i].sz = 1;
1469 n->args->argv[i].value = mandoc_malloc(sizeof(char *));
1470 n->args->argv[i].value[0] = mandoc_strdup(buf);
1472 /* Set our width! */
1473 n->norm->Bl.width = n->args->argv[i].value[0];
1474 return(1);
1478 static int
1479 post_bl_head(POST_ARGS)
1481 struct mdoc_node *np, *nn, *nnp;
1482 int i, j;
1484 if (LIST_column != mdoc->last->norm->Bl.type)
1485 /* FIXME: this should be ERROR class... */
1486 return(hwarn_eq0(mdoc));
1489 * Convert old-style lists, where the column width specifiers
1490 * trail as macro parameters, to the new-style ("normal-form")
1491 * lists where they're argument values following -column.
1494 /* First, disallow both types and allow normal-form. */
1497 * TODO: technically, we can accept both and just merge the two
1498 * lists, but I'll leave that for another day.
1501 if (mdoc->last->norm->Bl.ncols && mdoc->last->nchild) {
1502 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_COLUMNS);
1503 return(0);
1504 } else if (NULL == mdoc->last->child)
1505 return(1);
1507 np = mdoc->last->parent;
1508 assert(np->args);
1510 for (j = 0; j < (int)np->args->argc; j++)
1511 if (MDOC_Column == np->args->argv[j].arg)
1512 break;
1514 assert(j < (int)np->args->argc);
1515 assert(0 == np->args->argv[j].sz);
1518 * Accommodate for new-style groff column syntax. Shuffle the
1519 * child nodes, all of which must be TEXT, as arguments for the
1520 * column field. Then, delete the head children.
1523 np->args->argv[j].sz = (size_t)mdoc->last->nchild;
1524 np->args->argv[j].value = mandoc_malloc
1525 ((size_t)mdoc->last->nchild * sizeof(char *));
1527 mdoc->last->norm->Bl.ncols = np->args->argv[j].sz;
1528 mdoc->last->norm->Bl.cols = (void *)np->args->argv[j].value;
1530 for (i = 0, nn = mdoc->last->child; nn; i++) {
1531 np->args->argv[j].value[i] = nn->string;
1532 nn->string = NULL;
1533 nnp = nn;
1534 nn = nn->next;
1535 mdoc_node_delete(NULL, nnp);
1538 mdoc->last->nchild = 0;
1539 mdoc->last->child = NULL;
1541 return(1);
1544 static int
1545 post_bl(POST_ARGS)
1547 struct mdoc_node *n;
1549 if (MDOC_HEAD == mdoc->last->type)
1550 return(post_bl_head(mdoc));
1551 if (MDOC_BLOCK == mdoc->last->type)
1552 return(post_bl_block(mdoc));
1553 if (MDOC_BODY != mdoc->last->type)
1554 return(1);
1556 for (n = mdoc->last->child; n; n = n->next) {
1557 switch (n->tok) {
1558 case (MDOC_Lp):
1559 /* FALLTHROUGH */
1560 case (MDOC_Pp):
1561 mdoc_nmsg(mdoc, n, MANDOCERR_CHILD);
1562 /* FALLTHROUGH */
1563 case (MDOC_It):
1564 /* FALLTHROUGH */
1565 case (MDOC_Sm):
1566 continue;
1567 default:
1568 break;
1571 mdoc_nmsg(mdoc, n, MANDOCERR_SYNTCHILD);
1572 return(0);
1575 return(1);
1578 static int
1579 ebool(struct mdoc *mdoc)
1582 if (NULL == mdoc->last->child) {
1583 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_MACROEMPTY);
1584 mdoc_node_delete(mdoc, mdoc->last);
1585 return(1);
1587 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1);
1589 assert(MDOC_TEXT == mdoc->last->child->type);
1591 if (0 == strcmp(mdoc->last->child->string, "on"))
1592 return(1);
1593 if (0 == strcmp(mdoc->last->child->string, "off"))
1594 return(1);
1596 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADBOOL);
1597 return(1);
1600 static int
1601 post_root(POST_ARGS)
1603 int erc;
1604 struct mdoc_node *n;
1606 erc = 0;
1608 /* Check that we have a finished prologue. */
1610 if ( ! (MDOC_PBODY & mdoc->flags)) {
1611 erc++;
1612 mdoc_nmsg(mdoc, mdoc->first, MANDOCERR_NODOCPROLOG);
1615 n = mdoc->first;
1616 assert(n);
1618 /* Check that we begin with a proper `Sh'. */
1620 if (NULL == n->child) {
1621 erc++;
1622 mdoc_nmsg(mdoc, n, MANDOCERR_NODOCBODY);
1623 } else if (MDOC_BLOCK != n->child->type ||
1624 MDOC_Sh != n->child->tok) {
1625 erc++;
1626 /* Can this be lifted? See rxdebug.1 for example. */
1627 mdoc_nmsg(mdoc, n, MANDOCERR_NODOCBODY);
1630 return(erc ? 0 : 1);
1633 static int
1634 post_st(POST_ARGS)
1636 struct mdoc_node *ch;
1637 const char *p;
1639 if (NULL == (ch = mdoc->last->child)) {
1640 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_MACROEMPTY);
1641 mdoc_node_delete(mdoc, mdoc->last);
1642 return(1);
1645 assert(MDOC_TEXT == ch->type);
1647 if (NULL == (p = mdoc_a2st(ch->string))) {
1648 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADSTANDARD);
1649 mdoc_node_delete(mdoc, mdoc->last);
1650 } else {
1651 free(ch->string);
1652 ch->string = mandoc_strdup(p);
1655 return(1);
1658 static int
1659 post_rs(POST_ARGS)
1661 struct mdoc_node *nn, *next, *prev;
1662 int i, j;
1664 switch (mdoc->last->type) {
1665 case (MDOC_HEAD):
1666 check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0);
1667 return(1);
1668 case (MDOC_BODY):
1669 if (mdoc->last->child)
1670 break;
1671 check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0);
1672 return(1);
1673 default:
1674 return(1);
1678 * Make sure only certain types of nodes are allowed within the
1679 * the `Rs' body. Delete offending nodes and raise a warning.
1680 * Do this before re-ordering for the sake of clarity.
1683 next = NULL;
1684 for (nn = mdoc->last->child; nn; nn = next) {
1685 for (i = 0; i < RSORD_MAX; i++)
1686 if (nn->tok == rsord[i])
1687 break;
1689 if (i < RSORD_MAX) {
1690 if (MDOC__J == rsord[i] || MDOC__B == rsord[i])
1691 mdoc->last->norm->Rs.quote_T++;
1692 next = nn->next;
1693 continue;
1696 next = nn->next;
1697 mdoc_nmsg(mdoc, nn, MANDOCERR_CHILD);
1698 mdoc_node_delete(mdoc, nn);
1702 * Nothing to sort if only invalid nodes were found
1703 * inside the `Rs' body.
1706 if (NULL == mdoc->last->child)
1707 return(1);
1710 * The full `Rs' block needs special handling to order the
1711 * sub-elements according to `rsord'. Pick through each element
1712 * and correctly order it. This is a insertion sort.
1715 next = NULL;
1716 for (nn = mdoc->last->child->next; nn; nn = next) {
1717 /* Determine order of `nn'. */
1718 for (i = 0; i < RSORD_MAX; i++)
1719 if (rsord[i] == nn->tok)
1720 break;
1723 * Remove `nn' from the chain. This somewhat
1724 * repeats mdoc_node_unlink(), but since we're
1725 * just re-ordering, there's no need for the
1726 * full unlink process.
1729 if (NULL != (next = nn->next))
1730 next->prev = nn->prev;
1732 if (NULL != (prev = nn->prev))
1733 prev->next = nn->next;
1735 nn->prev = nn->next = NULL;
1738 * Scan back until we reach a node that's
1739 * ordered before `nn'.
1742 for ( ; prev ; prev = prev->prev) {
1743 /* Determine order of `prev'. */
1744 for (j = 0; j < RSORD_MAX; j++)
1745 if (rsord[j] == prev->tok)
1746 break;
1748 if (j <= i)
1749 break;
1753 * Set `nn' back into its correct place in front
1754 * of the `prev' node.
1757 nn->prev = prev;
1759 if (prev) {
1760 if (prev->next)
1761 prev->next->prev = nn;
1762 nn->next = prev->next;
1763 prev->next = nn;
1764 } else {
1765 mdoc->last->child->prev = nn;
1766 nn->next = mdoc->last->child;
1767 mdoc->last->child = nn;
1771 return(1);
1774 static int
1775 post_ns(POST_ARGS)
1778 if (MDOC_LINE & mdoc->last->flags)
1779 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNNS);
1780 return(1);
1783 static int
1784 post_sh(POST_ARGS)
1787 if (MDOC_HEAD == mdoc->last->type)
1788 return(post_sh_head(mdoc));
1789 if (MDOC_BODY == mdoc->last->type)
1790 return(post_sh_body(mdoc));
1792 return(1);
1795 static int
1796 post_sh_body(POST_ARGS)
1798 struct mdoc_node *n;
1800 if (SEC_NAME != mdoc->lastsec)
1801 return(1);
1804 * Warn if the NAME section doesn't contain the `Nm' and `Nd'
1805 * macros (can have multiple `Nm' and one `Nd'). Note that the
1806 * children of the BODY declaration can also be "text".
1809 if (NULL == (n = mdoc->last->child)) {
1810 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1811 return(1);
1814 for ( ; n && n->next; n = n->next) {
1815 if (MDOC_ELEM == n->type && MDOC_Nm == n->tok)
1816 continue;
1817 if (MDOC_TEXT == n->type)
1818 continue;
1819 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1822 assert(n);
1823 if (MDOC_BLOCK == n->type && MDOC_Nd == n->tok)
1824 return(1);
1826 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1827 return(1);
1830 static int
1831 post_sh_head(POST_ARGS)
1833 char buf[BUFSIZ];
1834 struct mdoc_node *n;
1835 enum mdoc_sec sec;
1836 int c;
1839 * Process a new section. Sections are either "named" or
1840 * "custom". Custom sections are user-defined, while named ones
1841 * follow a conventional order and may only appear in certain
1842 * manual sections.
1845 sec = SEC_CUSTOM;
1846 buf[0] = '\0';
1847 if (-1 == (c = concat(buf, mdoc->last->child, BUFSIZ))) {
1848 mdoc_nmsg(mdoc, mdoc->last->child, MANDOCERR_MEM);
1849 return(0);
1850 } else if (1 == c)
1851 sec = a2sec(buf);
1853 /* The NAME should be first. */
1855 if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed)
1856 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NAMESECFIRST);
1858 /* The SYNOPSIS gets special attention in other areas. */
1860 if (SEC_SYNOPSIS == sec)
1861 mdoc->flags |= MDOC_SYNOPSIS;
1862 else
1863 mdoc->flags &= ~MDOC_SYNOPSIS;
1865 /* Mark our last section. */
1867 mdoc->lastsec = sec;
1870 * Set the section attribute for the current HEAD, for its
1871 * parent BLOCK, and for the HEAD children; the latter can
1872 * only be TEXT nodes, so no recursion is needed.
1873 * For other blocks and elements, including .Sh BODY, this is
1874 * done when allocating the node data structures, but for .Sh
1875 * BLOCK and HEAD, the section is still unknown at that time.
1878 mdoc->last->parent->sec = sec;
1879 mdoc->last->sec = sec;
1880 for (n = mdoc->last->child; n; n = n->next)
1881 n->sec = sec;
1883 /* We don't care about custom sections after this. */
1885 if (SEC_CUSTOM == sec)
1886 return(1);
1889 * Check whether our non-custom section is being repeated or is
1890 * out of order.
1893 if (sec == mdoc->lastnamed)
1894 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECREP);
1896 if (sec < mdoc->lastnamed)
1897 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECOOO);
1899 /* Mark the last named section. */
1901 mdoc->lastnamed = sec;
1903 /* Check particular section/manual conventions. */
1905 assert(mdoc->meta.msec);
1907 switch (sec) {
1908 case (SEC_RETURN_VALUES):
1909 /* FALLTHROUGH */
1910 case (SEC_ERRORS):
1911 /* FALLTHROUGH */
1912 case (SEC_LIBRARY):
1913 if (*mdoc->meta.msec == '2')
1914 break;
1915 if (*mdoc->meta.msec == '3')
1916 break;
1917 if (*mdoc->meta.msec == '9')
1918 break;
1919 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECMSEC);
1920 break;
1921 default:
1922 break;
1925 return(1);
1928 static int
1929 post_ignpar(POST_ARGS)
1931 struct mdoc_node *np;
1933 if (MDOC_BODY != mdoc->last->type)
1934 return(1);
1936 if (NULL != (np = mdoc->last->child))
1937 if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
1938 mdoc_nmsg(mdoc, np, MANDOCERR_IGNPAR);
1939 mdoc_node_delete(mdoc, np);
1942 if (NULL != (np = mdoc->last->last))
1943 if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
1944 mdoc_nmsg(mdoc, np, MANDOCERR_IGNPAR);
1945 mdoc_node_delete(mdoc, np);
1948 return(1);
1951 static int
1952 pre_par(PRE_ARGS)
1955 if (NULL == mdoc->last)
1956 return(1);
1957 if (MDOC_ELEM != n->type && MDOC_BLOCK != n->type)
1958 return(1);
1961 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
1962 * block: `Lp', `Pp', or non-compact `Bd' or `Bl'.
1965 if (MDOC_Pp != mdoc->last->tok && MDOC_Lp != mdoc->last->tok)
1966 return(1);
1967 if (MDOC_Bl == n->tok && n->norm->Bl.comp)
1968 return(1);
1969 if (MDOC_Bd == n->tok && n->norm->Bd.comp)
1970 return(1);
1971 if (MDOC_It == n->tok && n->parent->norm->Bl.comp)
1972 return(1);
1974 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNPAR);
1975 mdoc_node_delete(mdoc, mdoc->last);
1976 return(1);
1979 static int
1980 pre_literal(PRE_ARGS)
1983 if (MDOC_BODY != n->type)
1984 return(1);
1987 * The `Dl' (note "el" not "one") and `Bd -literal' and `Bd
1988 * -unfilled' macros set MDOC_LITERAL on entrance to the body.
1991 switch (n->tok) {
1992 case (MDOC_Dl):
1993 mdoc->flags |= MDOC_LITERAL;
1994 break;
1995 case (MDOC_Bd):
1996 if (DISP_literal == n->norm->Bd.type)
1997 mdoc->flags |= MDOC_LITERAL;
1998 if (DISP_unfilled == n->norm->Bd.type)
1999 mdoc->flags |= MDOC_LITERAL;
2000 break;
2001 default:
2002 abort();
2003 /* NOTREACHED */
2006 return(1);
2009 static int
2010 post_dd(POST_ARGS)
2012 char buf[DATESIZE];
2013 struct mdoc_node *n;
2014 int c;
2016 if (mdoc->meta.date)
2017 free(mdoc->meta.date);
2019 n = mdoc->last;
2020 if (NULL == n->child || '\0' == n->child->string[0]) {
2021 mdoc->meta.date = mandoc_normdate
2022 (mdoc->parse, NULL, n->line, n->pos);
2023 return(1);
2026 buf[0] = '\0';
2027 if (-1 == (c = concat(buf, n->child, DATESIZE))) {
2028 mdoc_nmsg(mdoc, n->child, MANDOCERR_MEM);
2029 return(0);
2032 assert(c);
2033 mdoc->meta.date = mandoc_normdate
2034 (mdoc->parse, buf, n->line, n->pos);
2036 return(1);
2039 static int
2040 post_dt(POST_ARGS)
2042 struct mdoc_node *nn, *n;
2043 const char *cp;
2044 char *p;
2046 n = mdoc->last;
2048 if (mdoc->meta.title)
2049 free(mdoc->meta.title);
2050 if (mdoc->meta.vol)
2051 free(mdoc->meta.vol);
2052 if (mdoc->meta.arch)
2053 free(mdoc->meta.arch);
2055 mdoc->meta.title = mdoc->meta.vol = mdoc->meta.arch = NULL;
2057 /* First make all characters uppercase. */
2059 if (NULL != (nn = n->child))
2060 for (p = nn->string; *p; p++) {
2061 if (toupper((unsigned char)*p) == *p)
2062 continue;
2065 * FIXME: don't be lazy: have this make all
2066 * characters be uppercase and just warn once.
2068 mdoc_nmsg(mdoc, nn, MANDOCERR_UPPERCASE);
2069 break;
2072 /* Handles: `.Dt'
2073 * --> title = unknown, volume = local, msec = 0, arch = NULL
2076 if (NULL == (nn = n->child)) {
2077 /* XXX: make these macro values. */
2078 /* FIXME: warn about missing values. */
2079 mdoc->meta.title = mandoc_strdup("UNKNOWN");
2080 mdoc->meta.vol = mandoc_strdup("LOCAL");
2081 mdoc->meta.msec = mandoc_strdup("1");
2082 return(1);
2085 /* Handles: `.Dt TITLE'
2086 * --> title = TITLE, volume = local, msec = 0, arch = NULL
2089 mdoc->meta.title = mandoc_strdup
2090 ('\0' == nn->string[0] ? "UNKNOWN" : nn->string);
2092 if (NULL == (nn = nn->next)) {
2093 /* FIXME: warn about missing msec. */
2094 /* XXX: make this a macro value. */
2095 mdoc->meta.vol = mandoc_strdup("LOCAL");
2096 mdoc->meta.msec = mandoc_strdup("1");
2097 return(1);
2100 /* Handles: `.Dt TITLE SEC'
2101 * --> title = TITLE, volume = SEC is msec ?
2102 * format(msec) : SEC,
2103 * msec = SEC is msec ? atoi(msec) : 0,
2104 * arch = NULL
2107 cp = mandoc_a2msec(nn->string);
2108 if (cp) {
2109 mdoc->meta.vol = mandoc_strdup(cp);
2110 mdoc->meta.msec = mandoc_strdup(nn->string);
2111 } else {
2112 mdoc_nmsg(mdoc, n, MANDOCERR_BADMSEC);
2113 mdoc->meta.vol = mandoc_strdup(nn->string);
2114 mdoc->meta.msec = mandoc_strdup(nn->string);
2117 if (NULL == (nn = nn->next))
2118 return(1);
2120 /* Handles: `.Dt TITLE SEC VOL'
2121 * --> title = TITLE, volume = VOL is vol ?
2122 * format(VOL) :
2123 * VOL is arch ? format(arch) :
2124 * VOL
2127 cp = mdoc_a2vol(nn->string);
2128 if (cp) {
2129 free(mdoc->meta.vol);
2130 mdoc->meta.vol = mandoc_strdup(cp);
2131 } else {
2132 /* FIXME: warn about bad arch. */
2133 cp = mdoc_a2arch(nn->string);
2134 if (NULL == cp) {
2135 free(mdoc->meta.vol);
2136 mdoc->meta.vol = mandoc_strdup(nn->string);
2137 } else
2138 mdoc->meta.arch = mandoc_strdup(cp);
2141 /* Ignore any subsequent parameters... */
2142 /* FIXME: warn about subsequent parameters. */
2144 return(1);
2147 static int
2148 post_prol(POST_ARGS)
2151 * Remove prologue macros from the document after they're
2152 * processed. The final document uses mdoc_meta for these
2153 * values and discards the originals.
2156 mdoc_node_delete(mdoc, mdoc->last);
2157 if (mdoc->meta.title && mdoc->meta.date && mdoc->meta.os)
2158 mdoc->flags |= MDOC_PBODY;
2160 return(1);
2163 static int
2164 post_bx(POST_ARGS)
2166 struct mdoc_node *n;
2169 * Make `Bx's second argument always start with an uppercase
2170 * letter. Groff checks if it's an "accepted" term, but we just
2171 * uppercase blindly.
2174 n = mdoc->last->child;
2175 if (n && NULL != (n = n->next))
2176 *n->string = (char)toupper
2177 ((unsigned char)*n->string);
2179 return(1);
2182 static int
2183 post_os(POST_ARGS)
2185 struct mdoc_node *n;
2186 char buf[BUFSIZ];
2187 int c;
2188 #ifndef OSNAME
2189 struct utsname utsname;
2190 #endif
2192 n = mdoc->last;
2195 * Set the operating system by way of the `Os' macro. Note that
2196 * if an argument isn't provided and -DOSNAME="\"foo\"" is
2197 * provided during compilation, this value will be used instead
2198 * of filling in "sysname release" from uname().
2201 if (mdoc->meta.os)
2202 free(mdoc->meta.os);
2204 buf[0] = '\0';
2205 if (-1 == (c = concat(buf, n->child, BUFSIZ))) {
2206 mdoc_nmsg(mdoc, n->child, MANDOCERR_MEM);
2207 return(0);
2210 assert(c);
2212 /* XXX: yes, these can all be dynamically-adjusted buffers, but
2213 * it's really not worth the extra hackery.
2216 if ('\0' == buf[0]) {
2217 #ifdef OSNAME
2218 if (strlcat(buf, OSNAME, BUFSIZ) >= BUFSIZ) {
2219 mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2220 return(0);
2222 #else /*!OSNAME */
2223 if (-1 == uname(&utsname)) {
2224 mdoc_nmsg(mdoc, n, MANDOCERR_UNAME);
2225 mdoc->meta.os = mandoc_strdup("UNKNOWN");
2226 return(post_prol(mdoc));
2229 if (strlcat(buf, utsname.sysname, BUFSIZ) >= BUFSIZ) {
2230 mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2231 return(0);
2233 if (strlcat(buf, " ", BUFSIZ) >= BUFSIZ) {
2234 mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2235 return(0);
2237 if (strlcat(buf, utsname.release, BUFSIZ) >= BUFSIZ) {
2238 mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2239 return(0);
2241 #endif /*!OSNAME*/
2244 mdoc->meta.os = mandoc_strdup(buf);
2245 return(1);
2248 static int
2249 post_std(POST_ARGS)
2251 struct mdoc_node *nn, *n;
2253 n = mdoc->last;
2256 * Macros accepting `-std' as an argument have the name of the
2257 * current document (`Nm') filled in as the argument if it's not
2258 * provided.
2261 if (n->child)
2262 return(1);
2264 if (NULL == mdoc->meta.name)
2265 return(1);
2267 nn = n;
2268 mdoc->next = MDOC_NEXT_CHILD;
2270 if ( ! mdoc_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name))
2271 return(0);
2273 mdoc->last = nn;
2274 return(1);
2278 * Concatenate a node, stopping at the first non-text.
2279 * Concatenation is separated by a single whitespace.
2280 * Returns -1 on fatal (string overrun) error, 0 if child nodes were
2281 * encountered, 1 otherwise.
2283 static int
2284 concat(char *p, const struct mdoc_node *n, size_t sz)
2287 for ( ; NULL != n; n = n->next) {
2288 if (MDOC_TEXT != n->type)
2289 return(0);
2290 if ('\0' != p[0] && strlcat(p, " ", sz) >= sz)
2291 return(-1);
2292 if (strlcat(p, n->string, sz) >= sz)
2293 return(-1);
2294 concat(p, n->child, sz);
2297 return(1);
2300 static enum mdoc_sec
2301 a2sec(const char *p)
2303 int i;
2305 for (i = 0; i < (int)SEC__MAX; i++)
2306 if (secnames[i] && 0 == strcmp(p, secnames[i]))
2307 return((enum mdoc_sec)i);
2309 return(SEC_CUSTOM);
2312 static size_t
2313 macro2len(enum mdoct macro)
2316 switch (macro) {
2317 case(MDOC_Ad):
2318 return(12);
2319 case(MDOC_Ao):
2320 return(12);
2321 case(MDOC_An):
2322 return(12);
2323 case(MDOC_Aq):
2324 return(12);
2325 case(MDOC_Ar):
2326 return(12);
2327 case(MDOC_Bo):
2328 return(12);
2329 case(MDOC_Bq):
2330 return(12);
2331 case(MDOC_Cd):
2332 return(12);
2333 case(MDOC_Cm):
2334 return(10);
2335 case(MDOC_Do):
2336 return(10);
2337 case(MDOC_Dq):
2338 return(12);
2339 case(MDOC_Dv):
2340 return(12);
2341 case(MDOC_Eo):
2342 return(12);
2343 case(MDOC_Em):
2344 return(10);
2345 case(MDOC_Er):
2346 return(17);
2347 case(MDOC_Ev):
2348 return(15);
2349 case(MDOC_Fa):
2350 return(12);
2351 case(MDOC_Fl):
2352 return(10);
2353 case(MDOC_Fo):
2354 return(16);
2355 case(MDOC_Fn):
2356 return(16);
2357 case(MDOC_Ic):
2358 return(10);
2359 case(MDOC_Li):
2360 return(16);
2361 case(MDOC_Ms):
2362 return(6);
2363 case(MDOC_Nm):
2364 return(10);
2365 case(MDOC_No):
2366 return(12);
2367 case(MDOC_Oo):
2368 return(10);
2369 case(MDOC_Op):
2370 return(14);
2371 case(MDOC_Pa):
2372 return(32);
2373 case(MDOC_Pf):
2374 return(12);
2375 case(MDOC_Po):
2376 return(12);
2377 case(MDOC_Pq):
2378 return(12);
2379 case(MDOC_Ql):
2380 return(16);
2381 case(MDOC_Qo):
2382 return(12);
2383 case(MDOC_So):
2384 return(12);
2385 case(MDOC_Sq):
2386 return(12);
2387 case(MDOC_Sy):
2388 return(6);
2389 case(MDOC_Sx):
2390 return(16);
2391 case(MDOC_Tn):
2392 return(10);
2393 case(MDOC_Va):
2394 return(12);
2395 case(MDOC_Vt):
2396 return(12);
2397 case(MDOC_Xr):
2398 return(10);
2399 default:
2400 break;
2402 return(0);