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