1 /* $Id: mdoc_validate.c,v 1.358 2018/04/11 17:11:13 schwarze Exp $ */
3 * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2010-2018 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_argv(struct roff_man
*,
57 struct roff_node
*, struct mdoc_argv
*);
58 static void check_args(struct roff_man
*, struct roff_node
*);
59 static void check_text(struct roff_man
*, int, int, char *);
60 static void check_text_em(struct roff_man
*, int, int, char *);
61 static void check_toptext(struct roff_man
*, int, int, const char *);
62 static int child_an(const struct roff_node
*);
63 static size_t macro2len(enum roff_tok
);
64 static void rewrite_macro2len(struct roff_man
*, char **);
65 static int similar(const char *, const char *);
67 static void post_an(POST_ARGS
);
68 static void post_an_norm(POST_ARGS
);
69 static void post_at(POST_ARGS
);
70 static void post_bd(POST_ARGS
);
71 static void post_bf(POST_ARGS
);
72 static void post_bk(POST_ARGS
);
73 static void post_bl(POST_ARGS
);
74 static void post_bl_block(POST_ARGS
);
75 static void post_bl_head(POST_ARGS
);
76 static void post_bl_norm(POST_ARGS
);
77 static void post_bx(POST_ARGS
);
78 static void post_defaults(POST_ARGS
);
79 static void post_display(POST_ARGS
);
80 static void post_dd(POST_ARGS
);
81 static void post_delim(POST_ARGS
);
82 static void post_delim_nb(POST_ARGS
);
83 static void post_dt(POST_ARGS
);
84 static void post_en(POST_ARGS
);
85 static void post_es(POST_ARGS
);
86 static void post_eoln(POST_ARGS
);
87 static void post_ex(POST_ARGS
);
88 static void post_fa(POST_ARGS
);
89 static void post_fn(POST_ARGS
);
90 static void post_fname(POST_ARGS
);
91 static void post_fo(POST_ARGS
);
92 static void post_hyph(POST_ARGS
);
93 static void post_ignpar(POST_ARGS
);
94 static void post_it(POST_ARGS
);
95 static void post_lb(POST_ARGS
);
96 static void post_nd(POST_ARGS
);
97 static void post_nm(POST_ARGS
);
98 static void post_ns(POST_ARGS
);
99 static void post_obsolete(POST_ARGS
);
100 static void post_os(POST_ARGS
);
101 static void post_par(POST_ARGS
);
102 static void post_prevpar(POST_ARGS
);
103 static void post_root(POST_ARGS
);
104 static void post_rs(POST_ARGS
);
105 static void post_rv(POST_ARGS
);
106 static void post_sh(POST_ARGS
);
107 static void post_sh_head(POST_ARGS
);
108 static void post_sh_name(POST_ARGS
);
109 static void post_sh_see_also(POST_ARGS
);
110 static void post_sh_authors(POST_ARGS
);
111 static void post_sm(POST_ARGS
);
112 static void post_st(POST_ARGS
);
113 static void post_std(POST_ARGS
);
114 static void post_sx(POST_ARGS
);
115 static void post_useless(POST_ARGS
);
116 static void post_xr(POST_ARGS
);
117 static void post_xx(POST_ARGS
);
119 static const v_post __mdoc_valids
[MDOC_MAX
- MDOC_Dd
] = {
124 post_ignpar
, /* Ss */
126 post_display
, /* D1 */
127 post_display
, /* Dl */
128 post_display
, /* Bd */
133 post_delim_nb
, /* Ad */
136 post_defaults
, /* Ar */
138 post_delim_nb
, /* Cm */
139 post_delim_nb
, /* Dv */
140 post_delim_nb
, /* Er */
141 post_delim_nb
, /* Ev */
145 post_delim_nb
, /* Fl */
147 post_delim_nb
, /* Ft */
148 post_delim_nb
, /* Ic */
149 post_delim_nb
, /* In */
150 post_defaults
, /* Li */
153 post_delim_nb
, /* Op */
154 post_obsolete
, /* Ot */
155 post_defaults
, /* Pa */
158 post_delim_nb
, /* Va */
159 post_delim_nb
, /* Vt */
162 post_hyph
, /* %B */ /* FIXME: can be used outside Rs/Re. */
170 post_hyph
, /* %T */ /* FIXME: can be used outside Rs/Re. */
174 post_delim_nb
, /* Aq */
182 post_obsolete
, /* Db */
188 post_delim_nb
, /* Em */
191 post_delim_nb
, /* Ms */
199 post_delim_nb
, /* Pq */
201 post_delim_nb
, /* Ql */
203 post_delim_nb
, /* Qq */
208 post_delim_nb
, /* Sq */
211 post_delim_nb
, /* Sy */
212 post_useless
, /* Tn */
223 post_obsolete
, /* Hf */
224 post_obsolete
, /* Fr */
228 post_delim_nb
, /* Lk */
229 post_defaults
, /* Mt */
230 post_delim_nb
, /* Brq */
241 static const v_post
*const mdoc_valids
= __mdoc_valids
- MDOC_Dd
;
243 #define RSORD_MAX 14 /* Number of `Rs' blocks. */
245 static const enum roff_tok rsord
[RSORD_MAX
] = {
262 static const char * const secnames
[SEC__MAX
] = {
269 "IMPLEMENTATION NOTES",
284 "SECURITY CONSIDERATIONS",
290 mdoc_node_validate(struct roff_man
*mdoc
)
292 struct roff_node
*n
, *np
;
296 mdoc
->last
= mdoc
->last
->child
;
297 while (mdoc
->last
!= NULL
) {
298 mdoc_node_validate(mdoc
);
300 mdoc
->last
= mdoc
->last
->child
;
302 mdoc
->last
= mdoc
->last
->next
;
306 mdoc
->next
= ROFF_NEXT_SIBLING
;
310 if (n
->sec
!= SEC_SYNOPSIS
||
311 (np
->tok
!= MDOC_Cd
&& np
->tok
!= MDOC_Fd
))
312 check_text(mdoc
, n
->line
, n
->pos
, n
->string
);
313 if (np
->tok
!= MDOC_Ql
&& np
->tok
!= MDOC_Dl
&&
314 (np
->tok
!= MDOC_Bd
||
315 (mdoc
->flags
& MDOC_LITERAL
) == 0) &&
316 (np
->tok
!= MDOC_It
|| np
->type
!= ROFFT_HEAD
||
317 np
->parent
->parent
->norm
->Bl
.type
!= LIST_diag
))
318 check_text_em(mdoc
, n
->line
, n
->pos
, n
->string
);
319 if (np
->tok
== MDOC_It
|| (np
->type
== ROFFT_BODY
&&
320 (np
->tok
== MDOC_Sh
|| np
->tok
== MDOC_Ss
)))
321 check_toptext(mdoc
, n
->line
, n
->pos
, n
->string
);
331 check_args(mdoc
, mdoc
->last
);
334 * Closing delimiters are not special at the
335 * beginning of a block, opening delimiters
336 * are not special at the end.
339 if (n
->child
!= NULL
)
340 n
->child
->flags
&= ~NODE_DELIMC
;
342 n
->last
->flags
&= ~NODE_DELIMO
;
344 /* Call the macro's postprocessor. */
346 if (n
->tok
< ROFF_MAX
) {
359 assert(n
->tok
>= MDOC_Dd
&& n
->tok
< MDOC_MAX
);
360 p
= mdoc_valids
+ n
->tok
;
370 check_args(struct roff_man
*mdoc
, struct roff_node
*n
)
377 assert(n
->args
->argc
);
378 for (i
= 0; i
< (int)n
->args
->argc
; i
++)
379 check_argv(mdoc
, n
, &n
->args
->argv
[i
]);
383 check_argv(struct roff_man
*mdoc
, struct roff_node
*n
, struct mdoc_argv
*v
)
387 for (i
= 0; i
< (int)v
->sz
; i
++)
388 check_text(mdoc
, v
->line
, v
->pos
, v
->value
[i
]);
392 check_text(struct roff_man
*mdoc
, int ln
, int pos
, char *p
)
396 if (MDOC_LITERAL
& mdoc
->flags
)
399 for (cp
= p
; NULL
!= (p
= strchr(p
, '\t')); p
++)
400 mandoc_msg(MANDOCERR_FI_TAB
, mdoc
->parse
,
401 ln
, pos
+ (int)(p
- cp
), NULL
);
405 check_text_em(struct roff_man
*mdoc
, int ln
, int pos
, char *p
)
407 const struct roff_node
*np
, *nn
;
410 np
= mdoc
->last
->prev
;
411 nn
= mdoc
->last
->next
;
413 /* Look for em-dashes wrongly encoded as "--". */
415 for (cp
= p
; *cp
!= '\0'; cp
++) {
416 if (cp
[0] != '-' || cp
[1] != '-')
420 /* Skip input sequences of more than two '-'. */
428 /* Skip "--" directly attached to something else. */
430 if ((cp
- p
> 1 && cp
[-2] != ' ') ||
431 (cp
[1] != '\0' && cp
[1] != ' '))
434 /* Require a letter right before or right afterwards. */
437 isalpha((unsigned char)cp
[-3]) :
439 np
->type
== ROFFT_TEXT
&&
440 np
->string
!= '\0' &&
441 isalpha((unsigned char)np
->string
[
442 strlen(np
->string
) - 1])) ||
444 isalpha((unsigned char)cp
[2]) :
446 nn
->type
== ROFFT_TEXT
&&
447 nn
->string
!= '\0' &&
448 isalpha((unsigned char)*nn
->string
))) {
449 mandoc_msg(MANDOCERR_DASHDASH
, mdoc
->parse
,
450 ln
, pos
+ (int)(cp
- p
) - 1, NULL
);
457 check_toptext(struct roff_man
*mdoc
, int ln
, int pos
, const char *p
)
459 const char *cp
, *cpr
;
464 if ((cp
= strstr(p
, "OpenBSD")) != NULL
)
465 mandoc_msg(MANDOCERR_BX
, mdoc
->parse
,
466 ln
, pos
+ (cp
- p
), "Ox");
467 if ((cp
= strstr(p
, "NetBSD")) != NULL
)
468 mandoc_msg(MANDOCERR_BX
, mdoc
->parse
,
469 ln
, pos
+ (cp
- p
), "Nx");
470 if ((cp
= strstr(p
, "FreeBSD")) != NULL
)
471 mandoc_msg(MANDOCERR_BX
, mdoc
->parse
,
472 ln
, pos
+ (cp
- p
), "Fx");
473 if ((cp
= strstr(p
, "DragonFly")) != NULL
)
474 mandoc_msg(MANDOCERR_BX
, mdoc
->parse
,
475 ln
, pos
+ (cp
- p
), "Dx");
478 while ((cp
= strstr(cp
+ 1, "()")) != NULL
) {
479 for (cpr
= cp
- 1; cpr
>= p
; cpr
--)
480 if (*cpr
!= '_' && !isalnum((unsigned char)*cpr
))
482 if ((cpr
< p
|| *cpr
== ' ') && cpr
+ 1 < cp
) {
484 mandoc_vmsg(MANDOCERR_FUNC
, mdoc
->parse
,
486 "%.*s()", (int)(cp
- cpr
), cpr
);
492 post_delim(POST_ARGS
)
494 const struct roff_node
*nch
;
499 tok
= mdoc
->last
->tok
;
500 nch
= mdoc
->last
->last
;
501 if (nch
== NULL
|| nch
->type
!= ROFFT_TEXT
)
503 lc
= strchr(nch
->string
, '\0') - 1;
504 if (lc
< nch
->string
)
506 delim
= mdoc_isdelim(lc
);
507 if (delim
== DELIM_NONE
|| delim
== DELIM_OPEN
)
509 if (*lc
== ')' && (tok
== MDOC_Nd
|| tok
== MDOC_Sh
||
510 tok
== MDOC_Ss
|| tok
== MDOC_Fo
))
513 mandoc_vmsg(MANDOCERR_DELIM
, mdoc
->parse
,
514 nch
->line
, nch
->pos
+ (lc
- nch
->string
),
515 "%s%s %s", roff_name
[tok
],
516 nch
== mdoc
->last
->child
? "" : " ...", nch
->string
);
520 post_delim_nb(POST_ARGS
)
522 const struct roff_node
*nch
;
529 * Find candidates: at least two bytes,
530 * the last one a closing or middle delimiter.
533 tok
= mdoc
->last
->tok
;
534 nch
= mdoc
->last
->last
;
535 if (nch
== NULL
|| nch
->type
!= ROFFT_TEXT
)
537 lc
= strchr(nch
->string
, '\0') - 1;
538 if (lc
<= nch
->string
)
540 delim
= mdoc_isdelim(lc
);
541 if (delim
== DELIM_NONE
|| delim
== DELIM_OPEN
)
545 * Reduce false positives by allowing various cases.
548 /* Escaped delimiters. */
549 if (lc
> nch
->string
+ 1 && lc
[-2] == '\\' &&
550 (lc
[-1] == '&' || lc
[-1] == 'e'))
553 /* Specific byte sequences. */
556 for (cp
= lc
; cp
>= nch
->string
; cp
--)
561 if (lc
> nch
->string
+ 1 && lc
[-2] == '.' && lc
[-1] == '.')
575 for (cp
= lc
; cp
>= nch
->string
; cp
--)
580 if (lc
== nch
->string
+ 1 && lc
[-1] == '|')
586 /* Exactly two non-alphanumeric bytes. */
587 if (lc
== nch
->string
+ 1 && !isalnum((unsigned char)lc
[-1]))
590 /* At least three alphabetic words with a sentence ending. */
591 if (strchr("!.:?", *lc
) != NULL
&& (tok
== MDOC_Em
||
592 tok
== MDOC_Li
|| tok
== MDOC_Pq
|| tok
== MDOC_Sy
)) {
594 for (cp
= lc
- 1; cp
>= nch
->string
; cp
--) {
597 if (cp
> nch
->string
&& cp
[-1] == ',')
599 } else if (isalpha((unsigned int)*cp
)) {
607 mandoc_vmsg(MANDOCERR_DELIM_NB
, mdoc
->parse
,
608 nch
->line
, nch
->pos
+ (lc
- nch
->string
),
609 "%s%s %s", roff_name
[tok
],
610 nch
== mdoc
->last
->child
? "" : " ...", nch
->string
);
614 post_bl_norm(POST_ARGS
)
617 struct mdoc_argv
*argv
, *wa
;
619 enum mdocargt mdoclt
;
622 n
= mdoc
->last
->parent
;
623 n
->norm
->Bl
.type
= LIST__NONE
;
626 * First figure out which kind of list to use: bind ourselves to
627 * the first mentioned list type and warn about any remaining
628 * ones. If we find no list type, we default to LIST_item.
631 wa
= (n
->args
== NULL
) ? NULL
: n
->args
->argv
;
632 mdoclt
= MDOC_ARG_MAX
;
633 for (i
= 0; n
->args
&& i
< (int)n
->args
->argc
; i
++) {
634 argv
= n
->args
->argv
+ i
;
637 /* Set list types. */
671 /* Set list arguments. */
673 if (n
->norm
->Bl
.comp
)
674 mandoc_msg(MANDOCERR_ARG_REP
,
675 mdoc
->parse
, argv
->line
,
676 argv
->pos
, "Bl -compact");
677 n
->norm
->Bl
.comp
= 1;
682 mandoc_msg(MANDOCERR_ARG_EMPTY
,
683 mdoc
->parse
, argv
->line
,
684 argv
->pos
, "Bl -width");
685 n
->norm
->Bl
.width
= "0n";
688 if (NULL
!= n
->norm
->Bl
.width
)
689 mandoc_vmsg(MANDOCERR_ARG_REP
,
690 mdoc
->parse
, argv
->line
,
691 argv
->pos
, "Bl -width %s",
693 rewrite_macro2len(mdoc
, argv
->value
);
694 n
->norm
->Bl
.width
= argv
->value
[0];
698 mandoc_msg(MANDOCERR_ARG_EMPTY
,
699 mdoc
->parse
, argv
->line
,
700 argv
->pos
, "Bl -offset");
703 if (NULL
!= n
->norm
->Bl
.offs
)
704 mandoc_vmsg(MANDOCERR_ARG_REP
,
705 mdoc
->parse
, argv
->line
,
706 argv
->pos
, "Bl -offset %s",
708 rewrite_macro2len(mdoc
, argv
->value
);
709 n
->norm
->Bl
.offs
= argv
->value
[0];
714 if (LIST__NONE
== lt
)
718 /* Check: multiple list types. */
720 if (LIST__NONE
!= n
->norm
->Bl
.type
) {
721 mandoc_vmsg(MANDOCERR_BL_REP
,
722 mdoc
->parse
, n
->line
, n
->pos
,
723 "Bl -%s", mdoc_argnames
[argv
->arg
]);
727 /* The list type should come first. */
729 if (n
->norm
->Bl
.width
||
732 mandoc_vmsg(MANDOCERR_BL_LATETYPE
,
733 mdoc
->parse
, n
->line
, n
->pos
, "Bl -%s",
734 mdoc_argnames
[n
->args
->argv
[0].arg
]);
736 n
->norm
->Bl
.type
= lt
;
737 if (LIST_column
== lt
) {
738 n
->norm
->Bl
.ncols
= argv
->sz
;
739 n
->norm
->Bl
.cols
= (void *)argv
->value
;
743 /* Allow lists to default to LIST_item. */
745 if (LIST__NONE
== n
->norm
->Bl
.type
) {
746 mandoc_msg(MANDOCERR_BL_NOTYPE
, mdoc
->parse
,
747 n
->line
, n
->pos
, "Bl");
748 n
->norm
->Bl
.type
= LIST_item
;
753 * Validate the width field. Some list types don't need width
754 * types and should be warned about them. Others should have it
755 * and must also be warned. Yet others have a default and need
759 switch (n
->norm
->Bl
.type
) {
761 if (n
->norm
->Bl
.width
== NULL
)
762 mandoc_msg(MANDOCERR_BL_NOWIDTH
, mdoc
->parse
,
763 n
->line
, n
->pos
, "Bl -tag");
770 if (n
->norm
->Bl
.width
!= NULL
)
771 mandoc_vmsg(MANDOCERR_BL_SKIPW
, mdoc
->parse
,
772 wa
->line
, wa
->pos
, "Bl -%s",
773 mdoc_argnames
[mdoclt
]);
774 n
->norm
->Bl
.width
= NULL
;
779 if (n
->norm
->Bl
.width
== NULL
)
780 n
->norm
->Bl
.width
= "2n";
783 if (n
->norm
->Bl
.width
== NULL
)
784 n
->norm
->Bl
.width
= "3n";
795 struct mdoc_argv
*argv
;
800 for (i
= 0; n
->args
&& i
< (int)n
->args
->argc
; i
++) {
801 argv
= n
->args
->argv
+ i
;
821 mandoc_msg(MANDOCERR_BD_FILE
, mdoc
->parse
,
822 n
->line
, n
->pos
, NULL
);
826 mandoc_msg(MANDOCERR_ARG_EMPTY
,
827 mdoc
->parse
, argv
->line
,
828 argv
->pos
, "Bd -offset");
831 if (NULL
!= n
->norm
->Bd
.offs
)
832 mandoc_vmsg(MANDOCERR_ARG_REP
,
833 mdoc
->parse
, argv
->line
,
834 argv
->pos
, "Bd -offset %s",
836 rewrite_macro2len(mdoc
, argv
->value
);
837 n
->norm
->Bd
.offs
= argv
->value
[0];
840 if (n
->norm
->Bd
.comp
)
841 mandoc_msg(MANDOCERR_ARG_REP
,
842 mdoc
->parse
, argv
->line
,
843 argv
->pos
, "Bd -compact");
844 n
->norm
->Bd
.comp
= 1;
849 if (DISP__NONE
== dt
)
852 if (DISP__NONE
== n
->norm
->Bd
.type
)
853 n
->norm
->Bd
.type
= dt
;
855 mandoc_vmsg(MANDOCERR_BD_REP
,
856 mdoc
->parse
, n
->line
, n
->pos
,
857 "Bd -%s", mdoc_argnames
[argv
->arg
]);
860 if (DISP__NONE
== n
->norm
->Bd
.type
) {
861 mandoc_msg(MANDOCERR_BD_NOTYPE
, mdoc
->parse
,
862 n
->line
, n
->pos
, "Bd");
863 n
->norm
->Bd
.type
= DISP_ragged
;
868 * Stand-alone line macros.
872 post_an_norm(POST_ARGS
)
875 struct mdoc_argv
*argv
;
882 for (i
= 1; i
< n
->args
->argc
; i
++) {
883 argv
= n
->args
->argv
+ i
;
884 mandoc_vmsg(MANDOCERR_AN_REP
,
885 mdoc
->parse
, argv
->line
, argv
->pos
,
886 "An -%s", mdoc_argnames
[argv
->arg
]);
889 argv
= n
->args
->argv
;
890 if (argv
->arg
== MDOC_Split
)
891 n
->norm
->An
.auth
= AUTH_split
;
892 else if (argv
->arg
== MDOC_Nosplit
)
893 n
->norm
->An
.auth
= AUTH_nosplit
;
905 if (n
->child
!= NULL
)
906 mandoc_vmsg(MANDOCERR_ARG_SKIP
, mdoc
->parse
, n
->line
,
907 n
->pos
, "%s %s", roff_name
[n
->tok
], n
->child
->string
);
909 while (n
->child
!= NULL
)
910 roff_node_delete(mdoc
, n
->child
);
912 roff_word_alloc(mdoc
, n
->line
, n
->pos
, n
->tok
== MDOC_Bt
?
913 "is currently in beta test." : "currently under development.");
914 mdoc
->last
->flags
|= NODE_EOS
| NODE_NOSRC
;
919 build_list(struct roff_man
*mdoc
, int tok
)
924 n
= mdoc
->last
->next
;
925 for (ic
= 1;; ic
++) {
926 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, tok
);
927 mdoc
->last
->flags
|= NODE_NOSRC
;
928 mdoc_node_relink(mdoc
, n
);
929 n
= mdoc
->last
= mdoc
->last
->parent
;
930 mdoc
->next
= ROFF_NEXT_SIBLING
;
933 if (ic
> 1 || n
->next
->next
!= NULL
) {
934 roff_word_alloc(mdoc
, n
->line
, n
->pos
, ",");
935 mdoc
->last
->flags
|= NODE_DELIMC
| NODE_NOSRC
;
937 n
= mdoc
->last
->next
;
938 if (n
->next
== NULL
) {
939 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "and");
940 mdoc
->last
->flags
|= NODE_NOSRC
;
954 mdoc
->next
= ROFF_NEXT_CHILD
;
955 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "The");
956 mdoc
->last
->flags
|= NODE_NOSRC
;
958 if (mdoc
->last
->next
!= NULL
)
959 ic
= build_list(mdoc
, MDOC_Nm
);
960 else if (mdoc
->meta
.name
!= NULL
) {
961 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Nm
);
962 mdoc
->last
->flags
|= NODE_NOSRC
;
963 roff_word_alloc(mdoc
, n
->line
, n
->pos
, mdoc
->meta
.name
);
964 mdoc
->last
->flags
|= NODE_NOSRC
;
965 mdoc
->last
= mdoc
->last
->parent
;
966 mdoc
->next
= ROFF_NEXT_SIBLING
;
969 mandoc_msg(MANDOCERR_EX_NONAME
, mdoc
->parse
,
970 n
->line
, n
->pos
, "Ex");
974 roff_word_alloc(mdoc
, n
->line
, n
->pos
,
975 ic
> 1 ? "utilities exit\\~0" : "utility exits\\~0");
976 mdoc
->last
->flags
|= NODE_NOSRC
;
977 roff_word_alloc(mdoc
, n
->line
, n
->pos
,
978 "on success, and\\~>0 if an error occurs.");
979 mdoc
->last
->flags
|= NODE_EOS
| NODE_NOSRC
;
992 assert(n
->child
->type
== ROFFT_TEXT
);
993 mdoc
->next
= ROFF_NEXT_CHILD
;
995 if ((p
= mdoc_a2lib(n
->child
->string
)) != NULL
) {
996 n
->child
->flags
|= NODE_NOPRT
;
997 roff_word_alloc(mdoc
, n
->line
, n
->pos
, p
);
998 mdoc
->last
->flags
= NODE_NOSRC
;
1003 mandoc_vmsg(MANDOCERR_LB_BAD
, mdoc
->parse
, n
->child
->line
,
1004 n
->child
->pos
, "Lb %s", n
->child
->string
);
1006 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "library");
1007 mdoc
->last
->flags
= NODE_NOSRC
;
1008 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "\\(lq");
1009 mdoc
->last
->flags
= NODE_DELIMO
| NODE_NOSRC
;
1010 mdoc
->last
= mdoc
->last
->next
;
1011 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "\\(rq");
1012 mdoc
->last
->flags
= NODE_DELIMC
| NODE_NOSRC
;
1019 struct roff_node
*n
;
1025 mdoc
->next
= ROFF_NEXT_CHILD
;
1026 if (n
->child
!= NULL
) {
1027 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "The");
1028 mdoc
->last
->flags
|= NODE_NOSRC
;
1029 ic
= build_list(mdoc
, MDOC_Fn
);
1030 roff_word_alloc(mdoc
, n
->line
, n
->pos
,
1031 ic
> 1 ? "functions return" : "function returns");
1032 mdoc
->last
->flags
|= NODE_NOSRC
;
1033 roff_word_alloc(mdoc
, n
->line
, n
->pos
,
1034 "the value\\~0 if successful;");
1036 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "Upon successful "
1037 "completion, the value\\~0 is returned;");
1038 mdoc
->last
->flags
|= NODE_NOSRC
;
1040 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "otherwise "
1041 "the value\\~\\-1 is returned and the global variable");
1042 mdoc
->last
->flags
|= NODE_NOSRC
;
1043 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Va
);
1044 mdoc
->last
->flags
|= NODE_NOSRC
;
1045 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "errno");
1046 mdoc
->last
->flags
|= NODE_NOSRC
;
1047 mdoc
->last
= mdoc
->last
->parent
;
1048 mdoc
->next
= ROFF_NEXT_SIBLING
;
1049 roff_word_alloc(mdoc
, n
->line
, n
->pos
,
1050 "is set to indicate the error.");
1051 mdoc
->last
->flags
|= NODE_EOS
| NODE_NOSRC
;
1058 struct roff_node
*n
;
1063 if (n
->args
&& n
->args
->argc
== 1)
1064 if (n
->args
->argv
[0].arg
== MDOC_Std
)
1067 mandoc_msg(MANDOCERR_ARG_STD
, mdoc
->parse
,
1068 n
->line
, n
->pos
, roff_name
[n
->tok
]);
1074 struct roff_node
*n
, *nch
;
1079 assert(nch
->type
== ROFFT_TEXT
);
1081 if ((p
= mdoc_a2st(nch
->string
)) == NULL
) {
1082 mandoc_vmsg(MANDOCERR_ST_BAD
, mdoc
->parse
,
1083 nch
->line
, nch
->pos
, "St %s", nch
->string
);
1084 roff_node_delete(mdoc
, n
);
1088 nch
->flags
|= NODE_NOPRT
;
1089 mdoc
->next
= ROFF_NEXT_CHILD
;
1090 roff_word_alloc(mdoc
, nch
->line
, nch
->pos
, p
);
1091 mdoc
->last
->flags
|= NODE_NOSRC
;
1096 post_obsolete(POST_ARGS
)
1098 struct roff_node
*n
;
1101 if (n
->type
== ROFFT_ELEM
|| n
->type
== ROFFT_BLOCK
)
1102 mandoc_msg(MANDOCERR_MACRO_OBS
, mdoc
->parse
,
1103 n
->line
, n
->pos
, roff_name
[n
->tok
]);
1107 post_useless(POST_ARGS
)
1109 struct roff_node
*n
;
1112 mandoc_msg(MANDOCERR_MACRO_USELESS
, mdoc
->parse
,
1113 n
->line
, n
->pos
, roff_name
[n
->tok
]);
1123 struct roff_node
*np
, *nch
;
1126 * Unlike other data pointers, these are "housed" by the HEAD
1127 * element, which contains the goods.
1131 if (np
->type
!= ROFFT_HEAD
)
1134 assert(np
->parent
->type
== ROFFT_BLOCK
);
1135 assert(np
->parent
->tok
== MDOC_Bf
);
1137 /* Check the number of arguments. */
1140 if (np
->parent
->args
== NULL
) {
1142 mandoc_msg(MANDOCERR_BF_NOFONT
, mdoc
->parse
,
1143 np
->line
, np
->pos
, "Bf");
1149 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, mdoc
->parse
,
1150 nch
->line
, nch
->pos
, "Bf ... %s", nch
->string
);
1152 /* Extract argument into data. */
1154 if (np
->parent
->args
!= NULL
) {
1155 switch (np
->parent
->args
->argv
[0].arg
) {
1157 np
->norm
->Bf
.font
= FONT_Em
;
1160 np
->norm
->Bf
.font
= FONT_Li
;
1163 np
->norm
->Bf
.font
= FONT_Sy
;
1171 /* Extract parameter into data. */
1173 if ( ! strcmp(np
->child
->string
, "Em"))
1174 np
->norm
->Bf
.font
= FONT_Em
;
1175 else if ( ! strcmp(np
->child
->string
, "Li"))
1176 np
->norm
->Bf
.font
= FONT_Li
;
1177 else if ( ! strcmp(np
->child
->string
, "Sy"))
1178 np
->norm
->Bf
.font
= FONT_Sy
;
1180 mandoc_vmsg(MANDOCERR_BF_BADFONT
, mdoc
->parse
,
1181 np
->child
->line
, np
->child
->pos
,
1182 "Bf %s", np
->child
->string
);
1186 post_fname(POST_ARGS
)
1188 const struct roff_node
*n
;
1192 n
= mdoc
->last
->child
;
1193 pos
= strcspn(n
->string
, "()");
1194 cp
= n
->string
+ pos
;
1195 if ( ! (cp
[0] == '\0' || (cp
[0] == '(' && cp
[1] == '*')))
1196 mandoc_msg(MANDOCERR_FN_PAREN
, mdoc
->parse
,
1197 n
->line
, n
->pos
+ pos
, n
->string
);
1211 const struct roff_node
*n
;
1215 if (n
->type
!= ROFFT_HEAD
)
1218 if (n
->child
== NULL
) {
1219 mandoc_msg(MANDOCERR_FO_NOHEAD
, mdoc
->parse
,
1220 n
->line
, n
->pos
, "Fo");
1223 if (n
->child
!= n
->last
) {
1224 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, mdoc
->parse
,
1225 n
->child
->next
->line
, n
->child
->next
->pos
,
1226 "Fo ... %s", n
->child
->next
->string
);
1227 while (n
->child
!= n
->last
)
1228 roff_node_delete(mdoc
, n
->last
);
1238 const struct roff_node
*n
;
1241 for (n
= mdoc
->last
->child
; n
!= NULL
; n
= n
->next
) {
1242 for (cp
= n
->string
; *cp
!= '\0'; cp
++) {
1243 /* Ignore callbacks and alterations. */
1244 if (*cp
== '(' || *cp
== '{')
1248 mandoc_msg(MANDOCERR_FA_COMMA
, mdoc
->parse
,
1249 n
->line
, n
->pos
+ (cp
- n
->string
),
1254 post_delim_nb(mdoc
);
1260 struct roff_node
*n
;
1264 if (n
->sec
== SEC_NAME
&& n
->child
!= NULL
&&
1265 n
->child
->type
== ROFFT_TEXT
&& mdoc
->meta
.msec
!= NULL
)
1266 mandoc_xr_add(mdoc
->meta
.msec
, n
->child
->string
, -1, -1);
1268 if (n
->last
!= NULL
&&
1269 (n
->last
->tok
== MDOC_Pp
||
1270 n
->last
->tok
== MDOC_Lp
))
1271 mdoc_node_relink(mdoc
, n
->last
);
1273 if (mdoc
->meta
.name
== NULL
)
1274 deroff(&mdoc
->meta
.name
, n
);
1276 if (mdoc
->meta
.name
== NULL
||
1277 (mdoc
->lastsec
== SEC_NAME
&& n
->child
== NULL
))
1278 mandoc_msg(MANDOCERR_NM_NONAME
, mdoc
->parse
,
1279 n
->line
, n
->pos
, "Nm");
1283 post_delim_nb(mdoc
);
1292 if ((n
->child
!= NULL
&& n
->child
->type
== ROFFT_TEXT
) ||
1293 mdoc
->meta
.name
== NULL
)
1296 mdoc
->next
= ROFF_NEXT_CHILD
;
1297 roff_word_alloc(mdoc
, n
->line
, n
->pos
, mdoc
->meta
.name
);
1298 mdoc
->last
->flags
|= NODE_NOSRC
;
1305 struct roff_node
*n
;
1309 if (n
->type
!= ROFFT_BODY
)
1312 if (n
->sec
!= SEC_NAME
)
1313 mandoc_msg(MANDOCERR_ND_LATE
, mdoc
->parse
,
1314 n
->line
, n
->pos
, "Nd");
1316 if (n
->child
== NULL
)
1317 mandoc_msg(MANDOCERR_ND_EMPTY
, mdoc
->parse
,
1318 n
->line
, n
->pos
, "Nd");
1326 post_display(POST_ARGS
)
1328 struct roff_node
*n
, *np
;
1333 if (n
->end
!= ENDBODY_NOT
) {
1334 if (n
->tok
== MDOC_Bd
&&
1335 n
->body
->parent
->args
== NULL
)
1336 roff_node_delete(mdoc
, n
);
1337 } else if (n
->child
== NULL
)
1338 mandoc_msg(MANDOCERR_BLK_EMPTY
, mdoc
->parse
,
1339 n
->line
, n
->pos
, roff_name
[n
->tok
]);
1340 else if (n
->tok
== MDOC_D1
)
1344 if (n
->tok
== MDOC_Bd
) {
1345 if (n
->args
== NULL
) {
1346 mandoc_msg(MANDOCERR_BD_NOARG
,
1347 mdoc
->parse
, n
->line
, n
->pos
, "Bd");
1348 mdoc
->next
= ROFF_NEXT_SIBLING
;
1349 while (n
->body
->child
!= NULL
)
1350 mdoc_node_relink(mdoc
,
1352 roff_node_delete(mdoc
, n
);
1358 for (np
= n
->parent
; np
!= NULL
; np
= np
->parent
) {
1359 if (np
->type
== ROFFT_BLOCK
&& np
->tok
== MDOC_Bd
) {
1360 mandoc_vmsg(MANDOCERR_BD_NEST
,
1361 mdoc
->parse
, n
->line
, n
->pos
,
1362 "%s in Bd", roff_name
[n
->tok
]);
1373 post_defaults(POST_ARGS
)
1375 struct roff_node
*nn
;
1377 if (mdoc
->last
->child
!= NULL
) {
1378 post_delim_nb(mdoc
);
1383 * The `Ar' defaults to "file ..." if no value is provided as an
1384 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
1385 * gets an empty string.
1391 mdoc
->next
= ROFF_NEXT_CHILD
;
1392 roff_word_alloc(mdoc
, nn
->line
, nn
->pos
, "file");
1393 mdoc
->last
->flags
|= NODE_NOSRC
;
1394 roff_word_alloc(mdoc
, nn
->line
, nn
->pos
, "...");
1395 mdoc
->last
->flags
|= NODE_NOSRC
;
1399 mdoc
->next
= ROFF_NEXT_CHILD
;
1400 roff_word_alloc(mdoc
, nn
->line
, nn
->pos
, "~");
1401 mdoc
->last
->flags
|= NODE_NOSRC
;
1412 struct roff_node
*n
, *nch
;
1419 * If we have a child, look it up in the standard keys. If a
1420 * key exist, use that instead of the child; if it doesn't,
1421 * prefix "AT&T UNIX " to the existing data.
1425 if (nch
!= NULL
&& ((att
= mdoc_a2att(nch
->string
)) == NULL
))
1426 mandoc_vmsg(MANDOCERR_AT_BAD
, mdoc
->parse
,
1427 nch
->line
, nch
->pos
, "At %s", nch
->string
);
1429 mdoc
->next
= ROFF_NEXT_CHILD
;
1431 roff_word_alloc(mdoc
, nch
->line
, nch
->pos
, att
);
1432 nch
->flags
|= NODE_NOPRT
;
1434 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "AT&T UNIX");
1435 mdoc
->last
->flags
|= NODE_NOSRC
;
1442 struct roff_node
*np
, *nch
;
1448 if (np
->norm
->An
.auth
== AUTH__NONE
) {
1450 mandoc_msg(MANDOCERR_MACRO_EMPTY
, mdoc
->parse
,
1451 np
->line
, np
->pos
, "An");
1453 post_delim_nb(mdoc
);
1454 } else if (nch
!= NULL
)
1455 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, mdoc
->parse
,
1456 nch
->line
, nch
->pos
, "An ... %s", nch
->string
);
1463 post_obsolete(mdoc
);
1464 if (mdoc
->last
->type
== ROFFT_BLOCK
)
1465 mdoc
->last
->norm
->Es
= mdoc
->last_es
;
1472 post_obsolete(mdoc
);
1473 mdoc
->last_es
= mdoc
->last
;
1479 struct roff_node
*n
;
1483 post_delim_nb(mdoc
);
1498 if (n
->child
== NULL
)
1500 v
= n
->child
->string
;
1501 if ((v
[0] != '0' && v
[0] != '1') || v
[1] != '.' ||
1502 v
[2] < '0' || v
[2] > '9' ||
1503 v
[3] < 'a' || v
[3] > 'z' || v
[4] != '\0')
1505 n
->child
->flags
|= NODE_NOPRT
;
1506 mdoc
->next
= ROFF_NEXT_CHILD
;
1507 roff_word_alloc(mdoc
, n
->child
->line
, n
->child
->pos
, v
);
1508 v
= mdoc
->last
->string
;
1509 v
[3] = toupper((unsigned char)v
[3]);
1510 mdoc
->last
->flags
|= NODE_NOSRC
;
1522 mdoc
->next
= ROFF_NEXT_CHILD
;
1523 roff_word_alloc(mdoc
, n
->line
, n
->pos
, os
);
1524 mdoc
->last
->flags
|= NODE_NOSRC
;
1531 struct roff_node
*nbl
, *nit
, *nch
;
1538 if (nit
->type
!= ROFFT_BLOCK
)
1541 nbl
= nit
->parent
->parent
;
1542 lt
= nbl
->norm
->Bl
.type
;
1550 if (nit
->head
->child
== NULL
)
1551 mandoc_vmsg(MANDOCERR_IT_NOHEAD
,
1552 mdoc
->parse
, nit
->line
, nit
->pos
,
1554 mdoc_argnames
[nbl
->args
->argv
[0].arg
]);
1560 if (nit
->body
== NULL
|| nit
->body
->child
== NULL
)
1561 mandoc_vmsg(MANDOCERR_IT_NOBODY
,
1562 mdoc
->parse
, nit
->line
, nit
->pos
,
1564 mdoc_argnames
[nbl
->args
->argv
[0].arg
]);
1567 if ((nch
= nit
->head
->child
) != NULL
)
1568 mandoc_vmsg(MANDOCERR_ARG_SKIP
, mdoc
->parse
,
1569 nit
->line
, nit
->pos
, "It %s",
1570 nch
->string
== NULL
? roff_name
[nch
->tok
] :
1574 cols
= (int)nbl
->norm
->Bl
.ncols
;
1576 assert(nit
->head
->child
== NULL
);
1578 if (nit
->head
->next
->child
== NULL
&&
1579 nit
->head
->next
->next
== NULL
) {
1580 mandoc_msg(MANDOCERR_MACRO_EMPTY
, mdoc
->parse
,
1581 nit
->line
, nit
->pos
, "It");
1582 roff_node_delete(mdoc
, nit
);
1587 for (nch
= nit
->child
; nch
!= NULL
; nch
= nch
->next
) {
1588 if (nch
->type
!= ROFFT_BODY
)
1590 if (i
++ && nch
->flags
& NODE_LINE
)
1591 mandoc_msg(MANDOCERR_TA_LINE
, mdoc
->parse
,
1592 nch
->line
, nch
->pos
, "Ta");
1594 if (i
< cols
|| i
> cols
+ 1)
1595 mandoc_vmsg(MANDOCERR_BL_COL
,
1596 mdoc
->parse
, nit
->line
, nit
->pos
,
1597 "%d columns, %d cells", cols
, i
);
1598 else if (nit
->head
->next
->child
!= NULL
&&
1599 nit
->head
->next
->child
->line
> nit
->line
)
1600 mandoc_msg(MANDOCERR_IT_NOARG
, mdoc
->parse
,
1601 nit
->line
, nit
->pos
, "Bl -column It");
1609 post_bl_block(POST_ARGS
)
1611 struct roff_node
*n
, *ni
, *nc
;
1616 for (ni
= n
->body
->child
; ni
!= NULL
; ni
= ni
->next
) {
1617 if (ni
->body
== NULL
)
1619 nc
= ni
->body
->last
;
1620 while (nc
!= NULL
) {
1630 if (ni
->next
== NULL
) {
1631 mandoc_msg(MANDOCERR_PAR_MOVE
,
1632 mdoc
->parse
, nc
->line
, nc
->pos
,
1633 roff_name
[nc
->tok
]);
1634 mdoc_node_relink(mdoc
, nc
);
1635 } else if (n
->norm
->Bl
.comp
== 0 &&
1636 n
->norm
->Bl
.type
!= LIST_column
) {
1637 mandoc_vmsg(MANDOCERR_PAR_SKIP
,
1638 mdoc
->parse
, nc
->line
, nc
->pos
,
1639 "%s before It", roff_name
[nc
->tok
]);
1640 roff_node_delete(mdoc
, nc
);
1643 nc
= ni
->body
->last
;
1649 * If the argument of -offset or -width is a macro,
1650 * replace it with the associated default width.
1653 rewrite_macro2len(struct roff_man
*mdoc
, char **arg
)
1660 else if ( ! strcmp(*arg
, "Ds"))
1662 else if ((tok
= roffhash_find(mdoc
->mdocmac
, *arg
, 0)) == TOKEN_NONE
)
1665 width
= macro2len(tok
);
1668 mandoc_asprintf(arg
, "%zun", width
);
1672 post_bl_head(POST_ARGS
)
1674 struct roff_node
*nbl
, *nh
, *nch
, *nnext
;
1675 struct mdoc_argv
*argv
;
1681 if (nh
->norm
->Bl
.type
!= LIST_column
) {
1682 if ((nch
= nh
->child
) == NULL
)
1684 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, mdoc
->parse
,
1685 nch
->line
, nch
->pos
, "Bl ... %s", nch
->string
);
1686 while (nch
!= NULL
) {
1687 roff_node_delete(mdoc
, nch
);
1694 * Append old-style lists, where the column width specifiers
1695 * trail as macro parameters, to the new-style ("normal-form")
1696 * lists where they're argument values following -column.
1699 if (nh
->child
== NULL
)
1703 for (j
= 0; j
< (int)nbl
->args
->argc
; j
++)
1704 if (nbl
->args
->argv
[j
].arg
== MDOC_Column
)
1707 assert(j
< (int)nbl
->args
->argc
);
1710 * Accommodate for new-style groff column syntax. Shuffle the
1711 * child nodes, all of which must be TEXT, as arguments for the
1712 * column field. Then, delete the head children.
1715 argv
= nbl
->args
->argv
+ j
;
1717 for (nch
= nh
->child
; nch
!= NULL
; nch
= nch
->next
)
1719 argv
->value
= mandoc_reallocarray(argv
->value
,
1720 argv
->sz
, sizeof(char *));
1722 nh
->norm
->Bl
.ncols
= argv
->sz
;
1723 nh
->norm
->Bl
.cols
= (void *)argv
->value
;
1725 for (nch
= nh
->child
; nch
!= NULL
; nch
= nnext
) {
1726 argv
->value
[i
++] = nch
->string
;
1729 roff_node_delete(NULL
, nch
);
1737 struct roff_node
*nparent
, *nprev
; /* of the Bl block */
1738 struct roff_node
*nblock
, *nbody
; /* of the Bl */
1739 struct roff_node
*nchild
, *nnext
; /* of the Bl body */
1740 const char *prev_Er
;
1744 switch (nbody
->type
) {
1746 post_bl_block(mdoc
);
1756 if (nbody
->end
!= ENDBODY_NOT
)
1759 nchild
= nbody
->child
;
1760 if (nchild
== NULL
) {
1761 mandoc_msg(MANDOCERR_BLK_EMPTY
, mdoc
->parse
,
1762 nbody
->line
, nbody
->pos
, "Bl");
1765 while (nchild
!= NULL
) {
1766 nnext
= nchild
->next
;
1767 if (nchild
->tok
== MDOC_It
||
1768 (nchild
->tok
== MDOC_Sm
&&
1769 nnext
!= NULL
&& nnext
->tok
== MDOC_It
)) {
1775 * In .Bl -column, the first rows may be implicit,
1776 * that is, they may not start with .It macros.
1777 * Such rows may be followed by nodes generated on the
1778 * roff level, for example .TS, which cannot be moved
1779 * out of the list. In that case, wrap such roff nodes
1780 * into an implicit row.
1783 if (nchild
->prev
!= NULL
) {
1784 mdoc
->last
= nchild
;
1785 mdoc
->next
= ROFF_NEXT_SIBLING
;
1786 roff_block_alloc(mdoc
, nchild
->line
,
1787 nchild
->pos
, MDOC_It
);
1788 roff_head_alloc(mdoc
, nchild
->line
,
1789 nchild
->pos
, MDOC_It
);
1790 mdoc
->next
= ROFF_NEXT_SIBLING
;
1791 roff_body_alloc(mdoc
, nchild
->line
,
1792 nchild
->pos
, MDOC_It
);
1793 while (nchild
->tok
!= MDOC_It
) {
1794 mdoc_node_relink(mdoc
, nchild
);
1795 if ((nchild
= nnext
) == NULL
)
1797 nnext
= nchild
->next
;
1798 mdoc
->next
= ROFF_NEXT_SIBLING
;
1804 mandoc_msg(MANDOCERR_BL_MOVE
, mdoc
->parse
,
1805 nchild
->line
, nchild
->pos
, roff_name
[nchild
->tok
]);
1808 * Move the node out of the Bl block.
1809 * First, collect all required node pointers.
1812 nblock
= nbody
->parent
;
1813 nprev
= nblock
->prev
;
1814 nparent
= nblock
->parent
;
1817 * Unlink this child.
1820 nbody
->child
= nnext
;
1827 * Relink this child.
1830 nchild
->parent
= nparent
;
1831 nchild
->prev
= nprev
;
1832 nchild
->next
= nblock
;
1834 nblock
->prev
= nchild
;
1836 nparent
->child
= nchild
;
1838 nprev
->next
= nchild
;
1843 if (mdoc
->meta
.os_e
!= MANDOC_OS_NETBSD
)
1847 for (nchild
= nbody
->child
; nchild
!= NULL
; nchild
= nchild
->next
) {
1848 if (nchild
->tok
!= MDOC_It
)
1850 if ((nnext
= nchild
->head
->child
) == NULL
)
1852 if (nnext
->type
== ROFFT_BLOCK
)
1853 nnext
= nnext
->body
->child
;
1854 if (nnext
== NULL
|| nnext
->tok
!= MDOC_Er
)
1856 nnext
= nnext
->child
;
1857 if (prev_Er
!= NULL
) {
1858 order
= strcmp(prev_Er
, nnext
->string
);
1860 mandoc_vmsg(MANDOCERR_ER_ORDER
,
1861 mdoc
->parse
, nnext
->line
, nnext
->pos
,
1862 "Er %s %s (NetBSD)",
1863 prev_Er
, nnext
->string
);
1864 else if (order
== 0)
1865 mandoc_vmsg(MANDOCERR_ER_REP
,
1866 mdoc
->parse
, nnext
->line
, nnext
->pos
,
1867 "Er %s (NetBSD)", prev_Er
);
1869 prev_Er
= nnext
->string
;
1876 struct roff_node
*n
;
1880 if (n
->type
== ROFFT_BLOCK
&& n
->body
->child
== NULL
) {
1881 mandoc_msg(MANDOCERR_BLK_EMPTY
,
1882 mdoc
->parse
, n
->line
, n
->pos
, "Bk");
1883 roff_node_delete(mdoc
, n
);
1890 struct roff_node
*nch
;
1892 nch
= mdoc
->last
->child
;
1895 mdoc
->flags
^= MDOC_SMOFF
;
1899 assert(nch
->type
== ROFFT_TEXT
);
1901 if ( ! strcmp(nch
->string
, "on")) {
1902 mdoc
->flags
&= ~MDOC_SMOFF
;
1905 if ( ! strcmp(nch
->string
, "off")) {
1906 mdoc
->flags
|= MDOC_SMOFF
;
1910 mandoc_vmsg(MANDOCERR_SM_BAD
,
1911 mdoc
->parse
, nch
->line
, nch
->pos
,
1912 "%s %s", roff_name
[mdoc
->last
->tok
], nch
->string
);
1913 mdoc_node_relink(mdoc
, nch
);
1918 post_root(POST_ARGS
)
1920 const char *openbsd_arch
[] = {
1921 "alpha", "amd64", "arm64", "armv7", "hppa", "i386",
1922 "landisk", "loongson", "luna88k", "macppc", "mips64",
1923 "octeon", "sgi", "socppc", "sparc64", NULL
1925 const char *netbsd_arch
[] = {
1926 "acorn26", "acorn32", "algor", "alpha", "amiga",
1928 "bebox", "cats", "cesfic", "cobalt", "dreamcast",
1929 "emips", "evbarm", "evbmips", "evbppc", "evbsh3", "evbsh5",
1930 "hp300", "hpcarm", "hpcmips", "hpcsh", "hppa",
1931 "i386", "ibmnws", "luna68k",
1932 "mac68k", "macppc", "mipsco", "mmeye", "mvme68k", "mvmeppc",
1933 "netwinder", "news68k", "newsmips", "next68k",
1934 "pc532", "playstation2", "pmax", "pmppc", "prep",
1935 "sandpoint", "sbmips", "sgimips", "shark",
1936 "sparc", "sparc64", "sun2", "sun3",
1937 "vax", "walnut", "x68k", "x86", "x86_64", "xen", NULL
1939 const char **arches
[] = { NULL
, netbsd_arch
, openbsd_arch
};
1941 struct roff_node
*n
;
1944 /* Add missing prologue data. */
1946 if (mdoc
->meta
.date
== NULL
)
1947 mdoc
->meta
.date
= mdoc
->quick
? mandoc_strdup("") :
1948 mandoc_normdate(mdoc
, NULL
, 0, 0);
1950 if (mdoc
->meta
.title
== NULL
) {
1951 mandoc_msg(MANDOCERR_DT_NOTITLE
,
1952 mdoc
->parse
, 0, 0, "EOF");
1953 mdoc
->meta
.title
= mandoc_strdup("UNTITLED");
1956 if (mdoc
->meta
.vol
== NULL
)
1957 mdoc
->meta
.vol
= mandoc_strdup("LOCAL");
1959 if (mdoc
->meta
.os
== NULL
) {
1960 mandoc_msg(MANDOCERR_OS_MISSING
,
1961 mdoc
->parse
, 0, 0, NULL
);
1962 mdoc
->meta
.os
= mandoc_strdup("");
1963 } else if (mdoc
->meta
.os_e
&&
1964 (mdoc
->meta
.rcsids
& (1 << mdoc
->meta
.os_e
)) == 0)
1965 mandoc_msg(MANDOCERR_RCS_MISSING
, mdoc
->parse
, 0, 0,
1966 mdoc
->meta
.os_e
== MANDOC_OS_OPENBSD
?
1967 "(OpenBSD)" : "(NetBSD)");
1969 if (mdoc
->meta
.arch
!= NULL
&&
1970 (arch
= arches
[mdoc
->meta
.os_e
]) != NULL
) {
1971 while (*arch
!= NULL
&& strcmp(*arch
, mdoc
->meta
.arch
))
1973 if (*arch
== NULL
) {
1974 n
= mdoc
->first
->child
;
1975 while (n
->tok
!= MDOC_Dt
||
1977 n
->child
->next
== NULL
||
1978 n
->child
->next
->next
== NULL
)
1980 n
= n
->child
->next
->next
;
1981 mandoc_vmsg(MANDOCERR_ARCH_BAD
,
1982 mdoc
->parse
, n
->line
, n
->pos
,
1983 "Dt ... %s %s", mdoc
->meta
.arch
,
1984 mdoc
->meta
.os_e
== MANDOC_OS_OPENBSD
?
1985 "(OpenBSD)" : "(NetBSD)");
1989 /* Check that we begin with a proper `Sh'. */
1991 n
= mdoc
->first
->child
;
1993 (n
->type
== ROFFT_COMMENT
||
1994 (n
->tok
>= MDOC_Dd
&&
1995 mdoc_macros
[n
->tok
].flags
& MDOC_PROLOGUE
)))
1999 mandoc_msg(MANDOCERR_DOC_EMPTY
, mdoc
->parse
, 0, 0, NULL
);
2000 else if (n
->tok
!= MDOC_Sh
)
2001 mandoc_msg(MANDOCERR_SEC_BEFORE
, mdoc
->parse
,
2002 n
->line
, n
->pos
, roff_name
[n
->tok
]);
2008 struct roff_node
*np
, *nch
, *next
, *prev
;
2013 if (np
->type
!= ROFFT_BODY
)
2016 if (np
->child
== NULL
) {
2017 mandoc_msg(MANDOCERR_RS_EMPTY
, mdoc
->parse
,
2018 np
->line
, np
->pos
, "Rs");
2023 * The full `Rs' block needs special handling to order the
2024 * sub-elements according to `rsord'. Pick through each element
2025 * and correctly order it. This is an insertion sort.
2029 for (nch
= np
->child
->next
; nch
!= NULL
; nch
= next
) {
2030 /* Determine order number of this child. */
2031 for (i
= 0; i
< RSORD_MAX
; i
++)
2032 if (rsord
[i
] == nch
->tok
)
2035 if (i
== RSORD_MAX
) {
2036 mandoc_msg(MANDOCERR_RS_BAD
, mdoc
->parse
,
2037 nch
->line
, nch
->pos
, roff_name
[nch
->tok
]);
2039 } else if (nch
->tok
== MDOC__J
|| nch
->tok
== MDOC__B
)
2040 np
->norm
->Rs
.quote_T
++;
2043 * Remove this child from the chain. This somewhat
2044 * repeats roff_node_unlink(), but since we're
2045 * just re-ordering, there's no need for the
2046 * full unlink process.
2049 if ((next
= nch
->next
) != NULL
)
2050 next
->prev
= nch
->prev
;
2052 if ((prev
= nch
->prev
) != NULL
)
2053 prev
->next
= nch
->next
;
2055 nch
->prev
= nch
->next
= NULL
;
2058 * Scan back until we reach a node that's
2059 * to be ordered before this child.
2062 for ( ; prev
; prev
= prev
->prev
) {
2063 /* Determine order of `prev'. */
2064 for (j
= 0; j
< RSORD_MAX
; j
++)
2065 if (rsord
[j
] == prev
->tok
)
2075 * Set this child back into its correct place
2076 * in front of the `prev' node.
2082 np
->child
->prev
= nch
;
2083 nch
->next
= np
->child
;
2087 prev
->next
->prev
= nch
;
2088 nch
->next
= prev
->next
;
2095 * For some arguments of some macros,
2096 * convert all breakable hyphens into ASCII_HYPH.
2099 post_hyph(POST_ARGS
)
2101 struct roff_node
*nch
;
2104 for (nch
= mdoc
->last
->child
; nch
!= NULL
; nch
= nch
->next
) {
2105 if (nch
->type
!= ROFFT_TEXT
)
2110 while (*(++cp
) != '\0')
2112 isalpha((unsigned char)cp
[-1]) &&
2113 isalpha((unsigned char)cp
[1]))
2121 struct roff_node
*n
;
2124 if (n
->flags
& NODE_LINE
||
2125 (n
->next
!= NULL
&& n
->next
->flags
& NODE_DELIMC
))
2126 mandoc_msg(MANDOCERR_NS_SKIP
, mdoc
->parse
,
2127 n
->line
, n
->pos
, NULL
);
2143 switch (mdoc
->last
->type
) {
2148 switch (mdoc
->lastsec
) {
2153 post_sh_see_also(mdoc
);
2156 post_sh_authors(mdoc
);
2168 post_sh_name(POST_ARGS
)
2170 struct roff_node
*n
;
2175 for (n
= mdoc
->last
->child
; n
!= NULL
; n
= n
->next
) {
2178 if (hasnm
&& n
->child
!= NULL
)
2179 mandoc_vmsg(MANDOCERR_NAMESEC_PUNCT
,
2180 mdoc
->parse
, n
->line
, n
->pos
,
2181 "Nm %s", n
->child
->string
);
2186 if (n
->next
!= NULL
)
2187 mandoc_msg(MANDOCERR_NAMESEC_ND
,
2188 mdoc
->parse
, n
->line
, n
->pos
, NULL
);
2191 if (n
->type
== ROFFT_TEXT
&&
2192 n
->string
[0] == ',' && n
->string
[1] == '\0' &&
2193 n
->next
!= NULL
&& n
->next
->tok
== MDOC_Nm
) {
2199 mandoc_msg(MANDOCERR_NAMESEC_BAD
, mdoc
->parse
,
2200 n
->line
, n
->pos
, roff_name
[n
->tok
]);
2207 mandoc_msg(MANDOCERR_NAMESEC_NONM
, mdoc
->parse
,
2208 mdoc
->last
->line
, mdoc
->last
->pos
, NULL
);
2210 mandoc_msg(MANDOCERR_NAMESEC_NOND
, mdoc
->parse
,
2211 mdoc
->last
->line
, mdoc
->last
->pos
, NULL
);
2215 post_sh_see_also(POST_ARGS
)
2217 const struct roff_node
*n
;
2218 const char *name
, *sec
;
2219 const char *lastname
, *lastsec
, *lastpunct
;
2222 n
= mdoc
->last
->child
;
2223 lastname
= lastsec
= lastpunct
= NULL
;
2225 if (n
->tok
!= MDOC_Xr
||
2227 n
->child
->next
== NULL
)
2230 /* Process one .Xr node. */
2232 name
= n
->child
->string
;
2233 sec
= n
->child
->next
->string
;
2234 if (lastsec
!= NULL
) {
2235 if (lastpunct
[0] != ',' || lastpunct
[1] != '\0')
2236 mandoc_vmsg(MANDOCERR_XR_PUNCT
,
2237 mdoc
->parse
, n
->line
, n
->pos
,
2238 "%s before %s(%s)", lastpunct
,
2240 cmp
= strcmp(lastsec
, sec
);
2242 mandoc_vmsg(MANDOCERR_XR_ORDER
,
2243 mdoc
->parse
, n
->line
, n
->pos
,
2244 "%s(%s) after %s(%s)", name
,
2245 sec
, lastname
, lastsec
);
2246 else if (cmp
== 0 &&
2247 strcasecmp(lastname
, name
) > 0)
2248 mandoc_vmsg(MANDOCERR_XR_ORDER
,
2249 mdoc
->parse
, n
->line
, n
->pos
,
2250 "%s after %s", name
, lastname
);
2255 /* Process the following node. */
2260 if (n
->tok
== MDOC_Xr
) {
2264 if (n
->type
!= ROFFT_TEXT
)
2266 for (name
= n
->string
; *name
!= '\0'; name
++)
2267 if (isalpha((const unsigned char)*name
))
2269 lastpunct
= n
->string
;
2270 if (n
->next
== NULL
|| n
->next
->tok
== MDOC_Rs
)
2271 mandoc_vmsg(MANDOCERR_XR_PUNCT
, mdoc
->parse
,
2272 n
->line
, n
->pos
, "%s after %s(%s)",
2273 lastpunct
, lastname
, lastsec
);
2279 child_an(const struct roff_node
*n
)
2282 for (n
= n
->child
; n
!= NULL
; n
= n
->next
)
2283 if ((n
->tok
== MDOC_An
&& n
->child
!= NULL
) || child_an(n
))
2289 post_sh_authors(POST_ARGS
)
2292 if ( ! child_an(mdoc
->last
))
2293 mandoc_msg(MANDOCERR_AN_MISSING
, mdoc
->parse
,
2294 mdoc
->last
->line
, mdoc
->last
->pos
, NULL
);
2298 * Return an upper bound for the string distance (allowing
2299 * transpositions). Not a full Levenshtein implementation
2300 * because Levenshtein is quadratic in the string length
2301 * and this function is called for every standard name,
2302 * so the check for each custom name would be cubic.
2303 * The following crude heuristics is linear, resulting
2304 * in quadratic behaviour for checking one custom name,
2305 * which does not cause measurable slowdown.
2308 similar(const char *s1
, const char *s2
)
2310 const int maxdist
= 3;
2313 while (s1
[0] != '\0' && s2
[0] != '\0') {
2314 if (s1
[0] == s2
[0]) {
2319 if (++dist
> maxdist
)
2321 if (s1
[1] == s2
[1]) { /* replacement */
2324 } else if (s1
[0] == s2
[1] && s1
[1] == s2
[0]) {
2325 s1
+= 2; /* transposition */
2327 } else if (s1
[0] == s2
[1]) /* insertion */
2329 else if (s1
[1] == s2
[0]) /* deletion */
2334 dist
+= strlen(s1
) + strlen(s2
);
2335 return dist
> maxdist
? INT_MAX
: dist
;
2339 post_sh_head(POST_ARGS
)
2341 struct roff_node
*nch
;
2342 const char *goodsec
;
2343 const char *const *testsec
;
2348 * Process a new section. Sections are either "named" or
2349 * "custom". Custom sections are user-defined, while named ones
2350 * follow a conventional order and may only appear in certain
2354 sec
= mdoc
->last
->sec
;
2356 /* The NAME should be first. */
2358 if (sec
!= SEC_NAME
&& mdoc
->lastnamed
== SEC_NONE
)
2359 mandoc_vmsg(MANDOCERR_NAMESEC_FIRST
, mdoc
->parse
,
2360 mdoc
->last
->line
, mdoc
->last
->pos
, "Sh %s",
2361 sec
!= SEC_CUSTOM
? secnames
[sec
] :
2362 (nch
= mdoc
->last
->child
) == NULL
? "" :
2363 nch
->type
== ROFFT_TEXT
? nch
->string
:
2364 roff_name
[nch
->tok
]);
2366 /* The SYNOPSIS gets special attention in other areas. */
2368 if (sec
== SEC_SYNOPSIS
) {
2369 roff_setreg(mdoc
->roff
, "nS", 1, '=');
2370 mdoc
->flags
|= MDOC_SYNOPSIS
;
2372 roff_setreg(mdoc
->roff
, "nS", 0, '=');
2373 mdoc
->flags
&= ~MDOC_SYNOPSIS
;
2376 /* Mark our last section. */
2378 mdoc
->lastsec
= sec
;
2380 /* We don't care about custom sections after this. */
2382 if (sec
== SEC_CUSTOM
) {
2383 if ((nch
= mdoc
->last
->child
) == NULL
||
2384 nch
->type
!= ROFFT_TEXT
|| nch
->next
!= NULL
)
2388 for (testsec
= secnames
+ 1; *testsec
!= NULL
; testsec
++) {
2389 dist
= similar(nch
->string
, *testsec
);
2390 if (dist
< mindist
) {
2395 if (goodsec
!= NULL
)
2396 mandoc_vmsg(MANDOCERR_SEC_TYPO
, mdoc
->parse
,
2397 nch
->line
, nch
->pos
, "Sh %s instead of %s",
2398 nch
->string
, goodsec
);
2403 * Check whether our non-custom section is being repeated or is
2407 if (sec
== mdoc
->lastnamed
)
2408 mandoc_vmsg(MANDOCERR_SEC_REP
, mdoc
->parse
,
2409 mdoc
->last
->line
, mdoc
->last
->pos
,
2410 "Sh %s", secnames
[sec
]);
2412 if (sec
< mdoc
->lastnamed
)
2413 mandoc_vmsg(MANDOCERR_SEC_ORDER
, mdoc
->parse
,
2414 mdoc
->last
->line
, mdoc
->last
->pos
,
2415 "Sh %s", secnames
[sec
]);
2417 /* Mark the last named section. */
2419 mdoc
->lastnamed
= sec
;
2421 /* Check particular section/manual conventions. */
2423 if (mdoc
->meta
.msec
== NULL
)
2429 if (*mdoc
->meta
.msec
== '4')
2431 goodsec
= "2, 3, 4, 9";
2433 case SEC_RETURN_VALUES
:
2435 if (*mdoc
->meta
.msec
== '2')
2437 if (*mdoc
->meta
.msec
== '3')
2439 if (NULL
== goodsec
)
2440 goodsec
= "2, 3, 9";
2443 if (*mdoc
->meta
.msec
== '9')
2445 if (NULL
== goodsec
)
2447 mandoc_vmsg(MANDOCERR_SEC_MSEC
, mdoc
->parse
,
2448 mdoc
->last
->line
, mdoc
->last
->pos
,
2449 "Sh %s for %s only", secnames
[sec
], goodsec
);
2459 struct roff_node
*n
, *nch
;
2463 if (nch
->next
== NULL
) {
2464 mandoc_vmsg(MANDOCERR_XR_NOSEC
, mdoc
->parse
,
2465 n
->line
, n
->pos
, "Xr %s", nch
->string
);
2467 assert(nch
->next
== n
->last
);
2468 if(mandoc_xr_add(nch
->next
->string
, nch
->string
,
2469 nch
->line
, nch
->pos
))
2470 mandoc_vmsg(MANDOCERR_XR_SELF
, mdoc
->parse
,
2471 nch
->line
, nch
->pos
, "Xr %s %s",
2472 nch
->string
, nch
->next
->string
);
2474 post_delim_nb(mdoc
);
2478 post_ignpar(POST_ARGS
)
2480 struct roff_node
*np
;
2482 switch (mdoc
->last
->type
) {
2496 if ((np
= mdoc
->last
->child
) != NULL
)
2497 if (np
->tok
== MDOC_Pp
|| np
->tok
== MDOC_Lp
) {
2498 mandoc_vmsg(MANDOCERR_PAR_SKIP
,
2499 mdoc
->parse
, np
->line
, np
->pos
,
2500 "%s after %s", roff_name
[np
->tok
],
2501 roff_name
[mdoc
->last
->tok
]);
2502 roff_node_delete(mdoc
, np
);
2505 if ((np
= mdoc
->last
->last
) != NULL
)
2506 if (np
->tok
== MDOC_Pp
|| np
->tok
== MDOC_Lp
) {
2507 mandoc_vmsg(MANDOCERR_PAR_SKIP
, mdoc
->parse
,
2508 np
->line
, np
->pos
, "%s at the end of %s",
2510 roff_name
[mdoc
->last
->tok
]);
2511 roff_node_delete(mdoc
, np
);
2516 post_prevpar(POST_ARGS
)
2518 struct roff_node
*n
;
2521 if (NULL
== n
->prev
)
2523 if (n
->type
!= ROFFT_ELEM
&& n
->type
!= ROFFT_BLOCK
)
2527 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
2528 * block: `Lp', `Pp', or non-compact `Bd' or `Bl'.
2531 if (n
->prev
->tok
!= MDOC_Pp
&&
2532 n
->prev
->tok
!= MDOC_Lp
&&
2533 n
->prev
->tok
!= ROFF_br
)
2535 if (n
->tok
== MDOC_Bl
&& n
->norm
->Bl
.comp
)
2537 if (n
->tok
== MDOC_Bd
&& n
->norm
->Bd
.comp
)
2539 if (n
->tok
== MDOC_It
&& n
->parent
->norm
->Bl
.comp
)
2542 mandoc_vmsg(MANDOCERR_PAR_SKIP
, mdoc
->parse
,
2543 n
->prev
->line
, n
->prev
->pos
, "%s before %s",
2544 roff_name
[n
->prev
->tok
], roff_name
[n
->tok
]);
2545 roff_node_delete(mdoc
, n
->prev
);
2551 struct roff_node
*np
;
2554 if (np
->tok
!= ROFF_br
&& np
->tok
!= ROFF_sp
)
2557 if (np
->tok
== ROFF_sp
) {
2558 if (np
->child
!= NULL
&& np
->child
->next
!= NULL
)
2559 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, mdoc
->parse
,
2560 np
->child
->next
->line
, np
->child
->next
->pos
,
2561 "sp ... %s", np
->child
->next
->string
);
2562 } else if (np
->child
!= NULL
)
2563 mandoc_vmsg(MANDOCERR_ARG_SKIP
,
2564 mdoc
->parse
, np
->line
, np
->pos
, "%s %s",
2565 roff_name
[np
->tok
], np
->child
->string
);
2567 if ((np
= mdoc
->last
->prev
) == NULL
) {
2568 np
= mdoc
->last
->parent
;
2569 if (np
->tok
!= MDOC_Sh
&& np
->tok
!= MDOC_Ss
)
2571 } else if (np
->tok
!= MDOC_Pp
&& np
->tok
!= MDOC_Lp
&&
2572 (mdoc
->last
->tok
!= ROFF_br
||
2573 (np
->tok
!= ROFF_sp
&& np
->tok
!= ROFF_br
)))
2576 mandoc_vmsg(MANDOCERR_PAR_SKIP
, mdoc
->parse
,
2577 mdoc
->last
->line
, mdoc
->last
->pos
, "%s after %s",
2578 roff_name
[mdoc
->last
->tok
], roff_name
[np
->tok
]);
2579 roff_node_delete(mdoc
, mdoc
->last
);
2585 struct roff_node
*n
;
2589 n
->flags
|= NODE_NOPRT
;
2591 if (mdoc
->meta
.date
!= NULL
) {
2592 mandoc_msg(MANDOCERR_PROLOG_REP
, mdoc
->parse
,
2593 n
->line
, n
->pos
, "Dd");
2594 free(mdoc
->meta
.date
);
2595 } else if (mdoc
->flags
& MDOC_PBODY
)
2596 mandoc_msg(MANDOCERR_PROLOG_LATE
, mdoc
->parse
,
2597 n
->line
, n
->pos
, "Dd");
2598 else if (mdoc
->meta
.title
!= NULL
)
2599 mandoc_msg(MANDOCERR_PROLOG_ORDER
, mdoc
->parse
,
2600 n
->line
, n
->pos
, "Dd after Dt");
2601 else if (mdoc
->meta
.os
!= NULL
)
2602 mandoc_msg(MANDOCERR_PROLOG_ORDER
, mdoc
->parse
,
2603 n
->line
, n
->pos
, "Dd after Os");
2605 if (n
->child
== NULL
|| n
->child
->string
[0] == '\0') {
2606 mdoc
->meta
.date
= mdoc
->quick
? mandoc_strdup("") :
2607 mandoc_normdate(mdoc
, NULL
, n
->line
, n
->pos
);
2612 deroff(&datestr
, n
);
2614 mdoc
->meta
.date
= datestr
;
2616 mdoc
->meta
.date
= mandoc_normdate(mdoc
,
2617 datestr
, n
->line
, n
->pos
);
2625 struct roff_node
*nn
, *n
;
2630 n
->flags
|= NODE_NOPRT
;
2632 if (mdoc
->flags
& MDOC_PBODY
) {
2633 mandoc_msg(MANDOCERR_DT_LATE
, mdoc
->parse
,
2634 n
->line
, n
->pos
, "Dt");
2638 if (mdoc
->meta
.title
!= NULL
)
2639 mandoc_msg(MANDOCERR_PROLOG_REP
, mdoc
->parse
,
2640 n
->line
, n
->pos
, "Dt");
2641 else if (mdoc
->meta
.os
!= NULL
)
2642 mandoc_msg(MANDOCERR_PROLOG_ORDER
, mdoc
->parse
,
2643 n
->line
, n
->pos
, "Dt after Os");
2645 free(mdoc
->meta
.title
);
2646 free(mdoc
->meta
.msec
);
2647 free(mdoc
->meta
.vol
);
2648 free(mdoc
->meta
.arch
);
2650 mdoc
->meta
.title
= NULL
;
2651 mdoc
->meta
.msec
= NULL
;
2652 mdoc
->meta
.vol
= NULL
;
2653 mdoc
->meta
.arch
= NULL
;
2655 /* Mandatory first argument: title. */
2658 if (nn
== NULL
|| *nn
->string
== '\0') {
2659 mandoc_msg(MANDOCERR_DT_NOTITLE
,
2660 mdoc
->parse
, n
->line
, n
->pos
, "Dt");
2661 mdoc
->meta
.title
= mandoc_strdup("UNTITLED");
2663 mdoc
->meta
.title
= mandoc_strdup(nn
->string
);
2665 /* Check that all characters are uppercase. */
2667 for (p
= nn
->string
; *p
!= '\0'; p
++)
2668 if (islower((unsigned char)*p
)) {
2669 mandoc_vmsg(MANDOCERR_TITLE_CASE
,
2670 mdoc
->parse
, nn
->line
,
2671 nn
->pos
+ (p
- nn
->string
),
2672 "Dt %s", nn
->string
);
2677 /* Mandatory second argument: section. */
2683 mandoc_vmsg(MANDOCERR_MSEC_MISSING
,
2684 mdoc
->parse
, n
->line
, n
->pos
,
2685 "Dt %s", mdoc
->meta
.title
);
2686 mdoc
->meta
.vol
= mandoc_strdup("LOCAL");
2687 return; /* msec and arch remain NULL. */
2690 mdoc
->meta
.msec
= mandoc_strdup(nn
->string
);
2692 /* Infer volume title from section number. */
2694 cp
= mandoc_a2msec(nn
->string
);
2696 mandoc_vmsg(MANDOCERR_MSEC_BAD
, mdoc
->parse
,
2697 nn
->line
, nn
->pos
, "Dt ... %s", nn
->string
);
2698 mdoc
->meta
.vol
= mandoc_strdup(nn
->string
);
2700 mdoc
->meta
.vol
= mandoc_strdup(cp
);
2702 /* Optional third argument: architecture. */
2704 if ((nn
= nn
->next
) == NULL
)
2707 for (p
= nn
->string
; *p
!= '\0'; p
++)
2708 *p
= tolower((unsigned char)*p
);
2709 mdoc
->meta
.arch
= mandoc_strdup(nn
->string
);
2711 /* Ignore fourth and later arguments. */
2713 if ((nn
= nn
->next
) != NULL
)
2714 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, mdoc
->parse
,
2715 nn
->line
, nn
->pos
, "Dt ... %s", nn
->string
);
2721 struct roff_node
*n
, *nch
;
2724 post_delim_nb(mdoc
);
2730 macro
= !strcmp(nch
->string
, "Open") ? "Ox" :
2731 !strcmp(nch
->string
, "Net") ? "Nx" :
2732 !strcmp(nch
->string
, "Free") ? "Fx" :
2733 !strcmp(nch
->string
, "DragonFly") ? "Dx" : NULL
;
2735 mandoc_msg(MANDOCERR_BX
, mdoc
->parse
,
2736 n
->line
, n
->pos
, macro
);
2739 mdoc
->next
= ROFF_NEXT_SIBLING
;
2740 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Ns
);
2741 mdoc
->last
->flags
|= NODE_NOSRC
;
2742 mdoc
->next
= ROFF_NEXT_SIBLING
;
2744 mdoc
->next
= ROFF_NEXT_CHILD
;
2745 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "BSD");
2746 mdoc
->last
->flags
|= NODE_NOSRC
;
2753 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Ns
);
2754 mdoc
->last
->flags
|= NODE_NOSRC
;
2755 mdoc
->next
= ROFF_NEXT_SIBLING
;
2756 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "-");
2757 mdoc
->last
->flags
|= NODE_NOSRC
;
2758 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Ns
);
2759 mdoc
->last
->flags
|= NODE_NOSRC
;
2763 * Make `Bx's second argument always start with an uppercase
2764 * letter. Groff checks if it's an "accepted" term, but we just
2765 * uppercase blindly.
2768 *nch
->string
= (char)toupper((unsigned char)*nch
->string
);
2775 struct utsname utsname
;
2776 static char *defbuf
;
2778 struct roff_node
*n
;
2781 n
->flags
|= NODE_NOPRT
;
2783 if (mdoc
->meta
.os
!= NULL
)
2784 mandoc_msg(MANDOCERR_PROLOG_REP
, mdoc
->parse
,
2785 n
->line
, n
->pos
, "Os");
2786 else if (mdoc
->flags
& MDOC_PBODY
)
2787 mandoc_msg(MANDOCERR_PROLOG_LATE
, mdoc
->parse
,
2788 n
->line
, n
->pos
, "Os");
2793 * Set the operating system by way of the `Os' macro.
2794 * The order of precedence is:
2795 * 1. the argument of the `Os' macro, unless empty
2796 * 2. the -Ios=foo command line argument, if provided
2797 * 3. -DOSNAME="\"foo\"", if provided during compilation
2798 * 4. "sysname release" from uname(3)
2801 free(mdoc
->meta
.os
);
2802 mdoc
->meta
.os
= NULL
;
2803 deroff(&mdoc
->meta
.os
, n
);
2807 if (mdoc
->os_s
!= NULL
) {
2808 mdoc
->meta
.os
= mandoc_strdup(mdoc
->os_s
);
2813 mdoc
->meta
.os
= mandoc_strdup(OSNAME
);
2815 if (defbuf
== NULL
) {
2816 if (uname(&utsname
) == -1) {
2817 mandoc_msg(MANDOCERR_OS_UNAME
, mdoc
->parse
,
2818 n
->line
, n
->pos
, "Os");
2819 defbuf
= mandoc_strdup("UNKNOWN");
2821 mandoc_asprintf(&defbuf
, "%s %s",
2822 utsname
.sysname
, utsname
.release
);
2824 mdoc
->meta
.os
= mandoc_strdup(defbuf
);
2828 if (mdoc
->meta
.os_e
== MANDOC_OS_OTHER
) {
2829 if (strstr(mdoc
->meta
.os
, "OpenBSD") != NULL
)
2830 mdoc
->meta
.os_e
= MANDOC_OS_OPENBSD
;
2831 else if (strstr(mdoc
->meta
.os
, "NetBSD") != NULL
)
2832 mdoc
->meta
.os_e
= MANDOC_OS_NETBSD
;
2836 * This is the earliest point where we can check
2837 * Mdocdate conventions because we don't know
2838 * the operating system earlier.
2841 if (n
->child
!= NULL
)
2842 mandoc_vmsg(MANDOCERR_OS_ARG
, mdoc
->parse
,
2843 n
->child
->line
, n
->child
->pos
,
2844 "Os %s (%s)", n
->child
->string
,
2845 mdoc
->meta
.os_e
== MANDOC_OS_OPENBSD
?
2846 "OpenBSD" : "NetBSD");
2848 while (n
->tok
!= MDOC_Dd
)
2849 if ((n
= n
->prev
) == NULL
)
2851 if ((n
= n
->child
) == NULL
)
2853 if (strncmp(n
->string
, "$" "Mdocdate", 9)) {
2854 if (mdoc
->meta
.os_e
== MANDOC_OS_OPENBSD
)
2855 mandoc_vmsg(MANDOCERR_MDOCDATE_MISSING
,
2856 mdoc
->parse
, n
->line
, n
->pos
,
2857 "Dd %s (OpenBSD)", n
->string
);
2859 if (mdoc
->meta
.os_e
== MANDOC_OS_NETBSD
)
2860 mandoc_vmsg(MANDOCERR_MDOCDATE
,
2861 mdoc
->parse
, n
->line
, n
->pos
,
2862 "Dd %s (NetBSD)", n
->string
);
2867 mdoc_a2sec(const char *p
)
2871 for (i
= 0; i
< (int)SEC__MAX
; i
++)
2872 if (secnames
[i
] && 0 == strcmp(p
, secnames
[i
]))
2873 return (enum roff_sec
)i
;
2879 macro2len(enum roff_tok macro
)