]> git.cameronkatri.com Git - mandoc.git/blob - mdoc_validate.c
Get rid of the useless FATAL error "child violates parent syntax".
[mandoc.git] / mdoc_validate.c
1 /* $Id: mdoc_validate.c,v 1.235 2014/07/30 17:06:26 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 enum mandocerr er;
1214
1215 nit = mdoc->last;
1216 if (MDOC_BLOCK != nit->type)
1217 return(1);
1218
1219 nbl = nit->parent->parent;
1220 lt = nbl->norm->Bl.type;
1221
1222 switch (lt) {
1223 case LIST_tag:
1224 /* FALLTHROUGH */
1225 case LIST_hang:
1226 /* FALLTHROUGH */
1227 case LIST_ohang:
1228 /* FALLTHROUGH */
1229 case LIST_inset:
1230 /* FALLTHROUGH */
1231 case LIST_diag:
1232 if (NULL == nit->head->child)
1233 mandoc_msg(MANDOCERR_IT_NOHEAD,
1234 mdoc->parse, nit->line, nit->pos,
1235 mdoc_argnames[nbl->args->argv[0].arg]);
1236 break;
1237 case LIST_bullet:
1238 /* FALLTHROUGH */
1239 case LIST_dash:
1240 /* FALLTHROUGH */
1241 case LIST_enum:
1242 /* FALLTHROUGH */
1243 case LIST_hyphen:
1244 if (NULL == nit->body->child)
1245 mandoc_msg(MANDOCERR_IT_NOBODY,
1246 mdoc->parse, nit->line, nit->pos,
1247 mdoc_argnames[nbl->args->argv[0].arg]);
1248 /* FALLTHROUGH */
1249 case LIST_item:
1250 if (NULL != nit->head->child)
1251 mandoc_vmsg(MANDOCERR_ARG_SKIP,
1252 mdoc->parse, nit->line, nit->pos,
1253 "It %s", nit->head->child->string);
1254 break;
1255 case LIST_column:
1256 cols = (int)nbl->norm->Bl.ncols;
1257
1258 assert(NULL == nit->head->child);
1259
1260 for (i = 0, nch = nit->child; nch; nch = nch->next)
1261 if (MDOC_BODY == nch->type)
1262 i++;
1263
1264 if (i < cols)
1265 er = MANDOCERR_ARGCOUNT;
1266 else if (i == cols || i == cols + 1)
1267 break;
1268 else
1269 er = MANDOCERR_SYNTARGCOUNT;
1270
1271 mandoc_vmsg(er, mdoc->parse, nit->line, nit->pos,
1272 "columns == %d (have %d)", cols, i);
1273 return(MANDOCERR_ARGCOUNT == er);
1274 default:
1275 abort();
1276 }
1277
1278 return(1);
1279 }
1280
1281 static int
1282 post_bl_block(POST_ARGS)
1283 {
1284 struct mdoc_node *n, *ni, *nc;
1285
1286 /*
1287 * These are fairly complicated, so we've broken them into two
1288 * functions. post_bl_block_tag() is called when a -tag is
1289 * specified, but no -width (it must be guessed). The second
1290 * when a -width is specified (macro indicators must be
1291 * rewritten into real lengths).
1292 */
1293
1294 n = mdoc->last;
1295
1296 if (LIST_tag == n->norm->Bl.type &&
1297 NULL == n->norm->Bl.width) {
1298 if ( ! post_bl_block_tag(mdoc))
1299 return(0);
1300 assert(n->norm->Bl.width);
1301 } else if (NULL != n->norm->Bl.width) {
1302 if ( ! post_bl_block_width(mdoc))
1303 return(0);
1304 assert(n->norm->Bl.width);
1305 }
1306
1307 for (ni = n->body->child; ni; ni = ni->next) {
1308 if (NULL == ni->body)
1309 continue;
1310 nc = ni->body->last;
1311 while (NULL != nc) {
1312 switch (nc->tok) {
1313 case MDOC_Pp:
1314 /* FALLTHROUGH */
1315 case MDOC_Lp:
1316 /* FALLTHROUGH */
1317 case MDOC_br:
1318 break;
1319 default:
1320 nc = NULL;
1321 continue;
1322 }
1323 if (NULL == ni->next) {
1324 mandoc_msg(MANDOCERR_PAR_MOVE,
1325 mdoc->parse, nc->line, nc->pos,
1326 mdoc_macronames[nc->tok]);
1327 if ( ! mdoc_node_relink(mdoc, nc))
1328 return(0);
1329 } else if (0 == n->norm->Bl.comp &&
1330 LIST_column != n->norm->Bl.type) {
1331 mandoc_vmsg(MANDOCERR_PAR_SKIP,
1332 mdoc->parse, nc->line, nc->pos,
1333 "%s before It",
1334 mdoc_macronames[nc->tok]);
1335 mdoc_node_delete(mdoc, nc);
1336 } else
1337 break;
1338 nc = ni->body->last;
1339 }
1340 }
1341 return(1);
1342 }
1343
1344 static int
1345 post_bl_block_width(POST_ARGS)
1346 {
1347 size_t width;
1348 int i;
1349 enum mdoct tok;
1350 struct mdoc_node *n;
1351 char buf[24];
1352
1353 n = mdoc->last;
1354
1355 /*
1356 * Calculate the real width of a list from the -width string,
1357 * which may contain a macro (with a known default width), a
1358 * literal string, or a scaling width.
1359 *
1360 * If the value to -width is a macro, then we re-write it to be
1361 * the macro's width as set in share/tmac/mdoc/doc-common.
1362 */
1363
1364 if (0 == strcmp(n->norm->Bl.width, "Ds"))
1365 width = 6;
1366 else if (MDOC_MAX == (tok = mdoc_hash_find(n->norm->Bl.width)))
1367 return(1);
1368 else
1369 width = macro2len(tok);
1370
1371 /* The value already exists: free and reallocate it. */
1372
1373 assert(n->args);
1374
1375 for (i = 0; i < (int)n->args->argc; i++)
1376 if (MDOC_Width == n->args->argv[i].arg)
1377 break;
1378
1379 assert(i < (int)n->args->argc);
1380
1381 (void)snprintf(buf, sizeof(buf), "%un", (unsigned int)width);
1382 free(n->args->argv[i].value[0]);
1383 n->args->argv[i].value[0] = mandoc_strdup(buf);
1384
1385 /* Set our width! */
1386 n->norm->Bl.width = n->args->argv[i].value[0];
1387 return(1);
1388 }
1389
1390 static int
1391 post_bl_block_tag(POST_ARGS)
1392 {
1393 struct mdoc_node *n, *nn;
1394 size_t sz, ssz;
1395 int i;
1396 char buf[24];
1397
1398 /*
1399 * Calculate the -width for a `Bl -tag' list if it hasn't been
1400 * provided. Uses the first head macro. NOTE AGAIN: this is
1401 * ONLY if the -width argument has NOT been provided. See
1402 * post_bl_block_width() for converting the -width string.
1403 */
1404
1405 sz = 10;
1406 n = mdoc->last;
1407
1408 for (nn = n->body->child; nn; nn = nn->next) {
1409 if (MDOC_It != nn->tok)
1410 continue;
1411
1412 assert(MDOC_BLOCK == nn->type);
1413 nn = nn->head->child;
1414
1415 if (nn == NULL)
1416 break;
1417
1418 if (MDOC_TEXT == nn->type) {
1419 sz = strlen(nn->string) + 1;
1420 break;
1421 }
1422
1423 if (0 != (ssz = macro2len(nn->tok)))
1424 sz = ssz;
1425
1426 break;
1427 }
1428
1429 /* Defaults to ten ens. */
1430
1431 (void)snprintf(buf, sizeof(buf), "%un", (unsigned int)sz);
1432
1433 /*
1434 * We have to dynamically add this to the macro's argument list.
1435 * We're guaranteed that a MDOC_Width doesn't already exist.
1436 */
1437
1438 assert(n->args);
1439 i = (int)(n->args->argc)++;
1440
1441 n->args->argv = mandoc_reallocarray(n->args->argv,
1442 n->args->argc, sizeof(struct mdoc_argv));
1443
1444 n->args->argv[i].arg = MDOC_Width;
1445 n->args->argv[i].line = n->line;
1446 n->args->argv[i].pos = n->pos;
1447 n->args->argv[i].sz = 1;
1448 n->args->argv[i].value = mandoc_malloc(sizeof(char *));
1449 n->args->argv[i].value[0] = mandoc_strdup(buf);
1450
1451 /* Set our width! */
1452 n->norm->Bl.width = n->args->argv[i].value[0];
1453 return(1);
1454 }
1455
1456 static int
1457 post_bl_head(POST_ARGS)
1458 {
1459 struct mdoc_node *np, *nn, *nnp;
1460 int i, j;
1461
1462 if (LIST_column != mdoc->last->norm->Bl.type)
1463 /* FIXME: this should be ERROR class... */
1464 return(hwarn_eq0(mdoc));
1465
1466 /*
1467 * Convert old-style lists, where the column width specifiers
1468 * trail as macro parameters, to the new-style ("normal-form")
1469 * lists where they're argument values following -column.
1470 */
1471
1472 /* First, disallow both types and allow normal-form. */
1473
1474 /*
1475 * TODO: technically, we can accept both and just merge the two
1476 * lists, but I'll leave that for another day.
1477 */
1478
1479 if (mdoc->last->norm->Bl.ncols && mdoc->last->nchild) {
1480 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_COLUMNS);
1481 return(0);
1482 } else if (NULL == mdoc->last->child)
1483 return(1);
1484
1485 np = mdoc->last->parent;
1486 assert(np->args);
1487
1488 for (j = 0; j < (int)np->args->argc; j++)
1489 if (MDOC_Column == np->args->argv[j].arg)
1490 break;
1491
1492 assert(j < (int)np->args->argc);
1493 assert(0 == np->args->argv[j].sz);
1494
1495 /*
1496 * Accommodate for new-style groff column syntax. Shuffle the
1497 * child nodes, all of which must be TEXT, as arguments for the
1498 * column field. Then, delete the head children.
1499 */
1500
1501 np->args->argv[j].sz = (size_t)mdoc->last->nchild;
1502 np->args->argv[j].value = mandoc_reallocarray(NULL,
1503 (size_t)mdoc->last->nchild, sizeof(char *));
1504
1505 mdoc->last->norm->Bl.ncols = np->args->argv[j].sz;
1506 mdoc->last->norm->Bl.cols = (void *)np->args->argv[j].value;
1507
1508 for (i = 0, nn = mdoc->last->child; nn; i++) {
1509 np->args->argv[j].value[i] = nn->string;
1510 nn->string = NULL;
1511 nnp = nn;
1512 nn = nn->next;
1513 mdoc_node_delete(NULL, nnp);
1514 }
1515
1516 mdoc->last->nchild = 0;
1517 mdoc->last->child = NULL;
1518
1519 return(1);
1520 }
1521
1522 static int
1523 post_bl(POST_ARGS)
1524 {
1525 struct mdoc_node *nparent, *nprev; /* of the Bl block */
1526 struct mdoc_node *nblock, *nbody; /* of the Bl */
1527 struct mdoc_node *nchild, *nnext; /* of the Bl body */
1528
1529 nbody = mdoc->last;
1530 switch (nbody->type) {
1531 case MDOC_BLOCK:
1532 return(post_bl_block(mdoc));
1533 case MDOC_HEAD:
1534 return(post_bl_head(mdoc));
1535 case MDOC_BODY:
1536 break;
1537 default:
1538 return(1);
1539 }
1540
1541 nchild = nbody->child;
1542 while (NULL != nchild) {
1543 if (MDOC_It == nchild->tok || MDOC_Sm == nchild->tok) {
1544 nchild = nchild->next;
1545 continue;
1546 }
1547
1548 mandoc_msg(MANDOCERR_BL_MOVE, mdoc->parse,
1549 nchild->line, nchild->pos,
1550 mdoc_macronames[nchild->tok]);
1551
1552 /*
1553 * Move the node out of the Bl block.
1554 * First, collect all required node pointers.
1555 */
1556
1557 nblock = nbody->parent;
1558 nprev = nblock->prev;
1559 nparent = nblock->parent;
1560 nnext = nchild->next;
1561
1562 /*
1563 * Unlink this child.
1564 */
1565
1566 assert(NULL == nchild->prev);
1567 if (0 == --nbody->nchild) {
1568 nbody->child = NULL;
1569 nbody->last = NULL;
1570 assert(NULL == nnext);
1571 } else {
1572 nbody->child = nnext;
1573 nnext->prev = NULL;
1574 }
1575
1576 /*
1577 * Relink this child.
1578 */
1579
1580 nchild->parent = nparent;
1581 nchild->prev = nprev;
1582 nchild->next = nblock;
1583
1584 nblock->prev = nchild;
1585 nparent->nchild++;
1586 if (NULL == nprev)
1587 nparent->child = nchild;
1588 else
1589 nprev->next = nchild;
1590
1591 nchild = nnext;
1592 }
1593
1594 return(1);
1595 }
1596
1597 static int
1598 ebool(struct mdoc *mdoc)
1599 {
1600 struct mdoc_node *nch;
1601 enum mdoct tok;
1602
1603 tok = mdoc->last->tok;
1604 nch = mdoc->last->child;
1605
1606 if (NULL == nch) {
1607 if (MDOC_Sm == tok)
1608 mdoc->flags ^= MDOC_SMOFF;
1609 return(1);
1610 }
1611
1612 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_LT, 2);
1613
1614 assert(MDOC_TEXT == nch->type);
1615
1616 if (0 == strcmp(nch->string, "on")) {
1617 if (MDOC_Sm == tok)
1618 mdoc->flags &= ~MDOC_SMOFF;
1619 return(1);
1620 }
1621 if (0 == strcmp(nch->string, "off")) {
1622 if (MDOC_Sm == tok)
1623 mdoc->flags |= MDOC_SMOFF;
1624 return(1);
1625 }
1626
1627 mandoc_vmsg(MANDOCERR_SM_BAD,
1628 mdoc->parse, nch->line, nch->pos,
1629 "%s %s", mdoc_macronames[tok], nch->string);
1630 return(mdoc_node_relink(mdoc, nch));
1631 }
1632
1633 static int
1634 post_root(POST_ARGS)
1635 {
1636 struct mdoc_node *n;
1637
1638 /* Add missing prologue data. */
1639
1640 if ( ! (MDOC_PBODY & mdoc->flags)) {
1641 mandoc_msg(MANDOCERR_PROLOG_BAD, mdoc->parse, 0, 0, "EOF");
1642 if (mdoc->meta.date == NULL)
1643 mdoc->meta.date = mdoc->quick ?
1644 mandoc_strdup("") :
1645 mandoc_normdate(mdoc->parse, NULL, 0, 0);
1646 if (mdoc->meta.title == NULL)
1647 mdoc->meta.title = mandoc_strdup("UNKNOWN");
1648 if (mdoc->meta.vol == NULL)
1649 mdoc->meta.vol = mandoc_strdup("LOCAL");
1650 if (mdoc->meta.arch == NULL)
1651 mdoc->meta.msec = mandoc_strdup("1");
1652 if (mdoc->meta.os == NULL)
1653 mdoc->meta.os = mandoc_strdup("UNKNOWN");
1654 }
1655
1656 n = mdoc->first;
1657 assert(n);
1658
1659 /* Check that we begin with a proper `Sh'. */
1660
1661 if (NULL == n->child)
1662 mdoc_nmsg(mdoc, n, MANDOCERR_DOC_EMPTY);
1663 else if (MDOC_Sh != n->child->tok)
1664 mandoc_msg(MANDOCERR_SEC_BEFORE, mdoc->parse,
1665 n->child->line, n->child->pos,
1666 mdoc_macronames[n->child->tok]);
1667
1668 return(1);
1669 }
1670
1671 static int
1672 post_st(POST_ARGS)
1673 {
1674 struct mdoc_node *n, *nch;
1675 const char *p;
1676
1677 n = mdoc->last;
1678 nch = n->child;
1679
1680 if (NULL == nch) {
1681 mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse,
1682 n->line, n->pos, mdoc_macronames[n->tok]);
1683 mdoc_node_delete(mdoc, n);
1684 return(1);
1685 }
1686
1687 assert(MDOC_TEXT == nch->type);
1688
1689 if (NULL == (p = mdoc_a2st(nch->string))) {
1690 mandoc_msg(MANDOCERR_ST_BAD, mdoc->parse,
1691 nch->line, nch->pos, nch->string);
1692 mdoc_node_delete(mdoc, n);
1693 } else {
1694 free(nch->string);
1695 nch->string = mandoc_strdup(p);
1696 }
1697
1698 return(1);
1699 }
1700
1701 static int
1702 post_rs(POST_ARGS)
1703 {
1704 struct mdoc_node *nn, *next, *prev;
1705 int i, j;
1706
1707 switch (mdoc->last->type) {
1708 case MDOC_HEAD:
1709 check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0);
1710 return(1);
1711 case MDOC_BODY:
1712 if (mdoc->last->child)
1713 break;
1714 check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0);
1715 return(1);
1716 default:
1717 return(1);
1718 }
1719
1720 /*
1721 * The full `Rs' block needs special handling to order the
1722 * sub-elements according to `rsord'. Pick through each element
1723 * and correctly order it. This is an insertion sort.
1724 */
1725
1726 next = NULL;
1727 for (nn = mdoc->last->child->next; nn; nn = next) {
1728 /* Determine order of `nn'. */
1729 for (i = 0; i < RSORD_MAX; i++)
1730 if (rsord[i] == nn->tok)
1731 break;
1732
1733 if (i == RSORD_MAX) {
1734 mandoc_msg(MANDOCERR_RS_BAD,
1735 mdoc->parse, nn->line, nn->pos,
1736 mdoc_macronames[nn->tok]);
1737 i = -1;
1738 } else if (MDOC__J == nn->tok || MDOC__B == nn->tok)
1739 mdoc->last->norm->Rs.quote_T++;
1740
1741 /*
1742 * Remove `nn' from the chain. This somewhat
1743 * repeats mdoc_node_unlink(), but since we're
1744 * just re-ordering, there's no need for the
1745 * full unlink process.
1746 */
1747
1748 if (NULL != (next = nn->next))
1749 next->prev = nn->prev;
1750
1751 if (NULL != (prev = nn->prev))
1752 prev->next = nn->next;
1753
1754 nn->prev = nn->next = NULL;
1755
1756 /*
1757 * Scan back until we reach a node that's
1758 * ordered before `nn'.
1759 */
1760
1761 for ( ; prev ; prev = prev->prev) {
1762 /* Determine order of `prev'. */
1763 for (j = 0; j < RSORD_MAX; j++)
1764 if (rsord[j] == prev->tok)
1765 break;
1766 if (j == RSORD_MAX)
1767 j = -1;
1768
1769 if (j <= i)
1770 break;
1771 }
1772
1773 /*
1774 * Set `nn' back into its correct place in front
1775 * of the `prev' node.
1776 */
1777
1778 nn->prev = prev;
1779
1780 if (prev) {
1781 if (prev->next)
1782 prev->next->prev = nn;
1783 nn->next = prev->next;
1784 prev->next = nn;
1785 } else {
1786 mdoc->last->child->prev = nn;
1787 nn->next = mdoc->last->child;
1788 mdoc->last->child = nn;
1789 }
1790 }
1791
1792 return(1);
1793 }
1794
1795 /*
1796 * For some arguments of some macros,
1797 * convert all breakable hyphens into ASCII_HYPH.
1798 */
1799 static int
1800 post_hyph(POST_ARGS)
1801 {
1802 struct mdoc_node *n, *nch;
1803 char *cp;
1804
1805 n = mdoc->last;
1806 switch (n->type) {
1807 case MDOC_HEAD:
1808 if (MDOC_Sh == n->tok || MDOC_Ss == n->tok)
1809 break;
1810 return(1);
1811 case MDOC_BODY:
1812 if (MDOC_D1 == n->tok || MDOC_Nd == n->tok)
1813 break;
1814 return(1);
1815 case MDOC_ELEM:
1816 break;
1817 default:
1818 return(1);
1819 }
1820
1821 for (nch = n->child; nch; nch = nch->next) {
1822 if (MDOC_TEXT != nch->type)
1823 continue;
1824 cp = nch->string;
1825 if ('\0' == *cp)
1826 continue;
1827 while ('\0' != *(++cp))
1828 if ('-' == *cp &&
1829 isalpha((unsigned char)cp[-1]) &&
1830 isalpha((unsigned char)cp[1]))
1831 *cp = ASCII_HYPH;
1832 }
1833 return(1);
1834 }
1835
1836 static int
1837 post_ns(POST_ARGS)
1838 {
1839
1840 if (MDOC_LINE & mdoc->last->flags)
1841 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NS_SKIP);
1842 return(1);
1843 }
1844
1845 static int
1846 post_sh(POST_ARGS)
1847 {
1848
1849 if (MDOC_HEAD == mdoc->last->type)
1850 return(post_sh_head(mdoc));
1851 if (MDOC_BODY == mdoc->last->type)
1852 return(post_sh_body(mdoc));
1853
1854 return(1);
1855 }
1856
1857 static int
1858 post_sh_body(POST_ARGS)
1859 {
1860 struct mdoc_node *n;
1861
1862 if (SEC_NAME != mdoc->lastsec)
1863 return(1);
1864
1865 /*
1866 * Warn if the NAME section doesn't contain the `Nm' and `Nd'
1867 * macros (can have multiple `Nm' and one `Nd'). Note that the
1868 * children of the BODY declaration can also be "text".
1869 */
1870
1871 if (NULL == (n = mdoc->last->child)) {
1872 mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
1873 mdoc->last->line, mdoc->last->pos, "empty");
1874 return(1);
1875 }
1876
1877 for ( ; n && n->next; n = n->next) {
1878 if (MDOC_ELEM == n->type && MDOC_Nm == n->tok)
1879 continue;
1880 if (MDOC_TEXT == n->type)
1881 continue;
1882 mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
1883 n->line, n->pos, mdoc_macronames[n->tok]);
1884 }
1885
1886 assert(n);
1887 if (MDOC_BLOCK == n->type && MDOC_Nd == n->tok)
1888 return(1);
1889
1890 mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
1891 n->line, n->pos, mdoc_macronames[n->tok]);
1892 return(1);
1893 }
1894
1895 static int
1896 post_sh_head(POST_ARGS)
1897 {
1898 struct mdoc_node *n;
1899 const char *goodsec;
1900 char *secname;
1901 enum mdoc_sec sec;
1902
1903 /*
1904 * Process a new section. Sections are either "named" or
1905 * "custom". Custom sections are user-defined, while named ones
1906 * follow a conventional order and may only appear in certain
1907 * manual sections.
1908 */
1909
1910 secname = NULL;
1911 sec = SEC_CUSTOM;
1912 mdoc_deroff(&secname, mdoc->last);
1913 sec = NULL == secname ? SEC_CUSTOM : a2sec(secname);
1914
1915 /* The NAME should be first. */
1916
1917 if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed)
1918 mandoc_msg(MANDOCERR_NAMESEC_FIRST, mdoc->parse,
1919 mdoc->last->line, mdoc->last->pos, secname);
1920
1921 /* The SYNOPSIS gets special attention in other areas. */
1922
1923 if (SEC_SYNOPSIS == sec) {
1924 roff_setreg(mdoc->roff, "nS", 1, '=');
1925 mdoc->flags |= MDOC_SYNOPSIS;
1926 } else {
1927 roff_setreg(mdoc->roff, "nS", 0, '=');
1928 mdoc->flags &= ~MDOC_SYNOPSIS;
1929 }
1930
1931 /* Mark our last section. */
1932
1933 mdoc->lastsec = sec;
1934
1935 /*
1936 * Set the section attribute for the current HEAD, for its
1937 * parent BLOCK, and for the HEAD children; the latter can
1938 * only be TEXT nodes, so no recursion is needed.
1939 * For other blocks and elements, including .Sh BODY, this is
1940 * done when allocating the node data structures, but for .Sh
1941 * BLOCK and HEAD, the section is still unknown at that time.
1942 */
1943
1944 mdoc->last->parent->sec = sec;
1945 mdoc->last->sec = sec;
1946 for (n = mdoc->last->child; n; n = n->next)
1947 n->sec = sec;
1948
1949 /* We don't care about custom sections after this. */
1950
1951 if (SEC_CUSTOM == sec) {
1952 free(secname);
1953 return(1);
1954 }
1955
1956 /*
1957 * Check whether our non-custom section is being repeated or is
1958 * out of order.
1959 */
1960
1961 if (sec == mdoc->lastnamed)
1962 mandoc_msg(MANDOCERR_SEC_REP, mdoc->parse,
1963 mdoc->last->line, mdoc->last->pos, secname);
1964
1965 if (sec < mdoc->lastnamed)
1966 mandoc_msg(MANDOCERR_SEC_ORDER, mdoc->parse,
1967 mdoc->last->line, mdoc->last->pos, secname);
1968
1969 /* Mark the last named section. */
1970
1971 mdoc->lastnamed = sec;
1972
1973 /* Check particular section/manual conventions. */
1974
1975 assert(mdoc->meta.msec);
1976
1977 goodsec = NULL;
1978 switch (sec) {
1979 case SEC_ERRORS:
1980 if (*mdoc->meta.msec == '4')
1981 break;
1982 goodsec = "2, 3, 4, 9";
1983 /* FALLTHROUGH */
1984 case SEC_RETURN_VALUES:
1985 /* FALLTHROUGH */
1986 case SEC_LIBRARY:
1987 if (*mdoc->meta.msec == '2')
1988 break;
1989 if (*mdoc->meta.msec == '3')
1990 break;
1991 if (NULL == goodsec)
1992 goodsec = "2, 3, 9";
1993 /* FALLTHROUGH */
1994 case SEC_CONTEXT:
1995 if (*mdoc->meta.msec == '9')
1996 break;
1997 if (NULL == goodsec)
1998 goodsec = "9";
1999 mandoc_vmsg(MANDOCERR_SEC_MSEC, mdoc->parse,
2000 mdoc->last->line, mdoc->last->pos,
2001 "%s for %s only", secname, goodsec);
2002 break;
2003 default:
2004 break;
2005 }
2006
2007 free(secname);
2008 return(1);
2009 }
2010
2011 static int
2012 post_ignpar(POST_ARGS)
2013 {
2014 struct mdoc_node *np;
2015
2016 if (MDOC_BODY != mdoc->last->type)
2017 return(1);
2018
2019 if (NULL != (np = mdoc->last->child))
2020 if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
2021 mandoc_vmsg(MANDOCERR_PAR_SKIP,
2022 mdoc->parse, np->line, np->pos,
2023 "%s after %s", mdoc_macronames[np->tok],
2024 mdoc_macronames[mdoc->last->tok]);
2025 mdoc_node_delete(mdoc, np);
2026 }
2027
2028 if (NULL != (np = mdoc->last->last))
2029 if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
2030 mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2031 np->line, np->pos, "%s at the end of %s",
2032 mdoc_macronames[np->tok],
2033 mdoc_macronames[mdoc->last->tok]);
2034 mdoc_node_delete(mdoc, np);
2035 }
2036
2037 return(1);
2038 }
2039
2040 static int
2041 pre_par(PRE_ARGS)
2042 {
2043
2044 if (NULL == mdoc->last)
2045 return(1);
2046 if (MDOC_ELEM != n->type && MDOC_BLOCK != n->type)
2047 return(1);
2048
2049 /*
2050 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
2051 * block: `Lp', `Pp', or non-compact `Bd' or `Bl'.
2052 */
2053
2054 if (MDOC_Pp != mdoc->last->tok &&
2055 MDOC_Lp != mdoc->last->tok &&
2056 MDOC_br != mdoc->last->tok)
2057 return(1);
2058 if (MDOC_Bl == n->tok && n->norm->Bl.comp)
2059 return(1);
2060 if (MDOC_Bd == n->tok && n->norm->Bd.comp)
2061 return(1);
2062 if (MDOC_It == n->tok && n->parent->norm->Bl.comp)
2063 return(1);
2064
2065 mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2066 mdoc->last->line, mdoc->last->pos,
2067 "%s before %s", mdoc_macronames[mdoc->last->tok],
2068 mdoc_macronames[n->tok]);
2069 mdoc_node_delete(mdoc, mdoc->last);
2070 return(1);
2071 }
2072
2073 static int
2074 post_par(POST_ARGS)
2075 {
2076 struct mdoc_node *np;
2077
2078 if (MDOC_ELEM != mdoc->last->type &&
2079 MDOC_BLOCK != mdoc->last->type)
2080 return(1);
2081
2082 if (NULL == (np = mdoc->last->prev)) {
2083 np = mdoc->last->parent;
2084 if (MDOC_Sh != np->tok && MDOC_Ss != np->tok)
2085 return(1);
2086 } else {
2087 if (MDOC_Pp != np->tok && MDOC_Lp != np->tok &&
2088 (MDOC_br != mdoc->last->tok ||
2089 (MDOC_sp != np->tok && MDOC_br != np->tok)))
2090 return(1);
2091 }
2092
2093 mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2094 mdoc->last->line, mdoc->last->pos,
2095 "%s after %s", mdoc_macronames[mdoc->last->tok],
2096 mdoc_macronames[np->tok]);
2097 mdoc_node_delete(mdoc, mdoc->last);
2098 return(1);
2099 }
2100
2101 static int
2102 pre_literal(PRE_ARGS)
2103 {
2104
2105 if (MDOC_BODY != n->type)
2106 return(1);
2107
2108 /*
2109 * The `Dl' (note "el" not "one") and `Bd -literal' and `Bd
2110 * -unfilled' macros set MDOC_LITERAL on entrance to the body.
2111 */
2112
2113 switch (n->tok) {
2114 case MDOC_Dl:
2115 mdoc->flags |= MDOC_LITERAL;
2116 break;
2117 case MDOC_Bd:
2118 if (DISP_literal == n->norm->Bd.type)
2119 mdoc->flags |= MDOC_LITERAL;
2120 if (DISP_unfilled == n->norm->Bd.type)
2121 mdoc->flags |= MDOC_LITERAL;
2122 break;
2123 default:
2124 abort();
2125 /* NOTREACHED */
2126 }
2127
2128 return(1);
2129 }
2130
2131 static int
2132 post_dd(POST_ARGS)
2133 {
2134 struct mdoc_node *n;
2135 char *datestr;
2136
2137 if (mdoc->meta.date)
2138 free(mdoc->meta.date);
2139
2140 n = mdoc->last;
2141 if (NULL == n->child || '\0' == n->child->string[0]) {
2142 mdoc->meta.date = mdoc->quick ? mandoc_strdup("") :
2143 mandoc_normdate(mdoc->parse, NULL, n->line, n->pos);
2144 return(1);
2145 }
2146
2147 datestr = NULL;
2148 mdoc_deroff(&datestr, n);
2149 if (mdoc->quick)
2150 mdoc->meta.date = datestr;
2151 else {
2152 mdoc->meta.date = mandoc_normdate(mdoc->parse,
2153 datestr, n->line, n->pos);
2154 free(datestr);
2155 }
2156 return(1);
2157 }
2158
2159 static int
2160 post_dt(POST_ARGS)
2161 {
2162 struct mdoc_node *nn, *n;
2163 const char *cp;
2164 char *p;
2165
2166 n = mdoc->last;
2167
2168 if (mdoc->meta.title)
2169 free(mdoc->meta.title);
2170 if (mdoc->meta.vol)
2171 free(mdoc->meta.vol);
2172 if (mdoc->meta.arch)
2173 free(mdoc->meta.arch);
2174
2175 mdoc->meta.title = mdoc->meta.vol = mdoc->meta.arch = NULL;
2176
2177 /* First check that all characters are uppercase. */
2178
2179 if (NULL != (nn = n->child))
2180 for (p = nn->string; *p; p++) {
2181 if (toupper((unsigned char)*p) == *p)
2182 continue;
2183 mandoc_msg(MANDOCERR_TITLE_CASE,
2184 mdoc->parse, nn->line,
2185 nn->pos + (p - nn->string),
2186 nn->string);
2187 break;
2188 }
2189
2190 /* Handles: `.Dt'
2191 * title = unknown, volume = local, msec = 0, arch = NULL
2192 */
2193
2194 if (NULL == (nn = n->child)) {
2195 /* XXX: make these macro values. */
2196 /* FIXME: warn about missing values. */
2197 mdoc->meta.title = mandoc_strdup("UNKNOWN");
2198 mdoc->meta.vol = mandoc_strdup("LOCAL");
2199 mdoc->meta.msec = mandoc_strdup("1");
2200 return(1);
2201 }
2202
2203 /* Handles: `.Dt TITLE'
2204 * title = TITLE, volume = local, msec = 0, arch = NULL
2205 */
2206
2207 mdoc->meta.title = mandoc_strdup(
2208 '\0' == nn->string[0] ? "UNKNOWN" : nn->string);
2209
2210 if (NULL == (nn = nn->next)) {
2211 /* FIXME: warn about missing msec. */
2212 /* XXX: make this a macro value. */
2213 mdoc->meta.vol = mandoc_strdup("LOCAL");
2214 mdoc->meta.msec = mandoc_strdup("1");
2215 return(1);
2216 }
2217
2218 /* Handles: `.Dt TITLE SEC'
2219 * title = TITLE,
2220 * volume = SEC is msec ? format(msec) : SEC,
2221 * msec = SEC is msec ? atoi(msec) : 0,
2222 * arch = NULL
2223 */
2224
2225 cp = mandoc_a2msec(nn->string);
2226 if (cp) {
2227 mdoc->meta.vol = mandoc_strdup(cp);
2228 mdoc->meta.msec = mandoc_strdup(nn->string);
2229 } else {
2230 mandoc_msg(MANDOCERR_MSEC_BAD, mdoc->parse,
2231 nn->line, nn->pos, nn->string);
2232 mdoc->meta.vol = mandoc_strdup(nn->string);
2233 mdoc->meta.msec = mandoc_strdup(nn->string);
2234 }
2235
2236 if (NULL == (nn = nn->next))
2237 return(1);
2238
2239 /* Handles: `.Dt TITLE SEC VOL'
2240 * title = TITLE,
2241 * volume = VOL is vol ? format(VOL) :
2242 * VOL is arch ? format(arch) :
2243 * VOL
2244 */
2245
2246 cp = mdoc_a2vol(nn->string);
2247 if (cp) {
2248 free(mdoc->meta.vol);
2249 mdoc->meta.vol = mandoc_strdup(cp);
2250 } else {
2251 cp = mdoc_a2arch(nn->string);
2252 if (NULL == cp) {
2253 mandoc_msg(MANDOCERR_ARCH_BAD, mdoc->parse,
2254 nn->line, nn->pos, nn->string);
2255 free(mdoc->meta.vol);
2256 mdoc->meta.vol = mandoc_strdup(nn->string);
2257 } else
2258 mdoc->meta.arch = mandoc_strdup(cp);
2259 }
2260
2261 /* Ignore any subsequent parameters... */
2262 /* FIXME: warn about subsequent parameters. */
2263
2264 return(1);
2265 }
2266
2267 static int
2268 post_prol(POST_ARGS)
2269 {
2270 /*
2271 * Remove prologue macros from the document after they're
2272 * processed. The final document uses mdoc_meta for these
2273 * values and discards the originals.
2274 */
2275
2276 mdoc_node_delete(mdoc, mdoc->last);
2277 if (mdoc->meta.title && mdoc->meta.date && mdoc->meta.os)
2278 mdoc->flags |= MDOC_PBODY;
2279
2280 return(1);
2281 }
2282
2283 static int
2284 post_bx(POST_ARGS)
2285 {
2286 struct mdoc_node *n;
2287
2288 /*
2289 * Make `Bx's second argument always start with an uppercase
2290 * letter. Groff checks if it's an "accepted" term, but we just
2291 * uppercase blindly.
2292 */
2293
2294 n = mdoc->last->child;
2295 if (n && NULL != (n = n->next))
2296 *n->string = (char)toupper((unsigned char)*n->string);
2297
2298 return(1);
2299 }
2300
2301 static int
2302 post_os(POST_ARGS)
2303 {
2304 #ifndef OSNAME
2305 struct utsname utsname;
2306 static char *defbuf;
2307 #endif
2308 struct mdoc_node *n;
2309
2310 n = mdoc->last;
2311
2312 /*
2313 * Set the operating system by way of the `Os' macro.
2314 * The order of precedence is:
2315 * 1. the argument of the `Os' macro, unless empty
2316 * 2. the -Ios=foo command line argument, if provided
2317 * 3. -DOSNAME="\"foo\"", if provided during compilation
2318 * 4. "sysname release" from uname(3)
2319 */
2320
2321 free(mdoc->meta.os);
2322 mdoc->meta.os = NULL;
2323 mdoc_deroff(&mdoc->meta.os, n);
2324 if (mdoc->meta.os)
2325 return(1);
2326
2327 if (mdoc->defos) {
2328 mdoc->meta.os = mandoc_strdup(mdoc->defos);
2329 return(1);
2330 }
2331
2332 #ifdef OSNAME
2333 mdoc->meta.os = mandoc_strdup(OSNAME);
2334 #else /*!OSNAME */
2335 if (NULL == defbuf) {
2336 if (-1 == uname(&utsname)) {
2337 mandoc_msg(MANDOCERR_OS_UNAME, mdoc->parse,
2338 n->line, n->pos, "Os");
2339 defbuf = mandoc_strdup("UNKNOWN");
2340 } else
2341 mandoc_asprintf(&defbuf, "%s %s",
2342 utsname.sysname, utsname.release);
2343 }
2344 mdoc->meta.os = mandoc_strdup(defbuf);
2345 #endif /*!OSNAME*/
2346 return(1);
2347 }
2348
2349 /*
2350 * If no argument is provided,
2351 * fill in the name of the current manual page.
2352 */
2353 static int
2354 post_ex(POST_ARGS)
2355 {
2356 struct mdoc_node *n;
2357
2358 n = mdoc->last;
2359
2360 if (n->child)
2361 return(1);
2362
2363 if (mdoc->meta.name == NULL) {
2364 mdoc_nmsg(mdoc, n, MANDOCERR_EX_NONAME);
2365 return(1);
2366 }
2367
2368 mdoc->next = MDOC_NEXT_CHILD;
2369
2370 if ( ! mdoc_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name))
2371 return(0);
2372
2373 mdoc->last = n;
2374 return(1);
2375 }
2376
2377 static enum mdoc_sec
2378 a2sec(const char *p)
2379 {
2380 int i;
2381
2382 for (i = 0; i < (int)SEC__MAX; i++)
2383 if (secnames[i] && 0 == strcmp(p, secnames[i]))
2384 return((enum mdoc_sec)i);
2385
2386 return(SEC_CUSTOM);
2387 }
2388
2389 static size_t
2390 macro2len(enum mdoct macro)
2391 {
2392
2393 switch (macro) {
2394 case MDOC_Ad:
2395 return(12);
2396 case MDOC_Ao:
2397 return(12);
2398 case MDOC_An:
2399 return(12);
2400 case MDOC_Aq:
2401 return(12);
2402 case MDOC_Ar:
2403 return(12);
2404 case MDOC_Bo:
2405 return(12);
2406 case MDOC_Bq:
2407 return(12);
2408 case MDOC_Cd:
2409 return(12);
2410 case MDOC_Cm:
2411 return(10);
2412 case MDOC_Do:
2413 return(10);
2414 case MDOC_Dq:
2415 return(12);
2416 case MDOC_Dv:
2417 return(12);
2418 case MDOC_Eo:
2419 return(12);
2420 case MDOC_Em:
2421 return(10);
2422 case MDOC_Er:
2423 return(17);
2424 case MDOC_Ev:
2425 return(15);
2426 case MDOC_Fa:
2427 return(12);
2428 case MDOC_Fl:
2429 return(10);
2430 case MDOC_Fo:
2431 return(16);
2432 case MDOC_Fn:
2433 return(16);
2434 case MDOC_Ic:
2435 return(10);
2436 case MDOC_Li:
2437 return(16);
2438 case MDOC_Ms:
2439 return(6);
2440 case MDOC_Nm:
2441 return(10);
2442 case MDOC_No:
2443 return(12);
2444 case MDOC_Oo:
2445 return(10);
2446 case MDOC_Op:
2447 return(14);
2448 case MDOC_Pa:
2449 return(32);
2450 case MDOC_Pf:
2451 return(12);
2452 case MDOC_Po:
2453 return(12);
2454 case MDOC_Pq:
2455 return(12);
2456 case MDOC_Ql:
2457 return(16);
2458 case MDOC_Qo:
2459 return(12);
2460 case MDOC_So:
2461 return(12);
2462 case MDOC_Sq:
2463 return(12);
2464 case MDOC_Sy:
2465 return(6);
2466 case MDOC_Sx:
2467 return(16);
2468 case MDOC_Tn:
2469 return(10);
2470 case MDOC_Va:
2471 return(12);
2472 case MDOC_Vt:
2473 return(12);
2474 case MDOC_Xr:
2475 return(10);
2476 default:
2477 break;
2478 };
2479 return(0);
2480 }