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