Correct math and limerick.
[dragonfly.git] / contrib / mdocml / mdoc_validate.c
blobe08f86499a7eb91841b169693f8e3d0d0f9cefe9
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 struct mdoc_node *np;
602 if (MDOC_BLOCK != n->type) {
603 if (ENDBODY_NOT != n->end) {
604 assert(n->pending);
605 np = n->pending->parent;
606 } else
607 np = n->parent;
609 assert(np);
610 assert(MDOC_BLOCK == np->type);
611 assert(MDOC_Bl == np->tok);
612 return(1);
616 * First figure out which kind of list to use: bind ourselves to
617 * the first mentioned list type and warn about any remaining
618 * ones. If we find no list type, we default to LIST_item.
621 /* LINTED */
622 for (i = 0; n->args && i < (int)n->args->argc; i++) {
623 lt = LIST__NONE;
624 dup = comp = 0;
625 width = offs = NULL;
626 switch (n->args->argv[i].arg) {
627 /* Set list types. */
628 case (MDOC_Bullet):
629 lt = LIST_bullet;
630 break;
631 case (MDOC_Dash):
632 lt = LIST_dash;
633 break;
634 case (MDOC_Enum):
635 lt = LIST_enum;
636 break;
637 case (MDOC_Hyphen):
638 lt = LIST_hyphen;
639 break;
640 case (MDOC_Item):
641 lt = LIST_item;
642 break;
643 case (MDOC_Tag):
644 lt = LIST_tag;
645 break;
646 case (MDOC_Diag):
647 lt = LIST_diag;
648 break;
649 case (MDOC_Hang):
650 lt = LIST_hang;
651 break;
652 case (MDOC_Ohang):
653 lt = LIST_ohang;
654 break;
655 case (MDOC_Inset):
656 lt = LIST_inset;
657 break;
658 case (MDOC_Column):
659 lt = LIST_column;
660 break;
661 /* Set list arguments. */
662 case (MDOC_Compact):
663 dup = n->norm->Bl.comp;
664 comp = 1;
665 break;
666 case (MDOC_Width):
667 /* NB: this can be empty! */
668 if (n->args->argv[i].sz) {
669 width = n->args->argv[i].value[0];
670 dup = (NULL != n->norm->Bl.width);
671 break;
673 mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
674 break;
675 case (MDOC_Offset):
676 /* NB: this can be empty! */
677 if (n->args->argv[i].sz) {
678 offs = n->args->argv[i].value[0];
679 dup = (NULL != n->norm->Bl.offs);
680 break;
682 mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
683 break;
684 default:
685 continue;
688 /* Check: duplicate auxiliary arguments. */
690 if (dup)
691 mdoc_nmsg(mdoc, n, MANDOCERR_ARGVREP);
693 if (comp && ! dup)
694 n->norm->Bl.comp = comp;
695 if (offs && ! dup)
696 n->norm->Bl.offs = offs;
697 if (width && ! dup)
698 n->norm->Bl.width = width;
700 /* Check: multiple list types. */
702 if (LIST__NONE != lt && n->norm->Bl.type != LIST__NONE)
703 mdoc_nmsg(mdoc, n, MANDOCERR_LISTREP);
705 /* Assign list type. */
707 if (LIST__NONE != lt && n->norm->Bl.type == LIST__NONE) {
708 n->norm->Bl.type = lt;
709 /* Set column information, too. */
710 if (LIST_column == lt) {
711 n->norm->Bl.ncols =
712 n->args->argv[i].sz;
713 n->norm->Bl.cols = (void *)
714 n->args->argv[i].value;
718 /* The list type should come first. */
720 if (n->norm->Bl.type == LIST__NONE)
721 if (n->norm->Bl.width ||
722 n->norm->Bl.offs ||
723 n->norm->Bl.comp)
724 mdoc_nmsg(mdoc, n, MANDOCERR_LISTFIRST);
726 continue;
729 /* Allow lists to default to LIST_item. */
731 if (LIST__NONE == n->norm->Bl.type) {
732 mdoc_nmsg(mdoc, n, MANDOCERR_LISTTYPE);
733 n->norm->Bl.type = LIST_item;
737 * Validate the width field. Some list types don't need width
738 * types and should be warned about them. Others should have it
739 * and must also be warned. Yet others have a default and need
740 * no warning.
743 switch (n->norm->Bl.type) {
744 case (LIST_tag):
745 if (NULL == n->norm->Bl.width)
746 mdoc_nmsg(mdoc, n, MANDOCERR_NOWIDTHARG);
747 break;
748 case (LIST_column):
749 /* FALLTHROUGH */
750 case (LIST_diag):
751 /* FALLTHROUGH */
752 case (LIST_ohang):
753 /* FALLTHROUGH */
754 case (LIST_inset):
755 /* FALLTHROUGH */
756 case (LIST_item):
757 if (n->norm->Bl.width)
758 mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
759 break;
760 case (LIST_bullet):
761 /* FALLTHROUGH */
762 case (LIST_dash):
763 /* FALLTHROUGH */
764 case (LIST_hyphen):
765 if (NULL == n->norm->Bl.width)
766 n->norm->Bl.width = "2n";
767 break;
768 case (LIST_enum):
769 if (NULL == n->norm->Bl.width)
770 n->norm->Bl.width = "3n";
771 break;
772 default:
773 break;
776 return(1);
780 static int
781 pre_bd(PRE_ARGS)
783 int i, dup, comp;
784 enum mdoc_disp dt;
785 const char *offs;
786 struct mdoc_node *np;
788 if (MDOC_BLOCK != n->type) {
789 if (ENDBODY_NOT != n->end) {
790 assert(n->pending);
791 np = n->pending->parent;
792 } else
793 np = n->parent;
795 assert(np);
796 assert(MDOC_BLOCK == np->type);
797 assert(MDOC_Bd == np->tok);
798 return(1);
801 /* LINTED */
802 for (i = 0; n->args && i < (int)n->args->argc; i++) {
803 dt = DISP__NONE;
804 dup = comp = 0;
805 offs = NULL;
807 switch (n->args->argv[i].arg) {
808 case (MDOC_Centred):
809 dt = DISP_centred;
810 break;
811 case (MDOC_Ragged):
812 dt = DISP_ragged;
813 break;
814 case (MDOC_Unfilled):
815 dt = DISP_unfilled;
816 break;
817 case (MDOC_Filled):
818 dt = DISP_filled;
819 break;
820 case (MDOC_Literal):
821 dt = DISP_literal;
822 break;
823 case (MDOC_File):
824 mdoc_nmsg(mdoc, n, MANDOCERR_BADDISP);
825 return(0);
826 case (MDOC_Offset):
827 /* NB: this can be empty! */
828 if (n->args->argv[i].sz) {
829 offs = n->args->argv[i].value[0];
830 dup = (NULL != n->norm->Bd.offs);
831 break;
833 mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
834 break;
835 case (MDOC_Compact):
836 comp = 1;
837 dup = n->norm->Bd.comp;
838 break;
839 default:
840 abort();
841 /* NOTREACHED */
844 /* Check whether we have duplicates. */
846 if (dup)
847 mdoc_nmsg(mdoc, n, MANDOCERR_ARGVREP);
849 /* Make our auxiliary assignments. */
851 if (offs && ! dup)
852 n->norm->Bd.offs = offs;
853 if (comp && ! dup)
854 n->norm->Bd.comp = comp;
856 /* Check whether a type has already been assigned. */
858 if (DISP__NONE != dt && n->norm->Bd.type != DISP__NONE)
859 mdoc_nmsg(mdoc, n, MANDOCERR_DISPREP);
861 /* Make our type assignment. */
863 if (DISP__NONE != dt && n->norm->Bd.type == DISP__NONE)
864 n->norm->Bd.type = dt;
867 if (DISP__NONE == n->norm->Bd.type) {
868 mdoc_nmsg(mdoc, n, MANDOCERR_DISPTYPE);
869 n->norm->Bd.type = DISP_ragged;
872 return(1);
876 static int
877 pre_ss(PRE_ARGS)
880 if (MDOC_BLOCK != n->type)
881 return(1);
882 return(check_parent(mdoc, n, MDOC_Sh, MDOC_BODY));
886 static int
887 pre_sh(PRE_ARGS)
890 if (MDOC_BLOCK != n->type)
891 return(1);
892 return(check_parent(mdoc, n, MDOC_MAX, MDOC_ROOT));
896 static int
897 pre_it(PRE_ARGS)
900 if (MDOC_BLOCK != n->type)
901 return(1);
903 return(check_parent(mdoc, n, MDOC_Bl, MDOC_BODY));
907 static int
908 pre_an(PRE_ARGS)
910 int i;
912 if (NULL == n->args)
913 return(1);
915 for (i = 1; i < (int)n->args->argc; i++)
916 mdoc_pmsg(mdoc, n->args->argv[i].line,
917 n->args->argv[i].pos, MANDOCERR_IGNARGV);
919 if (MDOC_Split == n->args->argv[0].arg)
920 n->norm->An.auth = AUTH_split;
921 else if (MDOC_Nosplit == n->args->argv[0].arg)
922 n->norm->An.auth = AUTH_nosplit;
923 else
924 abort();
926 return(1);
929 static int
930 pre_std(PRE_ARGS)
933 if (n->args && 1 == n->args->argc)
934 if (MDOC_Std == n->args->argv[0].arg)
935 return(1);
937 mdoc_nmsg(mdoc, n, MANDOCERR_NOARGV);
938 return(1);
941 static int
942 pre_dt(PRE_ARGS)
945 if (NULL == mdoc->meta.date || mdoc->meta.os)
946 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
948 if (mdoc->meta.title)
949 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
951 return(1);
954 static int
955 pre_os(PRE_ARGS)
958 if (NULL == mdoc->meta.title || NULL == mdoc->meta.date)
959 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
961 if (mdoc->meta.os)
962 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
964 return(1);
967 static int
968 pre_dd(PRE_ARGS)
971 if (mdoc->meta.title || mdoc->meta.os)
972 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
974 if (mdoc->meta.date)
975 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
977 return(1);
981 static int
982 post_bf(POST_ARGS)
984 struct mdoc_node *np;
985 enum mdocargt arg;
988 * Unlike other data pointers, these are "housed" by the HEAD
989 * element, which contains the goods.
992 if (MDOC_HEAD != mdoc->last->type) {
993 if (ENDBODY_NOT != mdoc->last->end) {
994 assert(mdoc->last->pending);
995 np = mdoc->last->pending->parent->head;
996 } else if (MDOC_BLOCK != mdoc->last->type) {
997 np = mdoc->last->parent->head;
998 } else
999 np = mdoc->last->head;
1001 assert(np);
1002 assert(MDOC_HEAD == np->type);
1003 assert(MDOC_Bf == np->tok);
1004 return(1);
1007 np = mdoc->last;
1008 assert(MDOC_BLOCK == np->parent->type);
1009 assert(MDOC_Bf == np->parent->tok);
1012 * Cannot have both argument and parameter.
1013 * If neither is specified, let it through with a warning.
1016 if (np->parent->args && np->child) {
1017 mdoc_nmsg(mdoc, np, MANDOCERR_SYNTARGVCOUNT);
1018 return(0);
1019 } else if (NULL == np->parent->args && NULL == np->child) {
1020 mdoc_nmsg(mdoc, np, MANDOCERR_FONTTYPE);
1021 return(1);
1024 /* Extract argument into data. */
1026 if (np->parent->args) {
1027 arg = np->parent->args->argv[0].arg;
1028 if (MDOC_Emphasis == arg)
1029 np->norm->Bf.font = FONT_Em;
1030 else if (MDOC_Literal == arg)
1031 np->norm->Bf.font = FONT_Li;
1032 else if (MDOC_Symbolic == arg)
1033 np->norm->Bf.font = FONT_Sy;
1034 else
1035 abort();
1036 return(1);
1039 /* Extract parameter into data. */
1041 if (0 == strcmp(np->child->string, "Em"))
1042 np->norm->Bf.font = FONT_Em;
1043 else if (0 == strcmp(np->child->string, "Li"))
1044 np->norm->Bf.font = FONT_Li;
1045 else if (0 == strcmp(np->child->string, "Sy"))
1046 np->norm->Bf.font = FONT_Sy;
1047 else
1048 mdoc_nmsg(mdoc, np, MANDOCERR_FONTTYPE);
1050 return(1);
1053 static int
1054 post_lb(POST_ARGS)
1056 const char *p;
1057 char *buf;
1058 size_t sz;
1060 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1);
1062 assert(mdoc->last->child);
1063 assert(MDOC_TEXT == mdoc->last->child->type);
1065 p = mdoc_a2lib(mdoc->last->child->string);
1067 /* If lookup ok, replace with table value. */
1069 if (p) {
1070 free(mdoc->last->child->string);
1071 mdoc->last->child->string = mandoc_strdup(p);
1072 return(1);
1075 /* If not, use "library ``xxxx''. */
1077 sz = strlen(mdoc->last->child->string) +
1078 2 + strlen("\\(lqlibrary\\(rq");
1079 buf = mandoc_malloc(sz);
1080 snprintf(buf, sz, "library \\(lq%s\\(rq",
1081 mdoc->last->child->string);
1082 free(mdoc->last->child->string);
1083 mdoc->last->child->string = buf;
1084 return(1);
1087 static int
1088 post_eoln(POST_ARGS)
1091 if (mdoc->last->child)
1092 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST);
1093 return(1);
1097 static int
1098 post_vt(POST_ARGS)
1100 const struct mdoc_node *n;
1103 * The Vt macro comes in both ELEM and BLOCK form, both of which
1104 * have different syntaxes (yet more context-sensitive
1105 * behaviour). ELEM types must have a child, which is already
1106 * guaranteed by the in_line parsing routine; BLOCK types,
1107 * specifically the BODY, should only have TEXT children.
1110 if (MDOC_BODY != mdoc->last->type)
1111 return(1);
1113 for (n = mdoc->last->child; n; n = n->next)
1114 if (MDOC_TEXT != n->type)
1115 mdoc_nmsg(mdoc, n, MANDOCERR_CHILD);
1117 return(1);
1121 static int
1122 post_nm(POST_ARGS)
1124 char buf[BUFSIZ];
1125 int c;
1127 if (NULL != mdoc->meta.name)
1128 return(1);
1130 /* Try to use our children for setting the meta name. */
1132 if (NULL != mdoc->last->child) {
1133 buf[0] = '\0';
1134 c = concat(buf, mdoc->last->child, BUFSIZ);
1135 } else
1136 c = 0;
1138 switch (c) {
1139 case (-1):
1140 mdoc_nmsg(mdoc, mdoc->last->child, MANDOCERR_MEM);
1141 return(0);
1142 case (0):
1143 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NONAME);
1144 mdoc->meta.name = mandoc_strdup("UNKNOWN");
1145 break;
1146 default:
1147 mdoc->meta.name = mandoc_strdup(buf);
1148 break;
1150 return(1);
1153 static int
1154 post_literal(POST_ARGS)
1158 * The `Dl' (note "el" not "one") and `Bd' macros unset the
1159 * MDOC_LITERAL flag as they leave. Note that `Bd' only sets
1160 * this in literal mode, but it doesn't hurt to just switch it
1161 * off in general since displays can't be nested.
1164 if (MDOC_BODY == mdoc->last->type)
1165 mdoc->flags &= ~MDOC_LITERAL;
1167 return(1);
1170 static int
1171 post_defaults(POST_ARGS)
1173 struct mdoc_node *nn;
1176 * The `Ar' defaults to "file ..." if no value is provided as an
1177 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
1178 * gets an empty string.
1181 if (mdoc->last->child)
1182 return(1);
1184 nn = mdoc->last;
1185 mdoc->next = MDOC_NEXT_CHILD;
1187 switch (nn->tok) {
1188 case (MDOC_Ar):
1189 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "file"))
1190 return(0);
1191 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "..."))
1192 return(0);
1193 break;
1194 case (MDOC_At):
1195 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "AT&T"))
1196 return(0);
1197 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "UNIX"))
1198 return(0);
1199 break;
1200 case (MDOC_Li):
1201 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, ""))
1202 return(0);
1203 break;
1204 case (MDOC_Pa):
1205 /* FALLTHROUGH */
1206 case (MDOC_Mt):
1207 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "~"))
1208 return(0);
1209 break;
1210 default:
1211 abort();
1212 /* NOTREACHED */
1215 mdoc->last = nn;
1216 return(1);
1219 static int
1220 post_at(POST_ARGS)
1222 const char *p, *q;
1223 char *buf;
1224 size_t sz;
1227 * If we have a child, look it up in the standard keys. If a
1228 * key exist, use that instead of the child; if it doesn't,
1229 * prefix "AT&T UNIX " to the existing data.
1232 if (NULL == mdoc->last->child)
1233 return(1);
1235 assert(MDOC_TEXT == mdoc->last->child->type);
1236 p = mdoc_a2att(mdoc->last->child->string);
1238 if (p) {
1239 free(mdoc->last->child->string);
1240 mdoc->last->child->string = mandoc_strdup(p);
1241 } else {
1242 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADATT);
1243 p = "AT&T UNIX ";
1244 q = mdoc->last->child->string;
1245 sz = strlen(p) + strlen(q) + 1;
1246 buf = mandoc_malloc(sz);
1247 strlcpy(buf, p, sz);
1248 strlcat(buf, q, sz);
1249 free(mdoc->last->child->string);
1250 mdoc->last->child->string = buf;
1253 return(1);
1256 static int
1257 post_an(POST_ARGS)
1259 struct mdoc_node *np;
1261 np = mdoc->last;
1262 if (AUTH__NONE == np->norm->An.auth) {
1263 if (0 == np->child)
1264 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0);
1265 } else if (np->child)
1266 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0);
1268 return(1);
1272 static int
1273 post_it(POST_ARGS)
1275 int i, cols;
1276 enum mdoc_list lt;
1277 struct mdoc_node *n, *c;
1278 enum mandocerr er;
1280 if (MDOC_BLOCK != mdoc->last->type)
1281 return(1);
1283 n = mdoc->last->parent->parent;
1284 lt = n->norm->Bl.type;
1286 if (LIST__NONE == lt) {
1287 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_LISTTYPE);
1288 return(1);
1291 switch (lt) {
1292 case (LIST_tag):
1293 if (mdoc->last->head->child)
1294 break;
1295 /* FIXME: give this a dummy value. */
1296 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS);
1297 break;
1298 case (LIST_hang):
1299 /* FALLTHROUGH */
1300 case (LIST_ohang):
1301 /* FALLTHROUGH */
1302 case (LIST_inset):
1303 /* FALLTHROUGH */
1304 case (LIST_diag):
1305 if (NULL == mdoc->last->head->child)
1306 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS);
1307 break;
1308 case (LIST_bullet):
1309 /* FALLTHROUGH */
1310 case (LIST_dash):
1311 /* FALLTHROUGH */
1312 case (LIST_enum):
1313 /* FALLTHROUGH */
1314 case (LIST_hyphen):
1315 if (NULL == mdoc->last->body->child)
1316 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY);
1317 /* FALLTHROUGH */
1318 case (LIST_item):
1319 if (mdoc->last->head->child)
1320 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST);
1321 break;
1322 case (LIST_column):
1323 cols = (int)n->norm->Bl.ncols;
1325 assert(NULL == mdoc->last->head->child);
1327 if (NULL == mdoc->last->body->child)
1328 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY);
1330 for (i = 0, c = mdoc->last->child; c; c = c->next)
1331 if (MDOC_BODY == c->type)
1332 i++;
1334 if (i < cols)
1335 er = MANDOCERR_ARGCOUNT;
1336 else if (i == cols || i == cols + 1)
1337 break;
1338 else
1339 er = MANDOCERR_SYNTARGCOUNT;
1341 mandoc_vmsg(er, mdoc->parse, mdoc->last->line,
1342 mdoc->last->pos,
1343 "columns == %d (have %d)", cols, i);
1344 return(MANDOCERR_ARGCOUNT == er);
1345 default:
1346 break;
1349 return(1);
1352 static int
1353 post_bl_block(POST_ARGS)
1355 struct mdoc_node *n, *ni, *nc;
1358 * These are fairly complicated, so we've broken them into two
1359 * functions. post_bl_block_tag() is called when a -tag is
1360 * specified, but no -width (it must be guessed). The second
1361 * when a -width is specified (macro indicators must be
1362 * rewritten into real lengths).
1365 n = mdoc->last;
1367 if (LIST_tag == n->norm->Bl.type &&
1368 NULL == n->norm->Bl.width) {
1369 if ( ! post_bl_block_tag(mdoc))
1370 return(0);
1371 assert(n->norm->Bl.width);
1372 } else if (NULL != n->norm->Bl.width) {
1373 if ( ! post_bl_block_width(mdoc))
1374 return(0);
1375 assert(n->norm->Bl.width);
1378 for (ni = n->body->child; ni; ni = ni->next) {
1379 if (NULL == ni->body)
1380 continue;
1381 nc = ni->body->last;
1382 while (NULL != nc) {
1383 switch (nc->tok) {
1384 case (MDOC_Pp):
1385 /* FALLTHROUGH */
1386 case (MDOC_Lp):
1387 /* FALLTHROUGH */
1388 case (MDOC_br):
1389 break;
1390 default:
1391 nc = NULL;
1392 continue;
1394 if (NULL == ni->next) {
1395 mdoc_nmsg(mdoc, nc, MANDOCERR_MOVEPAR);
1396 if ( ! mdoc_node_relink(mdoc, nc))
1397 return(0);
1398 } else if (0 == n->norm->Bl.comp &&
1399 LIST_column != n->norm->Bl.type) {
1400 mdoc_nmsg(mdoc, nc, MANDOCERR_IGNPAR);
1401 mdoc_node_delete(mdoc, nc);
1402 } else
1403 break;
1404 nc = ni->body->last;
1407 return(1);
1410 static int
1411 post_bl_block_width(POST_ARGS)
1413 size_t width;
1414 int i;
1415 enum mdoct tok;
1416 struct mdoc_node *n;
1417 char buf[NUMSIZ];
1419 n = mdoc->last;
1422 * Calculate the real width of a list from the -width string,
1423 * which may contain a macro (with a known default width), a
1424 * literal string, or a scaling width.
1426 * If the value to -width is a macro, then we re-write it to be
1427 * the macro's width as set in share/tmac/mdoc/doc-common.
1430 if (0 == strcmp(n->norm->Bl.width, "Ds"))
1431 width = 6;
1432 else if (MDOC_MAX == (tok = mdoc_hash_find(n->norm->Bl.width)))
1433 return(1);
1434 else if (0 == (width = macro2len(tok))) {
1435 mdoc_nmsg(mdoc, n, MANDOCERR_BADWIDTH);
1436 return(1);
1439 /* The value already exists: free and reallocate it. */
1441 assert(n->args);
1443 for (i = 0; i < (int)n->args->argc; i++)
1444 if (MDOC_Width == n->args->argv[i].arg)
1445 break;
1447 assert(i < (int)n->args->argc);
1449 snprintf(buf, NUMSIZ, "%un", (unsigned int)width);
1450 free(n->args->argv[i].value[0]);
1451 n->args->argv[i].value[0] = mandoc_strdup(buf);
1453 /* Set our width! */
1454 n->norm->Bl.width = n->args->argv[i].value[0];
1455 return(1);
1458 static int
1459 post_bl_block_tag(POST_ARGS)
1461 struct mdoc_node *n, *nn;
1462 size_t sz, ssz;
1463 int i;
1464 char buf[NUMSIZ];
1467 * Calculate the -width for a `Bl -tag' list if it hasn't been
1468 * provided. Uses the first head macro. NOTE AGAIN: this is
1469 * ONLY if the -width argument has NOT been provided. See
1470 * post_bl_block_width() for converting the -width string.
1473 sz = 10;
1474 n = mdoc->last;
1476 for (nn = n->body->child; nn; nn = nn->next) {
1477 if (MDOC_It != nn->tok)
1478 continue;
1480 assert(MDOC_BLOCK == nn->type);
1481 nn = nn->head->child;
1483 if (nn == NULL)
1484 break;
1486 if (MDOC_TEXT == nn->type) {
1487 sz = strlen(nn->string) + 1;
1488 break;
1491 if (0 != (ssz = macro2len(nn->tok)))
1492 sz = ssz;
1494 break;
1497 /* Defaults to ten ens. */
1499 snprintf(buf, NUMSIZ, "%un", (unsigned int)sz);
1502 * We have to dynamically add this to the macro's argument list.
1503 * We're guaranteed that a MDOC_Width doesn't already exist.
1506 assert(n->args);
1507 i = (int)(n->args->argc)++;
1509 n->args->argv = mandoc_realloc(n->args->argv,
1510 n->args->argc * sizeof(struct mdoc_argv));
1512 n->args->argv[i].arg = MDOC_Width;
1513 n->args->argv[i].line = n->line;
1514 n->args->argv[i].pos = n->pos;
1515 n->args->argv[i].sz = 1;
1516 n->args->argv[i].value = mandoc_malloc(sizeof(char *));
1517 n->args->argv[i].value[0] = mandoc_strdup(buf);
1519 /* Set our width! */
1520 n->norm->Bl.width = n->args->argv[i].value[0];
1521 return(1);
1525 static int
1526 post_bl_head(POST_ARGS)
1528 struct mdoc_node *np, *nn, *nnp;
1529 int i, j;
1531 if (LIST_column != mdoc->last->norm->Bl.type)
1532 /* FIXME: this should be ERROR class... */
1533 return(hwarn_eq0(mdoc));
1536 * Convert old-style lists, where the column width specifiers
1537 * trail as macro parameters, to the new-style ("normal-form")
1538 * lists where they're argument values following -column.
1541 /* First, disallow both types and allow normal-form. */
1544 * TODO: technically, we can accept both and just merge the two
1545 * lists, but I'll leave that for another day.
1548 if (mdoc->last->norm->Bl.ncols && mdoc->last->nchild) {
1549 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_COLUMNS);
1550 return(0);
1551 } else if (NULL == mdoc->last->child)
1552 return(1);
1554 np = mdoc->last->parent;
1555 assert(np->args);
1557 for (j = 0; j < (int)np->args->argc; j++)
1558 if (MDOC_Column == np->args->argv[j].arg)
1559 break;
1561 assert(j < (int)np->args->argc);
1562 assert(0 == np->args->argv[j].sz);
1565 * Accommodate for new-style groff column syntax. Shuffle the
1566 * child nodes, all of which must be TEXT, as arguments for the
1567 * column field. Then, delete the head children.
1570 np->args->argv[j].sz = (size_t)mdoc->last->nchild;
1571 np->args->argv[j].value = mandoc_malloc
1572 ((size_t)mdoc->last->nchild * sizeof(char *));
1574 mdoc->last->norm->Bl.ncols = np->args->argv[j].sz;
1575 mdoc->last->norm->Bl.cols = (void *)np->args->argv[j].value;
1577 for (i = 0, nn = mdoc->last->child; nn; i++) {
1578 np->args->argv[j].value[i] = nn->string;
1579 nn->string = NULL;
1580 nnp = nn;
1581 nn = nn->next;
1582 mdoc_node_delete(NULL, nnp);
1585 mdoc->last->nchild = 0;
1586 mdoc->last->child = NULL;
1588 return(1);
1591 static int
1592 post_bl(POST_ARGS)
1594 struct mdoc_node *nparent, *nprev; /* of the Bl block */
1595 struct mdoc_node *nblock, *nbody; /* of the Bl */
1596 struct mdoc_node *nchild, *nnext; /* of the Bl body */
1598 nbody = mdoc->last;
1599 switch (nbody->type) {
1600 case (MDOC_BLOCK):
1601 return(post_bl_block(mdoc));
1602 case (MDOC_HEAD):
1603 return(post_bl_head(mdoc));
1604 case (MDOC_BODY):
1605 break;
1606 default:
1607 return(1);
1610 nchild = nbody->child;
1611 while (NULL != nchild) {
1612 if (MDOC_It == nchild->tok || MDOC_Sm == nchild->tok) {
1613 nchild = nchild->next;
1614 continue;
1617 mdoc_nmsg(mdoc, nchild, MANDOCERR_CHILD);
1620 * Move the node out of the Bl block.
1621 * First, collect all required node pointers.
1624 nblock = nbody->parent;
1625 nprev = nblock->prev;
1626 nparent = nblock->parent;
1627 nnext = nchild->next;
1630 * Unlink this child.
1633 assert(NULL == nchild->prev);
1634 if (0 == --nbody->nchild) {
1635 nbody->child = NULL;
1636 nbody->last = NULL;
1637 assert(NULL == nnext);
1638 } else {
1639 nbody->child = nnext;
1640 nnext->prev = NULL;
1644 * Relink this child.
1647 nchild->parent = nparent;
1648 nchild->prev = nprev;
1649 nchild->next = nblock;
1651 nblock->prev = nchild;
1652 nparent->nchild++;
1653 if (NULL == nprev)
1654 nparent->child = nchild;
1655 else
1656 nprev->next = nchild;
1658 nchild = nnext;
1661 return(1);
1664 static int
1665 ebool(struct mdoc *mdoc)
1668 if (NULL == mdoc->last->child) {
1669 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_MACROEMPTY);
1670 mdoc_node_delete(mdoc, mdoc->last);
1671 return(1);
1673 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1);
1675 assert(MDOC_TEXT == mdoc->last->child->type);
1677 if (0 == strcmp(mdoc->last->child->string, "on")) {
1678 if (MDOC_Sm == mdoc->last->tok)
1679 mdoc->flags &= ~MDOC_SMOFF;
1680 return(1);
1682 if (0 == strcmp(mdoc->last->child->string, "off")) {
1683 if (MDOC_Sm == mdoc->last->tok)
1684 mdoc->flags |= MDOC_SMOFF;
1685 return(1);
1688 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADBOOL);
1689 return(1);
1692 static int
1693 post_root(POST_ARGS)
1695 int erc;
1696 struct mdoc_node *n;
1698 erc = 0;
1700 /* Check that we have a finished prologue. */
1702 if ( ! (MDOC_PBODY & mdoc->flags)) {
1703 erc++;
1704 mdoc_nmsg(mdoc, mdoc->first, MANDOCERR_NODOCPROLOG);
1707 n = mdoc->first;
1708 assert(n);
1710 /* Check that we begin with a proper `Sh'. */
1712 if (NULL == n->child) {
1713 erc++;
1714 mdoc_nmsg(mdoc, n, MANDOCERR_NODOCBODY);
1715 } else if (MDOC_BLOCK != n->child->type ||
1716 MDOC_Sh != n->child->tok) {
1717 erc++;
1718 /* Can this be lifted? See rxdebug.1 for example. */
1719 mdoc_nmsg(mdoc, n, MANDOCERR_NODOCBODY);
1722 return(erc ? 0 : 1);
1725 static int
1726 post_st(POST_ARGS)
1728 struct mdoc_node *ch;
1729 const char *p;
1731 if (NULL == (ch = mdoc->last->child)) {
1732 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_MACROEMPTY);
1733 mdoc_node_delete(mdoc, mdoc->last);
1734 return(1);
1737 assert(MDOC_TEXT == ch->type);
1739 if (NULL == (p = mdoc_a2st(ch->string))) {
1740 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADSTANDARD);
1741 mdoc_node_delete(mdoc, mdoc->last);
1742 } else {
1743 free(ch->string);
1744 ch->string = mandoc_strdup(p);
1747 return(1);
1750 static int
1751 post_rs(POST_ARGS)
1753 struct mdoc_node *nn, *next, *prev;
1754 int i, j;
1756 switch (mdoc->last->type) {
1757 case (MDOC_HEAD):
1758 check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0);
1759 return(1);
1760 case (MDOC_BODY):
1761 if (mdoc->last->child)
1762 break;
1763 check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0);
1764 return(1);
1765 default:
1766 return(1);
1770 * Make sure only certain types of nodes are allowed within the
1771 * the `Rs' body. Delete offending nodes and raise a warning.
1772 * Do this before re-ordering for the sake of clarity.
1775 next = NULL;
1776 for (nn = mdoc->last->child; nn; nn = next) {
1777 for (i = 0; i < RSORD_MAX; i++)
1778 if (nn->tok == rsord[i])
1779 break;
1781 if (i < RSORD_MAX) {
1782 if (MDOC__J == rsord[i] || MDOC__B == rsord[i])
1783 mdoc->last->norm->Rs.quote_T++;
1784 next = nn->next;
1785 continue;
1788 next = nn->next;
1789 mdoc_nmsg(mdoc, nn, MANDOCERR_CHILD);
1790 mdoc_node_delete(mdoc, nn);
1794 * Nothing to sort if only invalid nodes were found
1795 * inside the `Rs' body.
1798 if (NULL == mdoc->last->child)
1799 return(1);
1802 * The full `Rs' block needs special handling to order the
1803 * sub-elements according to `rsord'. Pick through each element
1804 * and correctly order it. This is a insertion sort.
1807 next = NULL;
1808 for (nn = mdoc->last->child->next; nn; nn = next) {
1809 /* Determine order of `nn'. */
1810 for (i = 0; i < RSORD_MAX; i++)
1811 if (rsord[i] == nn->tok)
1812 break;
1815 * Remove `nn' from the chain. This somewhat
1816 * repeats mdoc_node_unlink(), but since we're
1817 * just re-ordering, there's no need for the
1818 * full unlink process.
1821 if (NULL != (next = nn->next))
1822 next->prev = nn->prev;
1824 if (NULL != (prev = nn->prev))
1825 prev->next = nn->next;
1827 nn->prev = nn->next = NULL;
1830 * Scan back until we reach a node that's
1831 * ordered before `nn'.
1834 for ( ; prev ; prev = prev->prev) {
1835 /* Determine order of `prev'. */
1836 for (j = 0; j < RSORD_MAX; j++)
1837 if (rsord[j] == prev->tok)
1838 break;
1840 if (j <= i)
1841 break;
1845 * Set `nn' back into its correct place in front
1846 * of the `prev' node.
1849 nn->prev = prev;
1851 if (prev) {
1852 if (prev->next)
1853 prev->next->prev = nn;
1854 nn->next = prev->next;
1855 prev->next = nn;
1856 } else {
1857 mdoc->last->child->prev = nn;
1858 nn->next = mdoc->last->child;
1859 mdoc->last->child = nn;
1863 return(1);
1867 * For some arguments of some macros,
1868 * convert all breakable hyphens into ASCII_HYPH.
1870 static int
1871 post_hyph(POST_ARGS)
1873 struct mdoc_node *n, *nch;
1874 char *cp;
1876 n = mdoc->last;
1877 switch (n->type) {
1878 case (MDOC_HEAD):
1879 if (MDOC_Sh == n->tok || MDOC_Ss == n->tok)
1880 break;
1881 return(1);
1882 case (MDOC_BODY):
1883 if (MDOC_D1 == n->tok || MDOC_Nd == n->tok)
1884 break;
1885 return(1);
1886 case (MDOC_ELEM):
1887 break;
1888 default:
1889 return(1);
1892 for (nch = n->child; nch; nch = nch->next) {
1893 if (MDOC_TEXT != nch->type)
1894 continue;
1895 cp = nch->string;
1896 if (3 > strnlen(cp, 3))
1897 continue;
1898 while ('\0' != *(++cp))
1899 if ('-' == *cp &&
1900 isalpha((unsigned char)cp[-1]) &&
1901 isalpha((unsigned char)cp[1]))
1902 *cp = ASCII_HYPH;
1904 return(1);
1907 static int
1908 post_ns(POST_ARGS)
1911 if (MDOC_LINE & mdoc->last->flags)
1912 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNNS);
1913 return(1);
1916 static int
1917 post_sh(POST_ARGS)
1920 if (MDOC_HEAD == mdoc->last->type)
1921 return(post_sh_head(mdoc));
1922 if (MDOC_BODY == mdoc->last->type)
1923 return(post_sh_body(mdoc));
1925 return(1);
1928 static int
1929 post_sh_body(POST_ARGS)
1931 struct mdoc_node *n;
1933 if (SEC_NAME != mdoc->lastsec)
1934 return(1);
1937 * Warn if the NAME section doesn't contain the `Nm' and `Nd'
1938 * macros (can have multiple `Nm' and one `Nd'). Note that the
1939 * children of the BODY declaration can also be "text".
1942 if (NULL == (n = mdoc->last->child)) {
1943 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1944 return(1);
1947 for ( ; n && n->next; n = n->next) {
1948 if (MDOC_ELEM == n->type && MDOC_Nm == n->tok)
1949 continue;
1950 if (MDOC_TEXT == n->type)
1951 continue;
1952 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1955 assert(n);
1956 if (MDOC_BLOCK == n->type && MDOC_Nd == n->tok)
1957 return(1);
1959 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1960 return(1);
1963 static int
1964 post_sh_head(POST_ARGS)
1966 char buf[BUFSIZ];
1967 struct mdoc_node *n;
1968 enum mdoc_sec sec;
1969 int c;
1972 * Process a new section. Sections are either "named" or
1973 * "custom". Custom sections are user-defined, while named ones
1974 * follow a conventional order and may only appear in certain
1975 * manual sections.
1978 sec = SEC_CUSTOM;
1979 buf[0] = '\0';
1980 if (-1 == (c = concat(buf, mdoc->last->child, BUFSIZ))) {
1981 mdoc_nmsg(mdoc, mdoc->last->child, MANDOCERR_MEM);
1982 return(0);
1983 } else if (1 == c)
1984 sec = a2sec(buf);
1986 /* The NAME should be first. */
1988 if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed)
1989 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NAMESECFIRST);
1991 /* The SYNOPSIS gets special attention in other areas. */
1993 if (SEC_SYNOPSIS == sec) {
1994 roff_setreg(mdoc->roff, "nS", 1, '=');
1995 mdoc->flags |= MDOC_SYNOPSIS;
1996 } else {
1997 roff_setreg(mdoc->roff, "nS", 0, '=');
1998 mdoc->flags &= ~MDOC_SYNOPSIS;
2001 /* Mark our last section. */
2003 mdoc->lastsec = sec;
2006 * Set the section attribute for the current HEAD, for its
2007 * parent BLOCK, and for the HEAD children; the latter can
2008 * only be TEXT nodes, so no recursion is needed.
2009 * For other blocks and elements, including .Sh BODY, this is
2010 * done when allocating the node data structures, but for .Sh
2011 * BLOCK and HEAD, the section is still unknown at that time.
2014 mdoc->last->parent->sec = sec;
2015 mdoc->last->sec = sec;
2016 for (n = mdoc->last->child; n; n = n->next)
2017 n->sec = sec;
2019 /* We don't care about custom sections after this. */
2021 if (SEC_CUSTOM == sec)
2022 return(1);
2025 * Check whether our non-custom section is being repeated or is
2026 * out of order.
2029 if (sec == mdoc->lastnamed)
2030 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECREP);
2032 if (sec < mdoc->lastnamed)
2033 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECOOO);
2035 /* Mark the last named section. */
2037 mdoc->lastnamed = sec;
2039 /* Check particular section/manual conventions. */
2041 assert(mdoc->meta.msec);
2043 switch (sec) {
2044 case (SEC_ERRORS):
2045 if (*mdoc->meta.msec == '4')
2046 break;
2047 /* FALLTHROUGH */
2048 case (SEC_RETURN_VALUES):
2049 /* FALLTHROUGH */
2050 case (SEC_LIBRARY):
2051 if (*mdoc->meta.msec == '2')
2052 break;
2053 if (*mdoc->meta.msec == '3')
2054 break;
2055 if (*mdoc->meta.msec == '9')
2056 break;
2057 mandoc_msg(MANDOCERR_SECMSEC, mdoc->parse,
2058 mdoc->last->line, mdoc->last->pos, buf);
2059 break;
2060 default:
2061 break;
2064 return(1);
2067 static int
2068 post_ignpar(POST_ARGS)
2070 struct mdoc_node *np;
2072 if (MDOC_BODY != mdoc->last->type)
2073 return(1);
2075 if (NULL != (np = mdoc->last->child))
2076 if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
2077 mdoc_nmsg(mdoc, np, MANDOCERR_IGNPAR);
2078 mdoc_node_delete(mdoc, np);
2081 if (NULL != (np = mdoc->last->last))
2082 if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
2083 mdoc_nmsg(mdoc, np, MANDOCERR_IGNPAR);
2084 mdoc_node_delete(mdoc, np);
2087 return(1);
2090 static int
2091 pre_par(PRE_ARGS)
2094 if (NULL == mdoc->last)
2095 return(1);
2096 if (MDOC_ELEM != n->type && MDOC_BLOCK != n->type)
2097 return(1);
2100 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
2101 * block: `Lp', `Pp', or non-compact `Bd' or `Bl'.
2104 if (MDOC_Pp != mdoc->last->tok &&
2105 MDOC_Lp != mdoc->last->tok &&
2106 MDOC_br != mdoc->last->tok)
2107 return(1);
2108 if (MDOC_Bl == n->tok && n->norm->Bl.comp)
2109 return(1);
2110 if (MDOC_Bd == n->tok && n->norm->Bd.comp)
2111 return(1);
2112 if (MDOC_It == n->tok && n->parent->norm->Bl.comp)
2113 return(1);
2115 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNPAR);
2116 mdoc_node_delete(mdoc, mdoc->last);
2117 return(1);
2120 static int
2121 post_par(POST_ARGS)
2124 if (MDOC_ELEM != mdoc->last->type &&
2125 MDOC_BLOCK != mdoc->last->type)
2126 return(1);
2128 if (NULL == mdoc->last->prev) {
2129 if (MDOC_Sh != mdoc->last->parent->tok &&
2130 MDOC_Ss != mdoc->last->parent->tok)
2131 return(1);
2132 } else {
2133 if (MDOC_Pp != mdoc->last->prev->tok &&
2134 MDOC_Lp != mdoc->last->prev->tok &&
2135 (MDOC_br != mdoc->last->tok ||
2136 (MDOC_sp != mdoc->last->prev->tok &&
2137 MDOC_br != mdoc->last->prev->tok)))
2138 return(1);
2141 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNPAR);
2142 mdoc_node_delete(mdoc, mdoc->last);
2143 return(1);
2146 static int
2147 pre_literal(PRE_ARGS)
2150 if (MDOC_BODY != n->type)
2151 return(1);
2154 * The `Dl' (note "el" not "one") and `Bd -literal' and `Bd
2155 * -unfilled' macros set MDOC_LITERAL on entrance to the body.
2158 switch (n->tok) {
2159 case (MDOC_Dl):
2160 mdoc->flags |= MDOC_LITERAL;
2161 break;
2162 case (MDOC_Bd):
2163 if (DISP_literal == n->norm->Bd.type)
2164 mdoc->flags |= MDOC_LITERAL;
2165 if (DISP_unfilled == n->norm->Bd.type)
2166 mdoc->flags |= MDOC_LITERAL;
2167 break;
2168 default:
2169 abort();
2170 /* NOTREACHED */
2173 return(1);
2176 static int
2177 post_dd(POST_ARGS)
2179 char buf[DATESIZE];
2180 struct mdoc_node *n;
2181 int c;
2183 if (mdoc->meta.date)
2184 free(mdoc->meta.date);
2186 n = mdoc->last;
2187 if (NULL == n->child || '\0' == n->child->string[0]) {
2188 mdoc->meta.date = mandoc_normdate
2189 (mdoc->parse, NULL, n->line, n->pos);
2190 return(1);
2193 buf[0] = '\0';
2194 if (-1 == (c = concat(buf, n->child, DATESIZE))) {
2195 mdoc_nmsg(mdoc, n->child, MANDOCERR_MEM);
2196 return(0);
2199 assert(c);
2200 mdoc->meta.date = mandoc_normdate
2201 (mdoc->parse, buf, n->line, n->pos);
2203 return(1);
2206 static int
2207 post_dt(POST_ARGS)
2209 struct mdoc_node *nn, *n;
2210 const char *cp;
2211 char *p;
2213 n = mdoc->last;
2215 if (mdoc->meta.title)
2216 free(mdoc->meta.title);
2217 if (mdoc->meta.vol)
2218 free(mdoc->meta.vol);
2219 if (mdoc->meta.arch)
2220 free(mdoc->meta.arch);
2222 mdoc->meta.title = mdoc->meta.vol = mdoc->meta.arch = NULL;
2224 /* First make all characters uppercase. */
2226 if (NULL != (nn = n->child))
2227 for (p = nn->string; *p; p++) {
2228 if (toupper((unsigned char)*p) == *p)
2229 continue;
2232 * FIXME: don't be lazy: have this make all
2233 * characters be uppercase and just warn once.
2235 mdoc_nmsg(mdoc, nn, MANDOCERR_UPPERCASE);
2236 break;
2239 /* Handles: `.Dt'
2240 * --> title = unknown, volume = local, msec = 0, arch = NULL
2243 if (NULL == (nn = n->child)) {
2244 /* XXX: make these macro values. */
2245 /* FIXME: warn about missing values. */
2246 mdoc->meta.title = mandoc_strdup("UNKNOWN");
2247 mdoc->meta.vol = mandoc_strdup("LOCAL");
2248 mdoc->meta.msec = mandoc_strdup("1");
2249 return(1);
2252 /* Handles: `.Dt TITLE'
2253 * --> title = TITLE, volume = local, msec = 0, arch = NULL
2256 mdoc->meta.title = mandoc_strdup
2257 ('\0' == nn->string[0] ? "UNKNOWN" : nn->string);
2259 if (NULL == (nn = nn->next)) {
2260 /* FIXME: warn about missing msec. */
2261 /* XXX: make this a macro value. */
2262 mdoc->meta.vol = mandoc_strdup("LOCAL");
2263 mdoc->meta.msec = mandoc_strdup("1");
2264 return(1);
2267 /* Handles: `.Dt TITLE SEC'
2268 * --> title = TITLE, volume = SEC is msec ?
2269 * format(msec) : SEC,
2270 * msec = SEC is msec ? atoi(msec) : 0,
2271 * arch = NULL
2274 cp = mandoc_a2msec(nn->string);
2275 if (cp) {
2276 mdoc->meta.vol = mandoc_strdup(cp);
2277 mdoc->meta.msec = mandoc_strdup(nn->string);
2278 } else {
2279 mdoc_nmsg(mdoc, n, MANDOCERR_BADMSEC);
2280 mdoc->meta.vol = mandoc_strdup(nn->string);
2281 mdoc->meta.msec = mandoc_strdup(nn->string);
2284 if (NULL == (nn = nn->next))
2285 return(1);
2287 /* Handles: `.Dt TITLE SEC VOL'
2288 * --> title = TITLE, volume = VOL is vol ?
2289 * format(VOL) :
2290 * VOL is arch ? format(arch) :
2291 * VOL
2294 cp = mdoc_a2vol(nn->string);
2295 if (cp) {
2296 free(mdoc->meta.vol);
2297 mdoc->meta.vol = mandoc_strdup(cp);
2298 } else {
2299 cp = mdoc_a2arch(nn->string);
2300 if (NULL == cp) {
2301 mdoc_nmsg(mdoc, nn, MANDOCERR_BADVOLARCH);
2302 free(mdoc->meta.vol);
2303 mdoc->meta.vol = mandoc_strdup(nn->string);
2304 } else
2305 mdoc->meta.arch = mandoc_strdup(cp);
2308 /* Ignore any subsequent parameters... */
2309 /* FIXME: warn about subsequent parameters. */
2311 return(1);
2314 static int
2315 post_prol(POST_ARGS)
2318 * Remove prologue macros from the document after they're
2319 * processed. The final document uses mdoc_meta for these
2320 * values and discards the originals.
2323 mdoc_node_delete(mdoc, mdoc->last);
2324 if (mdoc->meta.title && mdoc->meta.date && mdoc->meta.os)
2325 mdoc->flags |= MDOC_PBODY;
2327 return(1);
2330 static int
2331 post_bx(POST_ARGS)
2333 struct mdoc_node *n;
2336 * Make `Bx's second argument always start with an uppercase
2337 * letter. Groff checks if it's an "accepted" term, but we just
2338 * uppercase blindly.
2341 n = mdoc->last->child;
2342 if (n && NULL != (n = n->next))
2343 *n->string = (char)toupper
2344 ((unsigned char)*n->string);
2346 return(1);
2349 static int
2350 post_os(POST_ARGS)
2352 struct mdoc_node *n;
2353 char buf[BUFSIZ];
2354 int c;
2355 #ifndef OSNAME
2356 struct utsname utsname;
2357 #endif
2359 n = mdoc->last;
2362 * Set the operating system by way of the `Os' macro.
2363 * The order of precedence is:
2364 * 1. the argument of the `Os' macro, unless empty
2365 * 2. the -Ios=foo command line argument, if provided
2366 * 3. -DOSNAME="\"foo\"", if provided during compilation
2367 * 4. "sysname release" from uname(3)
2370 free(mdoc->meta.os);
2372 buf[0] = '\0';
2373 if (-1 == (c = concat(buf, n->child, BUFSIZ))) {
2374 mdoc_nmsg(mdoc, n->child, MANDOCERR_MEM);
2375 return(0);
2378 assert(c);
2380 if ('\0' == buf[0]) {
2381 if (mdoc->defos) {
2382 mdoc->meta.os = mandoc_strdup(mdoc->defos);
2383 return(1);
2385 #ifdef OSNAME
2386 if (strlcat(buf, OSNAME, BUFSIZ) >= BUFSIZ) {
2387 mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2388 return(0);
2390 #else /*!OSNAME */
2391 if (-1 == uname(&utsname)) {
2392 mdoc_nmsg(mdoc, n, MANDOCERR_UNAME);
2393 mdoc->meta.os = mandoc_strdup("UNKNOWN");
2394 return(post_prol(mdoc));
2397 if (strlcat(buf, utsname.sysname, BUFSIZ) >= BUFSIZ) {
2398 mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2399 return(0);
2401 if (strlcat(buf, " ", BUFSIZ) >= BUFSIZ) {
2402 mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2403 return(0);
2405 if (strlcat(buf, utsname.release, BUFSIZ) >= BUFSIZ) {
2406 mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2407 return(0);
2409 #endif /*!OSNAME*/
2412 mdoc->meta.os = mandoc_strdup(buf);
2413 return(1);
2416 static int
2417 post_std(POST_ARGS)
2419 struct mdoc_node *nn, *n;
2421 n = mdoc->last;
2424 * Macros accepting `-std' as an argument have the name of the
2425 * current document (`Nm') filled in as the argument if it's not
2426 * provided.
2429 if (n->child)
2430 return(1);
2432 if (NULL == mdoc->meta.name)
2433 return(1);
2435 nn = n;
2436 mdoc->next = MDOC_NEXT_CHILD;
2438 if ( ! mdoc_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name))
2439 return(0);
2441 mdoc->last = nn;
2442 return(1);
2446 * Concatenate a node, stopping at the first non-text.
2447 * Concatenation is separated by a single whitespace.
2448 * Returns -1 on fatal (string overrun) error, 0 if child nodes were
2449 * encountered, 1 otherwise.
2451 static int
2452 concat(char *p, const struct mdoc_node *n, size_t sz)
2455 for ( ; NULL != n; n = n->next) {
2456 if (MDOC_TEXT != n->type)
2457 return(0);
2458 if ('\0' != p[0] && strlcat(p, " ", sz) >= sz)
2459 return(-1);
2460 if (strlcat(p, n->string, sz) >= sz)
2461 return(-1);
2462 concat(p, n->child, sz);
2465 return(1);
2468 static enum mdoc_sec
2469 a2sec(const char *p)
2471 int i;
2473 for (i = 0; i < (int)SEC__MAX; i++)
2474 if (secnames[i] && 0 == strcmp(p, secnames[i]))
2475 return((enum mdoc_sec)i);
2477 return(SEC_CUSTOM);
2480 static size_t
2481 macro2len(enum mdoct macro)
2484 switch (macro) {
2485 case(MDOC_Ad):
2486 return(12);
2487 case(MDOC_Ao):
2488 return(12);
2489 case(MDOC_An):
2490 return(12);
2491 case(MDOC_Aq):
2492 return(12);
2493 case(MDOC_Ar):
2494 return(12);
2495 case(MDOC_Bo):
2496 return(12);
2497 case(MDOC_Bq):
2498 return(12);
2499 case(MDOC_Cd):
2500 return(12);
2501 case(MDOC_Cm):
2502 return(10);
2503 case(MDOC_Do):
2504 return(10);
2505 case(MDOC_Dq):
2506 return(12);
2507 case(MDOC_Dv):
2508 return(12);
2509 case(MDOC_Eo):
2510 return(12);
2511 case(MDOC_Em):
2512 return(10);
2513 case(MDOC_Er):
2514 return(17);
2515 case(MDOC_Ev):
2516 return(15);
2517 case(MDOC_Fa):
2518 return(12);
2519 case(MDOC_Fl):
2520 return(10);
2521 case(MDOC_Fo):
2522 return(16);
2523 case(MDOC_Fn):
2524 return(16);
2525 case(MDOC_Ic):
2526 return(10);
2527 case(MDOC_Li):
2528 return(16);
2529 case(MDOC_Ms):
2530 return(6);
2531 case(MDOC_Nm):
2532 return(10);
2533 case(MDOC_No):
2534 return(12);
2535 case(MDOC_Oo):
2536 return(10);
2537 case(MDOC_Op):
2538 return(14);
2539 case(MDOC_Pa):
2540 return(32);
2541 case(MDOC_Pf):
2542 return(12);
2543 case(MDOC_Po):
2544 return(12);
2545 case(MDOC_Pq):
2546 return(12);
2547 case(MDOC_Ql):
2548 return(16);
2549 case(MDOC_Qo):
2550 return(12);
2551 case(MDOC_So):
2552 return(12);
2553 case(MDOC_Sq):
2554 return(12);
2555 case(MDOC_Sy):
2556 return(6);
2557 case(MDOC_Sx):
2558 return(16);
2559 case(MDOC_Tn):
2560 return(10);
2561 case(MDOC_Va):
2562 return(12);
2563 case(MDOC_Vt):
2564 return(12);
2565 case(MDOC_Xr):
2566 return(10);
2567 default:
2568 break;
2570 return(0);