]> git.cameronkatri.com Git - mandoc.git/blob - mdoc_validate.c
In .Bl -column, if some of the column width declarations are given
[mandoc.git] / mdoc_validate.c
1 /* $Id: mdoc_validate.c,v 1.237 2014/07/31 00:41:10 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 static v_pre pres_an[] = { pre_an, NULL };
169 static v_pre pres_bd[] = { pre_display, pre_bd, pre_literal, pre_par, NULL };
170 static v_pre pres_bl[] = { pre_bl, pre_par, NULL };
171 static v_pre pres_d1[] = { pre_display, NULL };
172 static v_pre pres_dl[] = { pre_literal, pre_display, NULL };
173 static v_pre pres_dd[] = { pre_dd, NULL };
174 static v_pre pres_dt[] = { pre_dt, NULL };
175 static v_pre pres_it[] = { pre_par, NULL };
176 static v_pre pres_obsolete[] = { pre_obsolete, NULL };
177 static v_pre pres_os[] = { pre_os, NULL };
178 static v_pre pres_pp[] = { pre_par, NULL };
179 static v_pre pres_std[] = { pre_std, NULL };
180
181 static const struct valids mdoc_valids[MDOC_MAX] = {
182 { NULL, NULL }, /* Ap */
183 { pres_dd, posts_dd }, /* Dd */
184 { pres_dt, posts_dt }, /* Dt */
185 { pres_os, posts_os }, /* Os */
186 { NULL, posts_sh }, /* Sh */
187 { NULL, posts_ss }, /* Ss */
188 { pres_pp, posts_pp }, /* Pp */
189 { pres_d1, posts_d1 }, /* D1 */
190 { pres_dl, posts_dl }, /* Dl */
191 { pres_bd, posts_bd }, /* Bd */
192 { NULL, NULL }, /* Ed */
193 { pres_bl, posts_bl }, /* Bl */
194 { NULL, NULL }, /* El */
195 { pres_it, posts_it }, /* It */
196 { NULL, NULL }, /* Ad */
197 { pres_an, posts_an }, /* An */
198 { NULL, posts_defaults }, /* Ar */
199 { NULL, NULL }, /* Cd */
200 { NULL, NULL }, /* Cm */
201 { NULL, NULL }, /* Dv */
202 { NULL, NULL }, /* Er */
203 { NULL, NULL }, /* Ev */
204 { pres_std, posts_ex }, /* Ex */
205 { NULL, NULL }, /* Fa */
206 { NULL, posts_text }, /* Fd */
207 { NULL, NULL }, /* Fl */
208 { NULL, NULL }, /* Fn */
209 { NULL, NULL }, /* Ft */
210 { NULL, NULL }, /* Ic */
211 { NULL, posts_text1 }, /* In */
212 { NULL, posts_defaults }, /* Li */
213 { NULL, posts_nd }, /* Nd */
214 { NULL, posts_nm }, /* Nm */
215 { NULL, NULL }, /* Op */
216 { pres_obsolete, NULL }, /* Ot */
217 { NULL, posts_defaults }, /* Pa */
218 { pres_std, NULL }, /* Rv */
219 { NULL, posts_st }, /* St */
220 { NULL, NULL }, /* Va */
221 { NULL, posts_vt }, /* Vt */
222 { NULL, posts_text }, /* Xr */
223 { NULL, posts_text }, /* %A */
224 { NULL, posts_hyphtext }, /* %B */ /* FIXME: can be used outside Rs/Re. */
225 { NULL, posts_text }, /* %D */
226 { NULL, posts_text }, /* %I */
227 { NULL, posts_text }, /* %J */
228 { NULL, posts_hyphtext }, /* %N */
229 { NULL, posts_hyphtext }, /* %O */
230 { NULL, posts_text }, /* %P */
231 { NULL, posts_hyphtext }, /* %R */
232 { NULL, posts_hyphtext }, /* %T */ /* FIXME: can be used outside Rs/Re. */
233 { NULL, posts_text }, /* %V */
234 { NULL, NULL }, /* Ac */
235 { NULL, NULL }, /* Ao */
236 { NULL, NULL }, /* Aq */
237 { NULL, posts_at }, /* At */
238 { NULL, NULL }, /* Bc */
239 { NULL, posts_bf }, /* Bf */
240 { NULL, NULL }, /* Bo */
241 { NULL, NULL }, /* Bq */
242 { NULL, NULL }, /* Bsx */
243 { NULL, posts_bx }, /* Bx */
244 { NULL, posts_bool }, /* Db */
245 { NULL, NULL }, /* Dc */
246 { NULL, NULL }, /* Do */
247 { NULL, NULL }, /* Dq */
248 { NULL, NULL }, /* Ec */
249 { NULL, NULL }, /* Ef */
250 { NULL, NULL }, /* Em */
251 { NULL, NULL }, /* Eo */
252 { NULL, NULL }, /* Fx */
253 { NULL, NULL }, /* Ms */
254 { NULL, posts_notext }, /* No */
255 { NULL, posts_ns }, /* Ns */
256 { NULL, NULL }, /* Nx */
257 { NULL, NULL }, /* Ox */
258 { NULL, NULL }, /* Pc */
259 { NULL, posts_text1 }, /* Pf */
260 { NULL, NULL }, /* Po */
261 { NULL, NULL }, /* Pq */
262 { NULL, NULL }, /* Qc */
263 { NULL, NULL }, /* Ql */
264 { NULL, NULL }, /* Qo */
265 { NULL, NULL }, /* Qq */
266 { NULL, NULL }, /* Re */
267 { NULL, posts_rs }, /* Rs */
268 { NULL, NULL }, /* Sc */
269 { NULL, NULL }, /* So */
270 { NULL, NULL }, /* Sq */
271 { NULL, posts_bool }, /* Sm */
272 { NULL, posts_hyph }, /* Sx */
273 { NULL, NULL }, /* Sy */
274 { NULL, NULL }, /* Tn */
275 { NULL, NULL }, /* Ux */
276 { NULL, NULL }, /* Xc */
277 { NULL, NULL }, /* Xo */
278 { NULL, posts_fo }, /* Fo */
279 { NULL, NULL }, /* Fc */
280 { NULL, NULL }, /* Oo */
281 { NULL, NULL }, /* Oc */
282 { NULL, posts_bk }, /* Bk */
283 { NULL, NULL }, /* Ek */
284 { NULL, posts_eoln }, /* Bt */
285 { NULL, NULL }, /* Hf */
286 { pres_obsolete, NULL }, /* Fr */
287 { NULL, posts_eoln }, /* Ud */
288 { NULL, posts_lb }, /* Lb */
289 { pres_pp, posts_pp }, /* Lp */
290 { NULL, NULL }, /* Lk */
291 { NULL, posts_defaults }, /* Mt */
292 { NULL, NULL }, /* Brq */
293 { NULL, NULL }, /* Bro */
294 { NULL, NULL }, /* Brc */
295 { NULL, posts_text }, /* %C */
296 { pres_obsolete, posts_es }, /* Es */
297 { pres_obsolete, posts_en }, /* En */
298 { NULL, NULL }, /* Dx */
299 { NULL, posts_text }, /* %Q */
300 { NULL, posts_pp }, /* br */
301 { NULL, posts_sp }, /* sp */
302 { NULL, posts_text1 }, /* %U */
303 { NULL, NULL }, /* Ta */
304 { NULL, NULL }, /* ll */
305 };
306
307 #define RSORD_MAX 14 /* Number of `Rs' blocks. */
308
309 static const enum mdoct rsord[RSORD_MAX] = {
310 MDOC__A,
311 MDOC__T,
312 MDOC__B,
313 MDOC__I,
314 MDOC__J,
315 MDOC__R,
316 MDOC__N,
317 MDOC__V,
318 MDOC__U,
319 MDOC__P,
320 MDOC__Q,
321 MDOC__C,
322 MDOC__D,
323 MDOC__O
324 };
325
326 static const char * const secnames[SEC__MAX] = {
327 NULL,
328 "NAME",
329 "LIBRARY",
330 "SYNOPSIS",
331 "DESCRIPTION",
332 "CONTEXT",
333 "IMPLEMENTATION NOTES",
334 "RETURN VALUES",
335 "ENVIRONMENT",
336 "FILES",
337 "EXIT STATUS",
338 "EXAMPLES",
339 "DIAGNOSTICS",
340 "COMPATIBILITY",
341 "ERRORS",
342 "SEE ALSO",
343 "STANDARDS",
344 "HISTORY",
345 "AUTHORS",
346 "CAVEATS",
347 "BUGS",
348 "SECURITY CONSIDERATIONS",
349 NULL
350 };
351
352
353 int
354 mdoc_valid_pre(struct mdoc *mdoc, struct mdoc_node *n)
355 {
356 v_pre *p;
357 int line, pos;
358 char *tp;
359
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;
375 }
376
377 check_args(mdoc, n);
378
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);
385 }
386
387 int
388 mdoc_valid_post(struct mdoc *mdoc)
389 {
390 v_post *p;
391
392 if (MDOC_VALID & mdoc->last->flags)
393 return(1);
394 mdoc->last->flags |= MDOC_VALID;
395
396 switch (mdoc->last->type) {
397 case MDOC_TEXT:
398 /* FALLTHROUGH */
399 case MDOC_EQN:
400 /* FALLTHROUGH */
401 case MDOC_TBL:
402 return(1);
403 case MDOC_ROOT:
404 return(post_root(mdoc));
405 default:
406 break;
407 }
408
409 if (NULL == mdoc_valids[mdoc->last->tok].post)
410 return(1);
411 for (p = mdoc_valids[mdoc->last->tok].post; *p; p++)
412 if ( ! (*p)(mdoc))
413 return(0);
414
415 return(1);
416 }
417
418 static int
419 check_count(struct mdoc *mdoc, enum mdoc_type type,
420 enum check_lvl lvl, enum check_ineq ineq, int val)
421 {
422 const char *p;
423 enum mandocerr t;
424
425 if (mdoc->last->type != type)
426 return(1);
427
428 switch (ineq) {
429 case CHECK_LT:
430 p = "less than ";
431 if (mdoc->last->nchild < val)
432 return(1);
433 break;
434 case CHECK_GT:
435 p = "more than ";
436 if (mdoc->last->nchild > val)
437 return(1);
438 break;
439 case CHECK_EQ:
440 p = "";
441 if (val == mdoc->last->nchild)
442 return(1);
443 break;
444 default:
445 abort();
446 /* NOTREACHED */
447 }
448
449 t = lvl == CHECK_WARN ? MANDOCERR_ARGCWARN : MANDOCERR_ARGCOUNT;
450 mandoc_vmsg(t, mdoc->parse, mdoc->last->line,
451 mdoc->last->pos, "want %s%d children (have %d)",
452 p, val, mdoc->last->nchild);
453 return(1);
454 }
455
456 static int
457 berr_ge1(POST_ARGS)
458 {
459
460 return(check_count(mdoc, MDOC_BODY, CHECK_ERROR, CHECK_GT, 0));
461 }
462
463 static int
464 bwarn_ge1(POST_ARGS)
465 {
466 return(check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0));
467 }
468
469 static int
470 ewarn_eq0(POST_ARGS)
471 {
472 return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0));
473 }
474
475 static int
476 ewarn_eq1(POST_ARGS)
477 {
478 return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1));
479 }
480
481 static int
482 ewarn_ge1(POST_ARGS)
483 {
484 return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0));
485 }
486
487 static int
488 ewarn_le1(POST_ARGS)
489 {
490 return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_LT, 2));
491 }
492
493 static int
494 hwarn_eq0(POST_ARGS)
495 {
496 return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0));
497 }
498
499 static int
500 hwarn_eq1(POST_ARGS)
501 {
502 return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 1));
503 }
504
505 static int
506 hwarn_ge1(POST_ARGS)
507 {
508 return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_GT, 0));
509 }
510
511 static void
512 check_args(struct mdoc *mdoc, struct mdoc_node *n)
513 {
514 int i;
515
516 if (NULL == n->args)
517 return;
518
519 assert(n->args->argc);
520 for (i = 0; i < (int)n->args->argc; i++)
521 check_argv(mdoc, n, &n->args->argv[i]);
522 }
523
524 static void
525 check_argv(struct mdoc *mdoc, struct mdoc_node *n, struct mdoc_argv *v)
526 {
527 int i;
528
529 for (i = 0; i < (int)v->sz; i++)
530 check_text(mdoc, v->line, v->pos, v->value[i]);
531 }
532
533 static void
534 check_text(struct mdoc *mdoc, int ln, int pos, char *p)
535 {
536 char *cp;
537
538 if (MDOC_LITERAL & mdoc->flags)
539 return;
540
541 for (cp = p; NULL != (p = strchr(p, '\t')); p++)
542 mandoc_msg(MANDOCERR_FI_TAB, mdoc->parse,
543 ln, pos + (int)(p - cp), NULL);
544 }
545
546 static int
547 pre_display(PRE_ARGS)
548 {
549 struct mdoc_node *node;
550
551 if (MDOC_BLOCK != n->type)
552 return(1);
553
554 for (node = mdoc->last->parent; node; node = node->parent)
555 if (MDOC_BLOCK == node->type)
556 if (MDOC_Bd == node->tok)
557 break;
558
559 if (node)
560 mandoc_vmsg(MANDOCERR_BD_NEST,
561 mdoc->parse, n->line, n->pos,
562 "%s in Bd", mdoc_macronames[n->tok]);
563
564 return(1);
565 }
566
567 static int
568 pre_bl(PRE_ARGS)
569 {
570 struct mdoc_node *np;
571 struct mdoc_argv *argv;
572 int i;
573 enum mdoc_list lt;
574
575 if (MDOC_BLOCK != n->type) {
576 if (ENDBODY_NOT != n->end) {
577 assert(n->pending);
578 np = n->pending->parent;
579 } else
580 np = n->parent;
581
582 assert(np);
583 assert(MDOC_BLOCK == np->type);
584 assert(MDOC_Bl == np->tok);
585 return(1);
586 }
587
588 /*
589 * First figure out which kind of list to use: bind ourselves to
590 * the first mentioned list type and warn about any remaining
591 * ones. If we find no list type, we default to LIST_item.
592 */
593
594 for (i = 0; n->args && i < (int)n->args->argc; i++) {
595 argv = n->args->argv + i;
596 lt = LIST__NONE;
597 switch (argv->arg) {
598 /* Set list types. */
599 case MDOC_Bullet:
600 lt = LIST_bullet;
601 break;
602 case MDOC_Dash:
603 lt = LIST_dash;
604 break;
605 case MDOC_Enum:
606 lt = LIST_enum;
607 break;
608 case MDOC_Hyphen:
609 lt = LIST_hyphen;
610 break;
611 case MDOC_Item:
612 lt = LIST_item;
613 break;
614 case MDOC_Tag:
615 lt = LIST_tag;
616 break;
617 case MDOC_Diag:
618 lt = LIST_diag;
619 break;
620 case MDOC_Hang:
621 lt = LIST_hang;
622 break;
623 case MDOC_Ohang:
624 lt = LIST_ohang;
625 break;
626 case MDOC_Inset:
627 lt = LIST_inset;
628 break;
629 case MDOC_Column:
630 lt = LIST_column;
631 break;
632 /* Set list arguments. */
633 case MDOC_Compact:
634 if (n->norm->Bl.comp)
635 mandoc_msg(MANDOCERR_ARG_REP,
636 mdoc->parse, argv->line,
637 argv->pos, "Bl -compact");
638 n->norm->Bl.comp = 1;
639 break;
640 case MDOC_Width:
641 if (0 == argv->sz) {
642 mandoc_msg(MANDOCERR_ARG_EMPTY,
643 mdoc->parse, argv->line,
644 argv->pos, "Bl -width");
645 n->norm->Bl.width = "0n";
646 break;
647 }
648 if (NULL != n->norm->Bl.width)
649 mandoc_vmsg(MANDOCERR_ARG_REP,
650 mdoc->parse, argv->line,
651 argv->pos, "Bl -width %s",
652 argv->value[0]);
653 n->norm->Bl.width = argv->value[0];
654 break;
655 case MDOC_Offset:
656 if (0 == argv->sz) {
657 mandoc_msg(MANDOCERR_ARG_EMPTY,
658 mdoc->parse, argv->line,
659 argv->pos, "Bl -offset");
660 break;
661 }
662 if (NULL != n->norm->Bl.offs)
663 mandoc_vmsg(MANDOCERR_ARG_REP,
664 mdoc->parse, argv->line,
665 argv->pos, "Bl -offset %s",
666 argv->value[0]);
667 n->norm->Bl.offs = argv->value[0];
668 break;
669 default:
670 continue;
671 }
672 if (LIST__NONE == lt)
673 continue;
674
675 /* Check: multiple list types. */
676
677 if (LIST__NONE != n->norm->Bl.type) {
678 mandoc_msg(MANDOCERR_BL_REP,
679 mdoc->parse, n->line, n->pos,
680 mdoc_argnames[argv->arg]);
681 continue;
682 }
683
684 /* The list type should come first. */
685
686 if (n->norm->Bl.width ||
687 n->norm->Bl.offs ||
688 n->norm->Bl.comp)
689 mandoc_msg(MANDOCERR_BL_LATETYPE,
690 mdoc->parse, n->line, n->pos,
691 mdoc_argnames[n->args->argv[0].arg]);
692
693 n->norm->Bl.type = lt;
694 if (LIST_column == lt) {
695 n->norm->Bl.ncols = argv->sz;
696 n->norm->Bl.cols = (void *)argv->value;
697 }
698 }
699
700 /* Allow lists to default to LIST_item. */
701
702 if (LIST__NONE == n->norm->Bl.type) {
703 mdoc_nmsg(mdoc, n, MANDOCERR_BL_NOTYPE);
704 n->norm->Bl.type = LIST_item;
705 }
706
707 /*
708 * Validate the width field. Some list types don't need width
709 * types and should be warned about them. Others should have it
710 * and must also be warned. Yet others have a default and need
711 * no warning.
712 */
713
714 switch (n->norm->Bl.type) {
715 case LIST_tag:
716 if (NULL == n->norm->Bl.width)
717 mdoc_nmsg(mdoc, n, MANDOCERR_BL_NOWIDTH);
718 break;
719 case LIST_column:
720 /* FALLTHROUGH */
721 case LIST_diag:
722 /* FALLTHROUGH */
723 case LIST_ohang:
724 /* FALLTHROUGH */
725 case LIST_inset:
726 /* FALLTHROUGH */
727 case LIST_item:
728 if (n->norm->Bl.width)
729 mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
730 break;
731 case LIST_bullet:
732 /* FALLTHROUGH */
733 case LIST_dash:
734 /* FALLTHROUGH */
735 case LIST_hyphen:
736 if (NULL == n->norm->Bl.width)
737 n->norm->Bl.width = "2n";
738 break;
739 case LIST_enum:
740 if (NULL == n->norm->Bl.width)
741 n->norm->Bl.width = "3n";
742 break;
743 default:
744 break;
745 }
746
747 return(1);
748 }
749
750 static int
751 pre_bd(PRE_ARGS)
752 {
753 struct mdoc_node *np;
754 struct mdoc_argv *argv;
755 int i;
756 enum mdoc_disp dt;
757
758 if (MDOC_BLOCK != n->type) {
759 if (ENDBODY_NOT != n->end) {
760 assert(n->pending);
761 np = n->pending->parent;
762 } else
763 np = n->parent;
764
765 assert(np);
766 assert(MDOC_BLOCK == np->type);
767 assert(MDOC_Bd == np->tok);
768 return(1);
769 }
770
771 for (i = 0; n->args && i < (int)n->args->argc; i++) {
772 argv = n->args->argv + i;
773 dt = DISP__NONE;
774
775 switch (argv->arg) {
776 case MDOC_Centred:
777 dt = DISP_centered;
778 break;
779 case MDOC_Ragged:
780 dt = DISP_ragged;
781 break;
782 case MDOC_Unfilled:
783 dt = DISP_unfilled;
784 break;
785 case MDOC_Filled:
786 dt = DISP_filled;
787 break;
788 case MDOC_Literal:
789 dt = DISP_literal;
790 break;
791 case MDOC_File:
792 mdoc_nmsg(mdoc, n, MANDOCERR_BADDISP);
793 return(0);
794 case MDOC_Offset:
795 if (0 == argv->sz) {
796 mandoc_msg(MANDOCERR_ARG_EMPTY,
797 mdoc->parse, argv->line,
798 argv->pos, "Bd -offset");
799 break;
800 }
801 if (NULL != n->norm->Bd.offs)
802 mandoc_vmsg(MANDOCERR_ARG_REP,
803 mdoc->parse, argv->line,
804 argv->pos, "Bd -offset %s",
805 argv->value[0]);
806 n->norm->Bd.offs = argv->value[0];
807 break;
808 case MDOC_Compact:
809 if (n->norm->Bd.comp)
810 mandoc_msg(MANDOCERR_ARG_REP,
811 mdoc->parse, argv->line,
812 argv->pos, "Bd -compact");
813 n->norm->Bd.comp = 1;
814 break;
815 default:
816 abort();
817 /* NOTREACHED */
818 }
819 if (DISP__NONE == dt)
820 continue;
821
822 if (DISP__NONE == n->norm->Bd.type)
823 n->norm->Bd.type = dt;
824 else
825 mandoc_msg(MANDOCERR_BD_REP,
826 mdoc->parse, n->line, n->pos,
827 mdoc_argnames[argv->arg]);
828 }
829
830 if (DISP__NONE == n->norm->Bd.type) {
831 mdoc_nmsg(mdoc, n, MANDOCERR_BD_NOTYPE);
832 n->norm->Bd.type = DISP_ragged;
833 }
834
835 return(1);
836 }
837
838 static int
839 pre_an(PRE_ARGS)
840 {
841 int i;
842
843 if (NULL == n->args)
844 return(1);
845
846 for (i = 1; i < (int)n->args->argc; i++)
847 mdoc_pmsg(mdoc, n->args->argv[i].line,
848 n->args->argv[i].pos, MANDOCERR_IGNARGV);
849
850 if (MDOC_Split == n->args->argv[0].arg)
851 n->norm->An.auth = AUTH_split;
852 else if (MDOC_Nosplit == n->args->argv[0].arg)
853 n->norm->An.auth = AUTH_nosplit;
854 else
855 abort();
856
857 return(1);
858 }
859
860 static int
861 pre_std(PRE_ARGS)
862 {
863
864 if (n->args && 1 == n->args->argc)
865 if (MDOC_Std == n->args->argv[0].arg)
866 return(1);
867
868 mandoc_msg(MANDOCERR_ARG_STD, mdoc->parse,
869 n->line, n->pos, mdoc_macronames[n->tok]);
870 return(1);
871 }
872
873 static int
874 pre_obsolete(PRE_ARGS)
875 {
876
877 if (MDOC_ELEM == n->type || MDOC_BLOCK == n->type)
878 mandoc_msg(MANDOCERR_MACRO_OBS, mdoc->parse,
879 n->line, n->pos, mdoc_macronames[n->tok]);
880 return(1);
881 }
882
883 static int
884 pre_dt(PRE_ARGS)
885 {
886
887 if (NULL == mdoc->meta.date || mdoc->meta.os)
888 mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
889 n->line, n->pos, "Dt");
890
891 if (mdoc->meta.title)
892 mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
893 n->line, n->pos, "Dt");
894
895 return(1);
896 }
897
898 static int
899 pre_os(PRE_ARGS)
900 {
901
902 if (NULL == mdoc->meta.title || NULL == mdoc->meta.date)
903 mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
904 n->line, n->pos, "Os");
905
906 if (mdoc->meta.os)
907 mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
908 n->line, n->pos, "Os");
909
910 return(1);
911 }
912
913 static int
914 pre_dd(PRE_ARGS)
915 {
916
917 if (mdoc->meta.title || mdoc->meta.os)
918 mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
919 n->line, n->pos, "Dd");
920
921 if (mdoc->meta.date)
922 mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
923 n->line, n->pos, "Dd");
924
925 return(1);
926 }
927
928 static int
929 post_bf(POST_ARGS)
930 {
931 struct mdoc_node *np, *nch;
932 enum mdocargt arg;
933
934 /*
935 * Unlike other data pointers, these are "housed" by the HEAD
936 * element, which contains the goods.
937 */
938
939 if (MDOC_HEAD != mdoc->last->type) {
940 if (ENDBODY_NOT != mdoc->last->end) {
941 assert(mdoc->last->pending);
942 np = mdoc->last->pending->parent->head;
943 } else if (MDOC_BLOCK != mdoc->last->type) {
944 np = mdoc->last->parent->head;
945 } else
946 np = mdoc->last->head;
947
948 assert(np);
949 assert(MDOC_HEAD == np->type);
950 assert(MDOC_Bf == np->tok);
951 return(1);
952 }
953
954 np = mdoc->last;
955 assert(MDOC_BLOCK == np->parent->type);
956 assert(MDOC_Bf == np->parent->tok);
957
958 /* Check the number of arguments. */
959
960 nch = np->child;
961 if (NULL == np->parent->args) {
962 if (NULL == nch) {
963 mdoc_nmsg(mdoc, np, MANDOCERR_BF_NOFONT);
964 return(1);
965 }
966 nch = nch->next;
967 }
968 if (NULL != nch)
969 mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
970 nch->line, nch->pos, "Bf ... %s", nch->string);
971
972 /* Extract argument into data. */
973
974 if (np->parent->args) {
975 arg = np->parent->args->argv[0].arg;
976 if (MDOC_Emphasis == arg)
977 np->norm->Bf.font = FONT_Em;
978 else if (MDOC_Literal == arg)
979 np->norm->Bf.font = FONT_Li;
980 else if (MDOC_Symbolic == arg)
981 np->norm->Bf.font = FONT_Sy;
982 else
983 abort();
984 return(1);
985 }
986
987 /* Extract parameter into data. */
988
989 if (0 == strcmp(np->child->string, "Em"))
990 np->norm->Bf.font = FONT_Em;
991 else if (0 == strcmp(np->child->string, "Li"))
992 np->norm->Bf.font = FONT_Li;
993 else if (0 == strcmp(np->child->string, "Sy"))
994 np->norm->Bf.font = FONT_Sy;
995 else
996 mandoc_vmsg(MANDOCERR_BF_BADFONT, mdoc->parse,
997 np->child->line, np->child->pos,
998 "Bf %s", np->child->string);
999
1000 return(1);
1001 }
1002
1003 static int
1004 post_lb(POST_ARGS)
1005 {
1006 struct mdoc_node *n;
1007 const char *stdlibname;
1008 char *libname;
1009
1010 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1);
1011
1012 n = mdoc->last->child;
1013
1014 assert(n);
1015 assert(MDOC_TEXT == n->type);
1016
1017 if (NULL == (stdlibname = mdoc_a2lib(n->string)))
1018 mandoc_asprintf(&libname,
1019 "library \\(lq%s\\(rq", n->string);
1020 else
1021 libname = mandoc_strdup(stdlibname);
1022
1023 free(n->string);
1024 n->string = libname;
1025 return(1);
1026 }
1027
1028 static int
1029 post_eoln(POST_ARGS)
1030 {
1031 const struct mdoc_node *n;
1032
1033 n = mdoc->last;
1034 if (n->child)
1035 mandoc_vmsg(MANDOCERR_ARG_SKIP,
1036 mdoc->parse, n->line, n->pos,
1037 "%s %s", mdoc_macronames[n->tok],
1038 n->child->string);
1039 return(1);
1040 }
1041
1042 static int
1043 post_vt(POST_ARGS)
1044 {
1045 const struct mdoc_node *n;
1046
1047 /*
1048 * The Vt macro comes in both ELEM and BLOCK form, both of which
1049 * have different syntaxes (yet more context-sensitive
1050 * behaviour). ELEM types must have a child, which is already
1051 * guaranteed by the in_line parsing routine; BLOCK types,
1052 * specifically the BODY, should only have TEXT children.
1053 */
1054
1055 if (MDOC_BODY != mdoc->last->type)
1056 return(1);
1057
1058 for (n = mdoc->last->child; n; n = n->next)
1059 if (MDOC_TEXT != n->type)
1060 mandoc_msg(MANDOCERR_VT_CHILD, mdoc->parse,
1061 n->line, n->pos, mdoc_macronames[n->tok]);
1062
1063 return(1);
1064 }
1065
1066 static int
1067 post_nm(POST_ARGS)
1068 {
1069
1070 if (NULL != mdoc->meta.name)
1071 return(1);
1072
1073 mdoc_deroff(&mdoc->meta.name, mdoc->last);
1074
1075 if (NULL == mdoc->meta.name)
1076 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NM_NONAME);
1077 return(1);
1078 }
1079
1080 static int
1081 post_literal(POST_ARGS)
1082 {
1083
1084 /*
1085 * The `Dl' (note "el" not "one") and `Bd' macros unset the
1086 * MDOC_LITERAL flag as they leave. Note that `Bd' only sets
1087 * this in literal mode, but it doesn't hurt to just switch it
1088 * off in general since displays can't be nested.
1089 */
1090
1091 if (MDOC_BODY == mdoc->last->type)
1092 mdoc->flags &= ~MDOC_LITERAL;
1093
1094 return(1);
1095 }
1096
1097 static int
1098 post_defaults(POST_ARGS)
1099 {
1100 struct mdoc_node *nn;
1101
1102 /*
1103 * The `Ar' defaults to "file ..." if no value is provided as an
1104 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
1105 * gets an empty string.
1106 */
1107
1108 if (mdoc->last->child)
1109 return(1);
1110
1111 nn = mdoc->last;
1112 mdoc->next = MDOC_NEXT_CHILD;
1113
1114 switch (nn->tok) {
1115 case MDOC_Ar:
1116 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "file"))
1117 return(0);
1118 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "..."))
1119 return(0);
1120 break;
1121 case MDOC_At:
1122 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "AT&T"))
1123 return(0);
1124 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "UNIX"))
1125 return(0);
1126 break;
1127 case MDOC_Li:
1128 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, ""))
1129 return(0);
1130 break;
1131 case MDOC_Pa:
1132 /* FALLTHROUGH */
1133 case MDOC_Mt:
1134 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "~"))
1135 return(0);
1136 break;
1137 default:
1138 abort();
1139 /* NOTREACHED */
1140 }
1141
1142 mdoc->last = nn;
1143 return(1);
1144 }
1145
1146 static int
1147 post_at(POST_ARGS)
1148 {
1149 struct mdoc_node *n;
1150 const char *std_att;
1151 char *att;
1152
1153 /*
1154 * If we have a child, look it up in the standard keys. If a
1155 * key exist, use that instead of the child; if it doesn't,
1156 * prefix "AT&T UNIX " to the existing data.
1157 */
1158
1159 if (NULL == (n = mdoc->last->child))
1160 return(1);
1161
1162 assert(MDOC_TEXT == n->type);
1163 if (NULL == (std_att = mdoc_a2att(n->string))) {
1164 mandoc_msg(MANDOCERR_AT_BAD, mdoc->parse,
1165 n->line, n->pos, n->string);
1166 mandoc_asprintf(&att, "AT&T UNIX %s", n->string);
1167 } else
1168 att = mandoc_strdup(std_att);
1169
1170 free(n->string);
1171 n->string = att;
1172 return(1);
1173 }
1174
1175 static int
1176 post_an(POST_ARGS)
1177 {
1178 struct mdoc_node *np;
1179
1180 np = mdoc->last;
1181 if (AUTH__NONE == np->norm->An.auth) {
1182 if (0 == np->child)
1183 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0);
1184 } else if (np->child)
1185 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0);
1186
1187 return(1);
1188 }
1189
1190 static int
1191 post_en(POST_ARGS)
1192 {
1193
1194 if (MDOC_BLOCK == mdoc->last->type)
1195 mdoc->last->norm->Es = mdoc->last_es;
1196 return(1);
1197 }
1198
1199 static int
1200 post_es(POST_ARGS)
1201 {
1202
1203 mdoc->last_es = mdoc->last;
1204 return(1);
1205 }
1206
1207 static int
1208 post_it(POST_ARGS)
1209 {
1210 int i, cols;
1211 enum mdoc_list lt;
1212 struct mdoc_node *nbl, *nit, *nch;
1213
1214 nit = mdoc->last;
1215 if (MDOC_BLOCK != nit->type)
1216 return(1);
1217
1218 nbl = nit->parent->parent;
1219 lt = nbl->norm->Bl.type;
1220
1221 switch (lt) {
1222 case LIST_tag:
1223 /* FALLTHROUGH */
1224 case LIST_hang:
1225 /* FALLTHROUGH */
1226 case LIST_ohang:
1227 /* FALLTHROUGH */
1228 case LIST_inset:
1229 /* FALLTHROUGH */
1230 case LIST_diag:
1231 if (NULL == nit->head->child)
1232 mandoc_msg(MANDOCERR_IT_NOHEAD,
1233 mdoc->parse, nit->line, nit->pos,
1234 mdoc_argnames[nbl->args->argv[0].arg]);
1235 break;
1236 case LIST_bullet:
1237 /* FALLTHROUGH */
1238 case LIST_dash:
1239 /* FALLTHROUGH */
1240 case LIST_enum:
1241 /* FALLTHROUGH */
1242 case LIST_hyphen:
1243 if (NULL == nit->body->child)
1244 mandoc_msg(MANDOCERR_IT_NOBODY,
1245 mdoc->parse, nit->line, nit->pos,
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 mdoc_nmsg(mdoc, n, MANDOCERR_DOC_EMPTY);
1649 else if (MDOC_Sh != n->child->tok)
1650 mandoc_msg(MANDOCERR_SEC_BEFORE, mdoc->parse,
1651 n->child->line, n->child->pos,
1652 mdoc_macronames[n->child->tok]);
1653
1654 return(1);
1655 }
1656
1657 static int
1658 post_st(POST_ARGS)
1659 {
1660 struct mdoc_node *n, *nch;
1661 const char *p;
1662
1663 n = mdoc->last;
1664 nch = n->child;
1665
1666 if (NULL == nch) {
1667 mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse,
1668 n->line, n->pos, mdoc_macronames[n->tok]);
1669 mdoc_node_delete(mdoc, n);
1670 return(1);
1671 }
1672
1673 assert(MDOC_TEXT == nch->type);
1674
1675 if (NULL == (p = mdoc_a2st(nch->string))) {
1676 mandoc_msg(MANDOCERR_ST_BAD, mdoc->parse,
1677 nch->line, nch->pos, nch->string);
1678 mdoc_node_delete(mdoc, n);
1679 } else {
1680 free(nch->string);
1681 nch->string = mandoc_strdup(p);
1682 }
1683
1684 return(1);
1685 }
1686
1687 static int
1688 post_rs(POST_ARGS)
1689 {
1690 struct mdoc_node *nn, *next, *prev;
1691 int i, j;
1692
1693 switch (mdoc->last->type) {
1694 case MDOC_HEAD:
1695 check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0);
1696 return(1);
1697 case MDOC_BODY:
1698 if (mdoc->last->child)
1699 break;
1700 check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0);
1701 return(1);
1702 default:
1703 return(1);
1704 }
1705
1706 /*
1707 * The full `Rs' block needs special handling to order the
1708 * sub-elements according to `rsord'. Pick through each element
1709 * and correctly order it. This is an insertion sort.
1710 */
1711
1712 next = NULL;
1713 for (nn = mdoc->last->child->next; nn; nn = next) {
1714 /* Determine order of `nn'. */
1715 for (i = 0; i < RSORD_MAX; i++)
1716 if (rsord[i] == nn->tok)
1717 break;
1718
1719 if (i == RSORD_MAX) {
1720 mandoc_msg(MANDOCERR_RS_BAD,
1721 mdoc->parse, nn->line, nn->pos,
1722 mdoc_macronames[nn->tok]);
1723 i = -1;
1724 } else if (MDOC__J == nn->tok || MDOC__B == nn->tok)
1725 mdoc->last->norm->Rs.quote_T++;
1726
1727 /*
1728 * Remove `nn' from the chain. This somewhat
1729 * repeats mdoc_node_unlink(), but since we're
1730 * just re-ordering, there's no need for the
1731 * full unlink process.
1732 */
1733
1734 if (NULL != (next = nn->next))
1735 next->prev = nn->prev;
1736
1737 if (NULL != (prev = nn->prev))
1738 prev->next = nn->next;
1739
1740 nn->prev = nn->next = NULL;
1741
1742 /*
1743 * Scan back until we reach a node that's
1744 * ordered before `nn'.
1745 */
1746
1747 for ( ; prev ; prev = prev->prev) {
1748 /* Determine order of `prev'. */
1749 for (j = 0; j < RSORD_MAX; j++)
1750 if (rsord[j] == prev->tok)
1751 break;
1752 if (j == RSORD_MAX)
1753 j = -1;
1754
1755 if (j <= i)
1756 break;
1757 }
1758
1759 /*
1760 * Set `nn' back into its correct place in front
1761 * of the `prev' node.
1762 */
1763
1764 nn->prev = prev;
1765
1766 if (prev) {
1767 if (prev->next)
1768 prev->next->prev = nn;
1769 nn->next = prev->next;
1770 prev->next = nn;
1771 } else {
1772 mdoc->last->child->prev = nn;
1773 nn->next = mdoc->last->child;
1774 mdoc->last->child = nn;
1775 }
1776 }
1777
1778 return(1);
1779 }
1780
1781 /*
1782 * For some arguments of some macros,
1783 * convert all breakable hyphens into ASCII_HYPH.
1784 */
1785 static int
1786 post_hyph(POST_ARGS)
1787 {
1788 struct mdoc_node *n, *nch;
1789 char *cp;
1790
1791 n = mdoc->last;
1792 switch (n->type) {
1793 case MDOC_HEAD:
1794 if (MDOC_Sh == n->tok || MDOC_Ss == n->tok)
1795 break;
1796 return(1);
1797 case MDOC_BODY:
1798 if (MDOC_D1 == n->tok || MDOC_Nd == n->tok)
1799 break;
1800 return(1);
1801 case MDOC_ELEM:
1802 break;
1803 default:
1804 return(1);
1805 }
1806
1807 for (nch = n->child; nch; nch = nch->next) {
1808 if (MDOC_TEXT != nch->type)
1809 continue;
1810 cp = nch->string;
1811 if ('\0' == *cp)
1812 continue;
1813 while ('\0' != *(++cp))
1814 if ('-' == *cp &&
1815 isalpha((unsigned char)cp[-1]) &&
1816 isalpha((unsigned char)cp[1]))
1817 *cp = ASCII_HYPH;
1818 }
1819 return(1);
1820 }
1821
1822 static int
1823 post_ns(POST_ARGS)
1824 {
1825
1826 if (MDOC_LINE & mdoc->last->flags)
1827 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NS_SKIP);
1828 return(1);
1829 }
1830
1831 static int
1832 post_sh(POST_ARGS)
1833 {
1834
1835 if (MDOC_HEAD == mdoc->last->type)
1836 return(post_sh_head(mdoc));
1837 if (MDOC_BODY == mdoc->last->type)
1838 return(post_sh_body(mdoc));
1839
1840 return(1);
1841 }
1842
1843 static int
1844 post_sh_body(POST_ARGS)
1845 {
1846 struct mdoc_node *n;
1847
1848 if (SEC_NAME != mdoc->lastsec)
1849 return(1);
1850
1851 /*
1852 * Warn if the NAME section doesn't contain the `Nm' and `Nd'
1853 * macros (can have multiple `Nm' and one `Nd'). Note that the
1854 * children of the BODY declaration can also be "text".
1855 */
1856
1857 if (NULL == (n = mdoc->last->child)) {
1858 mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
1859 mdoc->last->line, mdoc->last->pos, "empty");
1860 return(1);
1861 }
1862
1863 for ( ; n && n->next; n = n->next) {
1864 if (MDOC_ELEM == n->type && MDOC_Nm == n->tok)
1865 continue;
1866 if (MDOC_TEXT == n->type)
1867 continue;
1868 mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
1869 n->line, n->pos, mdoc_macronames[n->tok]);
1870 }
1871
1872 assert(n);
1873 if (MDOC_BLOCK == n->type && MDOC_Nd == n->tok)
1874 return(1);
1875
1876 mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
1877 n->line, n->pos, mdoc_macronames[n->tok]);
1878 return(1);
1879 }
1880
1881 static int
1882 post_sh_head(POST_ARGS)
1883 {
1884 struct mdoc_node *n;
1885 const char *goodsec;
1886 char *secname;
1887 enum mdoc_sec sec;
1888
1889 /*
1890 * Process a new section. Sections are either "named" or
1891 * "custom". Custom sections are user-defined, while named ones
1892 * follow a conventional order and may only appear in certain
1893 * manual sections.
1894 */
1895
1896 secname = NULL;
1897 sec = SEC_CUSTOM;
1898 mdoc_deroff(&secname, mdoc->last);
1899 sec = NULL == secname ? SEC_CUSTOM : a2sec(secname);
1900
1901 /* The NAME should be first. */
1902
1903 if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed)
1904 mandoc_msg(MANDOCERR_NAMESEC_FIRST, mdoc->parse,
1905 mdoc->last->line, mdoc->last->pos, secname);
1906
1907 /* The SYNOPSIS gets special attention in other areas. */
1908
1909 if (SEC_SYNOPSIS == sec) {
1910 roff_setreg(mdoc->roff, "nS", 1, '=');
1911 mdoc->flags |= MDOC_SYNOPSIS;
1912 } else {
1913 roff_setreg(mdoc->roff, "nS", 0, '=');
1914 mdoc->flags &= ~MDOC_SYNOPSIS;
1915 }
1916
1917 /* Mark our last section. */
1918
1919 mdoc->lastsec = sec;
1920
1921 /*
1922 * Set the section attribute for the current HEAD, for its
1923 * parent BLOCK, and for the HEAD children; the latter can
1924 * only be TEXT nodes, so no recursion is needed.
1925 * For other blocks and elements, including .Sh BODY, this is
1926 * done when allocating the node data structures, but for .Sh
1927 * BLOCK and HEAD, the section is still unknown at that time.
1928 */
1929
1930 mdoc->last->parent->sec = sec;
1931 mdoc->last->sec = sec;
1932 for (n = mdoc->last->child; n; n = n->next)
1933 n->sec = sec;
1934
1935 /* We don't care about custom sections after this. */
1936
1937 if (SEC_CUSTOM == sec) {
1938 free(secname);
1939 return(1);
1940 }
1941
1942 /*
1943 * Check whether our non-custom section is being repeated or is
1944 * out of order.
1945 */
1946
1947 if (sec == mdoc->lastnamed)
1948 mandoc_msg(MANDOCERR_SEC_REP, mdoc->parse,
1949 mdoc->last->line, mdoc->last->pos, secname);
1950
1951 if (sec < mdoc->lastnamed)
1952 mandoc_msg(MANDOCERR_SEC_ORDER, mdoc->parse,
1953 mdoc->last->line, mdoc->last->pos, secname);
1954
1955 /* Mark the last named section. */
1956
1957 mdoc->lastnamed = sec;
1958
1959 /* Check particular section/manual conventions. */
1960
1961 assert(mdoc->meta.msec);
1962
1963 goodsec = NULL;
1964 switch (sec) {
1965 case SEC_ERRORS:
1966 if (*mdoc->meta.msec == '4')
1967 break;
1968 goodsec = "2, 3, 4, 9";
1969 /* FALLTHROUGH */
1970 case SEC_RETURN_VALUES:
1971 /* FALLTHROUGH */
1972 case SEC_LIBRARY:
1973 if (*mdoc->meta.msec == '2')
1974 break;
1975 if (*mdoc->meta.msec == '3')
1976 break;
1977 if (NULL == goodsec)
1978 goodsec = "2, 3, 9";
1979 /* FALLTHROUGH */
1980 case SEC_CONTEXT:
1981 if (*mdoc->meta.msec == '9')
1982 break;
1983 if (NULL == goodsec)
1984 goodsec = "9";
1985 mandoc_vmsg(MANDOCERR_SEC_MSEC, mdoc->parse,
1986 mdoc->last->line, mdoc->last->pos,
1987 "%s for %s only", secname, goodsec);
1988 break;
1989 default:
1990 break;
1991 }
1992
1993 free(secname);
1994 return(1);
1995 }
1996
1997 static int
1998 post_ignpar(POST_ARGS)
1999 {
2000 struct mdoc_node *np;
2001
2002 if (MDOC_BODY != mdoc->last->type)
2003 return(1);
2004
2005 if (NULL != (np = mdoc->last->child))
2006 if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
2007 mandoc_vmsg(MANDOCERR_PAR_SKIP,
2008 mdoc->parse, np->line, np->pos,
2009 "%s after %s", mdoc_macronames[np->tok],
2010 mdoc_macronames[mdoc->last->tok]);
2011 mdoc_node_delete(mdoc, np);
2012 }
2013
2014 if (NULL != (np = mdoc->last->last))
2015 if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
2016 mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2017 np->line, np->pos, "%s at the end of %s",
2018 mdoc_macronames[np->tok],
2019 mdoc_macronames[mdoc->last->tok]);
2020 mdoc_node_delete(mdoc, np);
2021 }
2022
2023 return(1);
2024 }
2025
2026 static int
2027 pre_par(PRE_ARGS)
2028 {
2029
2030 if (NULL == mdoc->last)
2031 return(1);
2032 if (MDOC_ELEM != n->type && MDOC_BLOCK != n->type)
2033 return(1);
2034
2035 /*
2036 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
2037 * block: `Lp', `Pp', or non-compact `Bd' or `Bl'.
2038 */
2039
2040 if (MDOC_Pp != mdoc->last->tok &&
2041 MDOC_Lp != mdoc->last->tok &&
2042 MDOC_br != mdoc->last->tok)
2043 return(1);
2044 if (MDOC_Bl == n->tok && n->norm->Bl.comp)
2045 return(1);
2046 if (MDOC_Bd == n->tok && n->norm->Bd.comp)
2047 return(1);
2048 if (MDOC_It == n->tok && n->parent->norm->Bl.comp)
2049 return(1);
2050
2051 mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2052 mdoc->last->line, mdoc->last->pos,
2053 "%s before %s", mdoc_macronames[mdoc->last->tok],
2054 mdoc_macronames[n->tok]);
2055 mdoc_node_delete(mdoc, mdoc->last);
2056 return(1);
2057 }
2058
2059 static int
2060 post_par(POST_ARGS)
2061 {
2062 struct mdoc_node *np;
2063
2064 if (MDOC_ELEM != mdoc->last->type &&
2065 MDOC_BLOCK != mdoc->last->type)
2066 return(1);
2067
2068 if (NULL == (np = mdoc->last->prev)) {
2069 np = mdoc->last->parent;
2070 if (MDOC_Sh != np->tok && MDOC_Ss != np->tok)
2071 return(1);
2072 } else {
2073 if (MDOC_Pp != np->tok && MDOC_Lp != np->tok &&
2074 (MDOC_br != mdoc->last->tok ||
2075 (MDOC_sp != np->tok && MDOC_br != np->tok)))
2076 return(1);
2077 }
2078
2079 mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2080 mdoc->last->line, mdoc->last->pos,
2081 "%s after %s", mdoc_macronames[mdoc->last->tok],
2082 mdoc_macronames[np->tok]);
2083 mdoc_node_delete(mdoc, mdoc->last);
2084 return(1);
2085 }
2086
2087 static int
2088 pre_literal(PRE_ARGS)
2089 {
2090
2091 if (MDOC_BODY != n->type)
2092 return(1);
2093
2094 /*
2095 * The `Dl' (note "el" not "one") and `Bd -literal' and `Bd
2096 * -unfilled' macros set MDOC_LITERAL on entrance to the body.
2097 */
2098
2099 switch (n->tok) {
2100 case MDOC_Dl:
2101 mdoc->flags |= MDOC_LITERAL;
2102 break;
2103 case MDOC_Bd:
2104 if (DISP_literal == n->norm->Bd.type)
2105 mdoc->flags |= MDOC_LITERAL;
2106 if (DISP_unfilled == n->norm->Bd.type)
2107 mdoc->flags |= MDOC_LITERAL;
2108 break;
2109 default:
2110 abort();
2111 /* NOTREACHED */
2112 }
2113
2114 return(1);
2115 }
2116
2117 static int
2118 post_dd(POST_ARGS)
2119 {
2120 struct mdoc_node *n;
2121 char *datestr;
2122
2123 if (mdoc->meta.date)
2124 free(mdoc->meta.date);
2125
2126 n = mdoc->last;
2127 if (NULL == n->child || '\0' == n->child->string[0]) {
2128 mdoc->meta.date = mdoc->quick ? mandoc_strdup("") :
2129 mandoc_normdate(mdoc->parse, NULL, n->line, n->pos);
2130 return(1);
2131 }
2132
2133 datestr = NULL;
2134 mdoc_deroff(&datestr, n);
2135 if (mdoc->quick)
2136 mdoc->meta.date = datestr;
2137 else {
2138 mdoc->meta.date = mandoc_normdate(mdoc->parse,
2139 datestr, n->line, n->pos);
2140 free(datestr);
2141 }
2142 return(1);
2143 }
2144
2145 static int
2146 post_dt(POST_ARGS)
2147 {
2148 struct mdoc_node *nn, *n;
2149 const char *cp;
2150 char *p;
2151
2152 n = mdoc->last;
2153
2154 if (mdoc->meta.title)
2155 free(mdoc->meta.title);
2156 if (mdoc->meta.vol)
2157 free(mdoc->meta.vol);
2158 if (mdoc->meta.arch)
2159 free(mdoc->meta.arch);
2160
2161 mdoc->meta.title = mdoc->meta.vol = mdoc->meta.arch = NULL;
2162
2163 /* First check that all characters are uppercase. */
2164
2165 if (NULL != (nn = n->child))
2166 for (p = nn->string; *p; p++) {
2167 if (toupper((unsigned char)*p) == *p)
2168 continue;
2169 mandoc_msg(MANDOCERR_TITLE_CASE,
2170 mdoc->parse, nn->line,
2171 nn->pos + (p - nn->string),
2172 nn->string);
2173 break;
2174 }
2175
2176 /* Handles: `.Dt'
2177 * title = unknown, volume = local, msec = 0, arch = NULL
2178 */
2179
2180 if (NULL == (nn = n->child)) {
2181 /* XXX: make these macro values. */
2182 /* FIXME: warn about missing values. */
2183 mdoc->meta.title = mandoc_strdup("UNKNOWN");
2184 mdoc->meta.vol = mandoc_strdup("LOCAL");
2185 mdoc->meta.msec = mandoc_strdup("1");
2186 return(1);
2187 }
2188
2189 /* Handles: `.Dt TITLE'
2190 * title = TITLE, volume = local, msec = 0, arch = NULL
2191 */
2192
2193 mdoc->meta.title = mandoc_strdup(
2194 '\0' == nn->string[0] ? "UNKNOWN" : nn->string);
2195
2196 if (NULL == (nn = nn->next)) {
2197 /* FIXME: warn about missing msec. */
2198 /* XXX: make this a macro value. */
2199 mdoc->meta.vol = mandoc_strdup("LOCAL");
2200 mdoc->meta.msec = mandoc_strdup("1");
2201 return(1);
2202 }
2203
2204 /* Handles: `.Dt TITLE SEC'
2205 * title = TITLE,
2206 * volume = SEC is msec ? format(msec) : SEC,
2207 * msec = SEC is msec ? atoi(msec) : 0,
2208 * arch = NULL
2209 */
2210
2211 cp = mandoc_a2msec(nn->string);
2212 if (cp) {
2213 mdoc->meta.vol = mandoc_strdup(cp);
2214 mdoc->meta.msec = mandoc_strdup(nn->string);
2215 } else {
2216 mandoc_msg(MANDOCERR_MSEC_BAD, mdoc->parse,
2217 nn->line, nn->pos, nn->string);
2218 mdoc->meta.vol = mandoc_strdup(nn->string);
2219 mdoc->meta.msec = mandoc_strdup(nn->string);
2220 }
2221
2222 if (NULL == (nn = nn->next))
2223 return(1);
2224
2225 /* Handles: `.Dt TITLE SEC VOL'
2226 * title = TITLE,
2227 * volume = VOL is vol ? format(VOL) :
2228 * VOL is arch ? format(arch) :
2229 * VOL
2230 */
2231
2232 cp = mdoc_a2vol(nn->string);
2233 if (cp) {
2234 free(mdoc->meta.vol);
2235 mdoc->meta.vol = mandoc_strdup(cp);
2236 } else {
2237 cp = mdoc_a2arch(nn->string);
2238 if (NULL == cp) {
2239 mandoc_msg(MANDOCERR_ARCH_BAD, mdoc->parse,
2240 nn->line, nn->pos, nn->string);
2241 free(mdoc->meta.vol);
2242 mdoc->meta.vol = mandoc_strdup(nn->string);
2243 } else
2244 mdoc->meta.arch = mandoc_strdup(cp);
2245 }
2246
2247 /* Ignore any subsequent parameters... */
2248 /* FIXME: warn about subsequent parameters. */
2249
2250 return(1);
2251 }
2252
2253 static int
2254 post_prol(POST_ARGS)
2255 {
2256 /*
2257 * Remove prologue macros from the document after they're
2258 * processed. The final document uses mdoc_meta for these
2259 * values and discards the originals.
2260 */
2261
2262 mdoc_node_delete(mdoc, mdoc->last);
2263 if (mdoc->meta.title && mdoc->meta.date && mdoc->meta.os)
2264 mdoc->flags |= MDOC_PBODY;
2265
2266 return(1);
2267 }
2268
2269 static int
2270 post_bx(POST_ARGS)
2271 {
2272 struct mdoc_node *n;
2273
2274 /*
2275 * Make `Bx's second argument always start with an uppercase
2276 * letter. Groff checks if it's an "accepted" term, but we just
2277 * uppercase blindly.
2278 */
2279
2280 n = mdoc->last->child;
2281 if (n && NULL != (n = n->next))
2282 *n->string = (char)toupper((unsigned char)*n->string);
2283
2284 return(1);
2285 }
2286
2287 static int
2288 post_os(POST_ARGS)
2289 {
2290 #ifndef OSNAME
2291 struct utsname utsname;
2292 static char *defbuf;
2293 #endif
2294 struct mdoc_node *n;
2295
2296 n = mdoc->last;
2297
2298 /*
2299 * Set the operating system by way of the `Os' macro.
2300 * The order of precedence is:
2301 * 1. the argument of the `Os' macro, unless empty
2302 * 2. the -Ios=foo command line argument, if provided
2303 * 3. -DOSNAME="\"foo\"", if provided during compilation
2304 * 4. "sysname release" from uname(3)
2305 */
2306
2307 free(mdoc->meta.os);
2308 mdoc->meta.os = NULL;
2309 mdoc_deroff(&mdoc->meta.os, n);
2310 if (mdoc->meta.os)
2311 return(1);
2312
2313 if (mdoc->defos) {
2314 mdoc->meta.os = mandoc_strdup(mdoc->defos);
2315 return(1);
2316 }
2317
2318 #ifdef OSNAME
2319 mdoc->meta.os = mandoc_strdup(OSNAME);
2320 #else /*!OSNAME */
2321 if (NULL == defbuf) {
2322 if (-1 == uname(&utsname)) {
2323 mandoc_msg(MANDOCERR_OS_UNAME, mdoc->parse,
2324 n->line, n->pos, "Os");
2325 defbuf = mandoc_strdup("UNKNOWN");
2326 } else
2327 mandoc_asprintf(&defbuf, "%s %s",
2328 utsname.sysname, utsname.release);
2329 }
2330 mdoc->meta.os = mandoc_strdup(defbuf);
2331 #endif /*!OSNAME*/
2332 return(1);
2333 }
2334
2335 /*
2336 * If no argument is provided,
2337 * fill in the name of the current manual page.
2338 */
2339 static int
2340 post_ex(POST_ARGS)
2341 {
2342 struct mdoc_node *n;
2343
2344 n = mdoc->last;
2345
2346 if (n->child)
2347 return(1);
2348
2349 if (mdoc->meta.name == NULL) {
2350 mdoc_nmsg(mdoc, n, MANDOCERR_EX_NONAME);
2351 return(1);
2352 }
2353
2354 mdoc->next = MDOC_NEXT_CHILD;
2355
2356 if ( ! mdoc_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name))
2357 return(0);
2358
2359 mdoc->last = n;
2360 return(1);
2361 }
2362
2363 static enum mdoc_sec
2364 a2sec(const char *p)
2365 {
2366 int i;
2367
2368 for (i = 0; i < (int)SEC__MAX; i++)
2369 if (secnames[i] && 0 == strcmp(p, secnames[i]))
2370 return((enum mdoc_sec)i);
2371
2372 return(SEC_CUSTOM);
2373 }
2374
2375 static size_t
2376 macro2len(enum mdoct macro)
2377 {
2378
2379 switch (macro) {
2380 case MDOC_Ad:
2381 return(12);
2382 case MDOC_Ao:
2383 return(12);
2384 case MDOC_An:
2385 return(12);
2386 case MDOC_Aq:
2387 return(12);
2388 case MDOC_Ar:
2389 return(12);
2390 case MDOC_Bo:
2391 return(12);
2392 case MDOC_Bq:
2393 return(12);
2394 case MDOC_Cd:
2395 return(12);
2396 case MDOC_Cm:
2397 return(10);
2398 case MDOC_Do:
2399 return(10);
2400 case MDOC_Dq:
2401 return(12);
2402 case MDOC_Dv:
2403 return(12);
2404 case MDOC_Eo:
2405 return(12);
2406 case MDOC_Em:
2407 return(10);
2408 case MDOC_Er:
2409 return(17);
2410 case MDOC_Ev:
2411 return(15);
2412 case MDOC_Fa:
2413 return(12);
2414 case MDOC_Fl:
2415 return(10);
2416 case MDOC_Fo:
2417 return(16);
2418 case MDOC_Fn:
2419 return(16);
2420 case MDOC_Ic:
2421 return(10);
2422 case MDOC_Li:
2423 return(16);
2424 case MDOC_Ms:
2425 return(6);
2426 case MDOC_Nm:
2427 return(10);
2428 case MDOC_No:
2429 return(12);
2430 case MDOC_Oo:
2431 return(10);
2432 case MDOC_Op:
2433 return(14);
2434 case MDOC_Pa:
2435 return(32);
2436 case MDOC_Pf:
2437 return(12);
2438 case MDOC_Po:
2439 return(12);
2440 case MDOC_Pq:
2441 return(12);
2442 case MDOC_Ql:
2443 return(16);
2444 case MDOC_Qo:
2445 return(12);
2446 case MDOC_So:
2447 return(12);
2448 case MDOC_Sq:
2449 return(12);
2450 case MDOC_Sy:
2451 return(6);
2452 case MDOC_Sx:
2453 return(16);
2454 case MDOC_Tn:
2455 return(10);
2456 case MDOC_Va:
2457 return(12);
2458 case MDOC_Vt:
2459 return(12);
2460 case MDOC_Xr:
2461 return(10);
2462 default:
2463 break;
2464 };
2465 return(0);
2466 }