1 /* $Id: mdoc_validate.c,v 1.350 2017/07/20 12:54:02 schwarze Exp $ */
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>
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.
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.
21 #include <sys/types.h>
23 #include <sys/utsname.h>
34 #include "mandoc_aux.h"
36 #include "mandoc_xr.h"
39 #include "libmandoc.h"
43 /* FIXME: .Bl -diag can't have non-text children in HEAD. */
45 #define POST_ARGS struct roff_man *mdoc
53 typedef void (*v_post)(POST_ARGS);
55 static int build_list(struct roff_man *, int);
56 static void check_text(struct roff_man *, int, int, char *);
57 static void check_argv(struct roff_man *,
58 struct roff_node *, struct mdoc_argv *);
59 static void check_args(struct roff_man *, struct roff_node *);
60 static void check_toptext(struct roff_man *, int, int, const char *);
61 static int child_an(const struct roff_node *);
62 static size_t macro2len(enum roff_tok);
63 static void rewrite_macro2len(struct roff_man *, char **);
64 static int similar(const char *, const char *);
66 static void post_an(POST_ARGS);
67 static void post_an_norm(POST_ARGS);
68 static void post_at(POST_ARGS);
69 static void post_bd(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_head(POST_ARGS);
75 static void post_bl_norm(POST_ARGS);
76 static void post_bx(POST_ARGS);
77 static void post_defaults(POST_ARGS);
78 static void post_display(POST_ARGS);
79 static void post_dd(POST_ARGS);
80 static void post_delim(POST_ARGS);
81 static void post_delim_nb(POST_ARGS);
82 static void post_dt(POST_ARGS);
83 static void post_en(POST_ARGS);
84 static void post_es(POST_ARGS);
85 static void post_eoln(POST_ARGS);
86 static void post_ex(POST_ARGS);
87 static void post_fa(POST_ARGS);
88 static void post_fn(POST_ARGS);
89 static void post_fname(POST_ARGS);
90 static void post_fo(POST_ARGS);
91 static void post_hyph(POST_ARGS);
92 static void post_ignpar(POST_ARGS);
93 static void post_it(POST_ARGS);
94 static void post_lb(POST_ARGS);
95 static void post_nd(POST_ARGS);
96 static void post_nm(POST_ARGS);
97 static void post_ns(POST_ARGS);
98 static void post_obsolete(POST_ARGS);
99 static void post_os(POST_ARGS);
100 static void post_par(POST_ARGS);
101 static void post_prevpar(POST_ARGS);
102 static void post_root(POST_ARGS);
103 static void post_rs(POST_ARGS);
104 static void post_rv(POST_ARGS);
105 static void post_sh(POST_ARGS);
106 static void post_sh_head(POST_ARGS);
107 static void post_sh_name(POST_ARGS);
108 static void post_sh_see_also(POST_ARGS);
109 static void post_sh_authors(POST_ARGS);
110 static void post_sm(POST_ARGS);
111 static void post_st(POST_ARGS);
112 static void post_std(POST_ARGS);
113 static void post_sx(POST_ARGS);
114 static void post_useless(POST_ARGS);
115 static void post_xr(POST_ARGS);
116 static void post_xx(POST_ARGS);
118 static const v_post __mdoc_valids[MDOC_MAX - MDOC_Dd] = {
123 post_ignpar, /* Ss */
125 post_display, /* D1 */
126 post_display, /* Dl */
127 post_display, /* Bd */
132 post_delim_nb, /* Ad */
135 post_defaults, /* Ar */
137 post_delim_nb, /* Cm */
138 post_delim_nb, /* Dv */
139 post_delim_nb, /* Er */
140 post_delim_nb, /* Ev */
144 post_delim_nb, /* Fl */
146 post_delim_nb, /* Ft */
147 post_delim_nb, /* Ic */
148 post_delim_nb, /* In */
149 post_defaults, /* Li */
152 post_delim_nb, /* Op */
153 post_obsolete, /* Ot */
154 post_defaults, /* Pa */
157 post_delim_nb, /* Va */
158 post_delim_nb, /* Vt */
161 post_hyph, /* %B */ /* FIXME: can be used outside Rs/Re. */
169 post_hyph, /* %T */ /* FIXME: can be used outside Rs/Re. */
172 post_delim_nb, /* Ao */
173 post_delim_nb, /* Aq */
177 post_delim_nb, /* Bo */
181 post_obsolete, /* Db */
187 post_delim_nb, /* Em */
190 post_delim_nb, /* Ms */
197 post_delim_nb, /* Po */
198 post_delim_nb, /* Pq */
200 post_delim_nb, /* Ql */
201 post_delim_nb, /* Qo */
202 post_delim_nb, /* Qq */
206 post_delim_nb, /* So */
207 post_delim_nb, /* Sq */
210 post_delim_nb, /* Sy */
211 post_useless, /* Tn */
217 post_delim_nb, /* Oo */
222 post_obsolete, /* Hf */
223 post_obsolete, /* Fr */
227 post_delim_nb, /* Lk */
228 post_defaults, /* Mt */
229 post_delim_nb, /* Brq */
230 post_delim_nb, /* Bro */
240 static const v_post *const mdoc_valids = __mdoc_valids - MDOC_Dd;
242 #define RSORD_MAX 14 /* Number of `Rs' blocks. */
244 static const enum roff_tok rsord[RSORD_MAX] = {
261 static const char * const secnames[SEC__MAX] = {
268 "IMPLEMENTATION NOTES",
283 "SECURITY CONSIDERATIONS",
289 mdoc_node_validate(struct roff_man *mdoc)
295 mdoc->last = mdoc->last->child;
296 while (mdoc->last != NULL) {
297 mdoc_node_validate(mdoc);
299 mdoc->last = mdoc->last->child;
301 mdoc->last = mdoc->last->next;
305 mdoc->next = ROFF_NEXT_SIBLING;
308 if (n->sec != SEC_SYNOPSIS ||
309 (n->parent->tok != MDOC_Cd && n->parent->tok != MDOC_Fd))
310 check_text(mdoc, n->line, n->pos, n->string);
311 if (n->parent->tok == MDOC_It ||
312 (n->parent->type == ROFFT_BODY &&
313 (n->parent->tok == MDOC_Sh ||
314 n->parent->tok == MDOC_Ss)))
315 check_toptext(mdoc, n->line, n->pos, n->string);
324 check_args(mdoc, mdoc->last);
327 * Closing delimiters are not special at the
328 * beginning of a block, opening delimiters
329 * are not special at the end.
332 if (n->child != NULL)
333 n->child->flags &= ~NODE_DELIMC;
335 n->last->flags &= ~NODE_DELIMO;
337 /* Call the macro's postprocessor. */
339 if (n->tok < ROFF_MAX) {
352 assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX);
353 p = mdoc_valids + n->tok;
363 check_args(struct roff_man *mdoc, struct roff_node *n)
370 assert(n->args->argc);
371 for (i = 0; i < (int)n->args->argc; i++)
372 check_argv(mdoc, n, &n->args->argv[i]);
376 check_argv(struct roff_man *mdoc, struct roff_node *n, struct mdoc_argv *v)
380 for (i = 0; i < (int)v->sz; i++)
381 check_text(mdoc, v->line, v->pos, v->value[i]);
385 check_text(struct roff_man *mdoc, int ln, int pos, char *p)
389 if (MDOC_LITERAL & mdoc->flags)
392 for (cp = p; NULL != (p = strchr(p, '\t')); p++)
393 mandoc_msg(MANDOCERR_FI_TAB, mdoc->parse,
394 ln, pos + (int)(p - cp), NULL);
398 check_toptext(struct roff_man *mdoc, int ln, int pos, const char *p)
400 const char *cp, *cpr;
405 if ((cp = strstr(p, "OpenBSD")) != NULL)
406 mandoc_msg(MANDOCERR_BX, mdoc->parse,
407 ln, pos + (cp - p), "Ox");
408 if ((cp = strstr(p, "NetBSD")) != NULL)
409 mandoc_msg(MANDOCERR_BX, mdoc->parse,
410 ln, pos + (cp - p), "Nx");
411 if ((cp = strstr(p, "FreeBSD")) != NULL)
412 mandoc_msg(MANDOCERR_BX, mdoc->parse,
413 ln, pos + (cp - p), "Fx");
414 if ((cp = strstr(p, "DragonFly")) != NULL)
415 mandoc_msg(MANDOCERR_BX, mdoc->parse,
416 ln, pos + (cp - p), "Dx");
419 while ((cp = strstr(cp + 1, "()")) != NULL) {
420 for (cpr = cp - 1; cpr >= p; cpr--)
421 if (*cpr != '_' && !isalnum((unsigned char)*cpr))
423 if ((cpr < p || *cpr == ' ') && cpr + 1 < cp) {
425 mandoc_vmsg(MANDOCERR_FUNC, mdoc->parse,
427 "%.*s()", (int)(cp - cpr), cpr);
433 post_delim(POST_ARGS)
435 const struct roff_node *nch;
440 tok = mdoc->last->tok;
441 nch = mdoc->last->last;
442 if (nch == NULL || nch->type != ROFFT_TEXT)
444 lc = strchr(nch->string, '\0') - 1;
445 if (lc < nch->string)
447 delim = mdoc_isdelim(lc);
448 if (delim == DELIM_NONE || delim == DELIM_OPEN)
450 if (*lc == ')' && (tok == MDOC_Nd || tok == MDOC_Sh ||
451 tok == MDOC_Ss || tok == MDOC_Fo))
454 mandoc_vmsg(MANDOCERR_DELIM, mdoc->parse,
455 nch->line, nch->pos + (lc - nch->string),
456 "%s%s %s", roff_name[tok],
457 nch == mdoc->last->child ? "" : " ...", nch->string);
461 post_delim_nb(POST_ARGS)
463 const struct roff_node *nch;
470 * Find candidates: at least two bytes,
471 * the last one a closing or middle delimiter.
474 tok = mdoc->last->tok;
475 nch = mdoc->last->last;
476 if (nch == NULL || nch->type != ROFFT_TEXT)
478 lc = strchr(nch->string, '\0') - 1;
479 if (lc <= nch->string)
481 delim = mdoc_isdelim(lc);
482 if (delim == DELIM_NONE || delim == DELIM_OPEN)
486 * Reduce false positives by allowing various cases.
489 /* Escaped delimiters. */
490 if (lc > nch->string + 1 && lc[-2] == '\\' &&
491 (lc[-1] == '&' || lc[-1] == 'e'))
494 /* Specific byte sequences. */
497 for (cp = lc; cp >= nch->string; cp--)
502 if (lc > nch->string + 1 && lc[-2] == '.' && lc[-1] == '.')
516 for (cp = lc; cp >= nch->string; cp--)
521 if (lc == nch->string + 1 && lc[-1] == '|')
527 /* Exactly two non-alphanumeric bytes. */
528 if (lc == nch->string + 1 && !isalnum((unsigned char)lc[-1]))
531 /* At least three alphabetic words with a sentence ending. */
532 if (strchr("!.:?", *lc) != NULL && (tok == MDOC_Em ||
533 tok == MDOC_Li || tok == MDOC_Po || tok == MDOC_Pq ||
536 for (cp = lc - 1; cp >= nch->string; cp--) {
539 if (cp > nch->string && cp[-1] == ',')
541 } else if (isalpha((unsigned int)*cp)) {
549 mandoc_vmsg(MANDOCERR_DELIM_NB, mdoc->parse,
550 nch->line, nch->pos + (lc - nch->string),
551 "%s%s %s", roff_name[tok],
552 nch == mdoc->last->child ? "" : " ...", nch->string);
556 post_bl_norm(POST_ARGS)
559 struct mdoc_argv *argv, *wa;
561 enum mdocargt mdoclt;
564 n = mdoc->last->parent;
565 n->norm->Bl.type = LIST__NONE;
568 * First figure out which kind of list to use: bind ourselves to
569 * the first mentioned list type and warn about any remaining
570 * ones. If we find no list type, we default to LIST_item.
573 wa = (n->args == NULL) ? NULL : n->args->argv;
574 mdoclt = MDOC_ARG_MAX;
575 for (i = 0; n->args && i < (int)n->args->argc; i++) {
576 argv = n->args->argv + i;
579 /* Set list types. */
613 /* Set list arguments. */
615 if (n->norm->Bl.comp)
616 mandoc_msg(MANDOCERR_ARG_REP,
617 mdoc->parse, argv->line,
618 argv->pos, "Bl -compact");
619 n->norm->Bl.comp = 1;
624 mandoc_msg(MANDOCERR_ARG_EMPTY,
625 mdoc->parse, argv->line,
626 argv->pos, "Bl -width");
627 n->norm->Bl.width = "0n";
630 if (NULL != n->norm->Bl.width)
631 mandoc_vmsg(MANDOCERR_ARG_REP,
632 mdoc->parse, argv->line,
633 argv->pos, "Bl -width %s",
635 rewrite_macro2len(mdoc, argv->value);
636 n->norm->Bl.width = argv->value[0];
640 mandoc_msg(MANDOCERR_ARG_EMPTY,
641 mdoc->parse, argv->line,
642 argv->pos, "Bl -offset");
645 if (NULL != n->norm->Bl.offs)
646 mandoc_vmsg(MANDOCERR_ARG_REP,
647 mdoc->parse, argv->line,
648 argv->pos, "Bl -offset %s",
650 rewrite_macro2len(mdoc, argv->value);
651 n->norm->Bl.offs = argv->value[0];
656 if (LIST__NONE == lt)
660 /* Check: multiple list types. */
662 if (LIST__NONE != n->norm->Bl.type) {
663 mandoc_vmsg(MANDOCERR_BL_REP,
664 mdoc->parse, n->line, n->pos,
665 "Bl -%s", mdoc_argnames[argv->arg]);
669 /* The list type should come first. */
671 if (n->norm->Bl.width ||
674 mandoc_vmsg(MANDOCERR_BL_LATETYPE,
675 mdoc->parse, n->line, n->pos, "Bl -%s",
676 mdoc_argnames[n->args->argv[0].arg]);
678 n->norm->Bl.type = lt;
679 if (LIST_column == lt) {
680 n->norm->Bl.ncols = argv->sz;
681 n->norm->Bl.cols = (void *)argv->value;
685 /* Allow lists to default to LIST_item. */
687 if (LIST__NONE == n->norm->Bl.type) {
688 mandoc_msg(MANDOCERR_BL_NOTYPE, mdoc->parse,
689 n->line, n->pos, "Bl");
690 n->norm->Bl.type = LIST_item;
695 * Validate the width field. Some list types don't need width
696 * types and should be warned about them. Others should have it
697 * and must also be warned. Yet others have a default and need
701 switch (n->norm->Bl.type) {
703 if (n->norm->Bl.width == NULL)
704 mandoc_msg(MANDOCERR_BL_NOWIDTH, mdoc->parse,
705 n->line, n->pos, "Bl -tag");
712 if (n->norm->Bl.width != NULL)
713 mandoc_vmsg(MANDOCERR_BL_SKIPW, mdoc->parse,
714 wa->line, wa->pos, "Bl -%s",
715 mdoc_argnames[mdoclt]);
716 n->norm->Bl.width = NULL;
721 if (n->norm->Bl.width == NULL)
722 n->norm->Bl.width = "2n";
725 if (n->norm->Bl.width == NULL)
726 n->norm->Bl.width = "3n";
737 struct mdoc_argv *argv;
742 for (i = 0; n->args && i < (int)n->args->argc; i++) {
743 argv = n->args->argv + i;
763 mandoc_msg(MANDOCERR_BD_FILE, mdoc->parse,
764 n->line, n->pos, NULL);
768 mandoc_msg(MANDOCERR_ARG_EMPTY,
769 mdoc->parse, argv->line,
770 argv->pos, "Bd -offset");
773 if (NULL != n->norm->Bd.offs)
774 mandoc_vmsg(MANDOCERR_ARG_REP,
775 mdoc->parse, argv->line,
776 argv->pos, "Bd -offset %s",
778 rewrite_macro2len(mdoc, argv->value);
779 n->norm->Bd.offs = argv->value[0];
782 if (n->norm->Bd.comp)
783 mandoc_msg(MANDOCERR_ARG_REP,
784 mdoc->parse, argv->line,
785 argv->pos, "Bd -compact");
786 n->norm->Bd.comp = 1;
791 if (DISP__NONE == dt)
794 if (DISP__NONE == n->norm->Bd.type)
795 n->norm->Bd.type = dt;
797 mandoc_vmsg(MANDOCERR_BD_REP,
798 mdoc->parse, n->line, n->pos,
799 "Bd -%s", mdoc_argnames[argv->arg]);
802 if (DISP__NONE == n->norm->Bd.type) {
803 mandoc_msg(MANDOCERR_BD_NOTYPE, mdoc->parse,
804 n->line, n->pos, "Bd");
805 n->norm->Bd.type = DISP_ragged;
810 * Stand-alone line macros.
814 post_an_norm(POST_ARGS)
817 struct mdoc_argv *argv;
824 for (i = 1; i < n->args->argc; i++) {
825 argv = n->args->argv + i;
826 mandoc_vmsg(MANDOCERR_AN_REP,
827 mdoc->parse, argv->line, argv->pos,
828 "An -%s", mdoc_argnames[argv->arg]);
831 argv = n->args->argv;
832 if (argv->arg == MDOC_Split)
833 n->norm->An.auth = AUTH_split;
834 else if (argv->arg == MDOC_Nosplit)
835 n->norm->An.auth = AUTH_nosplit;
847 if (n->child != NULL)
848 mandoc_vmsg(MANDOCERR_ARG_SKIP, mdoc->parse, n->line,
849 n->pos, "%s %s", roff_name[n->tok], n->child->string);
851 while (n->child != NULL)
852 roff_node_delete(mdoc, n->child);
854 roff_word_alloc(mdoc, n->line, n->pos, n->tok == MDOC_Bt ?
855 "is currently in beta test." : "currently under development.");
856 mdoc->last->flags |= NODE_EOS | NODE_NOSRC;
861 build_list(struct roff_man *mdoc, int tok)
866 n = mdoc->last->next;
867 for (ic = 1;; ic++) {
868 roff_elem_alloc(mdoc, n->line, n->pos, tok);
869 mdoc->last->flags |= NODE_NOSRC;
870 mdoc_node_relink(mdoc, n);
871 n = mdoc->last = mdoc->last->parent;
872 mdoc->next = ROFF_NEXT_SIBLING;
875 if (ic > 1 || n->next->next != NULL) {
876 roff_word_alloc(mdoc, n->line, n->pos, ",");
877 mdoc->last->flags |= NODE_DELIMC | NODE_NOSRC;
879 n = mdoc->last->next;
880 if (n->next == NULL) {
881 roff_word_alloc(mdoc, n->line, n->pos, "and");
882 mdoc->last->flags |= NODE_NOSRC;
896 mdoc->next = ROFF_NEXT_CHILD;
897 roff_word_alloc(mdoc, n->line, n->pos, "The");
898 mdoc->last->flags |= NODE_NOSRC;
900 if (mdoc->last->next != NULL)
901 ic = build_list(mdoc, MDOC_Nm);
902 else if (mdoc->meta.name != NULL) {
903 roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Nm);
904 mdoc->last->flags |= NODE_NOSRC;
905 roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name);
906 mdoc->last->flags |= NODE_NOSRC;
907 mdoc->last = mdoc->last->parent;
908 mdoc->next = ROFF_NEXT_SIBLING;
911 mandoc_msg(MANDOCERR_EX_NONAME, mdoc->parse,
912 n->line, n->pos, "Ex");
916 roff_word_alloc(mdoc, n->line, n->pos,
917 ic > 1 ? "utilities exit\\~0" : "utility exits\\~0");
918 mdoc->last->flags |= NODE_NOSRC;
919 roff_word_alloc(mdoc, n->line, n->pos,
920 "on success, and\\~>0 if an error occurs.");
921 mdoc->last->flags |= NODE_EOS | NODE_NOSRC;
934 assert(n->child->type == ROFFT_TEXT);
935 mdoc->next = ROFF_NEXT_CHILD;
937 if ((p = mdoc_a2lib(n->child->string)) != NULL) {
938 n->child->flags |= NODE_NOPRT;
939 roff_word_alloc(mdoc, n->line, n->pos, p);
940 mdoc->last->flags = NODE_NOSRC;
945 mandoc_vmsg(MANDOCERR_LB_BAD, mdoc->parse, n->child->line,
946 n->child->pos, "Lb %s", n->child->string);
948 roff_word_alloc(mdoc, n->line, n->pos, "library");
949 mdoc->last->flags = NODE_NOSRC;
950 roff_word_alloc(mdoc, n->line, n->pos, "\\(Lq");
951 mdoc->last->flags = NODE_DELIMO | NODE_NOSRC;
952 mdoc->last = mdoc->last->next;
953 roff_word_alloc(mdoc, n->line, n->pos, "\\(Rq");
954 mdoc->last->flags = NODE_DELIMC | NODE_NOSRC;
967 mdoc->next = ROFF_NEXT_CHILD;
968 if (n->child != NULL) {
969 roff_word_alloc(mdoc, n->line, n->pos, "The");
970 mdoc->last->flags |= NODE_NOSRC;
971 ic = build_list(mdoc, MDOC_Fn);
972 roff_word_alloc(mdoc, n->line, n->pos,
973 ic > 1 ? "functions return" : "function returns");
974 mdoc->last->flags |= NODE_NOSRC;
975 roff_word_alloc(mdoc, n->line, n->pos,
976 "the value\\~0 if successful;");
978 roff_word_alloc(mdoc, n->line, n->pos, "Upon successful "
979 "completion, the value\\~0 is returned;");
980 mdoc->last->flags |= NODE_NOSRC;
982 roff_word_alloc(mdoc, n->line, n->pos, "otherwise "
983 "the value\\~\\-1 is returned and the global variable");
984 mdoc->last->flags |= NODE_NOSRC;
985 roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Va);
986 mdoc->last->flags |= NODE_NOSRC;
987 roff_word_alloc(mdoc, n->line, n->pos, "errno");
988 mdoc->last->flags |= NODE_NOSRC;
989 mdoc->last = mdoc->last->parent;
990 mdoc->next = ROFF_NEXT_SIBLING;
991 roff_word_alloc(mdoc, n->line, n->pos,
992 "is set to indicate the error.");
993 mdoc->last->flags |= NODE_EOS | NODE_NOSRC;
1000 struct roff_node *n;
1005 if (n->args && n->args->argc == 1)
1006 if (n->args->argv[0].arg == MDOC_Std)
1009 mandoc_msg(MANDOCERR_ARG_STD, mdoc->parse,
1010 n->line, n->pos, roff_name[n->tok]);
1016 struct roff_node *n, *nch;
1021 assert(nch->type == ROFFT_TEXT);
1023 if ((p = mdoc_a2st(nch->string)) == NULL) {
1024 mandoc_vmsg(MANDOCERR_ST_BAD, mdoc->parse,
1025 nch->line, nch->pos, "St %s", nch->string);
1026 roff_node_delete(mdoc, n);
1030 nch->flags |= NODE_NOPRT;
1031 mdoc->next = ROFF_NEXT_CHILD;
1032 roff_word_alloc(mdoc, nch->line, nch->pos, p);
1033 mdoc->last->flags |= NODE_NOSRC;
1038 post_obsolete(POST_ARGS)
1040 struct roff_node *n;
1043 if (n->type == ROFFT_ELEM || n->type == ROFFT_BLOCK)
1044 mandoc_msg(MANDOCERR_MACRO_OBS, mdoc->parse,
1045 n->line, n->pos, roff_name[n->tok]);
1049 post_useless(POST_ARGS)
1051 struct roff_node *n;
1054 mandoc_msg(MANDOCERR_MACRO_USELESS, mdoc->parse,
1055 n->line, n->pos, roff_name[n->tok]);
1065 struct roff_node *np, *nch;
1068 * Unlike other data pointers, these are "housed" by the HEAD
1069 * element, which contains the goods.
1073 if (np->type != ROFFT_HEAD)
1076 assert(np->parent->type == ROFFT_BLOCK);
1077 assert(np->parent->tok == MDOC_Bf);
1079 /* Check the number of arguments. */
1082 if (np->parent->args == NULL) {
1084 mandoc_msg(MANDOCERR_BF_NOFONT, mdoc->parse,
1085 np->line, np->pos, "Bf");
1091 mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
1092 nch->line, nch->pos, "Bf ... %s", nch->string);
1094 /* Extract argument into data. */
1096 if (np->parent->args != NULL) {
1097 switch (np->parent->args->argv[0].arg) {
1099 np->norm->Bf.font = FONT_Em;
1102 np->norm->Bf.font = FONT_Li;
1105 np->norm->Bf.font = FONT_Sy;
1113 /* Extract parameter into data. */
1115 if ( ! strcmp(np->child->string, "Em"))
1116 np->norm->Bf.font = FONT_Em;
1117 else if ( ! strcmp(np->child->string, "Li"))
1118 np->norm->Bf.font = FONT_Li;
1119 else if ( ! strcmp(np->child->string, "Sy"))
1120 np->norm->Bf.font = FONT_Sy;
1122 mandoc_vmsg(MANDOCERR_BF_BADFONT, mdoc->parse,
1123 np->child->line, np->child->pos,
1124 "Bf %s", np->child->string);
1128 post_fname(POST_ARGS)
1130 const struct roff_node *n;
1134 n = mdoc->last->child;
1135 pos = strcspn(n->string, "()");
1136 cp = n->string + pos;
1137 if ( ! (cp[0] == '\0' || (cp[0] == '(' && cp[1] == '*')))
1138 mandoc_msg(MANDOCERR_FN_PAREN, mdoc->parse,
1139 n->line, n->pos + pos, n->string);
1140 if (n->sec == SEC_SYNOPSIS && mdoc->meta.msec != NULL)
1141 mandoc_xr_add(mdoc->meta.msec, n->string, -1, -1);
1155 const struct roff_node *n;
1159 if (n->type != ROFFT_HEAD)
1162 if (n->child == NULL) {
1163 mandoc_msg(MANDOCERR_FO_NOHEAD, mdoc->parse,
1164 n->line, n->pos, "Fo");
1167 if (n->child != n->last) {
1168 mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
1169 n->child->next->line, n->child->next->pos,
1170 "Fo ... %s", n->child->next->string);
1171 while (n->child != n->last)
1172 roff_node_delete(mdoc, n->last);
1182 const struct roff_node *n;
1185 for (n = mdoc->last->child; n != NULL; n = n->next) {
1186 for (cp = n->string; *cp != '\0'; cp++) {
1187 /* Ignore callbacks and alterations. */
1188 if (*cp == '(' || *cp == '{')
1192 mandoc_msg(MANDOCERR_FA_COMMA, mdoc->parse,
1193 n->line, n->pos + (cp - n->string),
1198 post_delim_nb(mdoc);
1204 struct roff_node *n;
1208 if ((n->sec == SEC_NAME || n->sec == SEC_SYNOPSIS) &&
1209 n->child != NULL && n->child->type == ROFFT_TEXT &&
1210 mdoc->meta.msec != NULL)
1211 mandoc_xr_add(mdoc->meta.msec, n->child->string, -1, -1);
1213 if (n->last != NULL &&
1214 (n->last->tok == MDOC_Pp ||
1215 n->last->tok == MDOC_Lp))
1216 mdoc_node_relink(mdoc, n->last);
1218 if (mdoc->meta.name == NULL)
1219 deroff(&mdoc->meta.name, n);
1221 if (mdoc->meta.name == NULL ||
1222 (mdoc->lastsec == SEC_NAME && n->child == NULL))
1223 mandoc_msg(MANDOCERR_NM_NONAME, mdoc->parse,
1224 n->line, n->pos, "Nm");
1228 post_delim_nb(mdoc);
1237 if ((n->child != NULL && n->child->type == ROFFT_TEXT) ||
1238 mdoc->meta.name == NULL)
1241 mdoc->next = ROFF_NEXT_CHILD;
1242 roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name);
1243 mdoc->last->flags |= NODE_NOSRC;
1250 struct roff_node *n;
1254 if (n->type != ROFFT_BODY)
1257 if (n->sec != SEC_NAME)
1258 mandoc_msg(MANDOCERR_ND_LATE, mdoc->parse,
1259 n->line, n->pos, "Nd");
1261 if (n->child == NULL)
1262 mandoc_msg(MANDOCERR_ND_EMPTY, mdoc->parse,
1263 n->line, n->pos, "Nd");
1271 post_display(POST_ARGS)
1273 struct roff_node *n, *np;
1278 if (n->end != ENDBODY_NOT) {
1279 if (n->tok == MDOC_Bd &&
1280 n->body->parent->args == NULL)
1281 roff_node_delete(mdoc, n);
1282 } else if (n->child == NULL)
1283 mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse,
1284 n->line, n->pos, roff_name[n->tok]);
1285 else if (n->tok == MDOC_D1)
1289 if (n->tok == MDOC_Bd) {
1290 if (n->args == NULL) {
1291 mandoc_msg(MANDOCERR_BD_NOARG,
1292 mdoc->parse, n->line, n->pos, "Bd");
1293 mdoc->next = ROFF_NEXT_SIBLING;
1294 while (n->body->child != NULL)
1295 mdoc_node_relink(mdoc,
1297 roff_node_delete(mdoc, n);
1303 for (np = n->parent; np != NULL; np = np->parent) {
1304 if (np->type == ROFFT_BLOCK && np->tok == MDOC_Bd) {
1305 mandoc_vmsg(MANDOCERR_BD_NEST,
1306 mdoc->parse, n->line, n->pos,
1307 "%s in Bd", roff_name[n->tok]);
1318 post_defaults(POST_ARGS)
1320 struct roff_node *nn;
1322 if (mdoc->last->child != NULL) {
1323 post_delim_nb(mdoc);
1328 * The `Ar' defaults to "file ..." if no value is provided as an
1329 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
1330 * gets an empty string.
1336 mdoc->next = ROFF_NEXT_CHILD;
1337 roff_word_alloc(mdoc, nn->line, nn->pos, "file");
1338 mdoc->last->flags |= NODE_NOSRC;
1339 roff_word_alloc(mdoc, nn->line, nn->pos, "...");
1340 mdoc->last->flags |= NODE_NOSRC;
1344 mdoc->next = ROFF_NEXT_CHILD;
1345 roff_word_alloc(mdoc, nn->line, nn->pos, "~");
1346 mdoc->last->flags |= NODE_NOSRC;
1357 struct roff_node *n, *nch;
1364 * If we have a child, look it up in the standard keys. If a
1365 * key exist, use that instead of the child; if it doesn't,
1366 * prefix "AT&T UNIX " to the existing data.
1370 if (nch != NULL && ((att = mdoc_a2att(nch->string)) == NULL))
1371 mandoc_vmsg(MANDOCERR_AT_BAD, mdoc->parse,
1372 nch->line, nch->pos, "At %s", nch->string);
1374 mdoc->next = ROFF_NEXT_CHILD;
1376 roff_word_alloc(mdoc, nch->line, nch->pos, att);
1377 nch->flags |= NODE_NOPRT;
1379 roff_word_alloc(mdoc, n->line, n->pos, "AT&T UNIX");
1380 mdoc->last->flags |= NODE_NOSRC;
1387 struct roff_node *np, *nch;
1393 if (np->norm->An.auth == AUTH__NONE) {
1395 mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse,
1396 np->line, np->pos, "An");
1398 post_delim_nb(mdoc);
1399 } else if (nch != NULL)
1400 mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
1401 nch->line, nch->pos, "An ... %s", nch->string);
1408 post_obsolete(mdoc);
1409 if (mdoc->last->type == ROFFT_BLOCK)
1410 mdoc->last->norm->Es = mdoc->last_es;
1417 post_obsolete(mdoc);
1418 mdoc->last_es = mdoc->last;
1424 struct roff_node *n;
1428 post_delim_nb(mdoc);
1443 if (n->child == NULL)
1445 v = n->child->string;
1446 if ((v[0] != '0' && v[0] != '1') || v[1] != '.' ||
1447 v[2] < '0' || v[2] > '9' ||
1448 v[3] < 'a' || v[3] > 'z' || v[4] != '\0')
1450 n->child->flags |= NODE_NOPRT;
1451 mdoc->next = ROFF_NEXT_CHILD;
1452 roff_word_alloc(mdoc, n->child->line, n->child->pos, v);
1453 v = mdoc->last->string;
1454 v[3] = toupper((unsigned char)v[3]);
1455 mdoc->last->flags |= NODE_NOSRC;
1467 mdoc->next = ROFF_NEXT_CHILD;
1468 roff_word_alloc(mdoc, n->line, n->pos, os);
1469 mdoc->last->flags |= NODE_NOSRC;
1476 struct roff_node *nbl, *nit, *nch;
1483 if (nit->type != ROFFT_BLOCK)
1486 nbl = nit->parent->parent;
1487 lt = nbl->norm->Bl.type;
1495 if (nit->head->child == NULL)
1496 mandoc_vmsg(MANDOCERR_IT_NOHEAD,
1497 mdoc->parse, nit->line, nit->pos,
1499 mdoc_argnames[nbl->args->argv[0].arg]);
1505 if (nit->body == NULL || nit->body->child == NULL)
1506 mandoc_vmsg(MANDOCERR_IT_NOBODY,
1507 mdoc->parse, nit->line, nit->pos,
1509 mdoc_argnames[nbl->args->argv[0].arg]);
1512 if ((nch = nit->head->child) != NULL)
1513 mandoc_vmsg(MANDOCERR_ARG_SKIP, mdoc->parse,
1514 nit->line, nit->pos, "It %s",
1515 nch->string == NULL ? roff_name[nch->tok] :
1519 cols = (int)nbl->norm->Bl.ncols;
1521 assert(nit->head->child == NULL);
1523 if (nit->head->next->child == NULL &&
1524 nit->head->next->next == NULL) {
1525 mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse,
1526 nit->line, nit->pos, "It");
1527 roff_node_delete(mdoc, nit);
1532 for (nch = nit->child; nch != NULL; nch = nch->next) {
1533 if (nch->type != ROFFT_BODY)
1535 if (i++ && nch->flags & NODE_LINE)
1536 mandoc_msg(MANDOCERR_TA_LINE, mdoc->parse,
1537 nch->line, nch->pos, "Ta");
1539 if (i < cols || i > cols + 1)
1540 mandoc_vmsg(MANDOCERR_BL_COL,
1541 mdoc->parse, nit->line, nit->pos,
1542 "%d columns, %d cells", cols, i);
1543 else if (nit->head->next->child != NULL &&
1544 nit->head->next->child->line > nit->line)
1545 mandoc_msg(MANDOCERR_IT_NOARG, mdoc->parse,
1546 nit->line, nit->pos, "Bl -column It");
1554 post_bl_block(POST_ARGS)
1556 struct roff_node *n, *ni, *nc;
1561 for (ni = n->body->child; ni != NULL; ni = ni->next) {
1562 if (ni->body == NULL)
1564 nc = ni->body->last;
1565 while (nc != NULL) {
1575 if (ni->next == NULL) {
1576 mandoc_msg(MANDOCERR_PAR_MOVE,
1577 mdoc->parse, nc->line, nc->pos,
1578 roff_name[nc->tok]);
1579 mdoc_node_relink(mdoc, nc);
1580 } else if (n->norm->Bl.comp == 0 &&
1581 n->norm->Bl.type != LIST_column) {
1582 mandoc_vmsg(MANDOCERR_PAR_SKIP,
1583 mdoc->parse, nc->line, nc->pos,
1584 "%s before It", roff_name[nc->tok]);
1585 roff_node_delete(mdoc, nc);
1588 nc = ni->body->last;
1594 * If the argument of -offset or -width is a macro,
1595 * replace it with the associated default width.
1598 rewrite_macro2len(struct roff_man *mdoc, char **arg)
1605 else if ( ! strcmp(*arg, "Ds"))
1607 else if ((tok = roffhash_find(mdoc->mdocmac, *arg, 0)) == TOKEN_NONE)
1610 width = macro2len(tok);
1613 mandoc_asprintf(arg, "%zun", width);
1617 post_bl_head(POST_ARGS)
1619 struct roff_node *nbl, *nh, *nch, *nnext;
1620 struct mdoc_argv *argv;
1626 if (nh->norm->Bl.type != LIST_column) {
1627 if ((nch = nh->child) == NULL)
1629 mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
1630 nch->line, nch->pos, "Bl ... %s", nch->string);
1631 while (nch != NULL) {
1632 roff_node_delete(mdoc, nch);
1639 * Append old-style lists, where the column width specifiers
1640 * trail as macro parameters, to the new-style ("normal-form")
1641 * lists where they're argument values following -column.
1644 if (nh->child == NULL)
1648 for (j = 0; j < (int)nbl->args->argc; j++)
1649 if (nbl->args->argv[j].arg == MDOC_Column)
1652 assert(j < (int)nbl->args->argc);
1655 * Accommodate for new-style groff column syntax. Shuffle the
1656 * child nodes, all of which must be TEXT, as arguments for the
1657 * column field. Then, delete the head children.
1660 argv = nbl->args->argv + j;
1662 for (nch = nh->child; nch != NULL; nch = nch->next)
1664 argv->value = mandoc_reallocarray(argv->value,
1665 argv->sz, sizeof(char *));
1667 nh->norm->Bl.ncols = argv->sz;
1668 nh->norm->Bl.cols = (void *)argv->value;
1670 for (nch = nh->child; nch != NULL; nch = nnext) {
1671 argv->value[i++] = nch->string;
1674 roff_node_delete(NULL, nch);
1682 struct roff_node *nparent, *nprev; /* of the Bl block */
1683 struct roff_node *nblock, *nbody; /* of the Bl */
1684 struct roff_node *nchild, *nnext; /* of the Bl body */
1685 const char *prev_Er;
1689 switch (nbody->type) {
1691 post_bl_block(mdoc);
1701 if (nbody->end != ENDBODY_NOT)
1704 nchild = nbody->child;
1705 if (nchild == NULL) {
1706 mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse,
1707 nbody->line, nbody->pos, "Bl");
1710 while (nchild != NULL) {
1711 nnext = nchild->next;
1712 if (nchild->tok == MDOC_It ||
1713 (nchild->tok == MDOC_Sm &&
1714 nnext != NULL && nnext->tok == MDOC_It)) {
1720 * In .Bl -column, the first rows may be implicit,
1721 * that is, they may not start with .It macros.
1722 * Such rows may be followed by nodes generated on the
1723 * roff level, for example .TS, which cannot be moved
1724 * out of the list. In that case, wrap such roff nodes
1725 * into an implicit row.
1728 if (nchild->prev != NULL) {
1729 mdoc->last = nchild;
1730 mdoc->next = ROFF_NEXT_SIBLING;
1731 roff_block_alloc(mdoc, nchild->line,
1732 nchild->pos, MDOC_It);
1733 roff_head_alloc(mdoc, nchild->line,
1734 nchild->pos, MDOC_It);
1735 mdoc->next = ROFF_NEXT_SIBLING;
1736 roff_body_alloc(mdoc, nchild->line,
1737 nchild->pos, MDOC_It);
1738 while (nchild->tok != MDOC_It) {
1739 mdoc_node_relink(mdoc, nchild);
1740 if ((nchild = nnext) == NULL)
1742 nnext = nchild->next;
1743 mdoc->next = ROFF_NEXT_SIBLING;
1749 mandoc_msg(MANDOCERR_BL_MOVE, mdoc->parse,
1750 nchild->line, nchild->pos, roff_name[nchild->tok]);
1753 * Move the node out of the Bl block.
1754 * First, collect all required node pointers.
1757 nblock = nbody->parent;
1758 nprev = nblock->prev;
1759 nparent = nblock->parent;
1762 * Unlink this child.
1765 nbody->child = nnext;
1772 * Relink this child.
1775 nchild->parent = nparent;
1776 nchild->prev = nprev;
1777 nchild->next = nblock;
1779 nblock->prev = nchild;
1781 nparent->child = nchild;
1783 nprev->next = nchild;
1788 if (mdoc->meta.os_e != MANDOC_OS_NETBSD)
1792 for (nchild = nbody->child; nchild != NULL; nchild = nchild->next) {
1793 if (nchild->tok != MDOC_It)
1795 if ((nnext = nchild->head->child) == NULL)
1797 if (nnext->type == ROFFT_BLOCK)
1798 nnext = nnext->body->child;
1799 if (nnext == NULL || nnext->tok != MDOC_Er)
1801 nnext = nnext->child;
1802 if (prev_Er != NULL) {
1803 order = strcmp(prev_Er, nnext->string);
1805 mandoc_vmsg(MANDOCERR_ER_ORDER,
1806 mdoc->parse, nnext->line, nnext->pos,
1807 "Er %s %s (NetBSD)",
1808 prev_Er, nnext->string);
1809 else if (order == 0)
1810 mandoc_vmsg(MANDOCERR_ER_REP,
1811 mdoc->parse, nnext->line, nnext->pos,
1812 "Er %s (NetBSD)", prev_Er);
1814 prev_Er = nnext->string;
1821 struct roff_node *n;
1825 if (n->type == ROFFT_BLOCK && n->body->child == NULL) {
1826 mandoc_msg(MANDOCERR_BLK_EMPTY,
1827 mdoc->parse, n->line, n->pos, "Bk");
1828 roff_node_delete(mdoc, n);
1835 struct roff_node *nch;
1837 nch = mdoc->last->child;
1840 mdoc->flags ^= MDOC_SMOFF;
1844 assert(nch->type == ROFFT_TEXT);
1846 if ( ! strcmp(nch->string, "on")) {
1847 mdoc->flags &= ~MDOC_SMOFF;
1850 if ( ! strcmp(nch->string, "off")) {
1851 mdoc->flags |= MDOC_SMOFF;
1855 mandoc_vmsg(MANDOCERR_SM_BAD,
1856 mdoc->parse, nch->line, nch->pos,
1857 "%s %s", roff_name[mdoc->last->tok], nch->string);
1858 mdoc_node_relink(mdoc, nch);
1863 post_root(POST_ARGS)
1865 const char *openbsd_arch[] = {
1866 "alpha", "amd64", "arm64", "armv7", "hppa", "i386",
1867 "landisk", "loongson", "luna88k", "macppc", "mips64",
1868 "octeon", "sgi", "socppc", "sparc64", NULL
1870 const char *netbsd_arch[] = {
1871 "acorn26", "acorn32", "algor", "alpha", "amiga",
1873 "bebox", "cats", "cesfic", "cobalt", "dreamcast",
1874 "emips", "evbarm", "evbmips", "evbppc", "evbsh3", "evbsh5",
1875 "hp300", "hpcarm", "hpcmips", "hpcsh", "hppa",
1876 "i386", "ibmnws", "luna68k",
1877 "mac68k", "macppc", "mipsco", "mmeye", "mvme68k", "mvmeppc",
1878 "netwinder", "news68k", "newsmips", "next68k",
1879 "pc532", "playstation2", "pmax", "pmppc", "prep",
1880 "sandpoint", "sbmips", "sgimips", "shark",
1881 "sparc", "sparc64", "sun2", "sun3",
1882 "vax", "walnut", "x68k", "x86", "x86_64", "xen", NULL
1884 const char **arches[] = { NULL, netbsd_arch, openbsd_arch };
1886 struct roff_node *n;
1889 /* Add missing prologue data. */
1891 if (mdoc->meta.date == NULL)
1892 mdoc->meta.date = mdoc->quick ? mandoc_strdup("") :
1893 mandoc_normdate(mdoc, NULL, 0, 0);
1895 if (mdoc->meta.title == NULL) {
1896 mandoc_msg(MANDOCERR_DT_NOTITLE,
1897 mdoc->parse, 0, 0, "EOF");
1898 mdoc->meta.title = mandoc_strdup("UNTITLED");
1901 if (mdoc->meta.vol == NULL)
1902 mdoc->meta.vol = mandoc_strdup("LOCAL");
1904 if (mdoc->meta.os == NULL) {
1905 mandoc_msg(MANDOCERR_OS_MISSING,
1906 mdoc->parse, 0, 0, NULL);
1907 mdoc->meta.os = mandoc_strdup("");
1908 } else if (mdoc->meta.os_e &&
1909 (mdoc->meta.rcsids & (1 << mdoc->meta.os_e)) == 0)
1910 mandoc_msg(MANDOCERR_RCS_MISSING, mdoc->parse, 0, 0,
1911 mdoc->meta.os_e == MANDOC_OS_OPENBSD ?
1912 "(OpenBSD)" : "(NetBSD)");
1914 if (mdoc->meta.arch != NULL &&
1915 (arch = arches[mdoc->meta.os_e]) != NULL) {
1916 while (*arch != NULL && strcmp(*arch, mdoc->meta.arch))
1918 if (*arch == NULL) {
1919 n = mdoc->first->child;
1920 while (n->tok != MDOC_Dt)
1922 n = n->child->next->next;
1923 mandoc_vmsg(MANDOCERR_ARCH_BAD,
1924 mdoc->parse, n->line, n->pos,
1925 "Dt ... %s %s", mdoc->meta.arch,
1926 mdoc->meta.os_e == MANDOC_OS_OPENBSD ?
1927 "(OpenBSD)" : "(NetBSD)");
1931 /* Check that we begin with a proper `Sh'. */
1933 n = mdoc->first->child;
1934 while (n != NULL && n->tok != TOKEN_NONE &&
1935 mdoc_macros[n->tok].flags & MDOC_PROLOGUE)
1939 mandoc_msg(MANDOCERR_DOC_EMPTY, mdoc->parse, 0, 0, NULL);
1940 else if (n->tok != MDOC_Sh)
1941 mandoc_msg(MANDOCERR_SEC_BEFORE, mdoc->parse,
1942 n->line, n->pos, roff_name[n->tok]);
1948 struct roff_node *np, *nch, *next, *prev;
1953 if (np->type != ROFFT_BODY)
1956 if (np->child == NULL) {
1957 mandoc_msg(MANDOCERR_RS_EMPTY, mdoc->parse,
1958 np->line, np->pos, "Rs");
1963 * The full `Rs' block needs special handling to order the
1964 * sub-elements according to `rsord'. Pick through each element
1965 * and correctly order it. This is an insertion sort.
1969 for (nch = np->child->next; nch != NULL; nch = next) {
1970 /* Determine order number of this child. */
1971 for (i = 0; i < RSORD_MAX; i++)
1972 if (rsord[i] == nch->tok)
1975 if (i == RSORD_MAX) {
1976 mandoc_msg(MANDOCERR_RS_BAD, mdoc->parse,
1977 nch->line, nch->pos, roff_name[nch->tok]);
1979 } else if (nch->tok == MDOC__J || nch->tok == MDOC__B)
1980 np->norm->Rs.quote_T++;
1983 * Remove this child from the chain. This somewhat
1984 * repeats roff_node_unlink(), but since we're
1985 * just re-ordering, there's no need for the
1986 * full unlink process.
1989 if ((next = nch->next) != NULL)
1990 next->prev = nch->prev;
1992 if ((prev = nch->prev) != NULL)
1993 prev->next = nch->next;
1995 nch->prev = nch->next = NULL;
1998 * Scan back until we reach a node that's
1999 * to be ordered before this child.
2002 for ( ; prev ; prev = prev->prev) {
2003 /* Determine order of `prev'. */
2004 for (j = 0; j < RSORD_MAX; j++)
2005 if (rsord[j] == prev->tok)
2015 * Set this child back into its correct place
2016 * in front of the `prev' node.
2022 np->child->prev = nch;
2023 nch->next = np->child;
2027 prev->next->prev = nch;
2028 nch->next = prev->next;
2035 * For some arguments of some macros,
2036 * convert all breakable hyphens into ASCII_HYPH.
2039 post_hyph(POST_ARGS)
2041 struct roff_node *nch;
2044 for (nch = mdoc->last->child; nch != NULL; nch = nch->next) {
2045 if (nch->type != ROFFT_TEXT)
2050 while (*(++cp) != '\0')
2052 isalpha((unsigned char)cp[-1]) &&
2053 isalpha((unsigned char)cp[1]))
2061 struct roff_node *n;
2064 if (n->flags & NODE_LINE ||
2065 (n->next != NULL && n->next->flags & NODE_DELIMC))
2066 mandoc_msg(MANDOCERR_NS_SKIP, mdoc->parse,
2067 n->line, n->pos, NULL);
2083 switch (mdoc->last->type) {
2088 switch (mdoc->lastsec) {
2093 post_sh_see_also(mdoc);
2096 post_sh_authors(mdoc);
2108 post_sh_name(POST_ARGS)
2110 struct roff_node *n;
2115 for (n = mdoc->last->child; n != NULL; n = n->next) {
2118 if (hasnm && n->child != NULL)
2119 mandoc_vmsg(MANDOCERR_NAMESEC_PUNCT,
2120 mdoc->parse, n->line, n->pos,
2121 "Nm %s", n->child->string);
2126 if (n->next != NULL)
2127 mandoc_msg(MANDOCERR_NAMESEC_ND,
2128 mdoc->parse, n->line, n->pos, NULL);
2131 if (n->type == ROFFT_TEXT &&
2132 n->string[0] == ',' && n->string[1] == '\0' &&
2133 n->next != NULL && n->next->tok == MDOC_Nm) {
2139 mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
2140 n->line, n->pos, roff_name[n->tok]);
2147 mandoc_msg(MANDOCERR_NAMESEC_NONM, mdoc->parse,
2148 mdoc->last->line, mdoc->last->pos, NULL);
2150 mandoc_msg(MANDOCERR_NAMESEC_NOND, mdoc->parse,
2151 mdoc->last->line, mdoc->last->pos, NULL);
2155 post_sh_see_also(POST_ARGS)
2157 const struct roff_node *n;
2158 const char *name, *sec;
2159 const char *lastname, *lastsec, *lastpunct;
2162 n = mdoc->last->child;
2163 lastname = lastsec = lastpunct = NULL;
2165 if (n->tok != MDOC_Xr ||
2167 n->child->next == NULL)
2170 /* Process one .Xr node. */
2172 name = n->child->string;
2173 sec = n->child->next->string;
2174 if (lastsec != NULL) {
2175 if (lastpunct[0] != ',' || lastpunct[1] != '\0')
2176 mandoc_vmsg(MANDOCERR_XR_PUNCT,
2177 mdoc->parse, n->line, n->pos,
2178 "%s before %s(%s)", lastpunct,
2180 cmp = strcmp(lastsec, sec);
2182 mandoc_vmsg(MANDOCERR_XR_ORDER,
2183 mdoc->parse, n->line, n->pos,
2184 "%s(%s) after %s(%s)", name,
2185 sec, lastname, lastsec);
2186 else if (cmp == 0 &&
2187 strcasecmp(lastname, name) > 0)
2188 mandoc_vmsg(MANDOCERR_XR_ORDER,
2189 mdoc->parse, n->line, n->pos,
2190 "%s after %s", name, lastname);
2195 /* Process the following node. */
2200 if (n->tok == MDOC_Xr) {
2204 if (n->type != ROFFT_TEXT)
2206 for (name = n->string; *name != '\0'; name++)
2207 if (isalpha((const unsigned char)*name))
2209 lastpunct = n->string;
2210 if (n->next == NULL || n->next->tok == MDOC_Rs)
2211 mandoc_vmsg(MANDOCERR_XR_PUNCT, mdoc->parse,
2212 n->line, n->pos, "%s after %s(%s)",
2213 lastpunct, lastname, lastsec);
2219 child_an(const struct roff_node *n)
2222 for (n = n->child; n != NULL; n = n->next)
2223 if ((n->tok == MDOC_An && n->child != NULL) || child_an(n))
2229 post_sh_authors(POST_ARGS)
2232 if ( ! child_an(mdoc->last))
2233 mandoc_msg(MANDOCERR_AN_MISSING, mdoc->parse,
2234 mdoc->last->line, mdoc->last->pos, NULL);
2238 * Return an upper bound for the string distance (allowing
2239 * transpositions). Not a full Levenshtein implementation
2240 * because Levenshtein is quadratic in the string length
2241 * and this function is called for every standard name,
2242 * so the check for each custom name would be cubic.
2243 * The following crude heuristics is linear, resulting
2244 * in quadratic behaviour for checking one custom name,
2245 * which does not cause measurable slowdown.
2248 similar(const char *s1, const char *s2)
2250 const int maxdist = 3;
2253 while (s1[0] != '\0' && s2[0] != '\0') {
2254 if (s1[0] == s2[0]) {
2259 if (++dist > maxdist)
2261 if (s1[1] == s2[1]) { /* replacement */
2264 } else if (s1[0] == s2[1] && s1[1] == s2[0]) {
2265 s1 += 2; /* transposition */
2267 } else if (s1[0] == s2[1]) /* insertion */
2269 else if (s1[1] == s2[0]) /* deletion */
2274 dist += strlen(s1) + strlen(s2);
2275 return dist > maxdist ? INT_MAX : dist;
2279 post_sh_head(POST_ARGS)
2281 struct roff_node *nch;
2282 const char *goodsec;
2283 const char *const *testsec;
2288 * Process a new section. Sections are either "named" or
2289 * "custom". Custom sections are user-defined, while named ones
2290 * follow a conventional order and may only appear in certain
2294 sec = mdoc->last->sec;
2296 /* The NAME should be first. */
2298 if (sec != SEC_NAME && mdoc->lastnamed == SEC_NONE)
2299 mandoc_vmsg(MANDOCERR_NAMESEC_FIRST, mdoc->parse,
2300 mdoc->last->line, mdoc->last->pos, "Sh %s",
2301 sec != SEC_CUSTOM ? secnames[sec] :
2302 (nch = mdoc->last->child) == NULL ? "" :
2303 nch->type == ROFFT_TEXT ? nch->string :
2304 roff_name[nch->tok]);
2306 /* The SYNOPSIS gets special attention in other areas. */
2308 if (sec == SEC_SYNOPSIS) {
2309 roff_setreg(mdoc->roff, "nS", 1, '=');
2310 mdoc->flags |= MDOC_SYNOPSIS;
2312 roff_setreg(mdoc->roff, "nS", 0, '=');
2313 mdoc->flags &= ~MDOC_SYNOPSIS;
2316 /* Mark our last section. */
2318 mdoc->lastsec = sec;
2320 /* We don't care about custom sections after this. */
2322 if (sec == SEC_CUSTOM) {
2323 if ((nch = mdoc->last->child) == NULL ||
2324 nch->type != ROFFT_TEXT || nch->next != NULL)
2328 for (testsec = secnames + 1; *testsec != NULL; testsec++) {
2329 dist = similar(nch->string, *testsec);
2330 if (dist < mindist) {
2335 if (goodsec != NULL)
2336 mandoc_vmsg(MANDOCERR_SEC_TYPO, mdoc->parse,
2337 nch->line, nch->pos, "Sh %s instead of %s",
2338 nch->string, goodsec);
2343 * Check whether our non-custom section is being repeated or is
2347 if (sec == mdoc->lastnamed)
2348 mandoc_vmsg(MANDOCERR_SEC_REP, mdoc->parse,
2349 mdoc->last->line, mdoc->last->pos,
2350 "Sh %s", secnames[sec]);
2352 if (sec < mdoc->lastnamed)
2353 mandoc_vmsg(MANDOCERR_SEC_ORDER, mdoc->parse,
2354 mdoc->last->line, mdoc->last->pos,
2355 "Sh %s", secnames[sec]);
2357 /* Mark the last named section. */
2359 mdoc->lastnamed = sec;
2361 /* Check particular section/manual conventions. */
2363 if (mdoc->meta.msec == NULL)
2369 if (*mdoc->meta.msec == '4')
2371 goodsec = "2, 3, 4, 9";
2373 case SEC_RETURN_VALUES:
2375 if (*mdoc->meta.msec == '2')
2377 if (*mdoc->meta.msec == '3')
2379 if (NULL == goodsec)
2380 goodsec = "2, 3, 9";
2383 if (*mdoc->meta.msec == '9')
2385 if (NULL == goodsec)
2387 mandoc_vmsg(MANDOCERR_SEC_MSEC, mdoc->parse,
2388 mdoc->last->line, mdoc->last->pos,
2389 "Sh %s for %s only", secnames[sec], goodsec);
2399 struct roff_node *n, *nch;
2403 if (nch->next == NULL) {
2404 mandoc_vmsg(MANDOCERR_XR_NOSEC, mdoc->parse,
2405 n->line, n->pos, "Xr %s", nch->string);
2407 assert(nch->next == n->last);
2408 if(mandoc_xr_add(nch->next->string, nch->string,
2409 nch->line, nch->pos))
2410 mandoc_vmsg(MANDOCERR_XR_SELF, mdoc->parse,
2411 nch->line, nch->pos, "Xr %s %s",
2412 nch->string, nch->next->string);
2414 post_delim_nb(mdoc);
2418 post_ignpar(POST_ARGS)
2420 struct roff_node *np;
2422 switch (mdoc->last->type) {
2436 if ((np = mdoc->last->child) != NULL)
2437 if (np->tok == MDOC_Pp || np->tok == MDOC_Lp) {
2438 mandoc_vmsg(MANDOCERR_PAR_SKIP,
2439 mdoc->parse, np->line, np->pos,
2440 "%s after %s", roff_name[np->tok],
2441 roff_name[mdoc->last->tok]);
2442 roff_node_delete(mdoc, np);
2445 if ((np = mdoc->last->last) != NULL)
2446 if (np->tok == MDOC_Pp || np->tok == MDOC_Lp) {
2447 mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2448 np->line, np->pos, "%s at the end of %s",
2450 roff_name[mdoc->last->tok]);
2451 roff_node_delete(mdoc, np);
2456 post_prevpar(POST_ARGS)
2458 struct roff_node *n;
2461 if (NULL == n->prev)
2463 if (n->type != ROFFT_ELEM && n->type != ROFFT_BLOCK)
2467 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
2468 * block: `Lp', `Pp', or non-compact `Bd' or `Bl'.
2471 if (n->prev->tok != MDOC_Pp &&
2472 n->prev->tok != MDOC_Lp &&
2473 n->prev->tok != ROFF_br)
2475 if (n->tok == MDOC_Bl && n->norm->Bl.comp)
2477 if (n->tok == MDOC_Bd && n->norm->Bd.comp)
2479 if (n->tok == MDOC_It && n->parent->norm->Bl.comp)
2482 mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2483 n->prev->line, n->prev->pos, "%s before %s",
2484 roff_name[n->prev->tok], roff_name[n->tok]);
2485 roff_node_delete(mdoc, n->prev);
2491 struct roff_node *np;
2494 if (np->tok != ROFF_br && np->tok != ROFF_sp)
2497 if (np->tok == ROFF_sp) {
2498 if (np->child != NULL && np->child->next != NULL)
2499 mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
2500 np->child->next->line, np->child->next->pos,
2501 "sp ... %s", np->child->next->string);
2502 } else if (np->child != NULL)
2503 mandoc_vmsg(MANDOCERR_ARG_SKIP,
2504 mdoc->parse, np->line, np->pos, "%s %s",
2505 roff_name[np->tok], np->child->string);
2507 if ((np = mdoc->last->prev) == NULL) {
2508 np = mdoc->last->parent;
2509 if (np->tok != MDOC_Sh && np->tok != MDOC_Ss)
2511 } else if (np->tok != MDOC_Pp && np->tok != MDOC_Lp &&
2512 (mdoc->last->tok != ROFF_br ||
2513 (np->tok != ROFF_sp && np->tok != ROFF_br)))
2516 mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2517 mdoc->last->line, mdoc->last->pos, "%s after %s",
2518 roff_name[mdoc->last->tok], roff_name[np->tok]);
2519 roff_node_delete(mdoc, mdoc->last);
2525 struct roff_node *n;
2529 n->flags |= NODE_NOPRT;
2531 if (mdoc->meta.date != NULL) {
2532 mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
2533 n->line, n->pos, "Dd");
2534 free(mdoc->meta.date);
2535 } else if (mdoc->flags & MDOC_PBODY)
2536 mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse,
2537 n->line, n->pos, "Dd");
2538 else if (mdoc->meta.title != NULL)
2539 mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
2540 n->line, n->pos, "Dd after Dt");
2541 else if (mdoc->meta.os != NULL)
2542 mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
2543 n->line, n->pos, "Dd after Os");
2545 if (n->child == NULL || n->child->string[0] == '\0') {
2546 mdoc->meta.date = mdoc->quick ? mandoc_strdup("") :
2547 mandoc_normdate(mdoc, NULL, n->line, n->pos);
2552 deroff(&datestr, n);
2554 mdoc->meta.date = datestr;
2556 mdoc->meta.date = mandoc_normdate(mdoc,
2557 datestr, n->line, n->pos);
2565 struct roff_node *nn, *n;
2570 n->flags |= NODE_NOPRT;
2572 if (mdoc->flags & MDOC_PBODY) {
2573 mandoc_msg(MANDOCERR_DT_LATE, mdoc->parse,
2574 n->line, n->pos, "Dt");
2578 if (mdoc->meta.title != NULL)
2579 mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
2580 n->line, n->pos, "Dt");
2581 else if (mdoc->meta.os != NULL)
2582 mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
2583 n->line, n->pos, "Dt after Os");
2585 free(mdoc->meta.title);
2586 free(mdoc->meta.msec);
2587 free(mdoc->meta.vol);
2588 free(mdoc->meta.arch);
2590 mdoc->meta.title = NULL;
2591 mdoc->meta.msec = NULL;
2592 mdoc->meta.vol = NULL;
2593 mdoc->meta.arch = NULL;
2595 /* Mandatory first argument: title. */
2598 if (nn == NULL || *nn->string == '\0') {
2599 mandoc_msg(MANDOCERR_DT_NOTITLE,
2600 mdoc->parse, n->line, n->pos, "Dt");
2601 mdoc->meta.title = mandoc_strdup("UNTITLED");
2603 mdoc->meta.title = mandoc_strdup(nn->string);
2605 /* Check that all characters are uppercase. */
2607 for (p = nn->string; *p != '\0'; p++)
2608 if (islower((unsigned char)*p)) {
2609 mandoc_vmsg(MANDOCERR_TITLE_CASE,
2610 mdoc->parse, nn->line,
2611 nn->pos + (p - nn->string),
2612 "Dt %s", nn->string);
2617 /* Mandatory second argument: section. */
2623 mandoc_vmsg(MANDOCERR_MSEC_MISSING,
2624 mdoc->parse, n->line, n->pos,
2625 "Dt %s", mdoc->meta.title);
2626 mdoc->meta.vol = mandoc_strdup("LOCAL");
2627 return; /* msec and arch remain NULL. */
2630 mdoc->meta.msec = mandoc_strdup(nn->string);
2632 /* Infer volume title from section number. */
2634 cp = mandoc_a2msec(nn->string);
2636 mandoc_vmsg(MANDOCERR_MSEC_BAD, mdoc->parse,
2637 nn->line, nn->pos, "Dt ... %s", nn->string);
2638 mdoc->meta.vol = mandoc_strdup(nn->string);
2640 mdoc->meta.vol = mandoc_strdup(cp);
2642 /* Optional third argument: architecture. */
2644 if ((nn = nn->next) == NULL)
2647 for (p = nn->string; *p != '\0'; p++)
2648 *p = tolower((unsigned char)*p);
2649 mdoc->meta.arch = mandoc_strdup(nn->string);
2651 /* Ignore fourth and later arguments. */
2653 if ((nn = nn->next) != NULL)
2654 mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
2655 nn->line, nn->pos, "Dt ... %s", nn->string);
2661 struct roff_node *n, *nch;
2664 post_delim_nb(mdoc);
2670 macro = !strcmp(nch->string, "Open") ? "Ox" :
2671 !strcmp(nch->string, "Net") ? "Nx" :
2672 !strcmp(nch->string, "Free") ? "Fx" :
2673 !strcmp(nch->string, "DragonFly") ? "Dx" : NULL;
2675 mandoc_msg(MANDOCERR_BX, mdoc->parse,
2676 n->line, n->pos, macro);
2679 mdoc->next = ROFF_NEXT_SIBLING;
2680 roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
2681 mdoc->last->flags |= NODE_NOSRC;
2682 mdoc->next = ROFF_NEXT_SIBLING;
2684 mdoc->next = ROFF_NEXT_CHILD;
2685 roff_word_alloc(mdoc, n->line, n->pos, "BSD");
2686 mdoc->last->flags |= NODE_NOSRC;
2693 roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
2694 mdoc->last->flags |= NODE_NOSRC;
2695 mdoc->next = ROFF_NEXT_SIBLING;
2696 roff_word_alloc(mdoc, n->line, n->pos, "-");
2697 mdoc->last->flags |= NODE_NOSRC;
2698 roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
2699 mdoc->last->flags |= NODE_NOSRC;
2703 * Make `Bx's second argument always start with an uppercase
2704 * letter. Groff checks if it's an "accepted" term, but we just
2705 * uppercase blindly.
2708 *nch->string = (char)toupper((unsigned char)*nch->string);
2715 struct utsname utsname;
2716 static char *defbuf;
2718 struct roff_node *n;
2721 n->flags |= NODE_NOPRT;
2723 if (mdoc->meta.os != NULL)
2724 mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
2725 n->line, n->pos, "Os");
2726 else if (mdoc->flags & MDOC_PBODY)
2727 mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse,
2728 n->line, n->pos, "Os");
2733 * Set the operating system by way of the `Os' macro.
2734 * The order of precedence is:
2735 * 1. the argument of the `Os' macro, unless empty
2736 * 2. the -Ios=foo command line argument, if provided
2737 * 3. -DOSNAME="\"foo\"", if provided during compilation
2738 * 4. "sysname release" from uname(3)
2741 free(mdoc->meta.os);
2742 mdoc->meta.os = NULL;
2743 deroff(&mdoc->meta.os, n);
2747 if (mdoc->os_s != NULL) {
2748 mdoc->meta.os = mandoc_strdup(mdoc->os_s);
2753 mdoc->meta.os = mandoc_strdup(OSNAME);
2755 if (defbuf == NULL) {
2756 if (uname(&utsname) == -1) {
2757 mandoc_msg(MANDOCERR_OS_UNAME, mdoc->parse,
2758 n->line, n->pos, "Os");
2759 defbuf = mandoc_strdup("UNKNOWN");
2761 mandoc_asprintf(&defbuf, "%s %s",
2762 utsname.sysname, utsname.release);
2764 mdoc->meta.os = mandoc_strdup(defbuf);
2768 if (mdoc->meta.os_e == MANDOC_OS_OTHER) {
2769 if (strstr(mdoc->meta.os, "OpenBSD") != NULL)
2770 mdoc->meta.os_e = MANDOC_OS_OPENBSD;
2771 else if (strstr(mdoc->meta.os, "NetBSD") != NULL)
2772 mdoc->meta.os_e = MANDOC_OS_NETBSD;
2776 * This is the earliest point where we can check
2777 * Mdocdate conventions because we don't know
2778 * the operating system earlier.
2781 if (n->child != NULL)
2782 mandoc_vmsg(MANDOCERR_OS_ARG, mdoc->parse,
2783 n->child->line, n->child->pos,
2784 "Os %s (%s)", n->child->string,
2785 mdoc->meta.os_e == MANDOC_OS_OPENBSD ?
2786 "OpenBSD" : "NetBSD");
2788 while (n->tok != MDOC_Dd)
2789 if ((n = n->prev) == NULL)
2791 if ((n = n->child) == NULL)
2793 if (strncmp(n->string, "$" "Mdocdate", 9)) {
2794 if (mdoc->meta.os_e == MANDOC_OS_OPENBSD)
2795 mandoc_vmsg(MANDOCERR_MDOCDATE_MISSING,
2796 mdoc->parse, n->line, n->pos,
2797 "Dd %s (OpenBSD)", n->string);
2799 if (mdoc->meta.os_e == MANDOC_OS_NETBSD)
2800 mandoc_vmsg(MANDOCERR_MDOCDATE,
2801 mdoc->parse, n->line, n->pos,
2802 "Dd %s (NetBSD)", n->string);
2807 mdoc_a2sec(const char *p)
2811 for (i = 0; i < (int)SEC__MAX; i++)
2812 if (secnames[i] && 0 == strcmp(p, secnames[i]))
2813 return (enum roff_sec)i;
2819 macro2len(enum roff_tok macro)