]> git.cameronkatri.com Git - mandoc.git/blob - mdoc_validate.c
demacrify: get rid of man_nmsg(), man_pmsg(), mdoc_nmsg(), mdoc_pmsg()
[mandoc.git] / mdoc_validate.c
1 /* $Id: mdoc_validate.c,v 1.240 2014/08/01 17:40:34 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, *wa;
572 int i;
573 enum mdocargt mdoclt;
574 enum mdoc_list lt;
575
576 if (MDOC_BLOCK != n->type) {
577 if (ENDBODY_NOT != n->end) {
578 assert(n->pending);
579 np = n->pending->parent;
580 } else
581 np = n->parent;
582
583 assert(np);
584 assert(MDOC_BLOCK == np->type);
585 assert(MDOC_Bl == np->tok);
586 return(1);
587 }
588
589 /*
590 * First figure out which kind of list to use: bind ourselves to
591 * the first mentioned list type and warn about any remaining
592 * ones. If we find no list type, we default to LIST_item.
593 */
594
595 wa = n->args->argv;
596 mdoclt = MDOC_ARG_MAX;
597 for (i = 0; n->args && i < (int)n->args->argc; i++) {
598 argv = n->args->argv + i;
599 lt = LIST__NONE;
600 switch (argv->arg) {
601 /* Set list types. */
602 case MDOC_Bullet:
603 lt = LIST_bullet;
604 break;
605 case MDOC_Dash:
606 lt = LIST_dash;
607 break;
608 case MDOC_Enum:
609 lt = LIST_enum;
610 break;
611 case MDOC_Hyphen:
612 lt = LIST_hyphen;
613 break;
614 case MDOC_Item:
615 lt = LIST_item;
616 break;
617 case MDOC_Tag:
618 lt = LIST_tag;
619 break;
620 case MDOC_Diag:
621 lt = LIST_diag;
622 break;
623 case MDOC_Hang:
624 lt = LIST_hang;
625 break;
626 case MDOC_Ohang:
627 lt = LIST_ohang;
628 break;
629 case MDOC_Inset:
630 lt = LIST_inset;
631 break;
632 case MDOC_Column:
633 lt = LIST_column;
634 break;
635 /* Set list arguments. */
636 case MDOC_Compact:
637 if (n->norm->Bl.comp)
638 mandoc_msg(MANDOCERR_ARG_REP,
639 mdoc->parse, argv->line,
640 argv->pos, "Bl -compact");
641 n->norm->Bl.comp = 1;
642 break;
643 case MDOC_Width:
644 wa = argv;
645 if (0 == argv->sz) {
646 mandoc_msg(MANDOCERR_ARG_EMPTY,
647 mdoc->parse, argv->line,
648 argv->pos, "Bl -width");
649 n->norm->Bl.width = "0n";
650 break;
651 }
652 if (NULL != n->norm->Bl.width)
653 mandoc_vmsg(MANDOCERR_ARG_REP,
654 mdoc->parse, argv->line,
655 argv->pos, "Bl -width %s",
656 argv->value[0]);
657 n->norm->Bl.width = argv->value[0];
658 break;
659 case MDOC_Offset:
660 if (0 == argv->sz) {
661 mandoc_msg(MANDOCERR_ARG_EMPTY,
662 mdoc->parse, argv->line,
663 argv->pos, "Bl -offset");
664 break;
665 }
666 if (NULL != n->norm->Bl.offs)
667 mandoc_vmsg(MANDOCERR_ARG_REP,
668 mdoc->parse, argv->line,
669 argv->pos, "Bl -offset %s",
670 argv->value[0]);
671 n->norm->Bl.offs = argv->value[0];
672 break;
673 default:
674 continue;
675 }
676 if (LIST__NONE == lt)
677 continue;
678 mdoclt = argv->arg;
679
680 /* Check: multiple list types. */
681
682 if (LIST__NONE != n->norm->Bl.type) {
683 mandoc_vmsg(MANDOCERR_BL_REP,
684 mdoc->parse, n->line, n->pos,
685 "Bl -%s", mdoc_argnames[argv->arg]);
686 continue;
687 }
688
689 /* The list type should come first. */
690
691 if (n->norm->Bl.width ||
692 n->norm->Bl.offs ||
693 n->norm->Bl.comp)
694 mandoc_vmsg(MANDOCERR_BL_LATETYPE,
695 mdoc->parse, n->line, n->pos, "Bl -%s",
696 mdoc_argnames[n->args->argv[0].arg]);
697
698 n->norm->Bl.type = lt;
699 if (LIST_column == lt) {
700 n->norm->Bl.ncols = argv->sz;
701 n->norm->Bl.cols = (void *)argv->value;
702 }
703 }
704
705 /* Allow lists to default to LIST_item. */
706
707 if (LIST__NONE == n->norm->Bl.type) {
708 mandoc_msg(MANDOCERR_BL_NOTYPE, mdoc->parse,
709 n->line, n->pos, "Bl");
710 n->norm->Bl.type = LIST_item;
711 }
712
713 /*
714 * Validate the width field. Some list types don't need width
715 * types and should be warned about them. Others should have it
716 * and must also be warned. Yet others have a default and need
717 * no warning.
718 */
719
720 switch (n->norm->Bl.type) {
721 case LIST_tag:
722 if (NULL == n->norm->Bl.width)
723 mandoc_msg(MANDOCERR_BL_NOWIDTH, mdoc->parse,
724 n->line, n->pos, "Bl -tag");
725 break;
726 case LIST_column:
727 /* FALLTHROUGH */
728 case LIST_diag:
729 /* FALLTHROUGH */
730 case LIST_ohang:
731 /* FALLTHROUGH */
732 case LIST_inset:
733 /* FALLTHROUGH */
734 case LIST_item:
735 if (n->norm->Bl.width)
736 mandoc_vmsg(MANDOCERR_BL_SKIPW, mdoc->parse,
737 wa->line, wa->pos, "Bl -%s",
738 mdoc_argnames[mdoclt]);
739 break;
740 case LIST_bullet:
741 /* FALLTHROUGH */
742 case LIST_dash:
743 /* FALLTHROUGH */
744 case LIST_hyphen:
745 if (NULL == n->norm->Bl.width)
746 n->norm->Bl.width = "2n";
747 break;
748 case LIST_enum:
749 if (NULL == n->norm->Bl.width)
750 n->norm->Bl.width = "3n";
751 break;
752 default:
753 break;
754 }
755
756 return(1);
757 }
758
759 static int
760 pre_bd(PRE_ARGS)
761 {
762 struct mdoc_node *np;
763 struct mdoc_argv *argv;
764 int i;
765 enum mdoc_disp dt;
766
767 if (MDOC_BLOCK != n->type) {
768 if (ENDBODY_NOT != n->end) {
769 assert(n->pending);
770 np = n->pending->parent;
771 } else
772 np = n->parent;
773
774 assert(np);
775 assert(MDOC_BLOCK == np->type);
776 assert(MDOC_Bd == np->tok);
777 return(1);
778 }
779
780 for (i = 0; n->args && i < (int)n->args->argc; i++) {
781 argv = n->args->argv + i;
782 dt = DISP__NONE;
783
784 switch (argv->arg) {
785 case MDOC_Centred:
786 dt = DISP_centered;
787 break;
788 case MDOC_Ragged:
789 dt = DISP_ragged;
790 break;
791 case MDOC_Unfilled:
792 dt = DISP_unfilled;
793 break;
794 case MDOC_Filled:
795 dt = DISP_filled;
796 break;
797 case MDOC_Literal:
798 dt = DISP_literal;
799 break;
800 case MDOC_File:
801 mandoc_msg(MANDOCERR_BD_FILE, mdoc->parse,
802 n->line, n->pos, NULL);
803 return(0);
804 case MDOC_Offset:
805 if (0 == argv->sz) {
806 mandoc_msg(MANDOCERR_ARG_EMPTY,
807 mdoc->parse, argv->line,
808 argv->pos, "Bd -offset");
809 break;
810 }
811 if (NULL != n->norm->Bd.offs)
812 mandoc_vmsg(MANDOCERR_ARG_REP,
813 mdoc->parse, argv->line,
814 argv->pos, "Bd -offset %s",
815 argv->value[0]);
816 n->norm->Bd.offs = argv->value[0];
817 break;
818 case MDOC_Compact:
819 if (n->norm->Bd.comp)
820 mandoc_msg(MANDOCERR_ARG_REP,
821 mdoc->parse, argv->line,
822 argv->pos, "Bd -compact");
823 n->norm->Bd.comp = 1;
824 break;
825 default:
826 abort();
827 /* NOTREACHED */
828 }
829 if (DISP__NONE == dt)
830 continue;
831
832 if (DISP__NONE == n->norm->Bd.type)
833 n->norm->Bd.type = dt;
834 else
835 mandoc_vmsg(MANDOCERR_BD_REP,
836 mdoc->parse, n->line, n->pos,
837 "Bd -%s", mdoc_argnames[argv->arg]);
838 }
839
840 if (DISP__NONE == n->norm->Bd.type) {
841 mandoc_msg(MANDOCERR_BD_NOTYPE, mdoc->parse,
842 n->line, n->pos, "Bd");
843 n->norm->Bd.type = DISP_ragged;
844 }
845
846 return(1);
847 }
848
849 static int
850 pre_an(PRE_ARGS)
851 {
852 struct mdoc_argv *argv;
853 size_t i;
854
855 if (n->args == NULL)
856 return(1);
857
858 for (i = 1; i < n->args->argc; i++) {
859 argv = n->args->argv + i;
860 mandoc_vmsg(MANDOCERR_AN_REP,
861 mdoc->parse, argv->line, argv->pos,
862 "An -%s", mdoc_argnames[argv->arg]);
863 }
864
865 argv = n->args->argv;
866 if (argv->arg == MDOC_Split)
867 n->norm->An.auth = AUTH_split;
868 else if (argv->arg == MDOC_Nosplit)
869 n->norm->An.auth = AUTH_nosplit;
870 else
871 abort();
872
873 return(1);
874 }
875
876 static int
877 pre_std(PRE_ARGS)
878 {
879
880 if (n->args && 1 == n->args->argc)
881 if (MDOC_Std == n->args->argv[0].arg)
882 return(1);
883
884 mandoc_msg(MANDOCERR_ARG_STD, mdoc->parse,
885 n->line, n->pos, mdoc_macronames[n->tok]);
886 return(1);
887 }
888
889 static int
890 pre_obsolete(PRE_ARGS)
891 {
892
893 if (MDOC_ELEM == n->type || MDOC_BLOCK == n->type)
894 mandoc_msg(MANDOCERR_MACRO_OBS, mdoc->parse,
895 n->line, n->pos, mdoc_macronames[n->tok]);
896 return(1);
897 }
898
899 static int
900 pre_dt(PRE_ARGS)
901 {
902
903 if (NULL == mdoc->meta.date || mdoc->meta.os)
904 mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
905 n->line, n->pos, "Dt");
906
907 if (mdoc->meta.title)
908 mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
909 n->line, n->pos, "Dt");
910
911 return(1);
912 }
913
914 static int
915 pre_os(PRE_ARGS)
916 {
917
918 if (NULL == mdoc->meta.title || NULL == mdoc->meta.date)
919 mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
920 n->line, n->pos, "Os");
921
922 if (mdoc->meta.os)
923 mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
924 n->line, n->pos, "Os");
925
926 return(1);
927 }
928
929 static int
930 pre_dd(PRE_ARGS)
931 {
932
933 if (mdoc->meta.title || mdoc->meta.os)
934 mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
935 n->line, n->pos, "Dd");
936
937 if (mdoc->meta.date)
938 mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
939 n->line, n->pos, "Dd");
940
941 return(1);
942 }
943
944 static int
945 post_bf(POST_ARGS)
946 {
947 struct mdoc_node *np, *nch;
948 enum mdocargt arg;
949
950 /*
951 * Unlike other data pointers, these are "housed" by the HEAD
952 * element, which contains the goods.
953 */
954
955 if (MDOC_HEAD != mdoc->last->type) {
956 if (ENDBODY_NOT != mdoc->last->end) {
957 assert(mdoc->last->pending);
958 np = mdoc->last->pending->parent->head;
959 } else if (MDOC_BLOCK != mdoc->last->type) {
960 np = mdoc->last->parent->head;
961 } else
962 np = mdoc->last->head;
963
964 assert(np);
965 assert(MDOC_HEAD == np->type);
966 assert(MDOC_Bf == np->tok);
967 return(1);
968 }
969
970 np = mdoc->last;
971 assert(MDOC_BLOCK == np->parent->type);
972 assert(MDOC_Bf == np->parent->tok);
973
974 /* Check the number of arguments. */
975
976 nch = np->child;
977 if (NULL == np->parent->args) {
978 if (NULL == nch) {
979 mandoc_msg(MANDOCERR_BF_NOFONT, mdoc->parse,
980 np->line, np->pos, "Bf");
981 return(1);
982 }
983 nch = nch->next;
984 }
985 if (NULL != nch)
986 mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
987 nch->line, nch->pos, "Bf ... %s", nch->string);
988
989 /* Extract argument into data. */
990
991 if (np->parent->args) {
992 arg = np->parent->args->argv[0].arg;
993 if (MDOC_Emphasis == arg)
994 np->norm->Bf.font = FONT_Em;
995 else if (MDOC_Literal == arg)
996 np->norm->Bf.font = FONT_Li;
997 else if (MDOC_Symbolic == arg)
998 np->norm->Bf.font = FONT_Sy;
999 else
1000 abort();
1001 return(1);
1002 }
1003
1004 /* Extract parameter into data. */
1005
1006 if (0 == strcmp(np->child->string, "Em"))
1007 np->norm->Bf.font = FONT_Em;
1008 else if (0 == strcmp(np->child->string, "Li"))
1009 np->norm->Bf.font = FONT_Li;
1010 else if (0 == strcmp(np->child->string, "Sy"))
1011 np->norm->Bf.font = FONT_Sy;
1012 else
1013 mandoc_vmsg(MANDOCERR_BF_BADFONT, mdoc->parse,
1014 np->child->line, np->child->pos,
1015 "Bf %s", np->child->string);
1016
1017 return(1);
1018 }
1019
1020 static int
1021 post_lb(POST_ARGS)
1022 {
1023 struct mdoc_node *n;
1024 const char *stdlibname;
1025 char *libname;
1026
1027 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1);
1028
1029 n = mdoc->last->child;
1030
1031 assert(n);
1032 assert(MDOC_TEXT == n->type);
1033
1034 if (NULL == (stdlibname = mdoc_a2lib(n->string)))
1035 mandoc_asprintf(&libname,
1036 "library \\(lq%s\\(rq", n->string);
1037 else
1038 libname = mandoc_strdup(stdlibname);
1039
1040 free(n->string);
1041 n->string = libname;
1042 return(1);
1043 }
1044
1045 static int
1046 post_eoln(POST_ARGS)
1047 {
1048 const struct mdoc_node *n;
1049
1050 n = mdoc->last;
1051 if (n->child)
1052 mandoc_vmsg(MANDOCERR_ARG_SKIP,
1053 mdoc->parse, n->line, n->pos,
1054 "%s %s", mdoc_macronames[n->tok],
1055 n->child->string);
1056 return(1);
1057 }
1058
1059 static int
1060 post_vt(POST_ARGS)
1061 {
1062 const struct mdoc_node *n;
1063
1064 /*
1065 * The Vt macro comes in both ELEM and BLOCK form, both of which
1066 * have different syntaxes (yet more context-sensitive
1067 * behaviour). ELEM types must have a child, which is already
1068 * guaranteed by the in_line parsing routine; BLOCK types,
1069 * specifically the BODY, should only have TEXT children.
1070 */
1071
1072 if (MDOC_BODY != mdoc->last->type)
1073 return(1);
1074
1075 for (n = mdoc->last->child; n; n = n->next)
1076 if (MDOC_TEXT != n->type)
1077 mandoc_msg(MANDOCERR_VT_CHILD, mdoc->parse,
1078 n->line, n->pos, mdoc_macronames[n->tok]);
1079
1080 return(1);
1081 }
1082
1083 static int
1084 post_nm(POST_ARGS)
1085 {
1086
1087 if (NULL != mdoc->meta.name)
1088 return(1);
1089
1090 mdoc_deroff(&mdoc->meta.name, mdoc->last);
1091
1092 if (NULL == mdoc->meta.name)
1093 mandoc_msg(MANDOCERR_NM_NONAME, mdoc->parse,
1094 mdoc->last->line, mdoc->last->pos, "Nm");
1095 return(1);
1096 }
1097
1098 static int
1099 post_literal(POST_ARGS)
1100 {
1101
1102 /*
1103 * The `Dl' (note "el" not "one") and `Bd' macros unset the
1104 * MDOC_LITERAL flag as they leave. Note that `Bd' only sets
1105 * this in literal mode, but it doesn't hurt to just switch it
1106 * off in general since displays can't be nested.
1107 */
1108
1109 if (MDOC_BODY == mdoc->last->type)
1110 mdoc->flags &= ~MDOC_LITERAL;
1111
1112 return(1);
1113 }
1114
1115 static int
1116 post_defaults(POST_ARGS)
1117 {
1118 struct mdoc_node *nn;
1119
1120 /*
1121 * The `Ar' defaults to "file ..." if no value is provided as an
1122 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
1123 * gets an empty string.
1124 */
1125
1126 if (mdoc->last->child)
1127 return(1);
1128
1129 nn = mdoc->last;
1130 mdoc->next = MDOC_NEXT_CHILD;
1131
1132 switch (nn->tok) {
1133 case MDOC_Ar:
1134 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "file"))
1135 return(0);
1136 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "..."))
1137 return(0);
1138 break;
1139 case MDOC_At:
1140 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "AT&T"))
1141 return(0);
1142 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "UNIX"))
1143 return(0);
1144 break;
1145 case MDOC_Li:
1146 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, ""))
1147 return(0);
1148 break;
1149 case MDOC_Pa:
1150 /* FALLTHROUGH */
1151 case MDOC_Mt:
1152 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "~"))
1153 return(0);
1154 break;
1155 default:
1156 abort();
1157 /* NOTREACHED */
1158 }
1159
1160 mdoc->last = nn;
1161 return(1);
1162 }
1163
1164 static int
1165 post_at(POST_ARGS)
1166 {
1167 struct mdoc_node *n;
1168 const char *std_att;
1169 char *att;
1170
1171 /*
1172 * If we have a child, look it up in the standard keys. If a
1173 * key exist, use that instead of the child; if it doesn't,
1174 * prefix "AT&T UNIX " to the existing data.
1175 */
1176
1177 if (NULL == (n = mdoc->last->child))
1178 return(1);
1179
1180 assert(MDOC_TEXT == n->type);
1181 if (NULL == (std_att = mdoc_a2att(n->string))) {
1182 mandoc_vmsg(MANDOCERR_AT_BAD, mdoc->parse,
1183 n->line, n->pos, "At %s", n->string);
1184 mandoc_asprintf(&att, "AT&T UNIX %s", n->string);
1185 } else
1186 att = mandoc_strdup(std_att);
1187
1188 free(n->string);
1189 n->string = att;
1190 return(1);
1191 }
1192
1193 static int
1194 post_an(POST_ARGS)
1195 {
1196 struct mdoc_node *np;
1197
1198 np = mdoc->last;
1199 if (AUTH__NONE == np->norm->An.auth) {
1200 if (0 == np->child)
1201 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0);
1202 } else if (np->child)
1203 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0);
1204
1205 return(1);
1206 }
1207
1208 static int
1209 post_en(POST_ARGS)
1210 {
1211
1212 if (MDOC_BLOCK == mdoc->last->type)
1213 mdoc->last->norm->Es = mdoc->last_es;
1214 return(1);
1215 }
1216
1217 static int
1218 post_es(POST_ARGS)
1219 {
1220
1221 mdoc->last_es = mdoc->last;
1222 return(1);
1223 }
1224
1225 static int
1226 post_it(POST_ARGS)
1227 {
1228 int i, cols;
1229 enum mdoc_list lt;
1230 struct mdoc_node *nbl, *nit, *nch;
1231
1232 nit = mdoc->last;
1233 if (MDOC_BLOCK != nit->type)
1234 return(1);
1235
1236 nbl = nit->parent->parent;
1237 lt = nbl->norm->Bl.type;
1238
1239 switch (lt) {
1240 case LIST_tag:
1241 /* FALLTHROUGH */
1242 case LIST_hang:
1243 /* FALLTHROUGH */
1244 case LIST_ohang:
1245 /* FALLTHROUGH */
1246 case LIST_inset:
1247 /* FALLTHROUGH */
1248 case LIST_diag:
1249 if (NULL == nit->head->child)
1250 mandoc_vmsg(MANDOCERR_IT_NOHEAD,
1251 mdoc->parse, nit->line, nit->pos,
1252 "Bl -%s It",
1253 mdoc_argnames[nbl->args->argv[0].arg]);
1254 break;
1255 case LIST_bullet:
1256 /* FALLTHROUGH */
1257 case LIST_dash:
1258 /* FALLTHROUGH */
1259 case LIST_enum:
1260 /* FALLTHROUGH */
1261 case LIST_hyphen:
1262 if (NULL == nit->body->child)
1263 mandoc_vmsg(MANDOCERR_IT_NOBODY,
1264 mdoc->parse, nit->line, nit->pos,
1265 "Bl -%s It",
1266 mdoc_argnames[nbl->args->argv[0].arg]);
1267 /* FALLTHROUGH */
1268 case LIST_item:
1269 if (NULL != nit->head->child)
1270 mandoc_vmsg(MANDOCERR_ARG_SKIP,
1271 mdoc->parse, nit->line, nit->pos,
1272 "It %s", nit->head->child->string);
1273 break;
1274 case LIST_column:
1275 cols = (int)nbl->norm->Bl.ncols;
1276
1277 assert(NULL == nit->head->child);
1278
1279 for (i = 0, nch = nit->child; nch; nch = nch->next)
1280 if (MDOC_BODY == nch->type)
1281 i++;
1282
1283 if (i < cols || i > cols + 1)
1284 mandoc_vmsg(MANDOCERR_ARGCOUNT,
1285 mdoc->parse, nit->line, nit->pos,
1286 "columns == %d (have %d)", cols, i);
1287 break;
1288 default:
1289 abort();
1290 }
1291
1292 return(1);
1293 }
1294
1295 static int
1296 post_bl_block(POST_ARGS)
1297 {
1298 struct mdoc_node *n, *ni, *nc;
1299
1300 /*
1301 * These are fairly complicated, so we've broken them into two
1302 * functions. post_bl_block_tag() is called when a -tag is
1303 * specified, but no -width (it must be guessed). The second
1304 * when a -width is specified (macro indicators must be
1305 * rewritten into real lengths).
1306 */
1307
1308 n = mdoc->last;
1309
1310 if (LIST_tag == n->norm->Bl.type &&
1311 NULL == n->norm->Bl.width) {
1312 if ( ! post_bl_block_tag(mdoc))
1313 return(0);
1314 assert(n->norm->Bl.width);
1315 } else if (NULL != n->norm->Bl.width) {
1316 if ( ! post_bl_block_width(mdoc))
1317 return(0);
1318 assert(n->norm->Bl.width);
1319 }
1320
1321 for (ni = n->body->child; ni; ni = ni->next) {
1322 if (NULL == ni->body)
1323 continue;
1324 nc = ni->body->last;
1325 while (NULL != nc) {
1326 switch (nc->tok) {
1327 case MDOC_Pp:
1328 /* FALLTHROUGH */
1329 case MDOC_Lp:
1330 /* FALLTHROUGH */
1331 case MDOC_br:
1332 break;
1333 default:
1334 nc = NULL;
1335 continue;
1336 }
1337 if (NULL == ni->next) {
1338 mandoc_msg(MANDOCERR_PAR_MOVE,
1339 mdoc->parse, nc->line, nc->pos,
1340 mdoc_macronames[nc->tok]);
1341 if ( ! mdoc_node_relink(mdoc, nc))
1342 return(0);
1343 } else if (0 == n->norm->Bl.comp &&
1344 LIST_column != n->norm->Bl.type) {
1345 mandoc_vmsg(MANDOCERR_PAR_SKIP,
1346 mdoc->parse, nc->line, nc->pos,
1347 "%s before It",
1348 mdoc_macronames[nc->tok]);
1349 mdoc_node_delete(mdoc, nc);
1350 } else
1351 break;
1352 nc = ni->body->last;
1353 }
1354 }
1355 return(1);
1356 }
1357
1358 static int
1359 post_bl_block_width(POST_ARGS)
1360 {
1361 size_t width;
1362 int i;
1363 enum mdoct tok;
1364 struct mdoc_node *n;
1365 char buf[24];
1366
1367 n = mdoc->last;
1368
1369 /*
1370 * Calculate the real width of a list from the -width string,
1371 * which may contain a macro (with a known default width), a
1372 * literal string, or a scaling width.
1373 *
1374 * If the value to -width is a macro, then we re-write it to be
1375 * the macro's width as set in share/tmac/mdoc/doc-common.
1376 */
1377
1378 if (0 == strcmp(n->norm->Bl.width, "Ds"))
1379 width = 6;
1380 else if (MDOC_MAX == (tok = mdoc_hash_find(n->norm->Bl.width)))
1381 return(1);
1382 else
1383 width = macro2len(tok);
1384
1385 /* The value already exists: free and reallocate it. */
1386
1387 assert(n->args);
1388
1389 for (i = 0; i < (int)n->args->argc; i++)
1390 if (MDOC_Width == n->args->argv[i].arg)
1391 break;
1392
1393 assert(i < (int)n->args->argc);
1394
1395 (void)snprintf(buf, sizeof(buf), "%un", (unsigned int)width);
1396 free(n->args->argv[i].value[0]);
1397 n->args->argv[i].value[0] = mandoc_strdup(buf);
1398
1399 /* Set our width! */
1400 n->norm->Bl.width = n->args->argv[i].value[0];
1401 return(1);
1402 }
1403
1404 static int
1405 post_bl_block_tag(POST_ARGS)
1406 {
1407 struct mdoc_node *n, *nn;
1408 size_t sz, ssz;
1409 int i;
1410 char buf[24];
1411
1412 /*
1413 * Calculate the -width for a `Bl -tag' list if it hasn't been
1414 * provided. Uses the first head macro. NOTE AGAIN: this is
1415 * ONLY if the -width argument has NOT been provided. See
1416 * post_bl_block_width() for converting the -width string.
1417 */
1418
1419 sz = 10;
1420 n = mdoc->last;
1421
1422 for (nn = n->body->child; nn; nn = nn->next) {
1423 if (MDOC_It != nn->tok)
1424 continue;
1425
1426 assert(MDOC_BLOCK == nn->type);
1427 nn = nn->head->child;
1428
1429 if (nn == NULL)
1430 break;
1431
1432 if (MDOC_TEXT == nn->type) {
1433 sz = strlen(nn->string) + 1;
1434 break;
1435 }
1436
1437 if (0 != (ssz = macro2len(nn->tok)))
1438 sz = ssz;
1439
1440 break;
1441 }
1442
1443 /* Defaults to ten ens. */
1444
1445 (void)snprintf(buf, sizeof(buf), "%un", (unsigned int)sz);
1446
1447 /*
1448 * We have to dynamically add this to the macro's argument list.
1449 * We're guaranteed that a MDOC_Width doesn't already exist.
1450 */
1451
1452 assert(n->args);
1453 i = (int)(n->args->argc)++;
1454
1455 n->args->argv = mandoc_reallocarray(n->args->argv,
1456 n->args->argc, sizeof(struct mdoc_argv));
1457
1458 n->args->argv[i].arg = MDOC_Width;
1459 n->args->argv[i].line = n->line;
1460 n->args->argv[i].pos = n->pos;
1461 n->args->argv[i].sz = 1;
1462 n->args->argv[i].value = mandoc_malloc(sizeof(char *));
1463 n->args->argv[i].value[0] = mandoc_strdup(buf);
1464
1465 /* Set our width! */
1466 n->norm->Bl.width = n->args->argv[i].value[0];
1467 return(1);
1468 }
1469
1470 static int
1471 post_bl_head(POST_ARGS)
1472 {
1473 struct mdoc_node *np, *nn, *nnp;
1474 struct mdoc_argv *argv;
1475 int i, j;
1476
1477 if (LIST_column != mdoc->last->norm->Bl.type)
1478 /* FIXME: this should be ERROR class... */
1479 return(hwarn_eq0(mdoc));
1480
1481 /*
1482 * Append old-style lists, where the column width specifiers
1483 * trail as macro parameters, to the new-style ("normal-form")
1484 * lists where they're argument values following -column.
1485 */
1486
1487 if (mdoc->last->child == NULL)
1488 return(1);
1489
1490 np = mdoc->last->parent;
1491 assert(np->args);
1492
1493 for (j = 0; j < (int)np->args->argc; j++)
1494 if (MDOC_Column == np->args->argv[j].arg)
1495 break;
1496
1497 assert(j < (int)np->args->argc);
1498
1499 /*
1500 * Accommodate for new-style groff column syntax. Shuffle the
1501 * child nodes, all of which must be TEXT, as arguments for the
1502 * column field. Then, delete the head children.
1503 */
1504
1505 argv = np->args->argv + j;
1506 i = argv->sz;
1507 argv->sz += mdoc->last->nchild;
1508 argv->value = mandoc_reallocarray(argv->value,
1509 argv->sz, sizeof(char *));
1510
1511 mdoc->last->norm->Bl.ncols = argv->sz;
1512 mdoc->last->norm->Bl.cols = (void *)argv->value;
1513
1514 for (nn = mdoc->last->child; nn; i++) {
1515 argv->value[i] = nn->string;
1516 nn->string = NULL;
1517 nnp = nn;
1518 nn = nn->next;
1519 mdoc_node_delete(NULL, nnp);
1520 }
1521
1522 mdoc->last->nchild = 0;
1523 mdoc->last->child = NULL;
1524
1525 return(1);
1526 }
1527
1528 static int
1529 post_bl(POST_ARGS)
1530 {
1531 struct mdoc_node *nparent, *nprev; /* of the Bl block */
1532 struct mdoc_node *nblock, *nbody; /* of the Bl */
1533 struct mdoc_node *nchild, *nnext; /* of the Bl body */
1534
1535 nbody = mdoc->last;
1536 switch (nbody->type) {
1537 case MDOC_BLOCK:
1538 return(post_bl_block(mdoc));
1539 case MDOC_HEAD:
1540 return(post_bl_head(mdoc));
1541 case MDOC_BODY:
1542 break;
1543 default:
1544 return(1);
1545 }
1546
1547 nchild = nbody->child;
1548 while (NULL != nchild) {
1549 if (MDOC_It == nchild->tok || MDOC_Sm == nchild->tok) {
1550 nchild = nchild->next;
1551 continue;
1552 }
1553
1554 mandoc_msg(MANDOCERR_BL_MOVE, mdoc->parse,
1555 nchild->line, nchild->pos,
1556 mdoc_macronames[nchild->tok]);
1557
1558 /*
1559 * Move the node out of the Bl block.
1560 * First, collect all required node pointers.
1561 */
1562
1563 nblock = nbody->parent;
1564 nprev = nblock->prev;
1565 nparent = nblock->parent;
1566 nnext = nchild->next;
1567
1568 /*
1569 * Unlink this child.
1570 */
1571
1572 assert(NULL == nchild->prev);
1573 if (0 == --nbody->nchild) {
1574 nbody->child = NULL;
1575 nbody->last = NULL;
1576 assert(NULL == nnext);
1577 } else {
1578 nbody->child = nnext;
1579 nnext->prev = NULL;
1580 }
1581
1582 /*
1583 * Relink this child.
1584 */
1585
1586 nchild->parent = nparent;
1587 nchild->prev = nprev;
1588 nchild->next = nblock;
1589
1590 nblock->prev = nchild;
1591 nparent->nchild++;
1592 if (NULL == nprev)
1593 nparent->child = nchild;
1594 else
1595 nprev->next = nchild;
1596
1597 nchild = nnext;
1598 }
1599
1600 return(1);
1601 }
1602
1603 static int
1604 ebool(struct mdoc *mdoc)
1605 {
1606 struct mdoc_node *nch;
1607 enum mdoct tok;
1608
1609 tok = mdoc->last->tok;
1610 nch = mdoc->last->child;
1611
1612 if (NULL == nch) {
1613 if (MDOC_Sm == tok)
1614 mdoc->flags ^= MDOC_SMOFF;
1615 return(1);
1616 }
1617
1618 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_LT, 2);
1619
1620 assert(MDOC_TEXT == nch->type);
1621
1622 if (0 == strcmp(nch->string, "on")) {
1623 if (MDOC_Sm == tok)
1624 mdoc->flags &= ~MDOC_SMOFF;
1625 return(1);
1626 }
1627 if (0 == strcmp(nch->string, "off")) {
1628 if (MDOC_Sm == tok)
1629 mdoc->flags |= MDOC_SMOFF;
1630 return(1);
1631 }
1632
1633 mandoc_vmsg(MANDOCERR_SM_BAD,
1634 mdoc->parse, nch->line, nch->pos,
1635 "%s %s", mdoc_macronames[tok], nch->string);
1636 return(mdoc_node_relink(mdoc, nch));
1637 }
1638
1639 static int
1640 post_root(POST_ARGS)
1641 {
1642 struct mdoc_node *n;
1643
1644 /* Add missing prologue data. */
1645
1646 if ( ! (MDOC_PBODY & mdoc->flags)) {
1647 mandoc_msg(MANDOCERR_PROLOG_BAD, mdoc->parse, 0, 0, "EOF");
1648 if (mdoc->meta.date == NULL)
1649 mdoc->meta.date = mdoc->quick ?
1650 mandoc_strdup("") :
1651 mandoc_normdate(mdoc->parse, NULL, 0, 0);
1652 if (mdoc->meta.title == NULL)
1653 mdoc->meta.title = mandoc_strdup("UNKNOWN");
1654 if (mdoc->meta.vol == NULL)
1655 mdoc->meta.vol = mandoc_strdup("LOCAL");
1656 if (mdoc->meta.arch == NULL)
1657 mdoc->meta.msec = mandoc_strdup("1");
1658 if (mdoc->meta.os == NULL)
1659 mdoc->meta.os = mandoc_strdup("UNKNOWN");
1660 }
1661
1662 n = mdoc->first;
1663 assert(n);
1664
1665 /* Check that we begin with a proper `Sh'. */
1666
1667 if (NULL == n->child)
1668 mandoc_msg(MANDOCERR_DOC_EMPTY, mdoc->parse,
1669 n->line, n->pos, NULL);
1670 else if (MDOC_Sh != n->child->tok)
1671 mandoc_msg(MANDOCERR_SEC_BEFORE, mdoc->parse,
1672 n->child->line, n->child->pos,
1673 mdoc_macronames[n->child->tok]);
1674
1675 return(1);
1676 }
1677
1678 static int
1679 post_st(POST_ARGS)
1680 {
1681 struct mdoc_node *n, *nch;
1682 const char *p;
1683
1684 n = mdoc->last;
1685 nch = n->child;
1686
1687 if (NULL == nch) {
1688 mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse,
1689 n->line, n->pos, mdoc_macronames[n->tok]);
1690 mdoc_node_delete(mdoc, n);
1691 return(1);
1692 }
1693
1694 assert(MDOC_TEXT == nch->type);
1695
1696 if (NULL == (p = mdoc_a2st(nch->string))) {
1697 mandoc_vmsg(MANDOCERR_ST_BAD, mdoc->parse,
1698 nch->line, nch->pos, "St %s", nch->string);
1699 mdoc_node_delete(mdoc, n);
1700 } else {
1701 free(nch->string);
1702 nch->string = mandoc_strdup(p);
1703 }
1704
1705 return(1);
1706 }
1707
1708 static int
1709 post_rs(POST_ARGS)
1710 {
1711 struct mdoc_node *nn, *next, *prev;
1712 int i, j;
1713
1714 switch (mdoc->last->type) {
1715 case MDOC_HEAD:
1716 check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0);
1717 return(1);
1718 case MDOC_BODY:
1719 if (mdoc->last->child)
1720 break;
1721 check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0);
1722 return(1);
1723 default:
1724 return(1);
1725 }
1726
1727 /*
1728 * The full `Rs' block needs special handling to order the
1729 * sub-elements according to `rsord'. Pick through each element
1730 * and correctly order it. This is an insertion sort.
1731 */
1732
1733 next = NULL;
1734 for (nn = mdoc->last->child->next; nn; nn = next) {
1735 /* Determine order of `nn'. */
1736 for (i = 0; i < RSORD_MAX; i++)
1737 if (rsord[i] == nn->tok)
1738 break;
1739
1740 if (i == RSORD_MAX) {
1741 mandoc_msg(MANDOCERR_RS_BAD,
1742 mdoc->parse, nn->line, nn->pos,
1743 mdoc_macronames[nn->tok]);
1744 i = -1;
1745 } else if (MDOC__J == nn->tok || MDOC__B == nn->tok)
1746 mdoc->last->norm->Rs.quote_T++;
1747
1748 /*
1749 * Remove `nn' from the chain. This somewhat
1750 * repeats mdoc_node_unlink(), but since we're
1751 * just re-ordering, there's no need for the
1752 * full unlink process.
1753 */
1754
1755 if (NULL != (next = nn->next))
1756 next->prev = nn->prev;
1757
1758 if (NULL != (prev = nn->prev))
1759 prev->next = nn->next;
1760
1761 nn->prev = nn->next = NULL;
1762
1763 /*
1764 * Scan back until we reach a node that's
1765 * ordered before `nn'.
1766 */
1767
1768 for ( ; prev ; prev = prev->prev) {
1769 /* Determine order of `prev'. */
1770 for (j = 0; j < RSORD_MAX; j++)
1771 if (rsord[j] == prev->tok)
1772 break;
1773 if (j == RSORD_MAX)
1774 j = -1;
1775
1776 if (j <= i)
1777 break;
1778 }
1779
1780 /*
1781 * Set `nn' back into its correct place in front
1782 * of the `prev' node.
1783 */
1784
1785 nn->prev = prev;
1786
1787 if (prev) {
1788 if (prev->next)
1789 prev->next->prev = nn;
1790 nn->next = prev->next;
1791 prev->next = nn;
1792 } else {
1793 mdoc->last->child->prev = nn;
1794 nn->next = mdoc->last->child;
1795 mdoc->last->child = nn;
1796 }
1797 }
1798
1799 return(1);
1800 }
1801
1802 /*
1803 * For some arguments of some macros,
1804 * convert all breakable hyphens into ASCII_HYPH.
1805 */
1806 static int
1807 post_hyph(POST_ARGS)
1808 {
1809 struct mdoc_node *n, *nch;
1810 char *cp;
1811
1812 n = mdoc->last;
1813 switch (n->type) {
1814 case MDOC_HEAD:
1815 if (MDOC_Sh == n->tok || MDOC_Ss == n->tok)
1816 break;
1817 return(1);
1818 case MDOC_BODY:
1819 if (MDOC_D1 == n->tok || MDOC_Nd == n->tok)
1820 break;
1821 return(1);
1822 case MDOC_ELEM:
1823 break;
1824 default:
1825 return(1);
1826 }
1827
1828 for (nch = n->child; nch; nch = nch->next) {
1829 if (MDOC_TEXT != nch->type)
1830 continue;
1831 cp = nch->string;
1832 if ('\0' == *cp)
1833 continue;
1834 while ('\0' != *(++cp))
1835 if ('-' == *cp &&
1836 isalpha((unsigned char)cp[-1]) &&
1837 isalpha((unsigned char)cp[1]))
1838 *cp = ASCII_HYPH;
1839 }
1840 return(1);
1841 }
1842
1843 static int
1844 post_ns(POST_ARGS)
1845 {
1846
1847 if (MDOC_LINE & mdoc->last->flags)
1848 mandoc_msg(MANDOCERR_NS_SKIP, mdoc->parse,
1849 mdoc->last->line, mdoc->last->pos, NULL);
1850 return(1);
1851 }
1852
1853 static int
1854 post_sh(POST_ARGS)
1855 {
1856
1857 if (MDOC_HEAD == mdoc->last->type)
1858 return(post_sh_head(mdoc));
1859 if (MDOC_BODY == mdoc->last->type)
1860 return(post_sh_body(mdoc));
1861
1862 return(1);
1863 }
1864
1865 static int
1866 post_sh_body(POST_ARGS)
1867 {
1868 struct mdoc_node *n;
1869
1870 if (SEC_NAME != mdoc->lastsec)
1871 return(1);
1872
1873 /*
1874 * Warn if the NAME section doesn't contain the `Nm' and `Nd'
1875 * macros (can have multiple `Nm' and one `Nd'). Note that the
1876 * children of the BODY declaration can also be "text".
1877 */
1878
1879 if (NULL == (n = mdoc->last->child)) {
1880 mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
1881 mdoc->last->line, mdoc->last->pos, "empty");
1882 return(1);
1883 }
1884
1885 for ( ; n && n->next; n = n->next) {
1886 if (MDOC_ELEM == n->type && MDOC_Nm == n->tok)
1887 continue;
1888 if (MDOC_TEXT == n->type)
1889 continue;
1890 mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
1891 n->line, n->pos, mdoc_macronames[n->tok]);
1892 }
1893
1894 assert(n);
1895 if (MDOC_BLOCK == n->type && MDOC_Nd == n->tok)
1896 return(1);
1897
1898 mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
1899 n->line, n->pos, mdoc_macronames[n->tok]);
1900 return(1);
1901 }
1902
1903 static int
1904 post_sh_head(POST_ARGS)
1905 {
1906 struct mdoc_node *n;
1907 const char *goodsec;
1908 char *secname;
1909 enum mdoc_sec sec;
1910
1911 /*
1912 * Process a new section. Sections are either "named" or
1913 * "custom". Custom sections are user-defined, while named ones
1914 * follow a conventional order and may only appear in certain
1915 * manual sections.
1916 */
1917
1918 secname = NULL;
1919 sec = SEC_CUSTOM;
1920 mdoc_deroff(&secname, mdoc->last);
1921 sec = NULL == secname ? SEC_CUSTOM : a2sec(secname);
1922
1923 /* The NAME should be first. */
1924
1925 if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed)
1926 mandoc_vmsg(MANDOCERR_NAMESEC_FIRST, mdoc->parse,
1927 mdoc->last->line, mdoc->last->pos,
1928 "Sh %s", secname);
1929
1930 /* The SYNOPSIS gets special attention in other areas. */
1931
1932 if (SEC_SYNOPSIS == sec) {
1933 roff_setreg(mdoc->roff, "nS", 1, '=');
1934 mdoc->flags |= MDOC_SYNOPSIS;
1935 } else {
1936 roff_setreg(mdoc->roff, "nS", 0, '=');
1937 mdoc->flags &= ~MDOC_SYNOPSIS;
1938 }
1939
1940 /* Mark our last section. */
1941
1942 mdoc->lastsec = sec;
1943
1944 /*
1945 * Set the section attribute for the current HEAD, for its
1946 * parent BLOCK, and for the HEAD children; the latter can
1947 * only be TEXT nodes, so no recursion is needed.
1948 * For other blocks and elements, including .Sh BODY, this is
1949 * done when allocating the node data structures, but for .Sh
1950 * BLOCK and HEAD, the section is still unknown at that time.
1951 */
1952
1953 mdoc->last->parent->sec = sec;
1954 mdoc->last->sec = sec;
1955 for (n = mdoc->last->child; n; n = n->next)
1956 n->sec = sec;
1957
1958 /* We don't care about custom sections after this. */
1959
1960 if (SEC_CUSTOM == sec) {
1961 free(secname);
1962 return(1);
1963 }
1964
1965 /*
1966 * Check whether our non-custom section is being repeated or is
1967 * out of order.
1968 */
1969
1970 if (sec == mdoc->lastnamed)
1971 mandoc_vmsg(MANDOCERR_SEC_REP, mdoc->parse,
1972 mdoc->last->line, mdoc->last->pos,
1973 "Sh %s", secname);
1974
1975 if (sec < mdoc->lastnamed)
1976 mandoc_vmsg(MANDOCERR_SEC_ORDER, mdoc->parse,
1977 mdoc->last->line, mdoc->last->pos,
1978 "Sh %s", secname);
1979
1980 /* Mark the last named section. */
1981
1982 mdoc->lastnamed = sec;
1983
1984 /* Check particular section/manual conventions. */
1985
1986 assert(mdoc->meta.msec);
1987
1988 goodsec = NULL;
1989 switch (sec) {
1990 case SEC_ERRORS:
1991 if (*mdoc->meta.msec == '4')
1992 break;
1993 goodsec = "2, 3, 4, 9";
1994 /* FALLTHROUGH */
1995 case SEC_RETURN_VALUES:
1996 /* FALLTHROUGH */
1997 case SEC_LIBRARY:
1998 if (*mdoc->meta.msec == '2')
1999 break;
2000 if (*mdoc->meta.msec == '3')
2001 break;
2002 if (NULL == goodsec)
2003 goodsec = "2, 3, 9";
2004 /* FALLTHROUGH */
2005 case SEC_CONTEXT:
2006 if (*mdoc->meta.msec == '9')
2007 break;
2008 if (NULL == goodsec)
2009 goodsec = "9";
2010 mandoc_vmsg(MANDOCERR_SEC_MSEC, mdoc->parse,
2011 mdoc->last->line, mdoc->last->pos,
2012 "Sh %s for %s only", secname, goodsec);
2013 break;
2014 default:
2015 break;
2016 }
2017
2018 free(secname);
2019 return(1);
2020 }
2021
2022 static int
2023 post_ignpar(POST_ARGS)
2024 {
2025 struct mdoc_node *np;
2026
2027 if (MDOC_BODY != mdoc->last->type)
2028 return(1);
2029
2030 if (NULL != (np = mdoc->last->child))
2031 if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
2032 mandoc_vmsg(MANDOCERR_PAR_SKIP,
2033 mdoc->parse, np->line, np->pos,
2034 "%s after %s", mdoc_macronames[np->tok],
2035 mdoc_macronames[mdoc->last->tok]);
2036 mdoc_node_delete(mdoc, np);
2037 }
2038
2039 if (NULL != (np = mdoc->last->last))
2040 if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
2041 mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2042 np->line, np->pos, "%s at the end of %s",
2043 mdoc_macronames[np->tok],
2044 mdoc_macronames[mdoc->last->tok]);
2045 mdoc_node_delete(mdoc, np);
2046 }
2047
2048 return(1);
2049 }
2050
2051 static int
2052 pre_par(PRE_ARGS)
2053 {
2054
2055 if (NULL == mdoc->last)
2056 return(1);
2057 if (MDOC_ELEM != n->type && MDOC_BLOCK != n->type)
2058 return(1);
2059
2060 /*
2061 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
2062 * block: `Lp', `Pp', or non-compact `Bd' or `Bl'.
2063 */
2064
2065 if (MDOC_Pp != mdoc->last->tok &&
2066 MDOC_Lp != mdoc->last->tok &&
2067 MDOC_br != mdoc->last->tok)
2068 return(1);
2069 if (MDOC_Bl == n->tok && n->norm->Bl.comp)
2070 return(1);
2071 if (MDOC_Bd == n->tok && n->norm->Bd.comp)
2072 return(1);
2073 if (MDOC_It == n->tok && n->parent->norm->Bl.comp)
2074 return(1);
2075
2076 mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2077 mdoc->last->line, mdoc->last->pos,
2078 "%s before %s", mdoc_macronames[mdoc->last->tok],
2079 mdoc_macronames[n->tok]);
2080 mdoc_node_delete(mdoc, mdoc->last);
2081 return(1);
2082 }
2083
2084 static int
2085 post_par(POST_ARGS)
2086 {
2087 struct mdoc_node *np;
2088
2089 if (MDOC_ELEM != mdoc->last->type &&
2090 MDOC_BLOCK != mdoc->last->type)
2091 return(1);
2092
2093 if (NULL == (np = mdoc->last->prev)) {
2094 np = mdoc->last->parent;
2095 if (MDOC_Sh != np->tok && MDOC_Ss != np->tok)
2096 return(1);
2097 } else {
2098 if (MDOC_Pp != np->tok && MDOC_Lp != np->tok &&
2099 (MDOC_br != mdoc->last->tok ||
2100 (MDOC_sp != np->tok && MDOC_br != np->tok)))
2101 return(1);
2102 }
2103
2104 mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2105 mdoc->last->line, mdoc->last->pos,
2106 "%s after %s", mdoc_macronames[mdoc->last->tok],
2107 mdoc_macronames[np->tok]);
2108 mdoc_node_delete(mdoc, mdoc->last);
2109 return(1);
2110 }
2111
2112 static int
2113 pre_literal(PRE_ARGS)
2114 {
2115
2116 if (MDOC_BODY != n->type)
2117 return(1);
2118
2119 /*
2120 * The `Dl' (note "el" not "one") and `Bd -literal' and `Bd
2121 * -unfilled' macros set MDOC_LITERAL on entrance to the body.
2122 */
2123
2124 switch (n->tok) {
2125 case MDOC_Dl:
2126 mdoc->flags |= MDOC_LITERAL;
2127 break;
2128 case MDOC_Bd:
2129 if (DISP_literal == n->norm->Bd.type)
2130 mdoc->flags |= MDOC_LITERAL;
2131 if (DISP_unfilled == n->norm->Bd.type)
2132 mdoc->flags |= MDOC_LITERAL;
2133 break;
2134 default:
2135 abort();
2136 /* NOTREACHED */
2137 }
2138
2139 return(1);
2140 }
2141
2142 static int
2143 post_dd(POST_ARGS)
2144 {
2145 struct mdoc_node *n;
2146 char *datestr;
2147
2148 if (mdoc->meta.date)
2149 free(mdoc->meta.date);
2150
2151 n = mdoc->last;
2152 if (NULL == n->child || '\0' == n->child->string[0]) {
2153 mdoc->meta.date = mdoc->quick ? mandoc_strdup("") :
2154 mandoc_normdate(mdoc->parse, NULL, n->line, n->pos);
2155 return(1);
2156 }
2157
2158 datestr = NULL;
2159 mdoc_deroff(&datestr, n);
2160 if (mdoc->quick)
2161 mdoc->meta.date = datestr;
2162 else {
2163 mdoc->meta.date = mandoc_normdate(mdoc->parse,
2164 datestr, n->line, n->pos);
2165 free(datestr);
2166 }
2167 return(1);
2168 }
2169
2170 static int
2171 post_dt(POST_ARGS)
2172 {
2173 struct mdoc_node *nn, *n;
2174 const char *cp;
2175 char *p;
2176
2177 n = mdoc->last;
2178
2179 if (mdoc->meta.title)
2180 free(mdoc->meta.title);
2181 if (mdoc->meta.vol)
2182 free(mdoc->meta.vol);
2183 if (mdoc->meta.arch)
2184 free(mdoc->meta.arch);
2185
2186 mdoc->meta.title = mdoc->meta.vol = mdoc->meta.arch = NULL;
2187
2188 /* First check that all characters are uppercase. */
2189
2190 if (NULL != (nn = n->child))
2191 for (p = nn->string; *p; p++) {
2192 if (toupper((unsigned char)*p) == *p)
2193 continue;
2194 mandoc_vmsg(MANDOCERR_TITLE_CASE,
2195 mdoc->parse, nn->line,
2196 nn->pos + (p - nn->string),
2197 "Dt %s", nn->string);
2198 break;
2199 }
2200
2201 /* Handles: `.Dt'
2202 * title = unknown, volume = local, msec = 0, arch = NULL
2203 */
2204
2205 if (NULL == (nn = n->child)) {
2206 /* XXX: make these macro values. */
2207 /* FIXME: warn about missing values. */
2208 mdoc->meta.title = mandoc_strdup("UNKNOWN");
2209 mdoc->meta.vol = mandoc_strdup("LOCAL");
2210 mdoc->meta.msec = mandoc_strdup("1");
2211 return(1);
2212 }
2213
2214 /* Handles: `.Dt TITLE'
2215 * title = TITLE, volume = local, msec = 0, arch = NULL
2216 */
2217
2218 mdoc->meta.title = mandoc_strdup(
2219 '\0' == nn->string[0] ? "UNKNOWN" : nn->string);
2220
2221 if (NULL == (nn = nn->next)) {
2222 /* FIXME: warn about missing msec. */
2223 /* XXX: make this a macro value. */
2224 mdoc->meta.vol = mandoc_strdup("LOCAL");
2225 mdoc->meta.msec = mandoc_strdup("1");
2226 return(1);
2227 }
2228
2229 /* Handles: `.Dt TITLE SEC'
2230 * title = TITLE,
2231 * volume = SEC is msec ? format(msec) : SEC,
2232 * msec = SEC is msec ? atoi(msec) : 0,
2233 * arch = NULL
2234 */
2235
2236 cp = mandoc_a2msec(nn->string);
2237 if (cp) {
2238 mdoc->meta.vol = mandoc_strdup(cp);
2239 mdoc->meta.msec = mandoc_strdup(nn->string);
2240 } else {
2241 mandoc_vmsg(MANDOCERR_MSEC_BAD, mdoc->parse,
2242 nn->line, nn->pos, "Dt ... %s", nn->string);
2243 mdoc->meta.vol = mandoc_strdup(nn->string);
2244 mdoc->meta.msec = mandoc_strdup(nn->string);
2245 }
2246
2247 if (NULL == (nn = nn->next))
2248 return(1);
2249
2250 /* Handles: `.Dt TITLE SEC VOL'
2251 * title = TITLE,
2252 * volume = VOL is vol ? format(VOL) :
2253 * VOL is arch ? format(arch) :
2254 * VOL
2255 */
2256
2257 cp = mdoc_a2vol(nn->string);
2258 if (cp) {
2259 free(mdoc->meta.vol);
2260 mdoc->meta.vol = mandoc_strdup(cp);
2261 } else {
2262 cp = mdoc_a2arch(nn->string);
2263 if (NULL == cp) {
2264 mandoc_vmsg(MANDOCERR_ARCH_BAD, mdoc->parse,
2265 nn->line, nn->pos, "Dt ... %s", nn->string);
2266 free(mdoc->meta.vol);
2267 mdoc->meta.vol = mandoc_strdup(nn->string);
2268 } else
2269 mdoc->meta.arch = mandoc_strdup(cp);
2270 }
2271
2272 /* Ignore any subsequent parameters... */
2273 /* FIXME: warn about subsequent parameters. */
2274
2275 return(1);
2276 }
2277
2278 static int
2279 post_prol(POST_ARGS)
2280 {
2281 /*
2282 * Remove prologue macros from the document after they're
2283 * processed. The final document uses mdoc_meta for these
2284 * values and discards the originals.
2285 */
2286
2287 mdoc_node_delete(mdoc, mdoc->last);
2288 if (mdoc->meta.title && mdoc->meta.date && mdoc->meta.os)
2289 mdoc->flags |= MDOC_PBODY;
2290
2291 return(1);
2292 }
2293
2294 static int
2295 post_bx(POST_ARGS)
2296 {
2297 struct mdoc_node *n;
2298
2299 /*
2300 * Make `Bx's second argument always start with an uppercase
2301 * letter. Groff checks if it's an "accepted" term, but we just
2302 * uppercase blindly.
2303 */
2304
2305 n = mdoc->last->child;
2306 if (n && NULL != (n = n->next))
2307 *n->string = (char)toupper((unsigned char)*n->string);
2308
2309 return(1);
2310 }
2311
2312 static int
2313 post_os(POST_ARGS)
2314 {
2315 #ifndef OSNAME
2316 struct utsname utsname;
2317 static char *defbuf;
2318 #endif
2319 struct mdoc_node *n;
2320
2321 n = mdoc->last;
2322
2323 /*
2324 * Set the operating system by way of the `Os' macro.
2325 * The order of precedence is:
2326 * 1. the argument of the `Os' macro, unless empty
2327 * 2. the -Ios=foo command line argument, if provided
2328 * 3. -DOSNAME="\"foo\"", if provided during compilation
2329 * 4. "sysname release" from uname(3)
2330 */
2331
2332 free(mdoc->meta.os);
2333 mdoc->meta.os = NULL;
2334 mdoc_deroff(&mdoc->meta.os, n);
2335 if (mdoc->meta.os)
2336 return(1);
2337
2338 if (mdoc->defos) {
2339 mdoc->meta.os = mandoc_strdup(mdoc->defos);
2340 return(1);
2341 }
2342
2343 #ifdef OSNAME
2344 mdoc->meta.os = mandoc_strdup(OSNAME);
2345 #else /*!OSNAME */
2346 if (NULL == defbuf) {
2347 if (-1 == uname(&utsname)) {
2348 mandoc_msg(MANDOCERR_OS_UNAME, mdoc->parse,
2349 n->line, n->pos, "Os");
2350 defbuf = mandoc_strdup("UNKNOWN");
2351 } else
2352 mandoc_asprintf(&defbuf, "%s %s",
2353 utsname.sysname, utsname.release);
2354 }
2355 mdoc->meta.os = mandoc_strdup(defbuf);
2356 #endif /*!OSNAME*/
2357 return(1);
2358 }
2359
2360 /*
2361 * If no argument is provided,
2362 * fill in the name of the current manual page.
2363 */
2364 static int
2365 post_ex(POST_ARGS)
2366 {
2367 struct mdoc_node *n;
2368
2369 n = mdoc->last;
2370
2371 if (n->child)
2372 return(1);
2373
2374 if (mdoc->meta.name == NULL) {
2375 mandoc_msg(MANDOCERR_EX_NONAME, mdoc->parse,
2376 n->line, n->pos, "Ex");
2377 return(1);
2378 }
2379
2380 mdoc->next = MDOC_NEXT_CHILD;
2381
2382 if ( ! mdoc_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name))
2383 return(0);
2384
2385 mdoc->last = n;
2386 return(1);
2387 }
2388
2389 static enum mdoc_sec
2390 a2sec(const char *p)
2391 {
2392 int i;
2393
2394 for (i = 0; i < (int)SEC__MAX; i++)
2395 if (secnames[i] && 0 == strcmp(p, secnames[i]))
2396 return((enum mdoc_sec)i);
2397
2398 return(SEC_CUSTOM);
2399 }
2400
2401 static size_t
2402 macro2len(enum mdoct macro)
2403 {
2404
2405 switch (macro) {
2406 case MDOC_Ad:
2407 return(12);
2408 case MDOC_Ao:
2409 return(12);
2410 case MDOC_An:
2411 return(12);
2412 case MDOC_Aq:
2413 return(12);
2414 case MDOC_Ar:
2415 return(12);
2416 case MDOC_Bo:
2417 return(12);
2418 case MDOC_Bq:
2419 return(12);
2420 case MDOC_Cd:
2421 return(12);
2422 case MDOC_Cm:
2423 return(10);
2424 case MDOC_Do:
2425 return(10);
2426 case MDOC_Dq:
2427 return(12);
2428 case MDOC_Dv:
2429 return(12);
2430 case MDOC_Eo:
2431 return(12);
2432 case MDOC_Em:
2433 return(10);
2434 case MDOC_Er:
2435 return(17);
2436 case MDOC_Ev:
2437 return(15);
2438 case MDOC_Fa:
2439 return(12);
2440 case MDOC_Fl:
2441 return(10);
2442 case MDOC_Fo:
2443 return(16);
2444 case MDOC_Fn:
2445 return(16);
2446 case MDOC_Ic:
2447 return(10);
2448 case MDOC_Li:
2449 return(16);
2450 case MDOC_Ms:
2451 return(6);
2452 case MDOC_Nm:
2453 return(10);
2454 case MDOC_No:
2455 return(12);
2456 case MDOC_Oo:
2457 return(10);
2458 case MDOC_Op:
2459 return(14);
2460 case MDOC_Pa:
2461 return(32);
2462 case MDOC_Pf:
2463 return(12);
2464 case MDOC_Po:
2465 return(12);
2466 case MDOC_Pq:
2467 return(12);
2468 case MDOC_Ql:
2469 return(16);
2470 case MDOC_Qo:
2471 return(12);
2472 case MDOC_So:
2473 return(12);
2474 case MDOC_Sq:
2475 return(12);
2476 case MDOC_Sy:
2477 return(6);
2478 case MDOC_Sx:
2479 return(16);
2480 case MDOC_Tn:
2481 return(10);
2482 case MDOC_Va:
2483 return(12);
2484 case MDOC_Vt:
2485 return(12);
2486 case MDOC_Xr:
2487 return(10);
2488 default:
2489 break;
2490 };
2491 return(0);
2492 }