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