Improve the process for GNU tools
[minix3.git] / external / bsd / mdocml / dist / mdoc_validate.c
blobe879366678a6f14394fd63a1ce4b94162a661419
1 /* Id: mdoc_validate.c,v 1.198 2013/12/15 21:23:52 schwarze Exp */
2 /*
3 * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2010, 2011, 2012, 2013 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_defaults(POST_ARGS);
101 static int post_dd(POST_ARGS);
102 static int post_dt(POST_ARGS);
103 static int post_eoln(POST_ARGS);
104 static int post_hyph(POST_ARGS);
105 static int post_ignpar(POST_ARGS);
106 static int post_it(POST_ARGS);
107 static int post_lb(POST_ARGS);
108 static int post_literal(POST_ARGS);
109 static int post_nm(POST_ARGS);
110 static int post_ns(POST_ARGS);
111 static int post_os(POST_ARGS);
112 static int post_par(POST_ARGS);
113 static int post_prol(POST_ARGS);
114 static int post_root(POST_ARGS);
115 static int post_rs(POST_ARGS);
116 static int post_sh(POST_ARGS);
117 static int post_sh_body(POST_ARGS);
118 static int post_sh_head(POST_ARGS);
119 static int post_st(POST_ARGS);
120 static int post_std(POST_ARGS);
121 static int post_vt(POST_ARGS);
122 static int pre_an(PRE_ARGS);
123 static int pre_bd(PRE_ARGS);
124 static int pre_bl(PRE_ARGS);
125 static int pre_dd(PRE_ARGS);
126 static int pre_display(PRE_ARGS);
127 static int pre_dt(PRE_ARGS);
128 static int pre_it(PRE_ARGS);
129 static int pre_literal(PRE_ARGS);
130 static int pre_os(PRE_ARGS);
131 static int pre_par(PRE_ARGS);
132 static int pre_sh(PRE_ARGS);
133 static int pre_ss(PRE_ARGS);
134 static int pre_std(PRE_ARGS);
136 static v_post posts_an[] = { post_an, NULL };
137 static v_post posts_at[] = { post_at, post_defaults, NULL };
138 static v_post posts_bd[] = { post_literal, hwarn_eq0, bwarn_ge1, NULL };
139 static v_post posts_bf[] = { hwarn_le1, post_bf, NULL };
140 static v_post posts_bk[] = { hwarn_eq0, bwarn_ge1, NULL };
141 static v_post posts_bl[] = { bwarn_ge1, post_bl, NULL };
142 static v_post posts_bx[] = { post_bx, NULL };
143 static v_post posts_bool[] = { ebool, NULL };
144 static v_post posts_eoln[] = { post_eoln, NULL };
145 static v_post posts_defaults[] = { post_defaults, NULL };
146 static v_post posts_d1[] = { bwarn_ge1, post_hyph, NULL };
147 static v_post posts_dd[] = { post_dd, post_prol, NULL };
148 static v_post posts_dl[] = { post_literal, bwarn_ge1, NULL };
149 static v_post posts_dt[] = { post_dt, post_prol, NULL };
150 static v_post posts_fo[] = { hwarn_eq1, bwarn_ge1, NULL };
151 static v_post posts_hyph[] = { post_hyph, NULL };
152 static v_post posts_hyphtext[] = { ewarn_ge1, post_hyph, NULL };
153 static v_post posts_it[] = { post_it, NULL };
154 static v_post posts_lb[] = { post_lb, NULL };
155 static v_post posts_nd[] = { berr_ge1, post_hyph, NULL };
156 static v_post posts_nm[] = { post_nm, NULL };
157 static v_post posts_notext[] = { ewarn_eq0, NULL };
158 static v_post posts_ns[] = { post_ns, NULL };
159 static v_post posts_os[] = { post_os, post_prol, NULL };
160 static v_post posts_pp[] = { post_par, ewarn_eq0, NULL };
161 static v_post posts_rs[] = { post_rs, NULL };
162 static v_post posts_sh[] = { post_ignpar,hwarn_ge1,post_sh,post_hyph,NULL };
163 static v_post posts_sp[] = { post_par, ewarn_le1, NULL };
164 static v_post posts_ss[] = { post_ignpar, hwarn_ge1, post_hyph, NULL };
165 static v_post posts_st[] = { post_st, NULL };
166 static v_post posts_std[] = { post_std, NULL };
167 static v_post posts_text[] = { ewarn_ge1, NULL };
168 static v_post posts_text1[] = { ewarn_eq1, NULL };
169 static v_post posts_vt[] = { post_vt, NULL };
170 static v_pre pres_an[] = { pre_an, NULL };
171 static v_pre pres_bd[] = { pre_display, pre_bd, pre_literal, pre_par, NULL };
172 static v_pre pres_bl[] = { pre_bl, pre_par, NULL };
173 static v_pre pres_d1[] = { pre_display, NULL };
174 static v_pre pres_dl[] = { pre_literal, pre_display, NULL };
175 static v_pre pres_dd[] = { pre_dd, NULL };
176 static v_pre pres_dt[] = { pre_dt, NULL };
177 static v_pre pres_it[] = { pre_it, pre_par, NULL };
178 static v_pre pres_os[] = { pre_os, NULL };
179 static v_pre pres_pp[] = { pre_par, NULL };
180 static v_pre pres_sh[] = { pre_sh, NULL };
181 static v_pre pres_ss[] = { pre_ss, NULL };
182 static v_pre pres_std[] = { pre_std, NULL };
184 static const struct valids mdoc_valids[MDOC_MAX] = {
185 { NULL, NULL }, /* Ap */
186 { pres_dd, posts_dd }, /* Dd */
187 { pres_dt, posts_dt }, /* Dt */
188 { pres_os, posts_os }, /* Os */
189 { pres_sh, posts_sh }, /* Sh */
190 { pres_ss, posts_ss }, /* Ss */
191 { pres_pp, posts_pp }, /* Pp */
192 { pres_d1, posts_d1 }, /* D1 */
193 { pres_dl, posts_dl }, /* Dl */
194 { pres_bd, posts_bd }, /* Bd */
195 { NULL, NULL }, /* Ed */
196 { pres_bl, posts_bl }, /* Bl */
197 { NULL, NULL }, /* El */
198 { pres_it, posts_it }, /* It */
199 { NULL, NULL }, /* Ad */
200 { pres_an, posts_an }, /* An */
201 { NULL, posts_defaults }, /* Ar */
202 { NULL, NULL }, /* Cd */
203 { NULL, NULL }, /* Cm */
204 { NULL, NULL }, /* Dv */
205 { NULL, NULL }, /* Er */
206 { NULL, NULL }, /* Ev */
207 { pres_std, posts_std }, /* Ex */
208 { NULL, NULL }, /* Fa */
209 { NULL, posts_text }, /* Fd */
210 { NULL, NULL }, /* Fl */
211 { NULL, NULL }, /* Fn */
212 { NULL, NULL }, /* Ft */
213 { NULL, NULL }, /* Ic */
214 { NULL, posts_text1 }, /* In */
215 { NULL, posts_defaults }, /* Li */
216 { NULL, posts_nd }, /* Nd */
217 { NULL, posts_nm }, /* Nm */
218 { NULL, NULL }, /* Op */
219 { NULL, NULL }, /* Ot */
220 { NULL, posts_defaults }, /* Pa */
221 { pres_std, posts_std }, /* Rv */
222 { NULL, posts_st }, /* St */
223 { NULL, NULL }, /* Va */
224 { NULL, posts_vt }, /* Vt */
225 { NULL, posts_text }, /* Xr */
226 { NULL, posts_text }, /* %A */
227 { NULL, posts_hyphtext }, /* %B */ /* FIXME: can be used outside Rs/Re. */
228 { NULL, posts_text }, /* %D */
229 { NULL, posts_text }, /* %I */
230 { NULL, posts_text }, /* %J */
231 { NULL, posts_hyphtext }, /* %N */
232 { NULL, posts_hyphtext }, /* %O */
233 { NULL, posts_text }, /* %P */
234 { NULL, posts_hyphtext }, /* %R */
235 { NULL, posts_hyphtext }, /* %T */ /* FIXME: can be used outside Rs/Re. */
236 { NULL, posts_text }, /* %V */
237 { NULL, NULL }, /* Ac */
238 { NULL, NULL }, /* Ao */
239 { NULL, NULL }, /* Aq */
240 { NULL, posts_at }, /* At */
241 { NULL, NULL }, /* Bc */
242 { NULL, posts_bf }, /* Bf */
243 { NULL, NULL }, /* Bo */
244 { NULL, NULL }, /* Bq */
245 { NULL, NULL }, /* Bsx */
246 { NULL, posts_bx }, /* Bx */
247 { NULL, posts_bool }, /* Db */
248 { NULL, NULL }, /* Dc */
249 { NULL, NULL }, /* Do */
250 { NULL, NULL }, /* Dq */
251 { NULL, NULL }, /* Ec */
252 { NULL, NULL }, /* Ef */
253 { NULL, NULL }, /* Em */
254 { NULL, NULL }, /* Eo */
255 { NULL, NULL }, /* Fx */
256 { NULL, NULL }, /* Ms */
257 { NULL, posts_notext }, /* No */
258 { NULL, posts_ns }, /* Ns */
259 { NULL, NULL }, /* Nx */
260 { NULL, NULL }, /* Ox */
261 { NULL, NULL }, /* Pc */
262 { NULL, posts_text1 }, /* Pf */
263 { NULL, NULL }, /* Po */
264 { NULL, NULL }, /* Pq */
265 { NULL, NULL }, /* Qc */
266 { NULL, NULL }, /* Ql */
267 { NULL, NULL }, /* Qo */
268 { NULL, NULL }, /* Qq */
269 { NULL, NULL }, /* Re */
270 { NULL, posts_rs }, /* Rs */
271 { NULL, NULL }, /* Sc */
272 { NULL, NULL }, /* So */
273 { NULL, NULL }, /* Sq */
274 { NULL, posts_bool }, /* Sm */
275 { NULL, posts_hyph }, /* Sx */
276 { NULL, NULL }, /* Sy */
277 { NULL, NULL }, /* Tn */
278 { NULL, NULL }, /* Ux */
279 { NULL, NULL }, /* Xc */
280 { NULL, NULL }, /* Xo */
281 { NULL, posts_fo }, /* Fo */
282 { NULL, NULL }, /* Fc */
283 { NULL, NULL }, /* Oo */
284 { NULL, NULL }, /* Oc */
285 { NULL, posts_bk }, /* Bk */
286 { NULL, NULL }, /* Ek */
287 { NULL, posts_eoln }, /* Bt */
288 { NULL, NULL }, /* Hf */
289 { NULL, NULL }, /* Fr */
290 { NULL, posts_eoln }, /* Ud */
291 { NULL, posts_lb }, /* Lb */
292 { pres_pp, posts_pp }, /* Lp */
293 { NULL, NULL }, /* Lk */
294 { NULL, posts_defaults }, /* Mt */
295 { NULL, NULL }, /* Brq */
296 { NULL, NULL }, /* Bro */
297 { NULL, NULL }, /* Brc */
298 { NULL, posts_text }, /* %C */
299 { NULL, NULL }, /* Es */
300 { NULL, NULL }, /* En */
301 { NULL, NULL }, /* Dx */
302 { NULL, posts_text }, /* %Q */
303 { NULL, posts_pp }, /* br */
304 { NULL, posts_sp }, /* sp */
305 { NULL, posts_text1 }, /* %U */
306 { NULL, NULL }, /* Ta */
309 #define RSORD_MAX 14 /* Number of `Rs' blocks. */
311 static const enum mdoct rsord[RSORD_MAX] = {
312 MDOC__A,
313 MDOC__T,
314 MDOC__B,
315 MDOC__I,
316 MDOC__J,
317 MDOC__R,
318 MDOC__N,
319 MDOC__V,
320 MDOC__U,
321 MDOC__P,
322 MDOC__Q,
323 MDOC__C,
324 MDOC__D,
325 MDOC__O
328 static const char * const secnames[SEC__MAX] = {
329 NULL,
330 "NAME",
331 "LIBRARY",
332 "SYNOPSIS",
333 "DESCRIPTION",
334 "IMPLEMENTATION NOTES",
335 "RETURN VALUES",
336 "ENVIRONMENT",
337 "FILES",
338 "EXIT STATUS",
339 "EXAMPLES",
340 "DIAGNOSTICS",
341 "COMPATIBILITY",
342 "ERRORS",
343 "SEE ALSO",
344 "STANDARDS",
345 "HISTORY",
346 "AUTHORS",
347 "CAVEATS",
348 "BUGS",
349 "SECURITY CONSIDERATIONS",
350 NULL
354 mdoc_valid_pre(struct mdoc *mdoc, struct mdoc_node *n)
356 v_pre *p;
357 int line, pos;
358 char *tp;
360 switch (n->type) {
361 case (MDOC_TEXT):
362 tp = n->string;
363 line = n->line;
364 pos = n->pos;
365 check_text(mdoc, line, pos, tp);
366 /* FALLTHROUGH */
367 case (MDOC_TBL):
368 /* FALLTHROUGH */
369 case (MDOC_EQN):
370 /* FALLTHROUGH */
371 case (MDOC_ROOT):
372 return(1);
373 default:
374 break;
377 check_args(mdoc, n);
379 if (NULL == mdoc_valids[n->tok].pre)
380 return(1);
381 for (p = mdoc_valids[n->tok].pre; *p; p++)
382 if ( ! (*p)(mdoc, n))
383 return(0);
384 return(1);
389 mdoc_valid_post(struct mdoc *mdoc)
391 v_post *p;
393 if (MDOC_VALID & mdoc->last->flags)
394 return(1);
395 mdoc->last->flags |= MDOC_VALID;
397 switch (mdoc->last->type) {
398 case (MDOC_TEXT):
399 /* FALLTHROUGH */
400 case (MDOC_EQN):
401 /* FALLTHROUGH */
402 case (MDOC_TBL):
403 return(1);
404 case (MDOC_ROOT):
405 return(post_root(mdoc));
406 default:
407 break;
410 if (NULL == mdoc_valids[mdoc->last->tok].post)
411 return(1);
412 for (p = mdoc_valids[mdoc->last->tok].post; *p; p++)
413 if ( ! (*p)(mdoc))
414 return(0);
416 return(1);
419 static int
420 check_count(struct mdoc *mdoc, enum mdoc_type type,
421 enum check_lvl lvl, enum check_ineq ineq, int val)
423 const char *p;
424 enum mandocerr t;
426 if (mdoc->last->type != type)
427 return(1);
429 switch (ineq) {
430 case (CHECK_LT):
431 p = "less than ";
432 if (mdoc->last->nchild < val)
433 return(1);
434 break;
435 case (CHECK_GT):
436 p = "more than ";
437 if (mdoc->last->nchild > val)
438 return(1);
439 break;
440 case (CHECK_EQ):
441 p = "";
442 if (val == mdoc->last->nchild)
443 return(1);
444 break;
445 default:
446 abort();
447 /* NOTREACHED */
450 t = lvl == CHECK_WARN ? MANDOCERR_ARGCWARN : MANDOCERR_ARGCOUNT;
451 mandoc_vmsg(t, mdoc->parse, mdoc->last->line, mdoc->last->pos,
452 "want %s%d children (have %d)",
453 p, val, mdoc->last->nchild);
454 return(1);
457 static int
458 berr_ge1(POST_ARGS)
461 return(check_count(mdoc, MDOC_BODY, CHECK_ERROR, CHECK_GT, 0));
464 static int
465 bwarn_ge1(POST_ARGS)
467 return(check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0));
470 static int
471 ewarn_eq0(POST_ARGS)
473 return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0));
476 static int
477 ewarn_eq1(POST_ARGS)
479 return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1));
482 static int
483 ewarn_ge1(POST_ARGS)
485 return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0));
488 static int
489 ewarn_le1(POST_ARGS)
491 return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_LT, 2));
494 static int
495 hwarn_eq0(POST_ARGS)
497 return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0));
500 static int
501 hwarn_eq1(POST_ARGS)
503 return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 1));
506 static int
507 hwarn_ge1(POST_ARGS)
509 return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_GT, 0));
512 static int
513 hwarn_le1(POST_ARGS)
515 return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_LT, 2));
518 static void
519 check_args(struct mdoc *mdoc, struct mdoc_node *n)
521 int i;
523 if (NULL == n->args)
524 return;
526 assert(n->args->argc);
527 for (i = 0; i < (int)n->args->argc; i++)
528 check_argv(mdoc, n, &n->args->argv[i]);
531 static void
532 check_argv(struct mdoc *mdoc, struct mdoc_node *n, struct mdoc_argv *v)
534 int i;
536 for (i = 0; i < (int)v->sz; i++)
537 check_text(mdoc, v->line, v->pos, v->value[i]);
539 /* FIXME: move to post_std(). */
541 if (MDOC_Std == v->arg)
542 if ( ! (v->sz || mdoc->meta.name))
543 mdoc_nmsg(mdoc, n, MANDOCERR_NONAME);
546 static void
547 check_text(struct mdoc *mdoc, int ln, int pos, char *p)
549 char *cp;
551 if (MDOC_LITERAL & mdoc->flags)
552 return;
554 for (cp = p; NULL != (p = strchr(p, '\t')); p++)
555 mdoc_pmsg(mdoc, ln, pos + (int)(p - cp), MANDOCERR_BADTAB);
558 static int
559 check_parent(PRE_ARGS, enum mdoct tok, enum mdoc_type t)
562 assert(n->parent);
563 if ((MDOC_ROOT == t || tok == n->parent->tok) &&
564 (t == n->parent->type))
565 return(1);
567 mandoc_vmsg(MANDOCERR_SYNTCHILD, mdoc->parse, n->line,
568 n->pos, "want parent %s", MDOC_ROOT == t ?
569 "<root>" : mdoc_macronames[tok]);
570 return(0);
574 static int
575 pre_display(PRE_ARGS)
577 struct mdoc_node *node;
579 if (MDOC_BLOCK != n->type)
580 return(1);
582 for (node = mdoc->last->parent; node; node = node->parent)
583 if (MDOC_BLOCK == node->type)
584 if (MDOC_Bd == node->tok)
585 break;
587 if (node)
588 mdoc_nmsg(mdoc, n, MANDOCERR_NESTEDDISP);
590 return(1);
594 static int
595 pre_bl(PRE_ARGS)
597 int i, comp, dup;
598 const char *offs, *width;
599 enum mdoc_list lt;
600 #if !defined(NDEBUG) && defined(__minix)
601 struct mdoc_node *np;
602 #endif /* !defined(NDEBUG) && defined(__minix) */
604 if (MDOC_BLOCK != n->type) {
605 #if !defined(NDEBUG) && defined(__minix)
606 if (ENDBODY_NOT != n->end) {
607 assert(n->pending);
608 np = n->pending->parent;
609 } else
610 np = n->parent;
612 assert(np);
613 assert(MDOC_BLOCK == np->type);
614 assert(MDOC_Bl == np->tok);
615 #endif /* !defined(NDEBUG) && defined(__minix) */
616 return(1);
620 * First figure out which kind of list to use: bind ourselves to
621 * the first mentioned list type and warn about any remaining
622 * ones. If we find no list type, we default to LIST_item.
625 /* LINTED */
626 for (i = 0; n->args && i < (int)n->args->argc; i++) {
627 lt = LIST__NONE;
628 dup = comp = 0;
629 width = offs = NULL;
630 switch (n->args->argv[i].arg) {
631 /* Set list types. */
632 case (MDOC_Bullet):
633 lt = LIST_bullet;
634 break;
635 case (MDOC_Dash):
636 lt = LIST_dash;
637 break;
638 case (MDOC_Enum):
639 lt = LIST_enum;
640 break;
641 case (MDOC_Hyphen):
642 lt = LIST_hyphen;
643 break;
644 case (MDOC_Item):
645 lt = LIST_item;
646 break;
647 case (MDOC_Tag):
648 lt = LIST_tag;
649 break;
650 case (MDOC_Diag):
651 lt = LIST_diag;
652 break;
653 case (MDOC_Hang):
654 lt = LIST_hang;
655 break;
656 case (MDOC_Ohang):
657 lt = LIST_ohang;
658 break;
659 case (MDOC_Inset):
660 lt = LIST_inset;
661 break;
662 case (MDOC_Column):
663 lt = LIST_column;
664 break;
665 /* Set list arguments. */
666 case (MDOC_Compact):
667 dup = n->norm->Bl.comp;
668 comp = 1;
669 break;
670 case (MDOC_Width):
671 /* NB: this can be empty! */
672 if (n->args->argv[i].sz) {
673 width = n->args->argv[i].value[0];
674 dup = (NULL != n->norm->Bl.width);
675 break;
677 mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
678 break;
679 case (MDOC_Offset):
680 /* NB: this can be empty! */
681 if (n->args->argv[i].sz) {
682 offs = n->args->argv[i].value[0];
683 dup = (NULL != n->norm->Bl.offs);
684 break;
686 mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
687 break;
688 default:
689 continue;
692 /* Check: duplicate auxiliary arguments. */
694 if (dup)
695 mdoc_nmsg(mdoc, n, MANDOCERR_ARGVREP);
697 if (comp && ! dup)
698 n->norm->Bl.comp = comp;
699 if (offs && ! dup)
700 n->norm->Bl.offs = offs;
701 if (width && ! dup)
702 n->norm->Bl.width = width;
704 /* Check: multiple list types. */
706 if (LIST__NONE != lt && n->norm->Bl.type != LIST__NONE)
707 mdoc_nmsg(mdoc, n, MANDOCERR_LISTREP);
709 /* Assign list type. */
711 if (LIST__NONE != lt && n->norm->Bl.type == LIST__NONE) {
712 n->norm->Bl.type = lt;
713 /* Set column information, too. */
714 if (LIST_column == lt) {
715 n->norm->Bl.ncols =
716 n->args->argv[i].sz;
717 n->norm->Bl.cols = (void *)
718 n->args->argv[i].value;
722 /* The list type should come first. */
724 if (n->norm->Bl.type == LIST__NONE)
725 if (n->norm->Bl.width ||
726 n->norm->Bl.offs ||
727 n->norm->Bl.comp)
728 mdoc_nmsg(mdoc, n, MANDOCERR_LISTFIRST);
730 continue;
733 /* Allow lists to default to LIST_item. */
735 if (LIST__NONE == n->norm->Bl.type) {
736 mdoc_nmsg(mdoc, n, MANDOCERR_LISTTYPE);
737 n->norm->Bl.type = LIST_item;
741 * Validate the width field. Some list types don't need width
742 * types and should be warned about them. Others should have it
743 * and must also be warned. Yet others have a default and need
744 * no warning.
747 switch (n->norm->Bl.type) {
748 case (LIST_tag):
749 if (NULL == n->norm->Bl.width)
750 mdoc_nmsg(mdoc, n, MANDOCERR_NOWIDTHARG);
751 break;
752 case (LIST_column):
753 /* FALLTHROUGH */
754 case (LIST_diag):
755 /* FALLTHROUGH */
756 case (LIST_ohang):
757 /* FALLTHROUGH */
758 case (LIST_inset):
759 /* FALLTHROUGH */
760 case (LIST_item):
761 if (n->norm->Bl.width)
762 mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
763 break;
764 case (LIST_bullet):
765 /* FALLTHROUGH */
766 case (LIST_dash):
767 /* FALLTHROUGH */
768 case (LIST_hyphen):
769 if (NULL == n->norm->Bl.width)
770 n->norm->Bl.width = "2n";
771 break;
772 case (LIST_enum):
773 if (NULL == n->norm->Bl.width)
774 n->norm->Bl.width = "3n";
775 break;
776 default:
777 break;
780 return(1);
784 static int
785 pre_bd(PRE_ARGS)
787 int i, dup, comp;
788 enum mdoc_disp dt;
789 const char *offs;
790 #if !defined(NDEBUG) && defined(__minix)
791 struct mdoc_node *np;
792 #endif /* !defined(NDEBUG) && defined(__minix) */
794 if (MDOC_BLOCK != n->type) {
795 #if !defined(NDEBUG) && defined(__minix)
796 if (ENDBODY_NOT != n->end) {
797 assert(n->pending);
798 np = n->pending->parent;
799 } else
800 np = n->parent;
802 assert(np);
803 assert(MDOC_BLOCK == np->type);
804 assert(MDOC_Bd == np->tok);
805 #endif /* !defined(NDEBUG) && defined(__minix) */
806 return(1);
809 /* LINTED */
810 for (i = 0; n->args && i < (int)n->args->argc; i++) {
811 dt = DISP__NONE;
812 dup = comp = 0;
813 offs = NULL;
815 switch (n->args->argv[i].arg) {
816 case (MDOC_Centred):
817 dt = DISP_centred;
818 break;
819 case (MDOC_Ragged):
820 dt = DISP_ragged;
821 break;
822 case (MDOC_Unfilled):
823 dt = DISP_unfilled;
824 break;
825 case (MDOC_Filled):
826 dt = DISP_filled;
827 break;
828 case (MDOC_Literal):
829 dt = DISP_literal;
830 break;
831 case (MDOC_File):
832 mdoc_nmsg(mdoc, n, MANDOCERR_BADDISP);
833 return(0);
834 case (MDOC_Offset):
835 /* NB: this can be empty! */
836 if (n->args->argv[i].sz) {
837 offs = n->args->argv[i].value[0];
838 dup = (NULL != n->norm->Bd.offs);
839 break;
841 mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
842 break;
843 case (MDOC_Compact):
844 comp = 1;
845 dup = n->norm->Bd.comp;
846 break;
847 default:
848 abort();
849 /* NOTREACHED */
852 /* Check whether we have duplicates. */
854 if (dup)
855 mdoc_nmsg(mdoc, n, MANDOCERR_ARGVREP);
857 /* Make our auxiliary assignments. */
859 if (offs && ! dup)
860 n->norm->Bd.offs = offs;
861 if (comp && ! dup)
862 n->norm->Bd.comp = comp;
864 /* Check whether a type has already been assigned. */
866 if (DISP__NONE != dt && n->norm->Bd.type != DISP__NONE)
867 mdoc_nmsg(mdoc, n, MANDOCERR_DISPREP);
869 /* Make our type assignment. */
871 if (DISP__NONE != dt && n->norm->Bd.type == DISP__NONE)
872 n->norm->Bd.type = dt;
875 if (DISP__NONE == n->norm->Bd.type) {
876 mdoc_nmsg(mdoc, n, MANDOCERR_DISPTYPE);
877 n->norm->Bd.type = DISP_ragged;
880 return(1);
884 static int
885 pre_ss(PRE_ARGS)
888 if (MDOC_BLOCK != n->type)
889 return(1);
890 return(check_parent(mdoc, n, MDOC_Sh, MDOC_BODY));
894 static int
895 pre_sh(PRE_ARGS)
898 if (MDOC_BLOCK != n->type)
899 return(1);
900 return(check_parent(mdoc, n, MDOC_MAX, MDOC_ROOT));
904 static int
905 pre_it(PRE_ARGS)
908 if (MDOC_BLOCK != n->type)
909 return(1);
911 return(check_parent(mdoc, n, MDOC_Bl, MDOC_BODY));
915 static int
916 pre_an(PRE_ARGS)
918 int i;
920 if (NULL == n->args)
921 return(1);
923 for (i = 1; i < (int)n->args->argc; i++)
924 mdoc_pmsg(mdoc, n->args->argv[i].line,
925 n->args->argv[i].pos, MANDOCERR_IGNARGV);
927 if (MDOC_Split == n->args->argv[0].arg)
928 n->norm->An.auth = AUTH_split;
929 else if (MDOC_Nosplit == n->args->argv[0].arg)
930 n->norm->An.auth = AUTH_nosplit;
931 else
932 abort();
934 return(1);
937 static int
938 pre_std(PRE_ARGS)
941 if (n->args && 1 == n->args->argc)
942 if (MDOC_Std == n->args->argv[0].arg)
943 return(1);
945 mdoc_nmsg(mdoc, n, MANDOCERR_NOARGV);
946 return(1);
949 static int
950 pre_dt(PRE_ARGS)
953 if (NULL == mdoc->meta.date || mdoc->meta.os)
954 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
956 if (mdoc->meta.title)
957 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
959 return(1);
962 static int
963 pre_os(PRE_ARGS)
966 if (NULL == mdoc->meta.title || NULL == mdoc->meta.date)
967 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
969 if (mdoc->meta.os)
970 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
972 return(1);
975 static int
976 pre_dd(PRE_ARGS)
979 if (mdoc->meta.title || mdoc->meta.os)
980 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
982 if (mdoc->meta.date)
983 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
985 return(1);
989 static int
990 post_bf(POST_ARGS)
992 struct mdoc_node *np;
993 enum mdocargt arg;
996 * Unlike other data pointers, these are "housed" by the HEAD
997 * element, which contains the goods.
1000 if (MDOC_HEAD != mdoc->last->type) {
1001 if (ENDBODY_NOT != mdoc->last->end) {
1002 assert(mdoc->last->pending);
1003 np = mdoc->last->pending->parent->head;
1004 } else if (MDOC_BLOCK != mdoc->last->type) {
1005 np = mdoc->last->parent->head;
1006 } else
1007 np = mdoc->last->head;
1009 assert(np);
1010 assert(MDOC_HEAD == np->type);
1011 assert(MDOC_Bf == np->tok);
1012 return(1);
1015 np = mdoc->last;
1016 assert(MDOC_BLOCK == np->parent->type);
1017 assert(MDOC_Bf == np->parent->tok);
1020 * Cannot have both argument and parameter.
1021 * If neither is specified, let it through with a warning.
1024 if (np->parent->args && np->child) {
1025 mdoc_nmsg(mdoc, np, MANDOCERR_SYNTARGVCOUNT);
1026 return(0);
1027 } else if (NULL == np->parent->args && NULL == np->child) {
1028 mdoc_nmsg(mdoc, np, MANDOCERR_FONTTYPE);
1029 return(1);
1032 /* Extract argument into data. */
1034 if (np->parent->args) {
1035 arg = np->parent->args->argv[0].arg;
1036 if (MDOC_Emphasis == arg)
1037 np->norm->Bf.font = FONT_Em;
1038 else if (MDOC_Literal == arg)
1039 np->norm->Bf.font = FONT_Li;
1040 else if (MDOC_Symbolic == arg)
1041 np->norm->Bf.font = FONT_Sy;
1042 else
1043 abort();
1044 return(1);
1047 /* Extract parameter into data. */
1049 if (0 == strcmp(np->child->string, "Em"))
1050 np->norm->Bf.font = FONT_Em;
1051 else if (0 == strcmp(np->child->string, "Li"))
1052 np->norm->Bf.font = FONT_Li;
1053 else if (0 == strcmp(np->child->string, "Sy"))
1054 np->norm->Bf.font = FONT_Sy;
1055 else
1056 mdoc_nmsg(mdoc, np, MANDOCERR_FONTTYPE);
1058 return(1);
1061 static int
1062 post_lb(POST_ARGS)
1064 const char *p;
1065 char *buf;
1066 size_t sz;
1068 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1);
1070 assert(mdoc->last->child);
1071 assert(MDOC_TEXT == mdoc->last->child->type);
1073 p = mdoc_a2lib(mdoc->last->child->string);
1075 /* If lookup ok, replace with table value. */
1077 if (p) {
1078 free(mdoc->last->child->string);
1079 mdoc->last->child->string = mandoc_strdup(p);
1080 return(1);
1083 /* If not, use "library ``xxxx''. */
1085 sz = strlen(mdoc->last->child->string) +
1086 2 + strlen("\\(lqlibrary\\(rq");
1087 buf = mandoc_malloc(sz);
1088 snprintf(buf, sz, "library \\(lq%s\\(rq",
1089 mdoc->last->child->string);
1090 free(mdoc->last->child->string);
1091 mdoc->last->child->string = buf;
1092 return(1);
1095 static int
1096 post_eoln(POST_ARGS)
1099 if (mdoc->last->child)
1100 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST);
1101 return(1);
1105 static int
1106 post_vt(POST_ARGS)
1108 const struct mdoc_node *n;
1111 * The Vt macro comes in both ELEM and BLOCK form, both of which
1112 * have different syntaxes (yet more context-sensitive
1113 * behaviour). ELEM types must have a child, which is already
1114 * guaranteed by the in_line parsing routine; BLOCK types,
1115 * specifically the BODY, should only have TEXT children.
1118 if (MDOC_BODY != mdoc->last->type)
1119 return(1);
1121 for (n = mdoc->last->child; n; n = n->next)
1122 if (MDOC_TEXT != n->type)
1123 mdoc_nmsg(mdoc, n, MANDOCERR_CHILD);
1125 return(1);
1129 static int
1130 post_nm(POST_ARGS)
1132 char buf[BUFSIZ];
1133 int c;
1135 if (NULL != mdoc->meta.name)
1136 return(1);
1138 /* Try to use our children for setting the meta name. */
1140 if (NULL != mdoc->last->child) {
1141 buf[0] = '\0';
1142 c = concat(buf, mdoc->last->child, BUFSIZ);
1143 } else
1144 c = 0;
1146 switch (c) {
1147 case (-1):
1148 mdoc_nmsg(mdoc, mdoc->last->child, MANDOCERR_MEM);
1149 return(0);
1150 case (0):
1151 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NONAME);
1152 mdoc->meta.name = mandoc_strdup("UNKNOWN");
1153 break;
1154 default:
1155 mdoc->meta.name = mandoc_strdup(buf);
1156 break;
1158 return(1);
1161 static int
1162 post_literal(POST_ARGS)
1166 * The `Dl' (note "el" not "one") and `Bd' macros unset the
1167 * MDOC_LITERAL flag as they leave. Note that `Bd' only sets
1168 * this in literal mode, but it doesn't hurt to just switch it
1169 * off in general since displays can't be nested.
1172 if (MDOC_BODY == mdoc->last->type)
1173 mdoc->flags &= ~MDOC_LITERAL;
1175 return(1);
1178 static int
1179 post_defaults(POST_ARGS)
1181 struct mdoc_node *nn;
1184 * The `Ar' defaults to "file ..." if no value is provided as an
1185 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
1186 * gets an empty string.
1189 if (mdoc->last->child)
1190 return(1);
1192 nn = mdoc->last;
1193 mdoc->next = MDOC_NEXT_CHILD;
1195 switch (nn->tok) {
1196 case (MDOC_Ar):
1197 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "file"))
1198 return(0);
1199 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "..."))
1200 return(0);
1201 break;
1202 case (MDOC_At):
1203 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "AT&T"))
1204 return(0);
1205 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "UNIX"))
1206 return(0);
1207 break;
1208 case (MDOC_Li):
1209 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, ""))
1210 return(0);
1211 break;
1212 case (MDOC_Pa):
1213 /* FALLTHROUGH */
1214 case (MDOC_Mt):
1215 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "~"))
1216 return(0);
1217 break;
1218 default:
1219 abort();
1220 /* NOTREACHED */
1223 mdoc->last = nn;
1224 return(1);
1227 static int
1228 post_at(POST_ARGS)
1230 const char *p, *q;
1231 char *buf;
1232 size_t sz;
1235 * If we have a child, look it up in the standard keys. If a
1236 * key exist, use that instead of the child; if it doesn't,
1237 * prefix "AT&T UNIX " to the existing data.
1240 if (NULL == mdoc->last->child)
1241 return(1);
1243 assert(MDOC_TEXT == mdoc->last->child->type);
1244 p = mdoc_a2att(mdoc->last->child->string);
1246 if (p) {
1247 free(mdoc->last->child->string);
1248 mdoc->last->child->string = mandoc_strdup(p);
1249 } else {
1250 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADATT);
1251 p = "AT&T UNIX ";
1252 q = mdoc->last->child->string;
1253 sz = strlen(p) + strlen(q) + 1;
1254 buf = mandoc_malloc(sz);
1255 strlcpy(buf, p, sz);
1256 strlcat(buf, q, sz);
1257 free(mdoc->last->child->string);
1258 mdoc->last->child->string = buf;
1261 return(1);
1264 static int
1265 post_an(POST_ARGS)
1267 struct mdoc_node *np;
1269 np = mdoc->last;
1270 if (AUTH__NONE == np->norm->An.auth) {
1271 if (0 == np->child)
1272 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0);
1273 } else if (np->child)
1274 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0);
1276 return(1);
1280 static int
1281 post_it(POST_ARGS)
1283 int i, cols;
1284 enum mdoc_list lt;
1285 struct mdoc_node *n, *c;
1286 enum mandocerr er;
1288 if (MDOC_BLOCK != mdoc->last->type)
1289 return(1);
1291 n = mdoc->last->parent->parent;
1292 lt = n->norm->Bl.type;
1294 if (LIST__NONE == lt) {
1295 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_LISTTYPE);
1296 return(1);
1299 switch (lt) {
1300 case (LIST_tag):
1301 if (mdoc->last->head->child)
1302 break;
1303 /* FIXME: give this a dummy value. */
1304 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS);
1305 break;
1306 case (LIST_hang):
1307 /* FALLTHROUGH */
1308 case (LIST_ohang):
1309 /* FALLTHROUGH */
1310 case (LIST_inset):
1311 /* FALLTHROUGH */
1312 case (LIST_diag):
1313 if (NULL == mdoc->last->head->child)
1314 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS);
1315 break;
1316 case (LIST_bullet):
1317 /* FALLTHROUGH */
1318 case (LIST_dash):
1319 /* FALLTHROUGH */
1320 case (LIST_enum):
1321 /* FALLTHROUGH */
1322 case (LIST_hyphen):
1323 if (NULL == mdoc->last->body->child)
1324 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY);
1325 /* FALLTHROUGH */
1326 case (LIST_item):
1327 if (mdoc->last->head->child)
1328 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST);
1329 break;
1330 case (LIST_column):
1331 cols = (int)n->norm->Bl.ncols;
1333 assert(NULL == mdoc->last->head->child);
1335 if (NULL == mdoc->last->body->child)
1336 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY);
1338 for (i = 0, c = mdoc->last->child; c; c = c->next)
1339 if (MDOC_BODY == c->type)
1340 i++;
1342 if (i < cols)
1343 er = MANDOCERR_ARGCOUNT;
1344 else if (i == cols || i == cols + 1)
1345 break;
1346 else
1347 er = MANDOCERR_SYNTARGCOUNT;
1349 mandoc_vmsg(er, mdoc->parse, mdoc->last->line,
1350 mdoc->last->pos,
1351 "columns == %d (have %d)", cols, i);
1352 return(MANDOCERR_ARGCOUNT == er);
1353 default:
1354 break;
1357 return(1);
1360 static int
1361 post_bl_block(POST_ARGS)
1363 struct mdoc_node *n, *ni, *nc;
1366 * These are fairly complicated, so we've broken them into two
1367 * functions. post_bl_block_tag() is called when a -tag is
1368 * specified, but no -width (it must be guessed). The second
1369 * when a -width is specified (macro indicators must be
1370 * rewritten into real lengths).
1373 n = mdoc->last;
1375 if (LIST_tag == n->norm->Bl.type &&
1376 NULL == n->norm->Bl.width) {
1377 if ( ! post_bl_block_tag(mdoc))
1378 return(0);
1379 assert(n->norm->Bl.width);
1380 } else if (NULL != n->norm->Bl.width) {
1381 if ( ! post_bl_block_width(mdoc))
1382 return(0);
1383 assert(n->norm->Bl.width);
1386 for (ni = n->body->child; ni; ni = ni->next) {
1387 if (NULL == ni->body)
1388 continue;
1389 nc = ni->body->last;
1390 while (NULL != nc) {
1391 switch (nc->tok) {
1392 case (MDOC_Pp):
1393 /* FALLTHROUGH */
1394 case (MDOC_Lp):
1395 /* FALLTHROUGH */
1396 case (MDOC_br):
1397 break;
1398 default:
1399 nc = NULL;
1400 continue;
1402 if (NULL == ni->next) {
1403 mdoc_nmsg(mdoc, nc, MANDOCERR_MOVEPAR);
1404 if ( ! mdoc_node_relink(mdoc, nc))
1405 return(0);
1406 } else if (0 == n->norm->Bl.comp &&
1407 LIST_column != n->norm->Bl.type) {
1408 mdoc_nmsg(mdoc, nc, MANDOCERR_IGNPAR);
1409 mdoc_node_delete(mdoc, nc);
1410 } else
1411 break;
1412 nc = ni->body->last;
1415 return(1);
1418 static int
1419 post_bl_block_width(POST_ARGS)
1421 size_t width;
1422 int i;
1423 enum mdoct tok;
1424 struct mdoc_node *n;
1425 char buf[NUMSIZ];
1427 n = mdoc->last;
1430 * Calculate the real width of a list from the -width string,
1431 * which may contain a macro (with a known default width), a
1432 * literal string, or a scaling width.
1434 * If the value to -width is a macro, then we re-write it to be
1435 * the macro's width as set in share/tmac/mdoc/doc-common.
1438 if (0 == strcmp(n->norm->Bl.width, "Ds"))
1439 width = 6;
1440 else if (MDOC_MAX == (tok = mdoc_hash_find(n->norm->Bl.width)))
1441 return(1);
1442 else if (0 == (width = macro2len(tok))) {
1443 mdoc_nmsg(mdoc, n, MANDOCERR_BADWIDTH);
1444 return(1);
1447 /* The value already exists: free and reallocate it. */
1449 assert(n->args);
1451 for (i = 0; i < (int)n->args->argc; i++)
1452 if (MDOC_Width == n->args->argv[i].arg)
1453 break;
1455 assert(i < (int)n->args->argc);
1457 snprintf(buf, NUMSIZ, "%un", (unsigned int)width);
1458 free(n->args->argv[i].value[0]);
1459 n->args->argv[i].value[0] = mandoc_strdup(buf);
1461 /* Set our width! */
1462 n->norm->Bl.width = n->args->argv[i].value[0];
1463 return(1);
1466 static int
1467 post_bl_block_tag(POST_ARGS)
1469 struct mdoc_node *n, *nn;
1470 size_t sz, ssz;
1471 int i;
1472 char buf[NUMSIZ];
1475 * Calculate the -width for a `Bl -tag' list if it hasn't been
1476 * provided. Uses the first head macro. NOTE AGAIN: this is
1477 * ONLY if the -width argument has NOT been provided. See
1478 * post_bl_block_width() for converting the -width string.
1481 sz = 10;
1482 n = mdoc->last;
1484 for (nn = n->body->child; nn; nn = nn->next) {
1485 if (MDOC_It != nn->tok)
1486 continue;
1488 assert(MDOC_BLOCK == nn->type);
1489 nn = nn->head->child;
1491 if (nn == NULL)
1492 break;
1494 if (MDOC_TEXT == nn->type) {
1495 sz = strlen(nn->string) + 1;
1496 break;
1499 if (0 != (ssz = macro2len(nn->tok)))
1500 sz = ssz;
1502 break;
1505 /* Defaults to ten ens. */
1507 snprintf(buf, NUMSIZ, "%un", (unsigned int)sz);
1510 * We have to dynamically add this to the macro's argument list.
1511 * We're guaranteed that a MDOC_Width doesn't already exist.
1514 assert(n->args);
1515 i = (int)(n->args->argc)++;
1517 n->args->argv = mandoc_realloc(n->args->argv,
1518 n->args->argc * sizeof(struct mdoc_argv));
1520 n->args->argv[i].arg = MDOC_Width;
1521 n->args->argv[i].line = n->line;
1522 n->args->argv[i].pos = n->pos;
1523 n->args->argv[i].sz = 1;
1524 n->args->argv[i].value = mandoc_malloc(sizeof(char *));
1525 n->args->argv[i].value[0] = mandoc_strdup(buf);
1527 /* Set our width! */
1528 n->norm->Bl.width = n->args->argv[i].value[0];
1529 return(1);
1533 static int
1534 post_bl_head(POST_ARGS)
1536 struct mdoc_node *np, *nn, *nnp;
1537 int i, j;
1539 if (LIST_column != mdoc->last->norm->Bl.type)
1540 /* FIXME: this should be ERROR class... */
1541 return(hwarn_eq0(mdoc));
1544 * Convert old-style lists, where the column width specifiers
1545 * trail as macro parameters, to the new-style ("normal-form")
1546 * lists where they're argument values following -column.
1549 /* First, disallow both types and allow normal-form. */
1552 * TODO: technically, we can accept both and just merge the two
1553 * lists, but I'll leave that for another day.
1556 if (mdoc->last->norm->Bl.ncols && mdoc->last->nchild) {
1557 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_COLUMNS);
1558 return(0);
1559 } else if (NULL == mdoc->last->child)
1560 return(1);
1562 np = mdoc->last->parent;
1563 assert(np->args);
1565 for (j = 0; j < (int)np->args->argc; j++)
1566 if (MDOC_Column == np->args->argv[j].arg)
1567 break;
1569 assert(j < (int)np->args->argc);
1570 assert(0 == np->args->argv[j].sz);
1573 * Accommodate for new-style groff column syntax. Shuffle the
1574 * child nodes, all of which must be TEXT, as arguments for the
1575 * column field. Then, delete the head children.
1578 np->args->argv[j].sz = (size_t)mdoc->last->nchild;
1579 np->args->argv[j].value = mandoc_malloc
1580 ((size_t)mdoc->last->nchild * sizeof(char *));
1582 mdoc->last->norm->Bl.ncols = np->args->argv[j].sz;
1583 mdoc->last->norm->Bl.cols = (void *)np->args->argv[j].value;
1585 for (i = 0, nn = mdoc->last->child; nn; i++) {
1586 np->args->argv[j].value[i] = nn->string;
1587 nn->string = NULL;
1588 nnp = nn;
1589 nn = nn->next;
1590 mdoc_node_delete(NULL, nnp);
1593 mdoc->last->nchild = 0;
1594 mdoc->last->child = NULL;
1596 return(1);
1599 static int
1600 post_bl(POST_ARGS)
1602 struct mdoc_node *nparent, *nprev; /* of the Bl block */
1603 struct mdoc_node *nblock, *nbody; /* of the Bl */
1604 struct mdoc_node *nchild, *nnext; /* of the Bl body */
1606 nbody = mdoc->last;
1607 switch (nbody->type) {
1608 case (MDOC_BLOCK):
1609 return(post_bl_block(mdoc));
1610 case (MDOC_HEAD):
1611 return(post_bl_head(mdoc));
1612 case (MDOC_BODY):
1613 break;
1614 default:
1615 return(1);
1618 nchild = nbody->child;
1619 while (NULL != nchild) {
1620 if (MDOC_It == nchild->tok || MDOC_Sm == nchild->tok) {
1621 nchild = nchild->next;
1622 continue;
1625 mdoc_nmsg(mdoc, nchild, MANDOCERR_CHILD);
1628 * Move the node out of the Bl block.
1629 * First, collect all required node pointers.
1632 nblock = nbody->parent;
1633 nprev = nblock->prev;
1634 nparent = nblock->parent;
1635 nnext = nchild->next;
1638 * Unlink this child.
1641 assert(NULL == nchild->prev);
1642 if (0 == --nbody->nchild) {
1643 nbody->child = NULL;
1644 nbody->last = NULL;
1645 assert(NULL == nnext);
1646 } else {
1647 nbody->child = nnext;
1648 nnext->prev = NULL;
1652 * Relink this child.
1655 nchild->parent = nparent;
1656 nchild->prev = nprev;
1657 nchild->next = nblock;
1659 nblock->prev = nchild;
1660 nparent->nchild++;
1661 if (NULL == nprev)
1662 nparent->child = nchild;
1663 else
1664 nprev->next = nchild;
1666 nchild = nnext;
1669 return(1);
1672 static int
1673 ebool(struct mdoc *mdoc)
1676 if (NULL == mdoc->last->child) {
1677 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_MACROEMPTY);
1678 mdoc_node_delete(mdoc, mdoc->last);
1679 return(1);
1681 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1);
1683 assert(MDOC_TEXT == mdoc->last->child->type);
1685 if (0 == strcmp(mdoc->last->child->string, "on")) {
1686 if (MDOC_Sm == mdoc->last->tok)
1687 mdoc->flags &= ~MDOC_SMOFF;
1688 return(1);
1690 if (0 == strcmp(mdoc->last->child->string, "off")) {
1691 if (MDOC_Sm == mdoc->last->tok)
1692 mdoc->flags |= MDOC_SMOFF;
1693 return(1);
1696 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADBOOL);
1697 return(1);
1700 static int
1701 post_root(POST_ARGS)
1703 int erc;
1704 struct mdoc_node *n;
1706 erc = 0;
1708 /* Check that we have a finished prologue. */
1710 if ( ! (MDOC_PBODY & mdoc->flags)) {
1711 erc++;
1712 mdoc_nmsg(mdoc, mdoc->first, MANDOCERR_NODOCPROLOG);
1715 n = mdoc->first;
1716 assert(n);
1718 /* Check that we begin with a proper `Sh'. */
1720 if (NULL == n->child) {
1721 erc++;
1722 mdoc_nmsg(mdoc, n, MANDOCERR_NODOCBODY);
1723 } else if (MDOC_BLOCK != n->child->type ||
1724 MDOC_Sh != n->child->tok) {
1725 erc++;
1726 /* Can this be lifted? See rxdebug.1 for example. */
1727 mdoc_nmsg(mdoc, n, MANDOCERR_NODOCBODY);
1730 return(erc ? 0 : 1);
1733 static int
1734 post_st(POST_ARGS)
1736 struct mdoc_node *ch;
1737 const char *p;
1739 if (NULL == (ch = mdoc->last->child)) {
1740 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_MACROEMPTY);
1741 mdoc_node_delete(mdoc, mdoc->last);
1742 return(1);
1745 assert(MDOC_TEXT == ch->type);
1747 if (NULL == (p = mdoc_a2st(ch->string))) {
1748 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADSTANDARD);
1749 mdoc_node_delete(mdoc, mdoc->last);
1750 } else {
1751 free(ch->string);
1752 ch->string = mandoc_strdup(p);
1755 return(1);
1758 static int
1759 post_rs(POST_ARGS)
1761 struct mdoc_node *nn, *next, *prev;
1762 int i, j;
1764 switch (mdoc->last->type) {
1765 case (MDOC_HEAD):
1766 check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0);
1767 return(1);
1768 case (MDOC_BODY):
1769 if (mdoc->last->child)
1770 break;
1771 check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0);
1772 return(1);
1773 default:
1774 return(1);
1778 * Make sure only certain types of nodes are allowed within the
1779 * the `Rs' body. Delete offending nodes and raise a warning.
1780 * Do this before re-ordering for the sake of clarity.
1783 next = NULL;
1784 for (nn = mdoc->last->child; nn; nn = next) {
1785 for (i = 0; i < RSORD_MAX; i++)
1786 if (nn->tok == rsord[i])
1787 break;
1789 if (i < RSORD_MAX) {
1790 if (MDOC__J == rsord[i] || MDOC__B == rsord[i])
1791 mdoc->last->norm->Rs.quote_T++;
1792 next = nn->next;
1793 continue;
1796 next = nn->next;
1797 mdoc_nmsg(mdoc, nn, MANDOCERR_CHILD);
1798 mdoc_node_delete(mdoc, nn);
1802 * Nothing to sort if only invalid nodes were found
1803 * inside the `Rs' body.
1806 if (NULL == mdoc->last->child)
1807 return(1);
1810 * The full `Rs' block needs special handling to order the
1811 * sub-elements according to `rsord'. Pick through each element
1812 * and correctly order it. This is a insertion sort.
1815 next = NULL;
1816 for (nn = mdoc->last->child->next; nn; nn = next) {
1817 /* Determine order of `nn'. */
1818 for (i = 0; i < RSORD_MAX; i++)
1819 if (rsord[i] == nn->tok)
1820 break;
1823 * Remove `nn' from the chain. This somewhat
1824 * repeats mdoc_node_unlink(), but since we're
1825 * just re-ordering, there's no need for the
1826 * full unlink process.
1829 if (NULL != (next = nn->next))
1830 next->prev = nn->prev;
1832 if (NULL != (prev = nn->prev))
1833 prev->next = nn->next;
1835 nn->prev = nn->next = NULL;
1838 * Scan back until we reach a node that's
1839 * ordered before `nn'.
1842 for ( ; prev ; prev = prev->prev) {
1843 /* Determine order of `prev'. */
1844 for (j = 0; j < RSORD_MAX; j++)
1845 if (rsord[j] == prev->tok)
1846 break;
1848 if (j <= i)
1849 break;
1853 * Set `nn' back into its correct place in front
1854 * of the `prev' node.
1857 nn->prev = prev;
1859 if (prev) {
1860 if (prev->next)
1861 prev->next->prev = nn;
1862 nn->next = prev->next;
1863 prev->next = nn;
1864 } else {
1865 mdoc->last->child->prev = nn;
1866 nn->next = mdoc->last->child;
1867 mdoc->last->child = nn;
1871 return(1);
1875 * For some arguments of some macros,
1876 * convert all breakable hyphens into ASCII_HYPH.
1878 static int
1879 post_hyph(POST_ARGS)
1881 struct mdoc_node *n, *nch;
1882 char *cp;
1884 n = mdoc->last;
1885 switch (n->type) {
1886 case (MDOC_HEAD):
1887 if (MDOC_Sh == n->tok || MDOC_Ss == n->tok)
1888 break;
1889 return(1);
1890 case (MDOC_BODY):
1891 if (MDOC_D1 == n->tok || MDOC_Nd == n->tok)
1892 break;
1893 return(1);
1894 case (MDOC_ELEM):
1895 break;
1896 default:
1897 return(1);
1900 for (nch = n->child; nch; nch = nch->next) {
1901 if (MDOC_TEXT != nch->type)
1902 continue;
1903 cp = nch->string;
1904 if (3 > strnlen(cp, 3))
1905 continue;
1906 while ('\0' != *(++cp))
1907 if ('-' == *cp &&
1908 isalpha((unsigned char)cp[-1]) &&
1909 isalpha((unsigned char)cp[1]))
1910 *cp = ASCII_HYPH;
1912 return(1);
1915 static int
1916 post_ns(POST_ARGS)
1919 if (MDOC_LINE & mdoc->last->flags)
1920 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNNS);
1921 return(1);
1924 static int
1925 post_sh(POST_ARGS)
1928 if (MDOC_HEAD == mdoc->last->type)
1929 return(post_sh_head(mdoc));
1930 if (MDOC_BODY == mdoc->last->type)
1931 return(post_sh_body(mdoc));
1933 return(1);
1936 static int
1937 post_sh_body(POST_ARGS)
1939 struct mdoc_node *n;
1941 if (SEC_NAME != mdoc->lastsec)
1942 return(1);
1945 * Warn if the NAME section doesn't contain the `Nm' and `Nd'
1946 * macros (can have multiple `Nm' and one `Nd'). Note that the
1947 * children of the BODY declaration can also be "text".
1950 if (NULL == (n = mdoc->last->child)) {
1951 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1952 return(1);
1955 for ( ; n && n->next; n = n->next) {
1956 if (MDOC_ELEM == n->type && MDOC_Nm == n->tok)
1957 continue;
1958 if (MDOC_TEXT == n->type)
1959 continue;
1960 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1963 assert(n);
1964 if (MDOC_BLOCK == n->type && MDOC_Nd == n->tok)
1965 return(1);
1967 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1968 return(1);
1971 static int
1972 post_sh_head(POST_ARGS)
1974 char buf[BUFSIZ];
1975 struct mdoc_node *n;
1976 enum mdoc_sec sec;
1977 int c;
1980 * Process a new section. Sections are either "named" or
1981 * "custom". Custom sections are user-defined, while named ones
1982 * follow a conventional order and may only appear in certain
1983 * manual sections.
1986 sec = SEC_CUSTOM;
1987 buf[0] = '\0';
1988 if (-1 == (c = concat(buf, mdoc->last->child, BUFSIZ))) {
1989 mdoc_nmsg(mdoc, mdoc->last->child, MANDOCERR_MEM);
1990 return(0);
1991 } else if (1 == c)
1992 sec = a2sec(buf);
1994 /* The NAME should be first. */
1996 if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed)
1997 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NAMESECFIRST);
1999 /* The SYNOPSIS gets special attention in other areas. */
2001 if (SEC_SYNOPSIS == sec) {
2002 roff_setreg(mdoc->roff, "nS", 1, '=');
2003 mdoc->flags |= MDOC_SYNOPSIS;
2004 } else {
2005 roff_setreg(mdoc->roff, "nS", 0, '=');
2006 mdoc->flags &= ~MDOC_SYNOPSIS;
2009 /* Mark our last section. */
2011 mdoc->lastsec = sec;
2014 * Set the section attribute for the current HEAD, for its
2015 * parent BLOCK, and for the HEAD children; the latter can
2016 * only be TEXT nodes, so no recursion is needed.
2017 * For other blocks and elements, including .Sh BODY, this is
2018 * done when allocating the node data structures, but for .Sh
2019 * BLOCK and HEAD, the section is still unknown at that time.
2022 mdoc->last->parent->sec = sec;
2023 mdoc->last->sec = sec;
2024 for (n = mdoc->last->child; n; n = n->next)
2025 n->sec = sec;
2027 /* We don't care about custom sections after this. */
2029 if (SEC_CUSTOM == sec)
2030 return(1);
2033 * Check whether our non-custom section is being repeated or is
2034 * out of order.
2037 if (sec == mdoc->lastnamed)
2038 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECREP);
2040 if (sec < mdoc->lastnamed)
2041 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECOOO);
2043 /* Mark the last named section. */
2045 mdoc->lastnamed = sec;
2047 /* Check particular section/manual conventions. */
2049 assert(mdoc->meta.msec);
2051 switch (sec) {
2052 case (SEC_RETURN_VALUES):
2053 /* FALLTHROUGH */
2054 case (SEC_ERRORS):
2055 /* FALLTHROUGH */
2056 case (SEC_LIBRARY):
2057 if (*mdoc->meta.msec == '2')
2058 break;
2059 if (*mdoc->meta.msec == '3')
2060 break;
2061 if (*mdoc->meta.msec == '9')
2062 break;
2063 mandoc_msg(MANDOCERR_SECMSEC, mdoc->parse,
2064 mdoc->last->line, mdoc->last->pos, buf);
2065 break;
2066 default:
2067 break;
2070 return(1);
2073 static int
2074 post_ignpar(POST_ARGS)
2076 struct mdoc_node *np;
2078 if (MDOC_BODY != mdoc->last->type)
2079 return(1);
2081 if (NULL != (np = mdoc->last->child))
2082 if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
2083 mdoc_nmsg(mdoc, np, MANDOCERR_IGNPAR);
2084 mdoc_node_delete(mdoc, np);
2087 if (NULL != (np = mdoc->last->last))
2088 if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
2089 mdoc_nmsg(mdoc, np, MANDOCERR_IGNPAR);
2090 mdoc_node_delete(mdoc, np);
2093 return(1);
2096 static int
2097 pre_par(PRE_ARGS)
2100 if (NULL == mdoc->last)
2101 return(1);
2102 if (MDOC_ELEM != n->type && MDOC_BLOCK != n->type)
2103 return(1);
2106 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
2107 * block: `Lp', `Pp', or non-compact `Bd' or `Bl'.
2110 if (MDOC_Pp != mdoc->last->tok &&
2111 MDOC_Lp != mdoc->last->tok &&
2112 MDOC_br != mdoc->last->tok)
2113 return(1);
2114 if (MDOC_Bl == n->tok && n->norm->Bl.comp)
2115 return(1);
2116 if (MDOC_Bd == n->tok && n->norm->Bd.comp)
2117 return(1);
2118 if (MDOC_It == n->tok && n->parent->norm->Bl.comp)
2119 return(1);
2121 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNPAR);
2122 mdoc_node_delete(mdoc, mdoc->last);
2123 return(1);
2126 static int
2127 post_par(POST_ARGS)
2130 if (MDOC_ELEM != mdoc->last->type &&
2131 MDOC_BLOCK != mdoc->last->type)
2132 return(1);
2134 if (NULL == mdoc->last->prev) {
2135 if (MDOC_Sh != mdoc->last->parent->tok &&
2136 MDOC_Ss != mdoc->last->parent->tok)
2137 return(1);
2138 } else {
2139 if (MDOC_Pp != mdoc->last->prev->tok &&
2140 MDOC_Lp != mdoc->last->prev->tok &&
2141 (MDOC_br != mdoc->last->tok ||
2142 (MDOC_sp != mdoc->last->prev->tok &&
2143 MDOC_br != mdoc->last->prev->tok)))
2144 return(1);
2147 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNPAR);
2148 mdoc_node_delete(mdoc, mdoc->last);
2149 return(1);
2152 static int
2153 pre_literal(PRE_ARGS)
2156 if (MDOC_BODY != n->type)
2157 return(1);
2160 * The `Dl' (note "el" not "one") and `Bd -literal' and `Bd
2161 * -unfilled' macros set MDOC_LITERAL on entrance to the body.
2164 switch (n->tok) {
2165 case (MDOC_Dl):
2166 mdoc->flags |= MDOC_LITERAL;
2167 break;
2168 case (MDOC_Bd):
2169 if (DISP_literal == n->norm->Bd.type)
2170 mdoc->flags |= MDOC_LITERAL;
2171 if (DISP_unfilled == n->norm->Bd.type)
2172 mdoc->flags |= MDOC_LITERAL;
2173 break;
2174 default:
2175 abort();
2176 /* NOTREACHED */
2179 return(1);
2182 static int
2183 post_dd(POST_ARGS)
2185 char buf[DATESIZE];
2186 struct mdoc_node *n;
2187 int c;
2189 if (mdoc->meta.date)
2190 free(mdoc->meta.date);
2192 n = mdoc->last;
2193 if (NULL == n->child || '\0' == n->child->string[0]) {
2194 mdoc->meta.date = mandoc_normdate
2195 (mdoc->parse, NULL, n->line, n->pos);
2196 return(1);
2199 buf[0] = '\0';
2200 if (-1 == (c = concat(buf, n->child, DATESIZE))) {
2201 mdoc_nmsg(mdoc, n->child, MANDOCERR_MEM);
2202 return(0);
2205 assert(c);
2206 mdoc->meta.date = mandoc_normdate
2207 (mdoc->parse, buf, n->line, n->pos);
2209 return(1);
2212 static int
2213 post_dt(POST_ARGS)
2215 struct mdoc_node *nn, *n;
2216 const char *cp;
2217 char *p;
2219 n = mdoc->last;
2221 if (mdoc->meta.title)
2222 free(mdoc->meta.title);
2223 if (mdoc->meta.vol)
2224 free(mdoc->meta.vol);
2225 if (mdoc->meta.arch)
2226 free(mdoc->meta.arch);
2228 mdoc->meta.title = mdoc->meta.vol = mdoc->meta.arch = NULL;
2230 /* First make all characters uppercase. */
2232 if (NULL != (nn = n->child))
2233 for (p = nn->string; *p; p++) {
2234 if (toupper((unsigned char)*p) == *p)
2235 continue;
2238 * FIXME: don't be lazy: have this make all
2239 * characters be uppercase and just warn once.
2241 mdoc_nmsg(mdoc, nn, MANDOCERR_UPPERCASE);
2242 break;
2245 /* Handles: `.Dt'
2246 * --> title = unknown, volume = local, msec = 0, arch = NULL
2249 if (NULL == (nn = n->child)) {
2250 /* XXX: make these macro values. */
2251 /* FIXME: warn about missing values. */
2252 mdoc->meta.title = mandoc_strdup("UNKNOWN");
2253 mdoc->meta.vol = mandoc_strdup("LOCAL");
2254 mdoc->meta.msec = mandoc_strdup("1");
2255 return(1);
2258 /* Handles: `.Dt TITLE'
2259 * --> title = TITLE, volume = local, msec = 0, arch = NULL
2262 mdoc->meta.title = mandoc_strdup
2263 ('\0' == nn->string[0] ? "UNKNOWN" : nn->string);
2265 if (NULL == (nn = nn->next)) {
2266 /* FIXME: warn about missing msec. */
2267 /* XXX: make this a macro value. */
2268 mdoc->meta.vol = mandoc_strdup("LOCAL");
2269 mdoc->meta.msec = mandoc_strdup("1");
2270 return(1);
2273 /* Handles: `.Dt TITLE SEC'
2274 * --> title = TITLE, volume = SEC is msec ?
2275 * format(msec) : SEC,
2276 * msec = SEC is msec ? atoi(msec) : 0,
2277 * arch = NULL
2280 cp = mandoc_a2msec(nn->string);
2281 if (cp) {
2282 mdoc->meta.vol = mandoc_strdup(cp);
2283 mdoc->meta.msec = mandoc_strdup(nn->string);
2284 } else {
2285 mdoc_nmsg(mdoc, n, MANDOCERR_BADMSEC);
2286 mdoc->meta.vol = mandoc_strdup(nn->string);
2287 mdoc->meta.msec = mandoc_strdup(nn->string);
2290 if (NULL == (nn = nn->next))
2291 return(1);
2293 /* Handles: `.Dt TITLE SEC VOL'
2294 * --> title = TITLE, volume = VOL is vol ?
2295 * format(VOL) :
2296 * VOL is arch ? format(arch) :
2297 * VOL
2300 cp = mdoc_a2vol(nn->string);
2301 if (cp) {
2302 free(mdoc->meta.vol);
2303 mdoc->meta.vol = mandoc_strdup(cp);
2304 } else {
2305 cp = mdoc_a2arch(nn->string);
2306 if (NULL == cp) {
2307 mdoc_nmsg(mdoc, nn, MANDOCERR_BADVOLARCH);
2308 free(mdoc->meta.vol);
2309 mdoc->meta.vol = mandoc_strdup(nn->string);
2310 } else
2311 mdoc->meta.arch = mandoc_strdup(cp);
2314 /* Ignore any subsequent parameters... */
2315 /* FIXME: warn about subsequent parameters. */
2317 return(1);
2320 static int
2321 post_prol(POST_ARGS)
2324 * Remove prologue macros from the document after they're
2325 * processed. The final document uses mdoc_meta for these
2326 * values and discards the originals.
2329 mdoc_node_delete(mdoc, mdoc->last);
2330 if (mdoc->meta.title && mdoc->meta.date && mdoc->meta.os)
2331 mdoc->flags |= MDOC_PBODY;
2333 return(1);
2336 static int
2337 post_bx(POST_ARGS)
2339 struct mdoc_node *n;
2342 * Make `Bx's second argument always start with an uppercase
2343 * letter. Groff checks if it's an "accepted" term, but we just
2344 * uppercase blindly.
2347 n = mdoc->last->child;
2348 if (n && NULL != (n = n->next))
2349 *n->string = (char)toupper
2350 ((unsigned char)*n->string);
2352 return(1);
2355 static int
2356 post_os(POST_ARGS)
2358 struct mdoc_node *n;
2359 char buf[BUFSIZ];
2360 int c;
2361 #ifndef OSNAME
2362 struct utsname utsname;
2363 #endif
2365 n = mdoc->last;
2368 * Set the operating system by way of the `Os' macro.
2369 * The order of precedence is:
2370 * 1. the argument of the `Os' macro, unless empty
2371 * 2. the -Ios=foo command line argument, if provided
2372 * 3. -DOSNAME="\"foo\"", if provided during compilation
2373 * 4. "sysname release" from uname(3)
2376 free(mdoc->meta.os);
2378 buf[0] = '\0';
2379 if (-1 == (c = concat(buf, n->child, BUFSIZ))) {
2380 mdoc_nmsg(mdoc, n->child, MANDOCERR_MEM);
2381 return(0);
2384 assert(c);
2386 if ('\0' == buf[0]) {
2387 if (mdoc->defos) {
2388 mdoc->meta.os = mandoc_strdup(mdoc->defos);
2389 return(1);
2391 #ifdef OSNAME
2392 if (strlcat(buf, OSNAME, BUFSIZ) >= BUFSIZ) {
2393 mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2394 return(0);
2396 #else /*!OSNAME */
2397 if (-1 == uname(&utsname)) {
2398 mdoc_nmsg(mdoc, n, MANDOCERR_UNAME);
2399 mdoc->meta.os = mandoc_strdup("UNKNOWN");
2400 return(post_prol(mdoc));
2403 if (strlcat(buf, utsname.sysname, BUFSIZ) >= BUFSIZ) {
2404 mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2405 return(0);
2407 if (strlcat(buf, " ", BUFSIZ) >= BUFSIZ) {
2408 mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2409 return(0);
2411 if (strlcat(buf, utsname.release, BUFSIZ) >= BUFSIZ) {
2412 mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2413 return(0);
2415 #endif /*!OSNAME*/
2418 mdoc->meta.os = mandoc_strdup(buf);
2419 return(1);
2422 static int
2423 post_std(POST_ARGS)
2425 struct mdoc_node *nn, *n;
2427 n = mdoc->last;
2430 * Macros accepting `-std' as an argument have the name of the
2431 * current document (`Nm') filled in as the argument if it's not
2432 * provided.
2435 if (n->child)
2436 return(1);
2438 if (NULL == mdoc->meta.name)
2439 return(1);
2441 nn = n;
2442 mdoc->next = MDOC_NEXT_CHILD;
2444 if ( ! mdoc_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name))
2445 return(0);
2447 mdoc->last = nn;
2448 return(1);
2452 * Concatenate a node, stopping at the first non-text.
2453 * Concatenation is separated by a single whitespace.
2454 * Returns -1 on fatal (string overrun) error, 0 if child nodes were
2455 * encountered, 1 otherwise.
2457 static int
2458 concat(char *p, const struct mdoc_node *n, size_t sz)
2461 for ( ; NULL != n; n = n->next) {
2462 if (MDOC_TEXT != n->type)
2463 return(0);
2464 if ('\0' != p[0] && strlcat(p, " ", sz) >= sz)
2465 return(-1);
2466 if (strlcat(p, n->string, sz) >= sz)
2467 return(-1);
2468 concat(p, n->child, sz);
2471 return(1);
2474 static enum mdoc_sec
2475 a2sec(const char *p)
2477 int i;
2479 for (i = 0; i < (int)SEC__MAX; i++)
2480 if (secnames[i] && 0 == strcmp(p, secnames[i]))
2481 return((enum mdoc_sec)i);
2483 return(SEC_CUSTOM);
2486 static size_t
2487 macro2len(enum mdoct macro)
2490 switch (macro) {
2491 case(MDOC_Ad):
2492 return(12);
2493 case(MDOC_Ao):
2494 return(12);
2495 case(MDOC_An):
2496 return(12);
2497 case(MDOC_Aq):
2498 return(12);
2499 case(MDOC_Ar):
2500 return(12);
2501 case(MDOC_Bo):
2502 return(12);
2503 case(MDOC_Bq):
2504 return(12);
2505 case(MDOC_Cd):
2506 return(12);
2507 case(MDOC_Cm):
2508 return(10);
2509 case(MDOC_Do):
2510 return(10);
2511 case(MDOC_Dq):
2512 return(12);
2513 case(MDOC_Dv):
2514 return(12);
2515 case(MDOC_Eo):
2516 return(12);
2517 case(MDOC_Em):
2518 return(10);
2519 case(MDOC_Er):
2520 return(17);
2521 case(MDOC_Ev):
2522 return(15);
2523 case(MDOC_Fa):
2524 return(12);
2525 case(MDOC_Fl):
2526 return(10);
2527 case(MDOC_Fo):
2528 return(16);
2529 case(MDOC_Fn):
2530 return(16);
2531 case(MDOC_Ic):
2532 return(10);
2533 case(MDOC_Li):
2534 return(16);
2535 case(MDOC_Ms):
2536 return(6);
2537 case(MDOC_Nm):
2538 return(10);
2539 case(MDOC_No):
2540 return(12);
2541 case(MDOC_Oo):
2542 return(10);
2543 case(MDOC_Op):
2544 return(14);
2545 case(MDOC_Pa):
2546 return(32);
2547 case(MDOC_Pf):
2548 return(12);
2549 case(MDOC_Po):
2550 return(12);
2551 case(MDOC_Pq):
2552 return(12);
2553 case(MDOC_Ql):
2554 return(16);
2555 case(MDOC_Qo):
2556 return(12);
2557 case(MDOC_So):
2558 return(12);
2559 case(MDOC_Sq):
2560 return(12);
2561 case(MDOC_Sy):
2562 return(6);
2563 case(MDOC_Sx):
2564 return(16);
2565 case(MDOC_Tn):
2566 return(10);
2567 case(MDOC_Va):
2568 return(12);
2569 case(MDOC_Vt):
2570 return(12);
2571 case(MDOC_Xr):
2572 return(10);
2573 default:
2574 break;
2576 return(0);