]> git.cameronkatri.com Git - mandoc.git/blob - mdoc_validate.c
Simplify by allowing only one pre-handler.
[mandoc.git] / mdoc_validate.c
1 /* $Id: mdoc_validate.c,v 1.241 2014/08/01 22:22:11 schwarze Exp $ */
2 /*
3 * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2010-2014 Ingo Schwarze <schwarze@openbsd.org>
5 * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22
23 #ifndef OSNAME
24 #include <sys/utsname.h>
25 #endif
26
27 #include <sys/types.h>
28
29 #include <assert.h>
30 #include <ctype.h>
31 #include <limits.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <time.h>
36
37 #include "mdoc.h"
38 #include "mandoc.h"
39 #include "mandoc_aux.h"
40 #include "libmdoc.h"
41 #include "libmandoc.h"
42
43 /* FIXME: .Bl -diag can't have non-text children in HEAD. */
44
45 #define PRE_ARGS struct mdoc *mdoc, struct mdoc_node *n
46 #define POST_ARGS struct mdoc *mdoc
47
48 enum check_ineq {
49 CHECK_LT,
50 CHECK_GT,
51 CHECK_EQ
52 };
53
54 enum check_lvl {
55 CHECK_WARN,
56 CHECK_ERROR,
57 };
58
59 typedef int (*v_pre)(PRE_ARGS);
60 typedef int (*v_post)(POST_ARGS);
61
62 struct valids {
63 v_pre pre;
64 v_post *post;
65 };
66
67 static int check_count(struct mdoc *, enum mdoc_type,
68 enum check_lvl, enum check_ineq, int);
69 static void check_text(struct mdoc *, int, int, char *);
70 static void check_argv(struct mdoc *,
71 struct mdoc_node *, struct mdoc_argv *);
72 static void check_args(struct mdoc *, struct mdoc_node *);
73 static enum mdoc_sec a2sec(const char *);
74 static size_t macro2len(enum mdoct);
75
76 static int ebool(POST_ARGS);
77 static int berr_ge1(POST_ARGS);
78 static int bwarn_ge1(POST_ARGS);
79 static int ewarn_eq0(POST_ARGS);
80 static int ewarn_eq1(POST_ARGS);
81 static int ewarn_ge1(POST_ARGS);
82 static int ewarn_le1(POST_ARGS);
83 static int hwarn_eq0(POST_ARGS);
84 static int hwarn_eq1(POST_ARGS);
85 static int hwarn_ge1(POST_ARGS);
86
87 static int post_an(POST_ARGS);
88 static int post_at(POST_ARGS);
89 static int post_bf(POST_ARGS);
90 static int post_bl(POST_ARGS);
91 static int post_bl_block(POST_ARGS);
92 static int post_bl_block_width(POST_ARGS);
93 static int post_bl_block_tag(POST_ARGS);
94 static int post_bl_head(POST_ARGS);
95 static int post_bx(POST_ARGS);
96 static int post_defaults(POST_ARGS);
97 static int post_dd(POST_ARGS);
98 static int post_dt(POST_ARGS);
99 static int post_en(POST_ARGS);
100 static int post_es(POST_ARGS);
101 static int post_eoln(POST_ARGS);
102 static int post_ex(POST_ARGS);
103 static int post_hyph(POST_ARGS);
104 static int post_ignpar(POST_ARGS);
105 static int post_it(POST_ARGS);
106 static int post_lb(POST_ARGS);
107 static int post_literal(POST_ARGS);
108 static int post_nm(POST_ARGS);
109 static int post_ns(POST_ARGS);
110 static int post_os(POST_ARGS);
111 static int post_par(POST_ARGS);
112 static int post_prol(POST_ARGS);
113 static int post_root(POST_ARGS);
114 static int post_rs(POST_ARGS);
115 static int post_sh(POST_ARGS);
116 static int post_sh_body(POST_ARGS);
117 static int post_sh_head(POST_ARGS);
118 static int post_st(POST_ARGS);
119 static int post_vt(POST_ARGS);
120 static int pre_an(PRE_ARGS);
121 static int pre_bd(PRE_ARGS);
122 static int pre_bl(PRE_ARGS);
123 static int pre_dd(PRE_ARGS);
124 static int pre_display(PRE_ARGS);
125 static int pre_dt(PRE_ARGS);
126 static int pre_literal(PRE_ARGS);
127 static int pre_obsolete(PRE_ARGS);
128 static int pre_os(PRE_ARGS);
129 static int pre_par(PRE_ARGS);
130 static int pre_std(PRE_ARGS);
131
132 static v_post posts_an[] = { post_an, NULL };
133 static v_post posts_at[] = { post_at, post_defaults, NULL };
134 static v_post posts_bd[] = { post_literal, hwarn_eq0, bwarn_ge1, NULL };
135 static v_post posts_bf[] = { post_bf, NULL };
136 static v_post posts_bk[] = { hwarn_eq0, bwarn_ge1, NULL };
137 static v_post posts_bl[] = { bwarn_ge1, post_bl, NULL };
138 static v_post posts_bx[] = { post_bx, NULL };
139 static v_post posts_bool[] = { ebool, NULL };
140 static v_post posts_eoln[] = { post_eoln, NULL };
141 static v_post posts_defaults[] = { post_defaults, NULL };
142 static v_post posts_d1[] = { bwarn_ge1, post_hyph, NULL };
143 static v_post posts_dd[] = { post_dd, post_prol, NULL };
144 static v_post posts_dl[] = { post_literal, bwarn_ge1, NULL };
145 static v_post posts_dt[] = { post_dt, post_prol, NULL };
146 static v_post posts_en[] = { post_en, NULL };
147 static v_post posts_es[] = { post_es, NULL };
148 static v_post posts_ex[] = { post_ex, NULL };
149 static v_post posts_fo[] = { hwarn_eq1, bwarn_ge1, NULL };
150 static v_post posts_hyph[] = { post_hyph, NULL };
151 static v_post posts_hyphtext[] = { ewarn_ge1, post_hyph, NULL };
152 static v_post posts_it[] = { post_it, NULL };
153 static v_post posts_lb[] = { post_lb, NULL };
154 static v_post posts_nd[] = { berr_ge1, post_hyph, NULL };
155 static v_post posts_nm[] = { post_nm, NULL };
156 static v_post posts_notext[] = { ewarn_eq0, NULL };
157 static v_post posts_ns[] = { post_ns, NULL };
158 static v_post posts_os[] = { post_os, post_prol, NULL };
159 static v_post posts_pp[] = { post_par, ewarn_eq0, NULL };
160 static v_post posts_rs[] = { post_rs, NULL };
161 static v_post posts_sh[] = { post_ignpar,hwarn_ge1,post_sh,post_hyph,NULL };
162 static v_post posts_sp[] = { post_par, ewarn_le1, NULL };
163 static v_post posts_ss[] = { post_ignpar, hwarn_ge1, post_hyph, NULL };
164 static v_post posts_st[] = { post_st, NULL };
165 static v_post posts_text[] = { ewarn_ge1, NULL };
166 static v_post posts_text1[] = { ewarn_eq1, NULL };
167 static v_post posts_vt[] = { post_vt, NULL };
168
169 static const struct valids mdoc_valids[MDOC_MAX] = {
170 { NULL, NULL }, /* Ap */
171 { pre_dd, posts_dd }, /* Dd */
172 { pre_dt, posts_dt }, /* Dt */
173 { pre_os, posts_os }, /* Os */
174 { NULL, posts_sh }, /* Sh */
175 { NULL, posts_ss }, /* Ss */
176 { pre_par, posts_pp }, /* Pp */
177 { pre_display, posts_d1 }, /* D1 */
178 { pre_literal, posts_dl }, /* Dl */
179 { pre_bd, posts_bd }, /* Bd */
180 { NULL, NULL }, /* Ed */
181 { pre_bl, posts_bl }, /* Bl */
182 { NULL, NULL }, /* El */
183 { pre_par, posts_it }, /* It */
184 { NULL, NULL }, /* Ad */
185 { pre_an, posts_an }, /* An */
186 { NULL, posts_defaults }, /* Ar */
187 { NULL, NULL }, /* Cd */
188 { NULL, NULL }, /* Cm */
189 { NULL, NULL }, /* Dv */
190 { NULL, NULL }, /* Er */
191 { NULL, NULL }, /* Ev */
192 { pre_std, posts_ex }, /* Ex */
193 { NULL, NULL }, /* Fa */
194 { NULL, posts_text }, /* Fd */
195 { NULL, NULL }, /* Fl */
196 { NULL, NULL }, /* Fn */
197 { NULL, NULL }, /* Ft */
198 { NULL, NULL }, /* Ic */
199 { NULL, posts_text1 }, /* In */
200 { NULL, posts_defaults }, /* Li */
201 { NULL, posts_nd }, /* Nd */
202 { NULL, posts_nm }, /* Nm */
203 { NULL, NULL }, /* Op */
204 { pre_obsolete, NULL }, /* Ot */
205 { NULL, posts_defaults }, /* Pa */
206 { pre_std, NULL }, /* Rv */
207 { NULL, posts_st }, /* St */
208 { NULL, NULL }, /* Va */
209 { NULL, posts_vt }, /* Vt */
210 { NULL, posts_text }, /* Xr */
211 { NULL, posts_text }, /* %A */
212 { NULL, posts_hyphtext }, /* %B */ /* FIXME: can be used outside Rs/Re. */
213 { NULL, posts_text }, /* %D */
214 { NULL, posts_text }, /* %I */
215 { NULL, posts_text }, /* %J */
216 { NULL, posts_hyphtext }, /* %N */
217 { NULL, posts_hyphtext }, /* %O */
218 { NULL, posts_text }, /* %P */
219 { NULL, posts_hyphtext }, /* %R */
220 { NULL, posts_hyphtext }, /* %T */ /* FIXME: can be used outside Rs/Re. */
221 { NULL, posts_text }, /* %V */
222 { NULL, NULL }, /* Ac */
223 { NULL, NULL }, /* Ao */
224 { NULL, NULL }, /* Aq */
225 { NULL, posts_at }, /* At */
226 { NULL, NULL }, /* Bc */
227 { NULL, posts_bf }, /* Bf */
228 { NULL, NULL }, /* Bo */
229 { NULL, NULL }, /* Bq */
230 { NULL, NULL }, /* Bsx */
231 { NULL, posts_bx }, /* Bx */
232 { NULL, posts_bool }, /* Db */
233 { NULL, NULL }, /* Dc */
234 { NULL, NULL }, /* Do */
235 { NULL, NULL }, /* Dq */
236 { NULL, NULL }, /* Ec */
237 { NULL, NULL }, /* Ef */
238 { NULL, NULL }, /* Em */
239 { NULL, NULL }, /* Eo */
240 { NULL, NULL }, /* Fx */
241 { NULL, NULL }, /* Ms */
242 { NULL, posts_notext }, /* No */
243 { NULL, posts_ns }, /* Ns */
244 { NULL, NULL }, /* Nx */
245 { NULL, NULL }, /* Ox */
246 { NULL, NULL }, /* Pc */
247 { NULL, posts_text1 }, /* Pf */
248 { NULL, NULL }, /* Po */
249 { NULL, NULL }, /* Pq */
250 { NULL, NULL }, /* Qc */
251 { NULL, NULL }, /* Ql */
252 { NULL, NULL }, /* Qo */
253 { NULL, NULL }, /* Qq */
254 { NULL, NULL }, /* Re */
255 { NULL, posts_rs }, /* Rs */
256 { NULL, NULL }, /* Sc */
257 { NULL, NULL }, /* So */
258 { NULL, NULL }, /* Sq */
259 { NULL, posts_bool }, /* Sm */
260 { NULL, posts_hyph }, /* Sx */
261 { NULL, NULL }, /* Sy */
262 { NULL, NULL }, /* Tn */
263 { NULL, NULL }, /* Ux */
264 { NULL, NULL }, /* Xc */
265 { NULL, NULL }, /* Xo */
266 { NULL, posts_fo }, /* Fo */
267 { NULL, NULL }, /* Fc */
268 { NULL, NULL }, /* Oo */
269 { NULL, NULL }, /* Oc */
270 { NULL, posts_bk }, /* Bk */
271 { NULL, NULL }, /* Ek */
272 { NULL, posts_eoln }, /* Bt */
273 { NULL, NULL }, /* Hf */
274 { pre_obsolete, NULL }, /* Fr */
275 { NULL, posts_eoln }, /* Ud */
276 { NULL, posts_lb }, /* Lb */
277 { pre_par, posts_pp }, /* Lp */
278 { NULL, NULL }, /* Lk */
279 { NULL, posts_defaults }, /* Mt */
280 { NULL, NULL }, /* Brq */
281 { NULL, NULL }, /* Bro */
282 { NULL, NULL }, /* Brc */
283 { NULL, posts_text }, /* %C */
284 { pre_obsolete, posts_es }, /* Es */
285 { pre_obsolete, posts_en }, /* En */
286 { NULL, NULL }, /* Dx */
287 { NULL, posts_text }, /* %Q */
288 { NULL, posts_pp }, /* br */
289 { NULL, posts_sp }, /* sp */
290 { NULL, posts_text1 }, /* %U */
291 { NULL, NULL }, /* Ta */
292 { NULL, NULL }, /* ll */
293 };
294
295 #define RSORD_MAX 14 /* Number of `Rs' blocks. */
296
297 static const enum mdoct rsord[RSORD_MAX] = {
298 MDOC__A,
299 MDOC__T,
300 MDOC__B,
301 MDOC__I,
302 MDOC__J,
303 MDOC__R,
304 MDOC__N,
305 MDOC__V,
306 MDOC__U,
307 MDOC__P,
308 MDOC__Q,
309 MDOC__C,
310 MDOC__D,
311 MDOC__O
312 };
313
314 static const char * const secnames[SEC__MAX] = {
315 NULL,
316 "NAME",
317 "LIBRARY",
318 "SYNOPSIS",
319 "DESCRIPTION",
320 "CONTEXT",
321 "IMPLEMENTATION NOTES",
322 "RETURN VALUES",
323 "ENVIRONMENT",
324 "FILES",
325 "EXIT STATUS",
326 "EXAMPLES",
327 "DIAGNOSTICS",
328 "COMPATIBILITY",
329 "ERRORS",
330 "SEE ALSO",
331 "STANDARDS",
332 "HISTORY",
333 "AUTHORS",
334 "CAVEATS",
335 "BUGS",
336 "SECURITY CONSIDERATIONS",
337 NULL
338 };
339
340
341 int
342 mdoc_valid_pre(struct mdoc *mdoc, struct mdoc_node *n)
343 {
344 v_pre p;
345
346 switch (n->type) {
347 case MDOC_TEXT:
348 check_text(mdoc, n->line, n->pos, n->string);
349 /* FALLTHROUGH */
350 case MDOC_TBL:
351 /* FALLTHROUGH */
352 case MDOC_EQN:
353 /* FALLTHROUGH */
354 case MDOC_ROOT:
355 return(1);
356 default:
357 break;
358 }
359
360 check_args(mdoc, n);
361 p = mdoc_valids[n->tok].pre;
362 return(*p ? (*p)(mdoc, n) : 1);
363 }
364
365 int
366 mdoc_valid_post(struct mdoc *mdoc)
367 {
368 v_post *p;
369
370 if (MDOC_VALID & mdoc->last->flags)
371 return(1);
372 mdoc->last->flags |= MDOC_VALID;
373
374 switch (mdoc->last->type) {
375 case MDOC_TEXT:
376 /* FALLTHROUGH */
377 case MDOC_EQN:
378 /* FALLTHROUGH */
379 case MDOC_TBL:
380 return(1);
381 case MDOC_ROOT:
382 return(post_root(mdoc));
383 default:
384 break;
385 }
386
387 if (NULL == mdoc_valids[mdoc->last->tok].post)
388 return(1);
389 for (p = mdoc_valids[mdoc->last->tok].post; *p; p++)
390 if ( ! (*p)(mdoc))
391 return(0);
392
393 return(1);
394 }
395
396 static int
397 check_count(struct mdoc *mdoc, enum mdoc_type type,
398 enum check_lvl lvl, enum check_ineq ineq, int val)
399 {
400 const char *p;
401 enum mandocerr t;
402
403 if (mdoc->last->type != type)
404 return(1);
405
406 switch (ineq) {
407 case CHECK_LT:
408 p = "less than ";
409 if (mdoc->last->nchild < val)
410 return(1);
411 break;
412 case CHECK_GT:
413 p = "more than ";
414 if (mdoc->last->nchild > val)
415 return(1);
416 break;
417 case CHECK_EQ:
418 p = "";
419 if (val == mdoc->last->nchild)
420 return(1);
421 break;
422 default:
423 abort();
424 /* NOTREACHED */
425 }
426
427 t = lvl == CHECK_WARN ? MANDOCERR_ARGCWARN : MANDOCERR_ARGCOUNT;
428 mandoc_vmsg(t, mdoc->parse, mdoc->last->line,
429 mdoc->last->pos, "want %s%d children (have %d)",
430 p, val, mdoc->last->nchild);
431 return(1);
432 }
433
434 static int
435 berr_ge1(POST_ARGS)
436 {
437
438 return(check_count(mdoc, MDOC_BODY, CHECK_ERROR, CHECK_GT, 0));
439 }
440
441 static int
442 bwarn_ge1(POST_ARGS)
443 {
444 return(check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0));
445 }
446
447 static int
448 ewarn_eq0(POST_ARGS)
449 {
450 return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0));
451 }
452
453 static int
454 ewarn_eq1(POST_ARGS)
455 {
456 return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1));
457 }
458
459 static int
460 ewarn_ge1(POST_ARGS)
461 {
462 return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0));
463 }
464
465 static int
466 ewarn_le1(POST_ARGS)
467 {
468 return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_LT, 2));
469 }
470
471 static int
472 hwarn_eq0(POST_ARGS)
473 {
474 return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0));
475 }
476
477 static int
478 hwarn_eq1(POST_ARGS)
479 {
480 return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 1));
481 }
482
483 static int
484 hwarn_ge1(POST_ARGS)
485 {
486 return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_GT, 0));
487 }
488
489 static void
490 check_args(struct mdoc *mdoc, struct mdoc_node *n)
491 {
492 int i;
493
494 if (NULL == n->args)
495 return;
496
497 assert(n->args->argc);
498 for (i = 0; i < (int)n->args->argc; i++)
499 check_argv(mdoc, n, &n->args->argv[i]);
500 }
501
502 static void
503 check_argv(struct mdoc *mdoc, struct mdoc_node *n, struct mdoc_argv *v)
504 {
505 int i;
506
507 for (i = 0; i < (int)v->sz; i++)
508 check_text(mdoc, v->line, v->pos, v->value[i]);
509 }
510
511 static void
512 check_text(struct mdoc *mdoc, int ln, int pos, char *p)
513 {
514 char *cp;
515
516 if (MDOC_LITERAL & mdoc->flags)
517 return;
518
519 for (cp = p; NULL != (p = strchr(p, '\t')); p++)
520 mandoc_msg(MANDOCERR_FI_TAB, mdoc->parse,
521 ln, pos + (int)(p - cp), NULL);
522 }
523
524 static int
525 pre_display(PRE_ARGS)
526 {
527 struct mdoc_node *node;
528
529 if (MDOC_BLOCK != n->type)
530 return(1);
531
532 for (node = mdoc->last->parent; node; node = node->parent)
533 if (MDOC_BLOCK == node->type)
534 if (MDOC_Bd == node->tok)
535 break;
536
537 if (node)
538 mandoc_vmsg(MANDOCERR_BD_NEST,
539 mdoc->parse, n->line, n->pos,
540 "%s in Bd", mdoc_macronames[n->tok]);
541
542 return(1);
543 }
544
545 static int
546 pre_bl(PRE_ARGS)
547 {
548 struct mdoc_node *np;
549 struct mdoc_argv *argv, *wa;
550 int i;
551 enum mdocargt mdoclt;
552 enum mdoc_list lt;
553
554 if (MDOC_BLOCK != n->type) {
555 if (ENDBODY_NOT != n->end) {
556 assert(n->pending);
557 np = n->pending->parent;
558 } else
559 np = n->parent;
560
561 assert(np);
562 assert(MDOC_BLOCK == np->type);
563 assert(MDOC_Bl == np->tok);
564 return(1);
565 }
566
567 /*
568 * First figure out which kind of list to use: bind ourselves to
569 * the first mentioned list type and warn about any remaining
570 * ones. If we find no list type, we default to LIST_item.
571 */
572
573 wa = n->args->argv;
574 mdoclt = MDOC_ARG_MAX;
575 for (i = 0; n->args && i < (int)n->args->argc; i++) {
576 argv = n->args->argv + i;
577 lt = LIST__NONE;
578 switch (argv->arg) {
579 /* Set list types. */
580 case MDOC_Bullet:
581 lt = LIST_bullet;
582 break;
583 case MDOC_Dash:
584 lt = LIST_dash;
585 break;
586 case MDOC_Enum:
587 lt = LIST_enum;
588 break;
589 case MDOC_Hyphen:
590 lt = LIST_hyphen;
591 break;
592 case MDOC_Item:
593 lt = LIST_item;
594 break;
595 case MDOC_Tag:
596 lt = LIST_tag;
597 break;
598 case MDOC_Diag:
599 lt = LIST_diag;
600 break;
601 case MDOC_Hang:
602 lt = LIST_hang;
603 break;
604 case MDOC_Ohang:
605 lt = LIST_ohang;
606 break;
607 case MDOC_Inset:
608 lt = LIST_inset;
609 break;
610 case MDOC_Column:
611 lt = LIST_column;
612 break;
613 /* Set list arguments. */
614 case MDOC_Compact:
615 if (n->norm->Bl.comp)
616 mandoc_msg(MANDOCERR_ARG_REP,
617 mdoc->parse, argv->line,
618 argv->pos, "Bl -compact");
619 n->norm->Bl.comp = 1;
620 break;
621 case MDOC_Width:
622 wa = argv;
623 if (0 == argv->sz) {
624 mandoc_msg(MANDOCERR_ARG_EMPTY,
625 mdoc->parse, argv->line,
626 argv->pos, "Bl -width");
627 n->norm->Bl.width = "0n";
628 break;
629 }
630 if (NULL != n->norm->Bl.width)
631 mandoc_vmsg(MANDOCERR_ARG_REP,
632 mdoc->parse, argv->line,
633 argv->pos, "Bl -width %s",
634 argv->value[0]);
635 n->norm->Bl.width = argv->value[0];
636 break;
637 case MDOC_Offset:
638 if (0 == argv->sz) {
639 mandoc_msg(MANDOCERR_ARG_EMPTY,
640 mdoc->parse, argv->line,
641 argv->pos, "Bl -offset");
642 break;
643 }
644 if (NULL != n->norm->Bl.offs)
645 mandoc_vmsg(MANDOCERR_ARG_REP,
646 mdoc->parse, argv->line,
647 argv->pos, "Bl -offset %s",
648 argv->value[0]);
649 n->norm->Bl.offs = argv->value[0];
650 break;
651 default:
652 continue;
653 }
654 if (LIST__NONE == lt)
655 continue;
656 mdoclt = argv->arg;
657
658 /* Check: multiple list types. */
659
660 if (LIST__NONE != n->norm->Bl.type) {
661 mandoc_vmsg(MANDOCERR_BL_REP,
662 mdoc->parse, n->line, n->pos,
663 "Bl -%s", mdoc_argnames[argv->arg]);
664 continue;
665 }
666
667 /* The list type should come first. */
668
669 if (n->norm->Bl.width ||
670 n->norm->Bl.offs ||
671 n->norm->Bl.comp)
672 mandoc_vmsg(MANDOCERR_BL_LATETYPE,
673 mdoc->parse, n->line, n->pos, "Bl -%s",
674 mdoc_argnames[n->args->argv[0].arg]);
675
676 n->norm->Bl.type = lt;
677 if (LIST_column == lt) {
678 n->norm->Bl.ncols = argv->sz;
679 n->norm->Bl.cols = (void *)argv->value;
680 }
681 }
682
683 /* Allow lists to default to LIST_item. */
684
685 if (LIST__NONE == n->norm->Bl.type) {
686 mandoc_msg(MANDOCERR_BL_NOTYPE, mdoc->parse,
687 n->line, n->pos, "Bl");
688 n->norm->Bl.type = LIST_item;
689 }
690
691 /*
692 * Validate the width field. Some list types don't need width
693 * types and should be warned about them. Others should have it
694 * and must also be warned. Yet others have a default and need
695 * no warning.
696 */
697
698 switch (n->norm->Bl.type) {
699 case LIST_tag:
700 if (NULL == n->norm->Bl.width)
701 mandoc_msg(MANDOCERR_BL_NOWIDTH, mdoc->parse,
702 n->line, n->pos, "Bl -tag");
703 break;
704 case LIST_column:
705 /* FALLTHROUGH */
706 case LIST_diag:
707 /* FALLTHROUGH */
708 case LIST_ohang:
709 /* FALLTHROUGH */
710 case LIST_inset:
711 /* FALLTHROUGH */
712 case LIST_item:
713 if (n->norm->Bl.width)
714 mandoc_vmsg(MANDOCERR_BL_SKIPW, mdoc->parse,
715 wa->line, wa->pos, "Bl -%s",
716 mdoc_argnames[mdoclt]);
717 break;
718 case LIST_bullet:
719 /* FALLTHROUGH */
720 case LIST_dash:
721 /* FALLTHROUGH */
722 case LIST_hyphen:
723 if (NULL == n->norm->Bl.width)
724 n->norm->Bl.width = "2n";
725 break;
726 case LIST_enum:
727 if (NULL == n->norm->Bl.width)
728 n->norm->Bl.width = "3n";
729 break;
730 default:
731 break;
732 }
733
734 return(pre_par(mdoc, n));
735 }
736
737 static int
738 pre_bd(PRE_ARGS)
739 {
740 struct mdoc_node *np;
741 struct mdoc_argv *argv;
742 int i;
743 enum mdoc_disp dt;
744
745 pre_literal(mdoc, n);
746
747 if (MDOC_BLOCK != n->type) {
748 if (ENDBODY_NOT != n->end) {
749 assert(n->pending);
750 np = n->pending->parent;
751 } else
752 np = n->parent;
753
754 assert(np);
755 assert(MDOC_BLOCK == np->type);
756 assert(MDOC_Bd == np->tok);
757 return(1);
758 }
759
760 for (i = 0; n->args && i < (int)n->args->argc; i++) {
761 argv = n->args->argv + i;
762 dt = DISP__NONE;
763
764 switch (argv->arg) {
765 case MDOC_Centred:
766 dt = DISP_centered;
767 break;
768 case MDOC_Ragged:
769 dt = DISP_ragged;
770 break;
771 case MDOC_Unfilled:
772 dt = DISP_unfilled;
773 break;
774 case MDOC_Filled:
775 dt = DISP_filled;
776 break;
777 case MDOC_Literal:
778 dt = DISP_literal;
779 break;
780 case MDOC_File:
781 mandoc_msg(MANDOCERR_BD_FILE, mdoc->parse,
782 n->line, n->pos, NULL);
783 return(0);
784 case MDOC_Offset:
785 if (0 == argv->sz) {
786 mandoc_msg(MANDOCERR_ARG_EMPTY,
787 mdoc->parse, argv->line,
788 argv->pos, "Bd -offset");
789 break;
790 }
791 if (NULL != n->norm->Bd.offs)
792 mandoc_vmsg(MANDOCERR_ARG_REP,
793 mdoc->parse, argv->line,
794 argv->pos, "Bd -offset %s",
795 argv->value[0]);
796 n->norm->Bd.offs = argv->value[0];
797 break;
798 case MDOC_Compact:
799 if (n->norm->Bd.comp)
800 mandoc_msg(MANDOCERR_ARG_REP,
801 mdoc->parse, argv->line,
802 argv->pos, "Bd -compact");
803 n->norm->Bd.comp = 1;
804 break;
805 default:
806 abort();
807 /* NOTREACHED */
808 }
809 if (DISP__NONE == dt)
810 continue;
811
812 if (DISP__NONE == n->norm->Bd.type)
813 n->norm->Bd.type = dt;
814 else
815 mandoc_vmsg(MANDOCERR_BD_REP,
816 mdoc->parse, n->line, n->pos,
817 "Bd -%s", mdoc_argnames[argv->arg]);
818 }
819
820 if (DISP__NONE == n->norm->Bd.type) {
821 mandoc_msg(MANDOCERR_BD_NOTYPE, mdoc->parse,
822 n->line, n->pos, "Bd");
823 n->norm->Bd.type = DISP_ragged;
824 }
825
826 return(pre_par(mdoc, n));
827 }
828
829 static int
830 pre_an(PRE_ARGS)
831 {
832 struct mdoc_argv *argv;
833 size_t i;
834
835 if (n->args == NULL)
836 return(1);
837
838 for (i = 1; i < n->args->argc; i++) {
839 argv = n->args->argv + i;
840 mandoc_vmsg(MANDOCERR_AN_REP,
841 mdoc->parse, argv->line, argv->pos,
842 "An -%s", mdoc_argnames[argv->arg]);
843 }
844
845 argv = n->args->argv;
846 if (argv->arg == MDOC_Split)
847 n->norm->An.auth = AUTH_split;
848 else if (argv->arg == MDOC_Nosplit)
849 n->norm->An.auth = AUTH_nosplit;
850 else
851 abort();
852
853 return(1);
854 }
855
856 static int
857 pre_std(PRE_ARGS)
858 {
859
860 if (n->args && 1 == n->args->argc)
861 if (MDOC_Std == n->args->argv[0].arg)
862 return(1);
863
864 mandoc_msg(MANDOCERR_ARG_STD, mdoc->parse,
865 n->line, n->pos, mdoc_macronames[n->tok]);
866 return(1);
867 }
868
869 static int
870 pre_obsolete(PRE_ARGS)
871 {
872
873 if (MDOC_ELEM == n->type || MDOC_BLOCK == n->type)
874 mandoc_msg(MANDOCERR_MACRO_OBS, mdoc->parse,
875 n->line, n->pos, mdoc_macronames[n->tok]);
876 return(1);
877 }
878
879 static int
880 pre_dt(PRE_ARGS)
881 {
882
883 if (NULL == mdoc->meta.date || mdoc->meta.os)
884 mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
885 n->line, n->pos, "Dt");
886
887 if (mdoc->meta.title)
888 mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
889 n->line, n->pos, "Dt");
890
891 return(1);
892 }
893
894 static int
895 pre_os(PRE_ARGS)
896 {
897
898 if (NULL == mdoc->meta.title || NULL == mdoc->meta.date)
899 mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
900 n->line, n->pos, "Os");
901
902 if (mdoc->meta.os)
903 mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
904 n->line, n->pos, "Os");
905
906 return(1);
907 }
908
909 static int
910 pre_dd(PRE_ARGS)
911 {
912
913 if (mdoc->meta.title || mdoc->meta.os)
914 mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
915 n->line, n->pos, "Dd");
916
917 if (mdoc->meta.date)
918 mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
919 n->line, n->pos, "Dd");
920
921 return(1);
922 }
923
924 static int
925 post_bf(POST_ARGS)
926 {
927 struct mdoc_node *np, *nch;
928 enum mdocargt arg;
929
930 /*
931 * Unlike other data pointers, these are "housed" by the HEAD
932 * element, which contains the goods.
933 */
934
935 if (MDOC_HEAD != mdoc->last->type) {
936 if (ENDBODY_NOT != mdoc->last->end) {
937 assert(mdoc->last->pending);
938 np = mdoc->last->pending->parent->head;
939 } else if (MDOC_BLOCK != mdoc->last->type) {
940 np = mdoc->last->parent->head;
941 } else
942 np = mdoc->last->head;
943
944 assert(np);
945 assert(MDOC_HEAD == np->type);
946 assert(MDOC_Bf == np->tok);
947 return(1);
948 }
949
950 np = mdoc->last;
951 assert(MDOC_BLOCK == np->parent->type);
952 assert(MDOC_Bf == np->parent->tok);
953
954 /* Check the number of arguments. */
955
956 nch = np->child;
957 if (NULL == np->parent->args) {
958 if (NULL == nch) {
959 mandoc_msg(MANDOCERR_BF_NOFONT, mdoc->parse,
960 np->line, np->pos, "Bf");
961 return(1);
962 }
963 nch = nch->next;
964 }
965 if (NULL != nch)
966 mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
967 nch->line, nch->pos, "Bf ... %s", nch->string);
968
969 /* Extract argument into data. */
970
971 if (np->parent->args) {
972 arg = np->parent->args->argv[0].arg;
973 if (MDOC_Emphasis == arg)
974 np->norm->Bf.font = FONT_Em;
975 else if (MDOC_Literal == arg)
976 np->norm->Bf.font = FONT_Li;
977 else if (MDOC_Symbolic == arg)
978 np->norm->Bf.font = FONT_Sy;
979 else
980 abort();
981 return(1);
982 }
983
984 /* Extract parameter into data. */
985
986 if (0 == strcmp(np->child->string, "Em"))
987 np->norm->Bf.font = FONT_Em;
988 else if (0 == strcmp(np->child->string, "Li"))
989 np->norm->Bf.font = FONT_Li;
990 else if (0 == strcmp(np->child->string, "Sy"))
991 np->norm->Bf.font = FONT_Sy;
992 else
993 mandoc_vmsg(MANDOCERR_BF_BADFONT, mdoc->parse,
994 np->child->line, np->child->pos,
995 "Bf %s", np->child->string);
996
997 return(1);
998 }
999
1000 static int
1001 post_lb(POST_ARGS)
1002 {
1003 struct mdoc_node *n;
1004 const char *stdlibname;
1005 char *libname;
1006
1007 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1);
1008
1009 n = mdoc->last->child;
1010
1011 assert(n);
1012 assert(MDOC_TEXT == n->type);
1013
1014 if (NULL == (stdlibname = mdoc_a2lib(n->string)))
1015 mandoc_asprintf(&libname,
1016 "library \\(lq%s\\(rq", n->string);
1017 else
1018 libname = mandoc_strdup(stdlibname);
1019
1020 free(n->string);
1021 n->string = libname;
1022 return(1);
1023 }
1024
1025 static int
1026 post_eoln(POST_ARGS)
1027 {
1028 const struct mdoc_node *n;
1029
1030 n = mdoc->last;
1031 if (n->child)
1032 mandoc_vmsg(MANDOCERR_ARG_SKIP,
1033 mdoc->parse, n->line, n->pos,
1034 "%s %s", mdoc_macronames[n->tok],
1035 n->child->string);
1036 return(1);
1037 }
1038
1039 static int
1040 post_vt(POST_ARGS)
1041 {
1042 const struct mdoc_node *n;
1043
1044 /*
1045 * The Vt macro comes in both ELEM and BLOCK form, both of which
1046 * have different syntaxes (yet more context-sensitive
1047 * behaviour). ELEM types must have a child, which is already
1048 * guaranteed by the in_line parsing routine; BLOCK types,
1049 * specifically the BODY, should only have TEXT children.
1050 */
1051
1052 if (MDOC_BODY != mdoc->last->type)
1053 return(1);
1054
1055 for (n = mdoc->last->child; n; n = n->next)
1056 if (MDOC_TEXT != n->type)
1057 mandoc_msg(MANDOCERR_VT_CHILD, mdoc->parse,
1058 n->line, n->pos, mdoc_macronames[n->tok]);
1059
1060 return(1);
1061 }
1062
1063 static int
1064 post_nm(POST_ARGS)
1065 {
1066
1067 if (NULL != mdoc->meta.name)
1068 return(1);
1069
1070 mdoc_deroff(&mdoc->meta.name, mdoc->last);
1071
1072 if (NULL == mdoc->meta.name)
1073 mandoc_msg(MANDOCERR_NM_NONAME, mdoc->parse,
1074 mdoc->last->line, mdoc->last->pos, "Nm");
1075 return(1);
1076 }
1077
1078 static int
1079 post_literal(POST_ARGS)
1080 {
1081
1082 /*
1083 * The `Dl' (note "el" not "one") and `Bd' macros unset the
1084 * MDOC_LITERAL flag as they leave. Note that `Bd' only sets
1085 * this in literal mode, but it doesn't hurt to just switch it
1086 * off in general since displays can't be nested.
1087 */
1088
1089 if (MDOC_BODY == mdoc->last->type)
1090 mdoc->flags &= ~MDOC_LITERAL;
1091
1092 return(1);
1093 }
1094
1095 static int
1096 post_defaults(POST_ARGS)
1097 {
1098 struct mdoc_node *nn;
1099
1100 /*
1101 * The `Ar' defaults to "file ..." if no value is provided as an
1102 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
1103 * gets an empty string.
1104 */
1105
1106 if (mdoc->last->child)
1107 return(1);
1108
1109 nn = mdoc->last;
1110 mdoc->next = MDOC_NEXT_CHILD;
1111
1112 switch (nn->tok) {
1113 case MDOC_Ar:
1114 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "file"))
1115 return(0);
1116 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "..."))
1117 return(0);
1118 break;
1119 case MDOC_At:
1120 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "AT&T"))
1121 return(0);
1122 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "UNIX"))
1123 return(0);
1124 break;
1125 case MDOC_Li:
1126 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, ""))
1127 return(0);
1128 break;
1129 case MDOC_Pa:
1130 /* FALLTHROUGH */
1131 case MDOC_Mt:
1132 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "~"))
1133 return(0);
1134 break;
1135 default:
1136 abort();
1137 /* NOTREACHED */
1138 }
1139
1140 mdoc->last = nn;
1141 return(1);
1142 }
1143
1144 static int
1145 post_at(POST_ARGS)
1146 {
1147 struct mdoc_node *n;
1148 const char *std_att;
1149 char *att;
1150
1151 /*
1152 * If we have a child, look it up in the standard keys. If a
1153 * key exist, use that instead of the child; if it doesn't,
1154 * prefix "AT&T UNIX " to the existing data.
1155 */
1156
1157 if (NULL == (n = mdoc->last->child))
1158 return(1);
1159
1160 assert(MDOC_TEXT == n->type);
1161 if (NULL == (std_att = mdoc_a2att(n->string))) {
1162 mandoc_vmsg(MANDOCERR_AT_BAD, mdoc->parse,
1163 n->line, n->pos, "At %s", n->string);
1164 mandoc_asprintf(&att, "AT&T UNIX %s", n->string);
1165 } else
1166 att = mandoc_strdup(std_att);
1167
1168 free(n->string);
1169 n->string = att;
1170 return(1);
1171 }
1172
1173 static int
1174 post_an(POST_ARGS)
1175 {
1176 struct mdoc_node *np;
1177
1178 np = mdoc->last;
1179 if (AUTH__NONE == np->norm->An.auth) {
1180 if (0 == np->child)
1181 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0);
1182 } else if (np->child)
1183 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0);
1184
1185 return(1);
1186 }
1187
1188 static int
1189 post_en(POST_ARGS)
1190 {
1191
1192 if (MDOC_BLOCK == mdoc->last->type)
1193 mdoc->last->norm->Es = mdoc->last_es;
1194 return(1);
1195 }
1196
1197 static int
1198 post_es(POST_ARGS)
1199 {
1200
1201 mdoc->last_es = mdoc->last;
1202 return(1);
1203 }
1204
1205 static int
1206 post_it(POST_ARGS)
1207 {
1208 int i, cols;
1209 enum mdoc_list lt;
1210 struct mdoc_node *nbl, *nit, *nch;
1211
1212 nit = mdoc->last;
1213 if (MDOC_BLOCK != nit->type)
1214 return(1);
1215
1216 nbl = nit->parent->parent;
1217 lt = nbl->norm->Bl.type;
1218
1219 switch (lt) {
1220 case LIST_tag:
1221 /* FALLTHROUGH */
1222 case LIST_hang:
1223 /* FALLTHROUGH */
1224 case LIST_ohang:
1225 /* FALLTHROUGH */
1226 case LIST_inset:
1227 /* FALLTHROUGH */
1228 case LIST_diag:
1229 if (NULL == nit->head->child)
1230 mandoc_vmsg(MANDOCERR_IT_NOHEAD,
1231 mdoc->parse, nit->line, nit->pos,
1232 "Bl -%s It",
1233 mdoc_argnames[nbl->args->argv[0].arg]);
1234 break;
1235 case LIST_bullet:
1236 /* FALLTHROUGH */
1237 case LIST_dash:
1238 /* FALLTHROUGH */
1239 case LIST_enum:
1240 /* FALLTHROUGH */
1241 case LIST_hyphen:
1242 if (NULL == nit->body->child)
1243 mandoc_vmsg(MANDOCERR_IT_NOBODY,
1244 mdoc->parse, nit->line, nit->pos,
1245 "Bl -%s It",
1246 mdoc_argnames[nbl->args->argv[0].arg]);
1247 /* FALLTHROUGH */
1248 case LIST_item:
1249 if (NULL != nit->head->child)
1250 mandoc_vmsg(MANDOCERR_ARG_SKIP,
1251 mdoc->parse, nit->line, nit->pos,
1252 "It %s", nit->head->child->string);
1253 break;
1254 case LIST_column:
1255 cols = (int)nbl->norm->Bl.ncols;
1256
1257 assert(NULL == nit->head->child);
1258
1259 for (i = 0, nch = nit->child; nch; nch = nch->next)
1260 if (MDOC_BODY == nch->type)
1261 i++;
1262
1263 if (i < cols || i > cols + 1)
1264 mandoc_vmsg(MANDOCERR_ARGCOUNT,
1265 mdoc->parse, nit->line, nit->pos,
1266 "columns == %d (have %d)", cols, i);
1267 break;
1268 default:
1269 abort();
1270 }
1271
1272 return(1);
1273 }
1274
1275 static int
1276 post_bl_block(POST_ARGS)
1277 {
1278 struct mdoc_node *n, *ni, *nc;
1279
1280 /*
1281 * These are fairly complicated, so we've broken them into two
1282 * functions. post_bl_block_tag() is called when a -tag is
1283 * specified, but no -width (it must be guessed). The second
1284 * when a -width is specified (macro indicators must be
1285 * rewritten into real lengths).
1286 */
1287
1288 n = mdoc->last;
1289
1290 if (LIST_tag == n->norm->Bl.type &&
1291 NULL == n->norm->Bl.width) {
1292 if ( ! post_bl_block_tag(mdoc))
1293 return(0);
1294 assert(n->norm->Bl.width);
1295 } else if (NULL != n->norm->Bl.width) {
1296 if ( ! post_bl_block_width(mdoc))
1297 return(0);
1298 assert(n->norm->Bl.width);
1299 }
1300
1301 for (ni = n->body->child; ni; ni = ni->next) {
1302 if (NULL == ni->body)
1303 continue;
1304 nc = ni->body->last;
1305 while (NULL != nc) {
1306 switch (nc->tok) {
1307 case MDOC_Pp:
1308 /* FALLTHROUGH */
1309 case MDOC_Lp:
1310 /* FALLTHROUGH */
1311 case MDOC_br:
1312 break;
1313 default:
1314 nc = NULL;
1315 continue;
1316 }
1317 if (NULL == ni->next) {
1318 mandoc_msg(MANDOCERR_PAR_MOVE,
1319 mdoc->parse, nc->line, nc->pos,
1320 mdoc_macronames[nc->tok]);
1321 if ( ! mdoc_node_relink(mdoc, nc))
1322 return(0);
1323 } else if (0 == n->norm->Bl.comp &&
1324 LIST_column != n->norm->Bl.type) {
1325 mandoc_vmsg(MANDOCERR_PAR_SKIP,
1326 mdoc->parse, nc->line, nc->pos,
1327 "%s before It",
1328 mdoc_macronames[nc->tok]);
1329 mdoc_node_delete(mdoc, nc);
1330 } else
1331 break;
1332 nc = ni->body->last;
1333 }
1334 }
1335 return(1);
1336 }
1337
1338 static int
1339 post_bl_block_width(POST_ARGS)
1340 {
1341 size_t width;
1342 int i;
1343 enum mdoct tok;
1344 struct mdoc_node *n;
1345 char buf[24];
1346
1347 n = mdoc->last;
1348
1349 /*
1350 * Calculate the real width of a list from the -width string,
1351 * which may contain a macro (with a known default width), a
1352 * literal string, or a scaling width.
1353 *
1354 * If the value to -width is a macro, then we re-write it to be
1355 * the macro's width as set in share/tmac/mdoc/doc-common.
1356 */
1357
1358 if (0 == strcmp(n->norm->Bl.width, "Ds"))
1359 width = 6;
1360 else if (MDOC_MAX == (tok = mdoc_hash_find(n->norm->Bl.width)))
1361 return(1);
1362 else
1363 width = macro2len(tok);
1364
1365 /* The value already exists: free and reallocate it. */
1366
1367 assert(n->args);
1368
1369 for (i = 0; i < (int)n->args->argc; i++)
1370 if (MDOC_Width == n->args->argv[i].arg)
1371 break;
1372
1373 assert(i < (int)n->args->argc);
1374
1375 (void)snprintf(buf, sizeof(buf), "%un", (unsigned int)width);
1376 free(n->args->argv[i].value[0]);
1377 n->args->argv[i].value[0] = mandoc_strdup(buf);
1378
1379 /* Set our width! */
1380 n->norm->Bl.width = n->args->argv[i].value[0];
1381 return(1);
1382 }
1383
1384 static int
1385 post_bl_block_tag(POST_ARGS)
1386 {
1387 struct mdoc_node *n, *nn;
1388 size_t sz, ssz;
1389 int i;
1390 char buf[24];
1391
1392 /*
1393 * Calculate the -width for a `Bl -tag' list if it hasn't been
1394 * provided. Uses the first head macro. NOTE AGAIN: this is
1395 * ONLY if the -width argument has NOT been provided. See
1396 * post_bl_block_width() for converting the -width string.
1397 */
1398
1399 sz = 10;
1400 n = mdoc->last;
1401
1402 for (nn = n->body->child; nn; nn = nn->next) {
1403 if (MDOC_It != nn->tok)
1404 continue;
1405
1406 assert(MDOC_BLOCK == nn->type);
1407 nn = nn->head->child;
1408
1409 if (nn == NULL)
1410 break;
1411
1412 if (MDOC_TEXT == nn->type) {
1413 sz = strlen(nn->string) + 1;
1414 break;
1415 }
1416
1417 if (0 != (ssz = macro2len(nn->tok)))
1418 sz = ssz;
1419
1420 break;
1421 }
1422
1423 /* Defaults to ten ens. */
1424
1425 (void)snprintf(buf, sizeof(buf), "%un", (unsigned int)sz);
1426
1427 /*
1428 * We have to dynamically add this to the macro's argument list.
1429 * We're guaranteed that a MDOC_Width doesn't already exist.
1430 */
1431
1432 assert(n->args);
1433 i = (int)(n->args->argc)++;
1434
1435 n->args->argv = mandoc_reallocarray(n->args->argv,
1436 n->args->argc, sizeof(struct mdoc_argv));
1437
1438 n->args->argv[i].arg = MDOC_Width;
1439 n->args->argv[i].line = n->line;
1440 n->args->argv[i].pos = n->pos;
1441 n->args->argv[i].sz = 1;
1442 n->args->argv[i].value = mandoc_malloc(sizeof(char *));
1443 n->args->argv[i].value[0] = mandoc_strdup(buf);
1444
1445 /* Set our width! */
1446 n->norm->Bl.width = n->args->argv[i].value[0];
1447 return(1);
1448 }
1449
1450 static int
1451 post_bl_head(POST_ARGS)
1452 {
1453 struct mdoc_node *np, *nn, *nnp;
1454 struct mdoc_argv *argv;
1455 int i, j;
1456
1457 if (LIST_column != mdoc->last->norm->Bl.type)
1458 /* FIXME: this should be ERROR class... */
1459 return(hwarn_eq0(mdoc));
1460
1461 /*
1462 * Append old-style lists, where the column width specifiers
1463 * trail as macro parameters, to the new-style ("normal-form")
1464 * lists where they're argument values following -column.
1465 */
1466
1467 if (mdoc->last->child == NULL)
1468 return(1);
1469
1470 np = mdoc->last->parent;
1471 assert(np->args);
1472
1473 for (j = 0; j < (int)np->args->argc; j++)
1474 if (MDOC_Column == np->args->argv[j].arg)
1475 break;
1476
1477 assert(j < (int)np->args->argc);
1478
1479 /*
1480 * Accommodate for new-style groff column syntax. Shuffle the
1481 * child nodes, all of which must be TEXT, as arguments for the
1482 * column field. Then, delete the head children.
1483 */
1484
1485 argv = np->args->argv + j;
1486 i = argv->sz;
1487 argv->sz += mdoc->last->nchild;
1488 argv->value = mandoc_reallocarray(argv->value,
1489 argv->sz, sizeof(char *));
1490
1491 mdoc->last->norm->Bl.ncols = argv->sz;
1492 mdoc->last->norm->Bl.cols = (void *)argv->value;
1493
1494 for (nn = mdoc->last->child; nn; i++) {
1495 argv->value[i] = nn->string;
1496 nn->string = NULL;
1497 nnp = nn;
1498 nn = nn->next;
1499 mdoc_node_delete(NULL, nnp);
1500 }
1501
1502 mdoc->last->nchild = 0;
1503 mdoc->last->child = NULL;
1504
1505 return(1);
1506 }
1507
1508 static int
1509 post_bl(POST_ARGS)
1510 {
1511 struct mdoc_node *nparent, *nprev; /* of the Bl block */
1512 struct mdoc_node *nblock, *nbody; /* of the Bl */
1513 struct mdoc_node *nchild, *nnext; /* of the Bl body */
1514
1515 nbody = mdoc->last;
1516 switch (nbody->type) {
1517 case MDOC_BLOCK:
1518 return(post_bl_block(mdoc));
1519 case MDOC_HEAD:
1520 return(post_bl_head(mdoc));
1521 case MDOC_BODY:
1522 break;
1523 default:
1524 return(1);
1525 }
1526
1527 nchild = nbody->child;
1528 while (NULL != nchild) {
1529 if (MDOC_It == nchild->tok || MDOC_Sm == nchild->tok) {
1530 nchild = nchild->next;
1531 continue;
1532 }
1533
1534 mandoc_msg(MANDOCERR_BL_MOVE, mdoc->parse,
1535 nchild->line, nchild->pos,
1536 mdoc_macronames[nchild->tok]);
1537
1538 /*
1539 * Move the node out of the Bl block.
1540 * First, collect all required node pointers.
1541 */
1542
1543 nblock = nbody->parent;
1544 nprev = nblock->prev;
1545 nparent = nblock->parent;
1546 nnext = nchild->next;
1547
1548 /*
1549 * Unlink this child.
1550 */
1551
1552 assert(NULL == nchild->prev);
1553 if (0 == --nbody->nchild) {
1554 nbody->child = NULL;
1555 nbody->last = NULL;
1556 assert(NULL == nnext);
1557 } else {
1558 nbody->child = nnext;
1559 nnext->prev = NULL;
1560 }
1561
1562 /*
1563 * Relink this child.
1564 */
1565
1566 nchild->parent = nparent;
1567 nchild->prev = nprev;
1568 nchild->next = nblock;
1569
1570 nblock->prev = nchild;
1571 nparent->nchild++;
1572 if (NULL == nprev)
1573 nparent->child = nchild;
1574 else
1575 nprev->next = nchild;
1576
1577 nchild = nnext;
1578 }
1579
1580 return(1);
1581 }
1582
1583 static int
1584 ebool(struct mdoc *mdoc)
1585 {
1586 struct mdoc_node *nch;
1587 enum mdoct tok;
1588
1589 tok = mdoc->last->tok;
1590 nch = mdoc->last->child;
1591
1592 if (NULL == nch) {
1593 if (MDOC_Sm == tok)
1594 mdoc->flags ^= MDOC_SMOFF;
1595 return(1);
1596 }
1597
1598 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_LT, 2);
1599
1600 assert(MDOC_TEXT == nch->type);
1601
1602 if (0 == strcmp(nch->string, "on")) {
1603 if (MDOC_Sm == tok)
1604 mdoc->flags &= ~MDOC_SMOFF;
1605 return(1);
1606 }
1607 if (0 == strcmp(nch->string, "off")) {
1608 if (MDOC_Sm == tok)
1609 mdoc->flags |= MDOC_SMOFF;
1610 return(1);
1611 }
1612
1613 mandoc_vmsg(MANDOCERR_SM_BAD,
1614 mdoc->parse, nch->line, nch->pos,
1615 "%s %s", mdoc_macronames[tok], nch->string);
1616 return(mdoc_node_relink(mdoc, nch));
1617 }
1618
1619 static int
1620 post_root(POST_ARGS)
1621 {
1622 struct mdoc_node *n;
1623
1624 /* Add missing prologue data. */
1625
1626 if ( ! (MDOC_PBODY & mdoc->flags)) {
1627 mandoc_msg(MANDOCERR_PROLOG_BAD, mdoc->parse, 0, 0, "EOF");
1628 if (mdoc->meta.date == NULL)
1629 mdoc->meta.date = mdoc->quick ?
1630 mandoc_strdup("") :
1631 mandoc_normdate(mdoc->parse, NULL, 0, 0);
1632 if (mdoc->meta.title == NULL)
1633 mdoc->meta.title = mandoc_strdup("UNKNOWN");
1634 if (mdoc->meta.vol == NULL)
1635 mdoc->meta.vol = mandoc_strdup("LOCAL");
1636 if (mdoc->meta.arch == NULL)
1637 mdoc->meta.msec = mandoc_strdup("1");
1638 if (mdoc->meta.os == NULL)
1639 mdoc->meta.os = mandoc_strdup("UNKNOWN");
1640 }
1641
1642 n = mdoc->first;
1643 assert(n);
1644
1645 /* Check that we begin with a proper `Sh'. */
1646
1647 if (NULL == n->child)
1648 mandoc_msg(MANDOCERR_DOC_EMPTY, mdoc->parse,
1649 n->line, n->pos, NULL);
1650 else if (MDOC_Sh != n->child->tok)
1651 mandoc_msg(MANDOCERR_SEC_BEFORE, mdoc->parse,
1652 n->child->line, n->child->pos,
1653 mdoc_macronames[n->child->tok]);
1654
1655 return(1);
1656 }
1657
1658 static int
1659 post_st(POST_ARGS)
1660 {
1661 struct mdoc_node *n, *nch;
1662 const char *p;
1663
1664 n = mdoc->last;
1665 nch = n->child;
1666
1667 if (NULL == nch) {
1668 mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse,
1669 n->line, n->pos, mdoc_macronames[n->tok]);
1670 mdoc_node_delete(mdoc, n);
1671 return(1);
1672 }
1673
1674 assert(MDOC_TEXT == nch->type);
1675
1676 if (NULL == (p = mdoc_a2st(nch->string))) {
1677 mandoc_vmsg(MANDOCERR_ST_BAD, mdoc->parse,
1678 nch->line, nch->pos, "St %s", nch->string);
1679 mdoc_node_delete(mdoc, n);
1680 } else {
1681 free(nch->string);
1682 nch->string = mandoc_strdup(p);
1683 }
1684
1685 return(1);
1686 }
1687
1688 static int
1689 post_rs(POST_ARGS)
1690 {
1691 struct mdoc_node *nn, *next, *prev;
1692 int i, j;
1693
1694 switch (mdoc->last->type) {
1695 case MDOC_HEAD:
1696 check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0);
1697 return(1);
1698 case MDOC_BODY:
1699 if (mdoc->last->child)
1700 break;
1701 check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0);
1702 return(1);
1703 default:
1704 return(1);
1705 }
1706
1707 /*
1708 * The full `Rs' block needs special handling to order the
1709 * sub-elements according to `rsord'. Pick through each element
1710 * and correctly order it. This is an insertion sort.
1711 */
1712
1713 next = NULL;
1714 for (nn = mdoc->last->child->next; nn; nn = next) {
1715 /* Determine order of `nn'. */
1716 for (i = 0; i < RSORD_MAX; i++)
1717 if (rsord[i] == nn->tok)
1718 break;
1719
1720 if (i == RSORD_MAX) {
1721 mandoc_msg(MANDOCERR_RS_BAD,
1722 mdoc->parse, nn->line, nn->pos,
1723 mdoc_macronames[nn->tok]);
1724 i = -1;
1725 } else if (MDOC__J == nn->tok || MDOC__B == nn->tok)
1726 mdoc->last->norm->Rs.quote_T++;
1727
1728 /*
1729 * Remove `nn' from the chain. This somewhat
1730 * repeats mdoc_node_unlink(), but since we're
1731 * just re-ordering, there's no need for the
1732 * full unlink process.
1733 */
1734
1735 if (NULL != (next = nn->next))
1736 next->prev = nn->prev;
1737
1738 if (NULL != (prev = nn->prev))
1739 prev->next = nn->next;
1740
1741 nn->prev = nn->next = NULL;
1742
1743 /*
1744 * Scan back until we reach a node that's
1745 * ordered before `nn'.
1746 */
1747
1748 for ( ; prev ; prev = prev->prev) {
1749 /* Determine order of `prev'. */
1750 for (j = 0; j < RSORD_MAX; j++)
1751 if (rsord[j] == prev->tok)
1752 break;
1753 if (j == RSORD_MAX)
1754 j = -1;
1755
1756 if (j <= i)
1757 break;
1758 }
1759
1760 /*
1761 * Set `nn' back into its correct place in front
1762 * of the `prev' node.
1763 */
1764
1765 nn->prev = prev;
1766
1767 if (prev) {
1768 if (prev->next)
1769 prev->next->prev = nn;
1770 nn->next = prev->next;
1771 prev->next = nn;
1772 } else {
1773 mdoc->last->child->prev = nn;
1774 nn->next = mdoc->last->child;
1775 mdoc->last->child = nn;
1776 }
1777 }
1778
1779 return(1);
1780 }
1781
1782 /*
1783 * For some arguments of some macros,
1784 * convert all breakable hyphens into ASCII_HYPH.
1785 */
1786 static int
1787 post_hyph(POST_ARGS)
1788 {
1789 struct mdoc_node *n, *nch;
1790 char *cp;
1791
1792 n = mdoc->last;
1793 switch (n->type) {
1794 case MDOC_HEAD:
1795 if (MDOC_Sh == n->tok || MDOC_Ss == n->tok)
1796 break;
1797 return(1);
1798 case MDOC_BODY:
1799 if (MDOC_D1 == n->tok || MDOC_Nd == n->tok)
1800 break;
1801 return(1);
1802 case MDOC_ELEM:
1803 break;
1804 default:
1805 return(1);
1806 }
1807
1808 for (nch = n->child; nch; nch = nch->next) {
1809 if (MDOC_TEXT != nch->type)
1810 continue;
1811 cp = nch->string;
1812 if ('\0' == *cp)
1813 continue;
1814 while ('\0' != *(++cp))
1815 if ('-' == *cp &&
1816 isalpha((unsigned char)cp[-1]) &&
1817 isalpha((unsigned char)cp[1]))
1818 *cp = ASCII_HYPH;
1819 }
1820 return(1);
1821 }
1822
1823 static int
1824 post_ns(POST_ARGS)
1825 {
1826
1827 if (MDOC_LINE & mdoc->last->flags)
1828 mandoc_msg(MANDOCERR_NS_SKIP, mdoc->parse,
1829 mdoc->last->line, mdoc->last->pos, NULL);
1830 return(1);
1831 }
1832
1833 static int
1834 post_sh(POST_ARGS)
1835 {
1836
1837 if (MDOC_HEAD == mdoc->last->type)
1838 return(post_sh_head(mdoc));
1839 if (MDOC_BODY == mdoc->last->type)
1840 return(post_sh_body(mdoc));
1841
1842 return(1);
1843 }
1844
1845 static int
1846 post_sh_body(POST_ARGS)
1847 {
1848 struct mdoc_node *n;
1849
1850 if (SEC_NAME != mdoc->lastsec)
1851 return(1);
1852
1853 /*
1854 * Warn if the NAME section doesn't contain the `Nm' and `Nd'
1855 * macros (can have multiple `Nm' and one `Nd'). Note that the
1856 * children of the BODY declaration can also be "text".
1857 */
1858
1859 if (NULL == (n = mdoc->last->child)) {
1860 mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
1861 mdoc->last->line, mdoc->last->pos, "empty");
1862 return(1);
1863 }
1864
1865 for ( ; n && n->next; n = n->next) {
1866 if (MDOC_ELEM == n->type && MDOC_Nm == n->tok)
1867 continue;
1868 if (MDOC_TEXT == n->type)
1869 continue;
1870 mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
1871 n->line, n->pos, mdoc_macronames[n->tok]);
1872 }
1873
1874 assert(n);
1875 if (MDOC_BLOCK == n->type && MDOC_Nd == n->tok)
1876 return(1);
1877
1878 mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
1879 n->line, n->pos, mdoc_macronames[n->tok]);
1880 return(1);
1881 }
1882
1883 static int
1884 post_sh_head(POST_ARGS)
1885 {
1886 struct mdoc_node *n;
1887 const char *goodsec;
1888 char *secname;
1889 enum mdoc_sec sec;
1890
1891 /*
1892 * Process a new section. Sections are either "named" or
1893 * "custom". Custom sections are user-defined, while named ones
1894 * follow a conventional order and may only appear in certain
1895 * manual sections.
1896 */
1897
1898 secname = NULL;
1899 sec = SEC_CUSTOM;
1900 mdoc_deroff(&secname, mdoc->last);
1901 sec = NULL == secname ? SEC_CUSTOM : a2sec(secname);
1902
1903 /* The NAME should be first. */
1904
1905 if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed)
1906 mandoc_vmsg(MANDOCERR_NAMESEC_FIRST, mdoc->parse,
1907 mdoc->last->line, mdoc->last->pos,
1908 "Sh %s", secname);
1909
1910 /* The SYNOPSIS gets special attention in other areas. */
1911
1912 if (SEC_SYNOPSIS == sec) {
1913 roff_setreg(mdoc->roff, "nS", 1, '=');
1914 mdoc->flags |= MDOC_SYNOPSIS;
1915 } else {
1916 roff_setreg(mdoc->roff, "nS", 0, '=');
1917 mdoc->flags &= ~MDOC_SYNOPSIS;
1918 }
1919
1920 /* Mark our last section. */
1921
1922 mdoc->lastsec = sec;
1923
1924 /*
1925 * Set the section attribute for the current HEAD, for its
1926 * parent BLOCK, and for the HEAD children; the latter can
1927 * only be TEXT nodes, so no recursion is needed.
1928 * For other blocks and elements, including .Sh BODY, this is
1929 * done when allocating the node data structures, but for .Sh
1930 * BLOCK and HEAD, the section is still unknown at that time.
1931 */
1932
1933 mdoc->last->parent->sec = sec;
1934 mdoc->last->sec = sec;
1935 for (n = mdoc->last->child; n; n = n->next)
1936 n->sec = sec;
1937
1938 /* We don't care about custom sections after this. */
1939
1940 if (SEC_CUSTOM == sec) {
1941 free(secname);
1942 return(1);
1943 }
1944
1945 /*
1946 * Check whether our non-custom section is being repeated or is
1947 * out of order.
1948 */
1949
1950 if (sec == mdoc->lastnamed)
1951 mandoc_vmsg(MANDOCERR_SEC_REP, mdoc->parse,
1952 mdoc->last->line, mdoc->last->pos,
1953 "Sh %s", secname);
1954
1955 if (sec < mdoc->lastnamed)
1956 mandoc_vmsg(MANDOCERR_SEC_ORDER, mdoc->parse,
1957 mdoc->last->line, mdoc->last->pos,
1958 "Sh %s", secname);
1959
1960 /* Mark the last named section. */
1961
1962 mdoc->lastnamed = sec;
1963
1964 /* Check particular section/manual conventions. */
1965
1966 assert(mdoc->meta.msec);
1967
1968 goodsec = NULL;
1969 switch (sec) {
1970 case SEC_ERRORS:
1971 if (*mdoc->meta.msec == '4')
1972 break;
1973 goodsec = "2, 3, 4, 9";
1974 /* FALLTHROUGH */
1975 case SEC_RETURN_VALUES:
1976 /* FALLTHROUGH */
1977 case SEC_LIBRARY:
1978 if (*mdoc->meta.msec == '2')
1979 break;
1980 if (*mdoc->meta.msec == '3')
1981 break;
1982 if (NULL == goodsec)
1983 goodsec = "2, 3, 9";
1984 /* FALLTHROUGH */
1985 case SEC_CONTEXT:
1986 if (*mdoc->meta.msec == '9')
1987 break;
1988 if (NULL == goodsec)
1989 goodsec = "9";
1990 mandoc_vmsg(MANDOCERR_SEC_MSEC, mdoc->parse,
1991 mdoc->last->line, mdoc->last->pos,
1992 "Sh %s for %s only", secname, goodsec);
1993 break;
1994 default:
1995 break;
1996 }
1997
1998 free(secname);
1999 return(1);
2000 }
2001
2002 static int
2003 post_ignpar(POST_ARGS)
2004 {
2005 struct mdoc_node *np;
2006
2007 if (MDOC_BODY != mdoc->last->type)
2008 return(1);
2009
2010 if (NULL != (np = mdoc->last->child))
2011 if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
2012 mandoc_vmsg(MANDOCERR_PAR_SKIP,
2013 mdoc->parse, np->line, np->pos,
2014 "%s after %s", mdoc_macronames[np->tok],
2015 mdoc_macronames[mdoc->last->tok]);
2016 mdoc_node_delete(mdoc, np);
2017 }
2018
2019 if (NULL != (np = mdoc->last->last))
2020 if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
2021 mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2022 np->line, np->pos, "%s at the end of %s",
2023 mdoc_macronames[np->tok],
2024 mdoc_macronames[mdoc->last->tok]);
2025 mdoc_node_delete(mdoc, np);
2026 }
2027
2028 return(1);
2029 }
2030
2031 static int
2032 pre_par(PRE_ARGS)
2033 {
2034
2035 if (NULL == mdoc->last)
2036 return(1);
2037 if (MDOC_ELEM != n->type && MDOC_BLOCK != n->type)
2038 return(1);
2039
2040 /*
2041 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
2042 * block: `Lp', `Pp', or non-compact `Bd' or `Bl'.
2043 */
2044
2045 if (MDOC_Pp != mdoc->last->tok &&
2046 MDOC_Lp != mdoc->last->tok &&
2047 MDOC_br != mdoc->last->tok)
2048 return(1);
2049 if (MDOC_Bl == n->tok && n->norm->Bl.comp)
2050 return(1);
2051 if (MDOC_Bd == n->tok && n->norm->Bd.comp)
2052 return(1);
2053 if (MDOC_It == n->tok && n->parent->norm->Bl.comp)
2054 return(1);
2055
2056 mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2057 mdoc->last->line, mdoc->last->pos,
2058 "%s before %s", mdoc_macronames[mdoc->last->tok],
2059 mdoc_macronames[n->tok]);
2060 mdoc_node_delete(mdoc, mdoc->last);
2061 return(1);
2062 }
2063
2064 static int
2065 post_par(POST_ARGS)
2066 {
2067 struct mdoc_node *np;
2068
2069 if (MDOC_ELEM != mdoc->last->type &&
2070 MDOC_BLOCK != mdoc->last->type)
2071 return(1);
2072
2073 if (NULL == (np = mdoc->last->prev)) {
2074 np = mdoc->last->parent;
2075 if (MDOC_Sh != np->tok && MDOC_Ss != np->tok)
2076 return(1);
2077 } else {
2078 if (MDOC_Pp != np->tok && MDOC_Lp != np->tok &&
2079 (MDOC_br != mdoc->last->tok ||
2080 (MDOC_sp != np->tok && MDOC_br != np->tok)))
2081 return(1);
2082 }
2083
2084 mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2085 mdoc->last->line, mdoc->last->pos,
2086 "%s after %s", mdoc_macronames[mdoc->last->tok],
2087 mdoc_macronames[np->tok]);
2088 mdoc_node_delete(mdoc, mdoc->last);
2089 return(1);
2090 }
2091
2092 static int
2093 pre_literal(PRE_ARGS)
2094 {
2095
2096 pre_display(mdoc, n);
2097
2098 if (MDOC_BODY != n->type)
2099 return(1);
2100
2101 /*
2102 * The `Dl' (note "el" not "one") and `Bd -literal' and `Bd
2103 * -unfilled' macros set MDOC_LITERAL on entrance to the body.
2104 */
2105
2106 switch (n->tok) {
2107 case MDOC_Dl:
2108 mdoc->flags |= MDOC_LITERAL;
2109 break;
2110 case MDOC_Bd:
2111 if (DISP_literal == n->norm->Bd.type)
2112 mdoc->flags |= MDOC_LITERAL;
2113 if (DISP_unfilled == n->norm->Bd.type)
2114 mdoc->flags |= MDOC_LITERAL;
2115 break;
2116 default:
2117 abort();
2118 /* NOTREACHED */
2119 }
2120
2121 return(1);
2122 }
2123
2124 static int
2125 post_dd(POST_ARGS)
2126 {
2127 struct mdoc_node *n;
2128 char *datestr;
2129
2130 if (mdoc->meta.date)
2131 free(mdoc->meta.date);
2132
2133 n = mdoc->last;
2134 if (NULL == n->child || '\0' == n->child->string[0]) {
2135 mdoc->meta.date = mdoc->quick ? mandoc_strdup("") :
2136 mandoc_normdate(mdoc->parse, NULL, n->line, n->pos);
2137 return(1);
2138 }
2139
2140 datestr = NULL;
2141 mdoc_deroff(&datestr, n);
2142 if (mdoc->quick)
2143 mdoc->meta.date = datestr;
2144 else {
2145 mdoc->meta.date = mandoc_normdate(mdoc->parse,
2146 datestr, n->line, n->pos);
2147 free(datestr);
2148 }
2149 return(1);
2150 }
2151
2152 static int
2153 post_dt(POST_ARGS)
2154 {
2155 struct mdoc_node *nn, *n;
2156 const char *cp;
2157 char *p;
2158
2159 n = mdoc->last;
2160
2161 if (mdoc->meta.title)
2162 free(mdoc->meta.title);
2163 if (mdoc->meta.vol)
2164 free(mdoc->meta.vol);
2165 if (mdoc->meta.arch)
2166 free(mdoc->meta.arch);
2167
2168 mdoc->meta.title = mdoc->meta.vol = mdoc->meta.arch = NULL;
2169
2170 /* First check that all characters are uppercase. */
2171
2172 if (NULL != (nn = n->child))
2173 for (p = nn->string; *p; p++) {
2174 if (toupper((unsigned char)*p) == *p)
2175 continue;
2176 mandoc_vmsg(MANDOCERR_TITLE_CASE,
2177 mdoc->parse, nn->line,
2178 nn->pos + (p - nn->string),
2179 "Dt %s", nn->string);
2180 break;
2181 }
2182
2183 /* Handles: `.Dt'
2184 * title = unknown, volume = local, msec = 0, arch = NULL
2185 */
2186
2187 if (NULL == (nn = n->child)) {
2188 /* XXX: make these macro values. */
2189 /* FIXME: warn about missing values. */
2190 mdoc->meta.title = mandoc_strdup("UNKNOWN");
2191 mdoc->meta.vol = mandoc_strdup("LOCAL");
2192 mdoc->meta.msec = mandoc_strdup("1");
2193 return(1);
2194 }
2195
2196 /* Handles: `.Dt TITLE'
2197 * title = TITLE, volume = local, msec = 0, arch = NULL
2198 */
2199
2200 mdoc->meta.title = mandoc_strdup(
2201 '\0' == nn->string[0] ? "UNKNOWN" : nn->string);
2202
2203 if (NULL == (nn = nn->next)) {
2204 /* FIXME: warn about missing msec. */
2205 /* XXX: make this a macro value. */
2206 mdoc->meta.vol = mandoc_strdup("LOCAL");
2207 mdoc->meta.msec = mandoc_strdup("1");
2208 return(1);
2209 }
2210
2211 /* Handles: `.Dt TITLE SEC'
2212 * title = TITLE,
2213 * volume = SEC is msec ? format(msec) : SEC,
2214 * msec = SEC is msec ? atoi(msec) : 0,
2215 * arch = NULL
2216 */
2217
2218 cp = mandoc_a2msec(nn->string);
2219 if (cp) {
2220 mdoc->meta.vol = mandoc_strdup(cp);
2221 mdoc->meta.msec = mandoc_strdup(nn->string);
2222 } else {
2223 mandoc_vmsg(MANDOCERR_MSEC_BAD, mdoc->parse,
2224 nn->line, nn->pos, "Dt ... %s", nn->string);
2225 mdoc->meta.vol = mandoc_strdup(nn->string);
2226 mdoc->meta.msec = mandoc_strdup(nn->string);
2227 }
2228
2229 if (NULL == (nn = nn->next))
2230 return(1);
2231
2232 /* Handles: `.Dt TITLE SEC VOL'
2233 * title = TITLE,
2234 * volume = VOL is vol ? format(VOL) :
2235 * VOL is arch ? format(arch) :
2236 * VOL
2237 */
2238
2239 cp = mdoc_a2vol(nn->string);
2240 if (cp) {
2241 free(mdoc->meta.vol);
2242 mdoc->meta.vol = mandoc_strdup(cp);
2243 } else {
2244 cp = mdoc_a2arch(nn->string);
2245 if (NULL == cp) {
2246 mandoc_vmsg(MANDOCERR_ARCH_BAD, mdoc->parse,
2247 nn->line, nn->pos, "Dt ... %s", nn->string);
2248 free(mdoc->meta.vol);
2249 mdoc->meta.vol = mandoc_strdup(nn->string);
2250 } else
2251 mdoc->meta.arch = mandoc_strdup(cp);
2252 }
2253
2254 /* Ignore any subsequent parameters... */
2255 /* FIXME: warn about subsequent parameters. */
2256
2257 return(1);
2258 }
2259
2260 static int
2261 post_prol(POST_ARGS)
2262 {
2263 /*
2264 * Remove prologue macros from the document after they're
2265 * processed. The final document uses mdoc_meta for these
2266 * values and discards the originals.
2267 */
2268
2269 mdoc_node_delete(mdoc, mdoc->last);
2270 if (mdoc->meta.title && mdoc->meta.date && mdoc->meta.os)
2271 mdoc->flags |= MDOC_PBODY;
2272
2273 return(1);
2274 }
2275
2276 static int
2277 post_bx(POST_ARGS)
2278 {
2279 struct mdoc_node *n;
2280
2281 /*
2282 * Make `Bx's second argument always start with an uppercase
2283 * letter. Groff checks if it's an "accepted" term, but we just
2284 * uppercase blindly.
2285 */
2286
2287 n = mdoc->last->child;
2288 if (n && NULL != (n = n->next))
2289 *n->string = (char)toupper((unsigned char)*n->string);
2290
2291 return(1);
2292 }
2293
2294 static int
2295 post_os(POST_ARGS)
2296 {
2297 #ifndef OSNAME
2298 struct utsname utsname;
2299 static char *defbuf;
2300 #endif
2301 struct mdoc_node *n;
2302
2303 n = mdoc->last;
2304
2305 /*
2306 * Set the operating system by way of the `Os' macro.
2307 * The order of precedence is:
2308 * 1. the argument of the `Os' macro, unless empty
2309 * 2. the -Ios=foo command line argument, if provided
2310 * 3. -DOSNAME="\"foo\"", if provided during compilation
2311 * 4. "sysname release" from uname(3)
2312 */
2313
2314 free(mdoc->meta.os);
2315 mdoc->meta.os = NULL;
2316 mdoc_deroff(&mdoc->meta.os, n);
2317 if (mdoc->meta.os)
2318 return(1);
2319
2320 if (mdoc->defos) {
2321 mdoc->meta.os = mandoc_strdup(mdoc->defos);
2322 return(1);
2323 }
2324
2325 #ifdef OSNAME
2326 mdoc->meta.os = mandoc_strdup(OSNAME);
2327 #else /*!OSNAME */
2328 if (NULL == defbuf) {
2329 if (-1 == uname(&utsname)) {
2330 mandoc_msg(MANDOCERR_OS_UNAME, mdoc->parse,
2331 n->line, n->pos, "Os");
2332 defbuf = mandoc_strdup("UNKNOWN");
2333 } else
2334 mandoc_asprintf(&defbuf, "%s %s",
2335 utsname.sysname, utsname.release);
2336 }
2337 mdoc->meta.os = mandoc_strdup(defbuf);
2338 #endif /*!OSNAME*/
2339 return(1);
2340 }
2341
2342 /*
2343 * If no argument is provided,
2344 * fill in the name of the current manual page.
2345 */
2346 static int
2347 post_ex(POST_ARGS)
2348 {
2349 struct mdoc_node *n;
2350
2351 n = mdoc->last;
2352
2353 if (n->child)
2354 return(1);
2355
2356 if (mdoc->meta.name == NULL) {
2357 mandoc_msg(MANDOCERR_EX_NONAME, mdoc->parse,
2358 n->line, n->pos, "Ex");
2359 return(1);
2360 }
2361
2362 mdoc->next = MDOC_NEXT_CHILD;
2363
2364 if ( ! mdoc_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name))
2365 return(0);
2366
2367 mdoc->last = n;
2368 return(1);
2369 }
2370
2371 static enum mdoc_sec
2372 a2sec(const char *p)
2373 {
2374 int i;
2375
2376 for (i = 0; i < (int)SEC__MAX; i++)
2377 if (secnames[i] && 0 == strcmp(p, secnames[i]))
2378 return((enum mdoc_sec)i);
2379
2380 return(SEC_CUSTOM);
2381 }
2382
2383 static size_t
2384 macro2len(enum mdoct macro)
2385 {
2386
2387 switch (macro) {
2388 case MDOC_Ad:
2389 return(12);
2390 case MDOC_Ao:
2391 return(12);
2392 case MDOC_An:
2393 return(12);
2394 case MDOC_Aq:
2395 return(12);
2396 case MDOC_Ar:
2397 return(12);
2398 case MDOC_Bo:
2399 return(12);
2400 case MDOC_Bq:
2401 return(12);
2402 case MDOC_Cd:
2403 return(12);
2404 case MDOC_Cm:
2405 return(10);
2406 case MDOC_Do:
2407 return(10);
2408 case MDOC_Dq:
2409 return(12);
2410 case MDOC_Dv:
2411 return(12);
2412 case MDOC_Eo:
2413 return(12);
2414 case MDOC_Em:
2415 return(10);
2416 case MDOC_Er:
2417 return(17);
2418 case MDOC_Ev:
2419 return(15);
2420 case MDOC_Fa:
2421 return(12);
2422 case MDOC_Fl:
2423 return(10);
2424 case MDOC_Fo:
2425 return(16);
2426 case MDOC_Fn:
2427 return(16);
2428 case MDOC_Ic:
2429 return(10);
2430 case MDOC_Li:
2431 return(16);
2432 case MDOC_Ms:
2433 return(6);
2434 case MDOC_Nm:
2435 return(10);
2436 case MDOC_No:
2437 return(12);
2438 case MDOC_Oo:
2439 return(10);
2440 case MDOC_Op:
2441 return(14);
2442 case MDOC_Pa:
2443 return(32);
2444 case MDOC_Pf:
2445 return(12);
2446 case MDOC_Po:
2447 return(12);
2448 case MDOC_Pq:
2449 return(12);
2450 case MDOC_Ql:
2451 return(16);
2452 case MDOC_Qo:
2453 return(12);
2454 case MDOC_So:
2455 return(12);
2456 case MDOC_Sq:
2457 return(12);
2458 case MDOC_Sy:
2459 return(6);
2460 case MDOC_Sx:
2461 return(16);
2462 case MDOC_Tn:
2463 return(10);
2464 case MDOC_Va:
2465 return(12);
2466 case MDOC_Vt:
2467 return(12);
2468 case MDOC_Xr:
2469 return(10);
2470 default:
2471 break;
2472 };
2473 return(0);
2474 }