1 /* $Id: mdoc_validate.c,v 1.360 2018/08/01 16:00:58 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])) ||
443 (cp
[1] != '\0' && cp
[2] != '\0' ?
444 isalpha((unsigned char)cp
[2]) :
446 nn
->type
== ROFFT_TEXT
&&
447 isalpha((unsigned char)*nn
->string
))) {
448 mandoc_msg(MANDOCERR_DASHDASH
, mdoc
->parse
,
449 ln
, pos
+ (int)(cp
- p
) - 1, NULL
);
456 check_toptext(struct roff_man
*mdoc
, int ln
, int pos
, const char *p
)
458 const char *cp
, *cpr
;
463 if ((cp
= strstr(p
, "OpenBSD")) != NULL
)
464 mandoc_msg(MANDOCERR_BX
, mdoc
->parse
,
465 ln
, pos
+ (cp
- p
), "Ox");
466 if ((cp
= strstr(p
, "NetBSD")) != NULL
)
467 mandoc_msg(MANDOCERR_BX
, mdoc
->parse
,
468 ln
, pos
+ (cp
- p
), "Nx");
469 if ((cp
= strstr(p
, "FreeBSD")) != NULL
)
470 mandoc_msg(MANDOCERR_BX
, mdoc
->parse
,
471 ln
, pos
+ (cp
- p
), "Fx");
472 if ((cp
= strstr(p
, "DragonFly")) != NULL
)
473 mandoc_msg(MANDOCERR_BX
, mdoc
->parse
,
474 ln
, pos
+ (cp
- p
), "Dx");
477 while ((cp
= strstr(cp
+ 1, "()")) != NULL
) {
478 for (cpr
= cp
- 1; cpr
>= p
; cpr
--)
479 if (*cpr
!= '_' && !isalnum((unsigned char)*cpr
))
481 if ((cpr
< p
|| *cpr
== ' ') && cpr
+ 1 < cp
) {
483 mandoc_vmsg(MANDOCERR_FUNC
, mdoc
->parse
,
485 "%.*s()", (int)(cp
- cpr
), cpr
);
491 post_delim(POST_ARGS
)
493 const struct roff_node
*nch
;
498 tok
= mdoc
->last
->tok
;
499 nch
= mdoc
->last
->last
;
500 if (nch
== NULL
|| nch
->type
!= ROFFT_TEXT
)
502 lc
= strchr(nch
->string
, '\0') - 1;
503 if (lc
< nch
->string
)
505 delim
= mdoc_isdelim(lc
);
506 if (delim
== DELIM_NONE
|| delim
== DELIM_OPEN
)
508 if (*lc
== ')' && (tok
== MDOC_Nd
|| tok
== MDOC_Sh
||
509 tok
== MDOC_Ss
|| tok
== MDOC_Fo
))
512 mandoc_vmsg(MANDOCERR_DELIM
, mdoc
->parse
,
513 nch
->line
, nch
->pos
+ (lc
- nch
->string
),
514 "%s%s %s", roff_name
[tok
],
515 nch
== mdoc
->last
->child
? "" : " ...", nch
->string
);
519 post_delim_nb(POST_ARGS
)
521 const struct roff_node
*nch
;
528 * Find candidates: at least two bytes,
529 * the last one a closing or middle delimiter.
532 tok
= mdoc
->last
->tok
;
533 nch
= mdoc
->last
->last
;
534 if (nch
== NULL
|| nch
->type
!= ROFFT_TEXT
)
536 lc
= strchr(nch
->string
, '\0') - 1;
537 if (lc
<= nch
->string
)
539 delim
= mdoc_isdelim(lc
);
540 if (delim
== DELIM_NONE
|| delim
== DELIM_OPEN
)
544 * Reduce false positives by allowing various cases.
547 /* Escaped delimiters. */
548 if (lc
> nch
->string
+ 1 && lc
[-2] == '\\' &&
549 (lc
[-1] == '&' || lc
[-1] == 'e'))
552 /* Specific byte sequences. */
555 for (cp
= lc
; cp
>= nch
->string
; cp
--)
560 if (lc
> nch
->string
+ 1 && lc
[-2] == '.' && lc
[-1] == '.')
574 for (cp
= lc
; cp
>= nch
->string
; cp
--)
579 if (lc
== nch
->string
+ 1 && lc
[-1] == '|')
585 /* Exactly two non-alphanumeric bytes. */
586 if (lc
== nch
->string
+ 1 && !isalnum((unsigned char)lc
[-1]))
589 /* At least three alphabetic words with a sentence ending. */
590 if (strchr("!.:?", *lc
) != NULL
&& (tok
== MDOC_Em
||
591 tok
== MDOC_Li
|| tok
== MDOC_Pq
|| tok
== MDOC_Sy
)) {
593 for (cp
= lc
- 1; cp
>= nch
->string
; cp
--) {
596 if (cp
> nch
->string
&& cp
[-1] == ',')
598 } else if (isalpha((unsigned int)*cp
)) {
606 mandoc_vmsg(MANDOCERR_DELIM_NB
, mdoc
->parse
,
607 nch
->line
, nch
->pos
+ (lc
- nch
->string
),
608 "%s%s %s", roff_name
[tok
],
609 nch
== mdoc
->last
->child
? "" : " ...", nch
->string
);
613 post_bl_norm(POST_ARGS
)
616 struct mdoc_argv
*argv
, *wa
;
618 enum mdocargt mdoclt
;
621 n
= mdoc
->last
->parent
;
622 n
->norm
->Bl
.type
= LIST__NONE
;
625 * First figure out which kind of list to use: bind ourselves to
626 * the first mentioned list type and warn about any remaining
627 * ones. If we find no list type, we default to LIST_item.
630 wa
= (n
->args
== NULL
) ? NULL
: n
->args
->argv
;
631 mdoclt
= MDOC_ARG_MAX
;
632 for (i
= 0; n
->args
&& i
< (int)n
->args
->argc
; i
++) {
633 argv
= n
->args
->argv
+ i
;
636 /* Set list types. */
670 /* Set list arguments. */
672 if (n
->norm
->Bl
.comp
)
673 mandoc_msg(MANDOCERR_ARG_REP
,
674 mdoc
->parse
, argv
->line
,
675 argv
->pos
, "Bl -compact");
676 n
->norm
->Bl
.comp
= 1;
681 mandoc_msg(MANDOCERR_ARG_EMPTY
,
682 mdoc
->parse
, argv
->line
,
683 argv
->pos
, "Bl -width");
684 n
->norm
->Bl
.width
= "0n";
687 if (NULL
!= n
->norm
->Bl
.width
)
688 mandoc_vmsg(MANDOCERR_ARG_REP
,
689 mdoc
->parse
, argv
->line
,
690 argv
->pos
, "Bl -width %s",
692 rewrite_macro2len(mdoc
, argv
->value
);
693 n
->norm
->Bl
.width
= argv
->value
[0];
697 mandoc_msg(MANDOCERR_ARG_EMPTY
,
698 mdoc
->parse
, argv
->line
,
699 argv
->pos
, "Bl -offset");
702 if (NULL
!= n
->norm
->Bl
.offs
)
703 mandoc_vmsg(MANDOCERR_ARG_REP
,
704 mdoc
->parse
, argv
->line
,
705 argv
->pos
, "Bl -offset %s",
707 rewrite_macro2len(mdoc
, argv
->value
);
708 n
->norm
->Bl
.offs
= argv
->value
[0];
713 if (LIST__NONE
== lt
)
717 /* Check: multiple list types. */
719 if (LIST__NONE
!= n
->norm
->Bl
.type
) {
720 mandoc_vmsg(MANDOCERR_BL_REP
,
721 mdoc
->parse
, n
->line
, n
->pos
,
722 "Bl -%s", mdoc_argnames
[argv
->arg
]);
726 /* The list type should come first. */
728 if (n
->norm
->Bl
.width
||
731 mandoc_vmsg(MANDOCERR_BL_LATETYPE
,
732 mdoc
->parse
, n
->line
, n
->pos
, "Bl -%s",
733 mdoc_argnames
[n
->args
->argv
[0].arg
]);
735 n
->norm
->Bl
.type
= lt
;
736 if (LIST_column
== lt
) {
737 n
->norm
->Bl
.ncols
= argv
->sz
;
738 n
->norm
->Bl
.cols
= (void *)argv
->value
;
742 /* Allow lists to default to LIST_item. */
744 if (LIST__NONE
== n
->norm
->Bl
.type
) {
745 mandoc_msg(MANDOCERR_BL_NOTYPE
, mdoc
->parse
,
746 n
->line
, n
->pos
, "Bl");
747 n
->norm
->Bl
.type
= LIST_item
;
752 * Validate the width field. Some list types don't need width
753 * types and should be warned about them. Others should have it
754 * and must also be warned. Yet others have a default and need
758 switch (n
->norm
->Bl
.type
) {
760 if (n
->norm
->Bl
.width
== NULL
)
761 mandoc_msg(MANDOCERR_BL_NOWIDTH
, mdoc
->parse
,
762 n
->line
, n
->pos
, "Bl -tag");
769 if (n
->norm
->Bl
.width
!= NULL
)
770 mandoc_vmsg(MANDOCERR_BL_SKIPW
, mdoc
->parse
,
771 wa
->line
, wa
->pos
, "Bl -%s",
772 mdoc_argnames
[mdoclt
]);
773 n
->norm
->Bl
.width
= NULL
;
778 if (n
->norm
->Bl
.width
== NULL
)
779 n
->norm
->Bl
.width
= "2n";
782 if (n
->norm
->Bl
.width
== NULL
)
783 n
->norm
->Bl
.width
= "3n";
794 struct mdoc_argv
*argv
;
799 for (i
= 0; n
->args
&& i
< (int)n
->args
->argc
; i
++) {
800 argv
= n
->args
->argv
+ i
;
820 mandoc_msg(MANDOCERR_BD_FILE
, mdoc
->parse
,
821 n
->line
, n
->pos
, NULL
);
825 mandoc_msg(MANDOCERR_ARG_EMPTY
,
826 mdoc
->parse
, argv
->line
,
827 argv
->pos
, "Bd -offset");
830 if (NULL
!= n
->norm
->Bd
.offs
)
831 mandoc_vmsg(MANDOCERR_ARG_REP
,
832 mdoc
->parse
, argv
->line
,
833 argv
->pos
, "Bd -offset %s",
835 rewrite_macro2len(mdoc
, argv
->value
);
836 n
->norm
->Bd
.offs
= argv
->value
[0];
839 if (n
->norm
->Bd
.comp
)
840 mandoc_msg(MANDOCERR_ARG_REP
,
841 mdoc
->parse
, argv
->line
,
842 argv
->pos
, "Bd -compact");
843 n
->norm
->Bd
.comp
= 1;
848 if (DISP__NONE
== dt
)
851 if (DISP__NONE
== n
->norm
->Bd
.type
)
852 n
->norm
->Bd
.type
= dt
;
854 mandoc_vmsg(MANDOCERR_BD_REP
,
855 mdoc
->parse
, n
->line
, n
->pos
,
856 "Bd -%s", mdoc_argnames
[argv
->arg
]);
859 if (DISP__NONE
== n
->norm
->Bd
.type
) {
860 mandoc_msg(MANDOCERR_BD_NOTYPE
, mdoc
->parse
,
861 n
->line
, n
->pos
, "Bd");
862 n
->norm
->Bd
.type
= DISP_ragged
;
867 * Stand-alone line macros.
871 post_an_norm(POST_ARGS
)
874 struct mdoc_argv
*argv
;
881 for (i
= 1; i
< n
->args
->argc
; i
++) {
882 argv
= n
->args
->argv
+ i
;
883 mandoc_vmsg(MANDOCERR_AN_REP
,
884 mdoc
->parse
, argv
->line
, argv
->pos
,
885 "An -%s", mdoc_argnames
[argv
->arg
]);
888 argv
= n
->args
->argv
;
889 if (argv
->arg
== MDOC_Split
)
890 n
->norm
->An
.auth
= AUTH_split
;
891 else if (argv
->arg
== MDOC_Nosplit
)
892 n
->norm
->An
.auth
= AUTH_nosplit
;
904 if (n
->child
!= NULL
)
905 mandoc_vmsg(MANDOCERR_ARG_SKIP
, mdoc
->parse
, n
->line
,
906 n
->pos
, "%s %s", roff_name
[n
->tok
], n
->child
->string
);
908 while (n
->child
!= NULL
)
909 roff_node_delete(mdoc
, n
->child
);
911 roff_word_alloc(mdoc
, n
->line
, n
->pos
, n
->tok
== MDOC_Bt
?
912 "is currently in beta test." : "currently under development.");
913 mdoc
->last
->flags
|= NODE_EOS
| NODE_NOSRC
;
918 build_list(struct roff_man
*mdoc
, int tok
)
923 n
= mdoc
->last
->next
;
924 for (ic
= 1;; ic
++) {
925 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, tok
);
926 mdoc
->last
->flags
|= NODE_NOSRC
;
927 mdoc_node_relink(mdoc
, n
);
928 n
= mdoc
->last
= mdoc
->last
->parent
;
929 mdoc
->next
= ROFF_NEXT_SIBLING
;
932 if (ic
> 1 || n
->next
->next
!= NULL
) {
933 roff_word_alloc(mdoc
, n
->line
, n
->pos
, ",");
934 mdoc
->last
->flags
|= NODE_DELIMC
| NODE_NOSRC
;
936 n
= mdoc
->last
->next
;
937 if (n
->next
== NULL
) {
938 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "and");
939 mdoc
->last
->flags
|= NODE_NOSRC
;
953 mdoc
->next
= ROFF_NEXT_CHILD
;
954 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "The");
955 mdoc
->last
->flags
|= NODE_NOSRC
;
957 if (mdoc
->last
->next
!= NULL
)
958 ic
= build_list(mdoc
, MDOC_Nm
);
959 else if (mdoc
->meta
.name
!= NULL
) {
960 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Nm
);
961 mdoc
->last
->flags
|= NODE_NOSRC
;
962 roff_word_alloc(mdoc
, n
->line
, n
->pos
, mdoc
->meta
.name
);
963 mdoc
->last
->flags
|= NODE_NOSRC
;
964 mdoc
->last
= mdoc
->last
->parent
;
965 mdoc
->next
= ROFF_NEXT_SIBLING
;
968 mandoc_msg(MANDOCERR_EX_NONAME
, mdoc
->parse
,
969 n
->line
, n
->pos
, "Ex");
973 roff_word_alloc(mdoc
, n
->line
, n
->pos
,
974 ic
> 1 ? "utilities exit\\~0" : "utility exits\\~0");
975 mdoc
->last
->flags
|= NODE_NOSRC
;
976 roff_word_alloc(mdoc
, n
->line
, n
->pos
,
977 "on success, and\\~>0 if an error occurs.");
978 mdoc
->last
->flags
|= NODE_EOS
| NODE_NOSRC
;
991 assert(n
->child
->type
== ROFFT_TEXT
);
992 mdoc
->next
= ROFF_NEXT_CHILD
;
994 if ((p
= mdoc_a2lib(n
->child
->string
)) != NULL
) {
995 n
->child
->flags
|= NODE_NOPRT
;
996 roff_word_alloc(mdoc
, n
->line
, n
->pos
, p
);
997 mdoc
->last
->flags
= NODE_NOSRC
;
1002 mandoc_vmsg(MANDOCERR_LB_BAD
, mdoc
->parse
, n
->child
->line
,
1003 n
->child
->pos
, "Lb %s", n
->child
->string
);
1005 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "library");
1006 mdoc
->last
->flags
= NODE_NOSRC
;
1007 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "\\(lq");
1008 mdoc
->last
->flags
= NODE_DELIMO
| NODE_NOSRC
;
1009 mdoc
->last
= mdoc
->last
->next
;
1010 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "\\(rq");
1011 mdoc
->last
->flags
= NODE_DELIMC
| NODE_NOSRC
;
1018 struct roff_node
*n
;
1024 mdoc
->next
= ROFF_NEXT_CHILD
;
1025 if (n
->child
!= NULL
) {
1026 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "The");
1027 mdoc
->last
->flags
|= NODE_NOSRC
;
1028 ic
= build_list(mdoc
, MDOC_Fn
);
1029 roff_word_alloc(mdoc
, n
->line
, n
->pos
,
1030 ic
> 1 ? "functions return" : "function returns");
1031 mdoc
->last
->flags
|= NODE_NOSRC
;
1032 roff_word_alloc(mdoc
, n
->line
, n
->pos
,
1033 "the value\\~0 if successful;");
1035 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "Upon successful "
1036 "completion, the value\\~0 is returned;");
1037 mdoc
->last
->flags
|= NODE_NOSRC
;
1039 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "otherwise "
1040 "the value\\~\\-1 is returned and the global variable");
1041 mdoc
->last
->flags
|= NODE_NOSRC
;
1042 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Va
);
1043 mdoc
->last
->flags
|= NODE_NOSRC
;
1044 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "errno");
1045 mdoc
->last
->flags
|= NODE_NOSRC
;
1046 mdoc
->last
= mdoc
->last
->parent
;
1047 mdoc
->next
= ROFF_NEXT_SIBLING
;
1048 roff_word_alloc(mdoc
, n
->line
, n
->pos
,
1049 "is set to indicate the error.");
1050 mdoc
->last
->flags
|= NODE_EOS
| NODE_NOSRC
;
1057 struct roff_node
*n
;
1062 if (n
->args
&& n
->args
->argc
== 1)
1063 if (n
->args
->argv
[0].arg
== MDOC_Std
)
1066 mandoc_msg(MANDOCERR_ARG_STD
, mdoc
->parse
,
1067 n
->line
, n
->pos
, roff_name
[n
->tok
]);
1073 struct roff_node
*n
, *nch
;
1078 assert(nch
->type
== ROFFT_TEXT
);
1080 if ((p
= mdoc_a2st(nch
->string
)) == NULL
) {
1081 mandoc_vmsg(MANDOCERR_ST_BAD
, mdoc
->parse
,
1082 nch
->line
, nch
->pos
, "St %s", nch
->string
);
1083 roff_node_delete(mdoc
, n
);
1087 nch
->flags
|= NODE_NOPRT
;
1088 mdoc
->next
= ROFF_NEXT_CHILD
;
1089 roff_word_alloc(mdoc
, nch
->line
, nch
->pos
, p
);
1090 mdoc
->last
->flags
|= NODE_NOSRC
;
1095 post_obsolete(POST_ARGS
)
1097 struct roff_node
*n
;
1100 if (n
->type
== ROFFT_ELEM
|| n
->type
== ROFFT_BLOCK
)
1101 mandoc_msg(MANDOCERR_MACRO_OBS
, mdoc
->parse
,
1102 n
->line
, n
->pos
, roff_name
[n
->tok
]);
1106 post_useless(POST_ARGS
)
1108 struct roff_node
*n
;
1111 mandoc_msg(MANDOCERR_MACRO_USELESS
, mdoc
->parse
,
1112 n
->line
, n
->pos
, roff_name
[n
->tok
]);
1122 struct roff_node
*np
, *nch
;
1125 * Unlike other data pointers, these are "housed" by the HEAD
1126 * element, which contains the goods.
1130 if (np
->type
!= ROFFT_HEAD
)
1133 assert(np
->parent
->type
== ROFFT_BLOCK
);
1134 assert(np
->parent
->tok
== MDOC_Bf
);
1136 /* Check the number of arguments. */
1139 if (np
->parent
->args
== NULL
) {
1141 mandoc_msg(MANDOCERR_BF_NOFONT
, mdoc
->parse
,
1142 np
->line
, np
->pos
, "Bf");
1148 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, mdoc
->parse
,
1149 nch
->line
, nch
->pos
, "Bf ... %s", nch
->string
);
1151 /* Extract argument into data. */
1153 if (np
->parent
->args
!= NULL
) {
1154 switch (np
->parent
->args
->argv
[0].arg
) {
1156 np
->norm
->Bf
.font
= FONT_Em
;
1159 np
->norm
->Bf
.font
= FONT_Li
;
1162 np
->norm
->Bf
.font
= FONT_Sy
;
1170 /* Extract parameter into data. */
1172 if ( ! strcmp(np
->child
->string
, "Em"))
1173 np
->norm
->Bf
.font
= FONT_Em
;
1174 else if ( ! strcmp(np
->child
->string
, "Li"))
1175 np
->norm
->Bf
.font
= FONT_Li
;
1176 else if ( ! strcmp(np
->child
->string
, "Sy"))
1177 np
->norm
->Bf
.font
= FONT_Sy
;
1179 mandoc_vmsg(MANDOCERR_BF_BADFONT
, mdoc
->parse
,
1180 np
->child
->line
, np
->child
->pos
,
1181 "Bf %s", np
->child
->string
);
1185 post_fname(POST_ARGS
)
1187 const struct roff_node
*n
;
1191 n
= mdoc
->last
->child
;
1192 pos
= strcspn(n
->string
, "()");
1193 cp
= n
->string
+ pos
;
1194 if ( ! (cp
[0] == '\0' || (cp
[0] == '(' && cp
[1] == '*')))
1195 mandoc_msg(MANDOCERR_FN_PAREN
, mdoc
->parse
,
1196 n
->line
, n
->pos
+ pos
, n
->string
);
1210 const struct roff_node
*n
;
1214 if (n
->type
!= ROFFT_HEAD
)
1217 if (n
->child
== NULL
) {
1218 mandoc_msg(MANDOCERR_FO_NOHEAD
, mdoc
->parse
,
1219 n
->line
, n
->pos
, "Fo");
1222 if (n
->child
!= n
->last
) {
1223 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, mdoc
->parse
,
1224 n
->child
->next
->line
, n
->child
->next
->pos
,
1225 "Fo ... %s", n
->child
->next
->string
);
1226 while (n
->child
!= n
->last
)
1227 roff_node_delete(mdoc
, n
->last
);
1237 const struct roff_node
*n
;
1240 for (n
= mdoc
->last
->child
; n
!= NULL
; n
= n
->next
) {
1241 for (cp
= n
->string
; *cp
!= '\0'; cp
++) {
1242 /* Ignore callbacks and alterations. */
1243 if (*cp
== '(' || *cp
== '{')
1247 mandoc_msg(MANDOCERR_FA_COMMA
, mdoc
->parse
,
1248 n
->line
, n
->pos
+ (cp
- n
->string
),
1253 post_delim_nb(mdoc
);
1259 struct roff_node
*n
;
1263 if (n
->sec
== SEC_NAME
&& n
->child
!= NULL
&&
1264 n
->child
->type
== ROFFT_TEXT
&& mdoc
->meta
.msec
!= NULL
)
1265 mandoc_xr_add(mdoc
->meta
.msec
, n
->child
->string
, -1, -1);
1267 if (n
->last
!= NULL
&&
1268 (n
->last
->tok
== MDOC_Pp
||
1269 n
->last
->tok
== MDOC_Lp
))
1270 mdoc_node_relink(mdoc
, n
->last
);
1272 if (mdoc
->meta
.name
== NULL
)
1273 deroff(&mdoc
->meta
.name
, n
);
1275 if (mdoc
->meta
.name
== NULL
||
1276 (mdoc
->lastsec
== SEC_NAME
&& n
->child
== NULL
))
1277 mandoc_msg(MANDOCERR_NM_NONAME
, mdoc
->parse
,
1278 n
->line
, n
->pos
, "Nm");
1282 post_delim_nb(mdoc
);
1291 if ((n
->child
!= NULL
&& n
->child
->type
== ROFFT_TEXT
) ||
1292 mdoc
->meta
.name
== NULL
)
1295 mdoc
->next
= ROFF_NEXT_CHILD
;
1296 roff_word_alloc(mdoc
, n
->line
, n
->pos
, mdoc
->meta
.name
);
1297 mdoc
->last
->flags
|= NODE_NOSRC
;
1304 struct roff_node
*n
;
1308 if (n
->type
!= ROFFT_BODY
)
1311 if (n
->sec
!= SEC_NAME
)
1312 mandoc_msg(MANDOCERR_ND_LATE
, mdoc
->parse
,
1313 n
->line
, n
->pos
, "Nd");
1315 if (n
->child
== NULL
)
1316 mandoc_msg(MANDOCERR_ND_EMPTY
, mdoc
->parse
,
1317 n
->line
, n
->pos
, "Nd");
1325 post_display(POST_ARGS
)
1327 struct roff_node
*n
, *np
;
1332 if (n
->end
!= ENDBODY_NOT
) {
1333 if (n
->tok
== MDOC_Bd
&&
1334 n
->body
->parent
->args
== NULL
)
1335 roff_node_delete(mdoc
, n
);
1336 } else if (n
->child
== NULL
)
1337 mandoc_msg(MANDOCERR_BLK_EMPTY
, mdoc
->parse
,
1338 n
->line
, n
->pos
, roff_name
[n
->tok
]);
1339 else if (n
->tok
== MDOC_D1
)
1343 if (n
->tok
== MDOC_Bd
) {
1344 if (n
->args
== NULL
) {
1345 mandoc_msg(MANDOCERR_BD_NOARG
,
1346 mdoc
->parse
, n
->line
, n
->pos
, "Bd");
1347 mdoc
->next
= ROFF_NEXT_SIBLING
;
1348 while (n
->body
->child
!= NULL
)
1349 mdoc_node_relink(mdoc
,
1351 roff_node_delete(mdoc
, n
);
1357 for (np
= n
->parent
; np
!= NULL
; np
= np
->parent
) {
1358 if (np
->type
== ROFFT_BLOCK
&& np
->tok
== MDOC_Bd
) {
1359 mandoc_vmsg(MANDOCERR_BD_NEST
,
1360 mdoc
->parse
, n
->line
, n
->pos
,
1361 "%s in Bd", roff_name
[n
->tok
]);
1372 post_defaults(POST_ARGS
)
1374 struct roff_node
*nn
;
1376 if (mdoc
->last
->child
!= NULL
) {
1377 post_delim_nb(mdoc
);
1382 * The `Ar' defaults to "file ..." if no value is provided as an
1383 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
1384 * gets an empty string.
1390 mdoc
->next
= ROFF_NEXT_CHILD
;
1391 roff_word_alloc(mdoc
, nn
->line
, nn
->pos
, "file");
1392 mdoc
->last
->flags
|= NODE_NOSRC
;
1393 roff_word_alloc(mdoc
, nn
->line
, nn
->pos
, "...");
1394 mdoc
->last
->flags
|= NODE_NOSRC
;
1398 mdoc
->next
= ROFF_NEXT_CHILD
;
1399 roff_word_alloc(mdoc
, nn
->line
, nn
->pos
, "~");
1400 mdoc
->last
->flags
|= NODE_NOSRC
;
1411 struct roff_node
*n
, *nch
;
1418 * If we have a child, look it up in the standard keys. If a
1419 * key exist, use that instead of the child; if it doesn't,
1420 * prefix "AT&T UNIX " to the existing data.
1424 if (nch
!= NULL
&& ((att
= mdoc_a2att(nch
->string
)) == NULL
))
1425 mandoc_vmsg(MANDOCERR_AT_BAD
, mdoc
->parse
,
1426 nch
->line
, nch
->pos
, "At %s", nch
->string
);
1428 mdoc
->next
= ROFF_NEXT_CHILD
;
1430 roff_word_alloc(mdoc
, nch
->line
, nch
->pos
, att
);
1431 nch
->flags
|= NODE_NOPRT
;
1433 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "AT&T UNIX");
1434 mdoc
->last
->flags
|= NODE_NOSRC
;
1441 struct roff_node
*np
, *nch
;
1447 if (np
->norm
->An
.auth
== AUTH__NONE
) {
1449 mandoc_msg(MANDOCERR_MACRO_EMPTY
, mdoc
->parse
,
1450 np
->line
, np
->pos
, "An");
1452 post_delim_nb(mdoc
);
1453 } else if (nch
!= NULL
)
1454 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, mdoc
->parse
,
1455 nch
->line
, nch
->pos
, "An ... %s", nch
->string
);
1462 post_obsolete(mdoc
);
1463 if (mdoc
->last
->type
== ROFFT_BLOCK
)
1464 mdoc
->last
->norm
->Es
= mdoc
->last_es
;
1471 post_obsolete(mdoc
);
1472 mdoc
->last_es
= mdoc
->last
;
1478 struct roff_node
*n
;
1482 post_delim_nb(mdoc
);
1497 if (n
->child
== NULL
)
1499 v
= n
->child
->string
;
1500 if ((v
[0] != '0' && v
[0] != '1') || v
[1] != '.' ||
1501 v
[2] < '0' || v
[2] > '9' ||
1502 v
[3] < 'a' || v
[3] > 'z' || v
[4] != '\0')
1504 n
->child
->flags
|= NODE_NOPRT
;
1505 mdoc
->next
= ROFF_NEXT_CHILD
;
1506 roff_word_alloc(mdoc
, n
->child
->line
, n
->child
->pos
, v
);
1507 v
= mdoc
->last
->string
;
1508 v
[3] = toupper((unsigned char)v
[3]);
1509 mdoc
->last
->flags
|= NODE_NOSRC
;
1521 mdoc
->next
= ROFF_NEXT_CHILD
;
1522 roff_word_alloc(mdoc
, n
->line
, n
->pos
, os
);
1523 mdoc
->last
->flags
|= NODE_NOSRC
;
1530 struct roff_node
*nbl
, *nit
, *nch
;
1537 if (nit
->type
!= ROFFT_BLOCK
)
1540 nbl
= nit
->parent
->parent
;
1541 lt
= nbl
->norm
->Bl
.type
;
1549 if (nit
->head
->child
== NULL
)
1550 mandoc_vmsg(MANDOCERR_IT_NOHEAD
,
1551 mdoc
->parse
, nit
->line
, nit
->pos
,
1553 mdoc_argnames
[nbl
->args
->argv
[0].arg
]);
1559 if (nit
->body
== NULL
|| nit
->body
->child
== NULL
)
1560 mandoc_vmsg(MANDOCERR_IT_NOBODY
,
1561 mdoc
->parse
, nit
->line
, nit
->pos
,
1563 mdoc_argnames
[nbl
->args
->argv
[0].arg
]);
1566 if ((nch
= nit
->head
->child
) != NULL
)
1567 mandoc_vmsg(MANDOCERR_ARG_SKIP
, mdoc
->parse
,
1568 nit
->line
, nit
->pos
, "It %s",
1569 nch
->string
== NULL
? roff_name
[nch
->tok
] :
1573 cols
= (int)nbl
->norm
->Bl
.ncols
;
1575 assert(nit
->head
->child
== NULL
);
1577 if (nit
->head
->next
->child
== NULL
&&
1578 nit
->head
->next
->next
== NULL
) {
1579 mandoc_msg(MANDOCERR_MACRO_EMPTY
, mdoc
->parse
,
1580 nit
->line
, nit
->pos
, "It");
1581 roff_node_delete(mdoc
, nit
);
1586 for (nch
= nit
->child
; nch
!= NULL
; nch
= nch
->next
) {
1587 if (nch
->type
!= ROFFT_BODY
)
1589 if (i
++ && nch
->flags
& NODE_LINE
)
1590 mandoc_msg(MANDOCERR_TA_LINE
, mdoc
->parse
,
1591 nch
->line
, nch
->pos
, "Ta");
1593 if (i
< cols
|| i
> cols
+ 1)
1594 mandoc_vmsg(MANDOCERR_BL_COL
,
1595 mdoc
->parse
, nit
->line
, nit
->pos
,
1596 "%d columns, %d cells", cols
, i
);
1597 else if (nit
->head
->next
->child
!= NULL
&&
1598 nit
->head
->next
->child
->line
> nit
->line
)
1599 mandoc_msg(MANDOCERR_IT_NOARG
, mdoc
->parse
,
1600 nit
->line
, nit
->pos
, "Bl -column It");
1608 post_bl_block(POST_ARGS
)
1610 struct roff_node
*n
, *ni
, *nc
;
1615 for (ni
= n
->body
->child
; ni
!= NULL
; ni
= ni
->next
) {
1616 if (ni
->body
== NULL
)
1618 nc
= ni
->body
->last
;
1619 while (nc
!= NULL
) {
1629 if (ni
->next
== NULL
) {
1630 mandoc_msg(MANDOCERR_PAR_MOVE
,
1631 mdoc
->parse
, nc
->line
, nc
->pos
,
1632 roff_name
[nc
->tok
]);
1633 mdoc_node_relink(mdoc
, nc
);
1634 } else if (n
->norm
->Bl
.comp
== 0 &&
1635 n
->norm
->Bl
.type
!= LIST_column
) {
1636 mandoc_vmsg(MANDOCERR_PAR_SKIP
,
1637 mdoc
->parse
, nc
->line
, nc
->pos
,
1638 "%s before It", roff_name
[nc
->tok
]);
1639 roff_node_delete(mdoc
, nc
);
1642 nc
= ni
->body
->last
;
1648 * If the argument of -offset or -width is a macro,
1649 * replace it with the associated default width.
1652 rewrite_macro2len(struct roff_man
*mdoc
, char **arg
)
1659 else if ( ! strcmp(*arg
, "Ds"))
1661 else if ((tok
= roffhash_find(mdoc
->mdocmac
, *arg
, 0)) == TOKEN_NONE
)
1664 width
= macro2len(tok
);
1667 mandoc_asprintf(arg
, "%zun", width
);
1671 post_bl_head(POST_ARGS
)
1673 struct roff_node
*nbl
, *nh
, *nch
, *nnext
;
1674 struct mdoc_argv
*argv
;
1680 if (nh
->norm
->Bl
.type
!= LIST_column
) {
1681 if ((nch
= nh
->child
) == NULL
)
1683 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, mdoc
->parse
,
1684 nch
->line
, nch
->pos
, "Bl ... %s", nch
->string
);
1685 while (nch
!= NULL
) {
1686 roff_node_delete(mdoc
, nch
);
1693 * Append old-style lists, where the column width specifiers
1694 * trail as macro parameters, to the new-style ("normal-form")
1695 * lists where they're argument values following -column.
1698 if (nh
->child
== NULL
)
1702 for (j
= 0; j
< (int)nbl
->args
->argc
; j
++)
1703 if (nbl
->args
->argv
[j
].arg
== MDOC_Column
)
1706 assert(j
< (int)nbl
->args
->argc
);
1709 * Accommodate for new-style groff column syntax. Shuffle the
1710 * child nodes, all of which must be TEXT, as arguments for the
1711 * column field. Then, delete the head children.
1714 argv
= nbl
->args
->argv
+ j
;
1716 for (nch
= nh
->child
; nch
!= NULL
; nch
= nch
->next
)
1718 argv
->value
= mandoc_reallocarray(argv
->value
,
1719 argv
->sz
, sizeof(char *));
1721 nh
->norm
->Bl
.ncols
= argv
->sz
;
1722 nh
->norm
->Bl
.cols
= (void *)argv
->value
;
1724 for (nch
= nh
->child
; nch
!= NULL
; nch
= nnext
) {
1725 argv
->value
[i
++] = nch
->string
;
1728 roff_node_delete(NULL
, nch
);
1736 struct roff_node
*nparent
, *nprev
; /* of the Bl block */
1737 struct roff_node
*nblock
, *nbody
; /* of the Bl */
1738 struct roff_node
*nchild
, *nnext
; /* of the Bl body */
1739 const char *prev_Er
;
1743 switch (nbody
->type
) {
1745 post_bl_block(mdoc
);
1755 if (nbody
->end
!= ENDBODY_NOT
)
1758 nchild
= nbody
->child
;
1759 if (nchild
== NULL
) {
1760 mandoc_msg(MANDOCERR_BLK_EMPTY
, mdoc
->parse
,
1761 nbody
->line
, nbody
->pos
, "Bl");
1764 while (nchild
!= NULL
) {
1765 nnext
= nchild
->next
;
1766 if (nchild
->tok
== MDOC_It
||
1767 (nchild
->tok
== MDOC_Sm
&&
1768 nnext
!= NULL
&& nnext
->tok
== MDOC_It
)) {
1774 * In .Bl -column, the first rows may be implicit,
1775 * that is, they may not start with .It macros.
1776 * Such rows may be followed by nodes generated on the
1777 * roff level, for example .TS, which cannot be moved
1778 * out of the list. In that case, wrap such roff nodes
1779 * into an implicit row.
1782 if (nchild
->prev
!= NULL
) {
1783 mdoc
->last
= nchild
;
1784 mdoc
->next
= ROFF_NEXT_SIBLING
;
1785 roff_block_alloc(mdoc
, nchild
->line
,
1786 nchild
->pos
, MDOC_It
);
1787 roff_head_alloc(mdoc
, nchild
->line
,
1788 nchild
->pos
, MDOC_It
);
1789 mdoc
->next
= ROFF_NEXT_SIBLING
;
1790 roff_body_alloc(mdoc
, nchild
->line
,
1791 nchild
->pos
, MDOC_It
);
1792 while (nchild
->tok
!= MDOC_It
) {
1793 mdoc_node_relink(mdoc
, nchild
);
1794 if ((nchild
= nnext
) == NULL
)
1796 nnext
= nchild
->next
;
1797 mdoc
->next
= ROFF_NEXT_SIBLING
;
1803 mandoc_msg(MANDOCERR_BL_MOVE
, mdoc
->parse
,
1804 nchild
->line
, nchild
->pos
, roff_name
[nchild
->tok
]);
1807 * Move the node out of the Bl block.
1808 * First, collect all required node pointers.
1811 nblock
= nbody
->parent
;
1812 nprev
= nblock
->prev
;
1813 nparent
= nblock
->parent
;
1816 * Unlink this child.
1819 nbody
->child
= nnext
;
1826 * Relink this child.
1829 nchild
->parent
= nparent
;
1830 nchild
->prev
= nprev
;
1831 nchild
->next
= nblock
;
1833 nblock
->prev
= nchild
;
1835 nparent
->child
= nchild
;
1837 nprev
->next
= nchild
;
1842 if (mdoc
->meta
.os_e
!= MANDOC_OS_NETBSD
)
1846 for (nchild
= nbody
->child
; nchild
!= NULL
; nchild
= nchild
->next
) {
1847 if (nchild
->tok
!= MDOC_It
)
1849 if ((nnext
= nchild
->head
->child
) == NULL
)
1851 if (nnext
->type
== ROFFT_BLOCK
)
1852 nnext
= nnext
->body
->child
;
1853 if (nnext
== NULL
|| nnext
->tok
!= MDOC_Er
)
1855 nnext
= nnext
->child
;
1856 if (prev_Er
!= NULL
) {
1857 order
= strcmp(prev_Er
, nnext
->string
);
1859 mandoc_vmsg(MANDOCERR_ER_ORDER
,
1860 mdoc
->parse
, nnext
->line
, nnext
->pos
,
1861 "Er %s %s (NetBSD)",
1862 prev_Er
, nnext
->string
);
1863 else if (order
== 0)
1864 mandoc_vmsg(MANDOCERR_ER_REP
,
1865 mdoc
->parse
, nnext
->line
, nnext
->pos
,
1866 "Er %s (NetBSD)", prev_Er
);
1868 prev_Er
= nnext
->string
;
1875 struct roff_node
*n
;
1879 if (n
->type
== ROFFT_BLOCK
&& n
->body
->child
== NULL
) {
1880 mandoc_msg(MANDOCERR_BLK_EMPTY
,
1881 mdoc
->parse
, n
->line
, n
->pos
, "Bk");
1882 roff_node_delete(mdoc
, n
);
1889 struct roff_node
*nch
;
1891 nch
= mdoc
->last
->child
;
1894 mdoc
->flags
^= MDOC_SMOFF
;
1898 assert(nch
->type
== ROFFT_TEXT
);
1900 if ( ! strcmp(nch
->string
, "on")) {
1901 mdoc
->flags
&= ~MDOC_SMOFF
;
1904 if ( ! strcmp(nch
->string
, "off")) {
1905 mdoc
->flags
|= MDOC_SMOFF
;
1909 mandoc_vmsg(MANDOCERR_SM_BAD
,
1910 mdoc
->parse
, nch
->line
, nch
->pos
,
1911 "%s %s", roff_name
[mdoc
->last
->tok
], nch
->string
);
1912 mdoc_node_relink(mdoc
, nch
);
1917 post_root(POST_ARGS
)
1919 const char *openbsd_arch
[] = {
1920 "alpha", "amd64", "arm64", "armv7", "hppa", "i386",
1921 "landisk", "loongson", "luna88k", "macppc", "mips64",
1922 "octeon", "sgi", "socppc", "sparc64", NULL
1924 const char *netbsd_arch
[] = {
1925 "acorn26", "acorn32", "algor", "alpha", "amiga",
1927 "bebox", "cats", "cesfic", "cobalt", "dreamcast",
1928 "emips", "evbarm", "evbmips", "evbppc", "evbsh3", "evbsh5",
1929 "hp300", "hpcarm", "hpcmips", "hpcsh", "hppa",
1930 "i386", "ibmnws", "luna68k",
1931 "mac68k", "macppc", "mipsco", "mmeye", "mvme68k", "mvmeppc",
1932 "netwinder", "news68k", "newsmips", "next68k",
1933 "pc532", "playstation2", "pmax", "pmppc", "prep",
1934 "sandpoint", "sbmips", "sgimips", "shark",
1935 "sparc", "sparc64", "sun2", "sun3",
1936 "vax", "walnut", "x68k", "x86", "x86_64", "xen", NULL
1938 const char **arches
[] = { NULL
, netbsd_arch
, openbsd_arch
};
1940 struct roff_node
*n
;
1943 /* Add missing prologue data. */
1945 if (mdoc
->meta
.date
== NULL
)
1946 mdoc
->meta
.date
= mdoc
->quick
? mandoc_strdup("") :
1947 mandoc_normdate(mdoc
, NULL
, 0, 0);
1949 if (mdoc
->meta
.title
== NULL
) {
1950 mandoc_msg(MANDOCERR_DT_NOTITLE
,
1951 mdoc
->parse
, 0, 0, "EOF");
1952 mdoc
->meta
.title
= mandoc_strdup("UNTITLED");
1955 if (mdoc
->meta
.vol
== NULL
)
1956 mdoc
->meta
.vol
= mandoc_strdup("LOCAL");
1958 if (mdoc
->meta
.os
== NULL
) {
1959 mandoc_msg(MANDOCERR_OS_MISSING
,
1960 mdoc
->parse
, 0, 0, NULL
);
1961 mdoc
->meta
.os
= mandoc_strdup("");
1962 } else if (mdoc
->meta
.os_e
&&
1963 (mdoc
->meta
.rcsids
& (1 << mdoc
->meta
.os_e
)) == 0)
1964 mandoc_msg(MANDOCERR_RCS_MISSING
, mdoc
->parse
, 0, 0,
1965 mdoc
->meta
.os_e
== MANDOC_OS_OPENBSD
?
1966 "(OpenBSD)" : "(NetBSD)");
1968 if (mdoc
->meta
.arch
!= NULL
&&
1969 (arch
= arches
[mdoc
->meta
.os_e
]) != NULL
) {
1970 while (*arch
!= NULL
&& strcmp(*arch
, mdoc
->meta
.arch
))
1972 if (*arch
== NULL
) {
1973 n
= mdoc
->first
->child
;
1974 while (n
->tok
!= MDOC_Dt
||
1976 n
->child
->next
== NULL
||
1977 n
->child
->next
->next
== NULL
)
1979 n
= n
->child
->next
->next
;
1980 mandoc_vmsg(MANDOCERR_ARCH_BAD
,
1981 mdoc
->parse
, n
->line
, n
->pos
,
1982 "Dt ... %s %s", mdoc
->meta
.arch
,
1983 mdoc
->meta
.os_e
== MANDOC_OS_OPENBSD
?
1984 "(OpenBSD)" : "(NetBSD)");
1988 /* Check that we begin with a proper `Sh'. */
1990 n
= mdoc
->first
->child
;
1992 (n
->type
== ROFFT_COMMENT
||
1993 (n
->tok
>= MDOC_Dd
&&
1994 mdoc_macros
[n
->tok
].flags
& MDOC_PROLOGUE
)))
1998 mandoc_msg(MANDOCERR_DOC_EMPTY
, mdoc
->parse
, 0, 0, NULL
);
1999 else if (n
->tok
!= MDOC_Sh
)
2000 mandoc_msg(MANDOCERR_SEC_BEFORE
, mdoc
->parse
,
2001 n
->line
, n
->pos
, roff_name
[n
->tok
]);
2007 struct roff_node
*np
, *nch
, *next
, *prev
;
2012 if (np
->type
!= ROFFT_BODY
)
2015 if (np
->child
== NULL
) {
2016 mandoc_msg(MANDOCERR_RS_EMPTY
, mdoc
->parse
,
2017 np
->line
, np
->pos
, "Rs");
2022 * The full `Rs' block needs special handling to order the
2023 * sub-elements according to `rsord'. Pick through each element
2024 * and correctly order it. This is an insertion sort.
2028 for (nch
= np
->child
->next
; nch
!= NULL
; nch
= next
) {
2029 /* Determine order number of this child. */
2030 for (i
= 0; i
< RSORD_MAX
; i
++)
2031 if (rsord
[i
] == nch
->tok
)
2034 if (i
== RSORD_MAX
) {
2035 mandoc_msg(MANDOCERR_RS_BAD
, mdoc
->parse
,
2036 nch
->line
, nch
->pos
, roff_name
[nch
->tok
]);
2038 } else if (nch
->tok
== MDOC__J
|| nch
->tok
== MDOC__B
)
2039 np
->norm
->Rs
.quote_T
++;
2042 * Remove this child from the chain. This somewhat
2043 * repeats roff_node_unlink(), but since we're
2044 * just re-ordering, there's no need for the
2045 * full unlink process.
2048 if ((next
= nch
->next
) != NULL
)
2049 next
->prev
= nch
->prev
;
2051 if ((prev
= nch
->prev
) != NULL
)
2052 prev
->next
= nch
->next
;
2054 nch
->prev
= nch
->next
= NULL
;
2057 * Scan back until we reach a node that's
2058 * to be ordered before this child.
2061 for ( ; prev
; prev
= prev
->prev
) {
2062 /* Determine order of `prev'. */
2063 for (j
= 0; j
< RSORD_MAX
; j
++)
2064 if (rsord
[j
] == prev
->tok
)
2074 * Set this child back into its correct place
2075 * in front of the `prev' node.
2081 np
->child
->prev
= nch
;
2082 nch
->next
= np
->child
;
2086 prev
->next
->prev
= nch
;
2087 nch
->next
= prev
->next
;
2094 * For some arguments of some macros,
2095 * convert all breakable hyphens into ASCII_HYPH.
2098 post_hyph(POST_ARGS
)
2100 struct roff_node
*nch
;
2103 for (nch
= mdoc
->last
->child
; nch
!= NULL
; nch
= nch
->next
) {
2104 if (nch
->type
!= ROFFT_TEXT
)
2109 while (*(++cp
) != '\0')
2111 isalpha((unsigned char)cp
[-1]) &&
2112 isalpha((unsigned char)cp
[1]))
2120 struct roff_node
*n
;
2123 if (n
->flags
& NODE_LINE
||
2124 (n
->next
!= NULL
&& n
->next
->flags
& NODE_DELIMC
))
2125 mandoc_msg(MANDOCERR_NS_SKIP
, mdoc
->parse
,
2126 n
->line
, n
->pos
, NULL
);
2142 switch (mdoc
->last
->type
) {
2147 switch (mdoc
->lastsec
) {
2152 post_sh_see_also(mdoc
);
2155 post_sh_authors(mdoc
);
2167 post_sh_name(POST_ARGS
)
2169 struct roff_node
*n
;
2174 for (n
= mdoc
->last
->child
; n
!= NULL
; n
= n
->next
) {
2177 if (hasnm
&& n
->child
!= NULL
)
2178 mandoc_vmsg(MANDOCERR_NAMESEC_PUNCT
,
2179 mdoc
->parse
, n
->line
, n
->pos
,
2180 "Nm %s", n
->child
->string
);
2185 if (n
->next
!= NULL
)
2186 mandoc_msg(MANDOCERR_NAMESEC_ND
,
2187 mdoc
->parse
, n
->line
, n
->pos
, NULL
);
2190 if (n
->type
== ROFFT_TEXT
&&
2191 n
->string
[0] == ',' && n
->string
[1] == '\0' &&
2192 n
->next
!= NULL
&& n
->next
->tok
== MDOC_Nm
) {
2198 mandoc_msg(MANDOCERR_NAMESEC_BAD
, mdoc
->parse
,
2199 n
->line
, n
->pos
, roff_name
[n
->tok
]);
2206 mandoc_msg(MANDOCERR_NAMESEC_NONM
, mdoc
->parse
,
2207 mdoc
->last
->line
, mdoc
->last
->pos
, NULL
);
2209 mandoc_msg(MANDOCERR_NAMESEC_NOND
, mdoc
->parse
,
2210 mdoc
->last
->line
, mdoc
->last
->pos
, NULL
);
2214 post_sh_see_also(POST_ARGS
)
2216 const struct roff_node
*n
;
2217 const char *name
, *sec
;
2218 const char *lastname
, *lastsec
, *lastpunct
;
2221 n
= mdoc
->last
->child
;
2222 lastname
= lastsec
= lastpunct
= NULL
;
2224 if (n
->tok
!= MDOC_Xr
||
2226 n
->child
->next
== NULL
)
2229 /* Process one .Xr node. */
2231 name
= n
->child
->string
;
2232 sec
= n
->child
->next
->string
;
2233 if (lastsec
!= NULL
) {
2234 if (lastpunct
[0] != ',' || lastpunct
[1] != '\0')
2235 mandoc_vmsg(MANDOCERR_XR_PUNCT
,
2236 mdoc
->parse
, n
->line
, n
->pos
,
2237 "%s before %s(%s)", lastpunct
,
2239 cmp
= strcmp(lastsec
, sec
);
2241 mandoc_vmsg(MANDOCERR_XR_ORDER
,
2242 mdoc
->parse
, n
->line
, n
->pos
,
2243 "%s(%s) after %s(%s)", name
,
2244 sec
, lastname
, lastsec
);
2245 else if (cmp
== 0 &&
2246 strcasecmp(lastname
, name
) > 0)
2247 mandoc_vmsg(MANDOCERR_XR_ORDER
,
2248 mdoc
->parse
, n
->line
, n
->pos
,
2249 "%s after %s", name
, lastname
);
2254 /* Process the following node. */
2259 if (n
->tok
== MDOC_Xr
) {
2263 if (n
->type
!= ROFFT_TEXT
)
2265 for (name
= n
->string
; *name
!= '\0'; name
++)
2266 if (isalpha((const unsigned char)*name
))
2268 lastpunct
= n
->string
;
2269 if (n
->next
== NULL
|| n
->next
->tok
== MDOC_Rs
)
2270 mandoc_vmsg(MANDOCERR_XR_PUNCT
, mdoc
->parse
,
2271 n
->line
, n
->pos
, "%s after %s(%s)",
2272 lastpunct
, lastname
, lastsec
);
2278 child_an(const struct roff_node
*n
)
2281 for (n
= n
->child
; n
!= NULL
; n
= n
->next
)
2282 if ((n
->tok
== MDOC_An
&& n
->child
!= NULL
) || child_an(n
))
2288 post_sh_authors(POST_ARGS
)
2291 if ( ! child_an(mdoc
->last
))
2292 mandoc_msg(MANDOCERR_AN_MISSING
, mdoc
->parse
,
2293 mdoc
->last
->line
, mdoc
->last
->pos
, NULL
);
2297 * Return an upper bound for the string distance (allowing
2298 * transpositions). Not a full Levenshtein implementation
2299 * because Levenshtein is quadratic in the string length
2300 * and this function is called for every standard name,
2301 * so the check for each custom name would be cubic.
2302 * The following crude heuristics is linear, resulting
2303 * in quadratic behaviour for checking one custom name,
2304 * which does not cause measurable slowdown.
2307 similar(const char *s1
, const char *s2
)
2309 const int maxdist
= 3;
2312 while (s1
[0] != '\0' && s2
[0] != '\0') {
2313 if (s1
[0] == s2
[0]) {
2318 if (++dist
> maxdist
)
2320 if (s1
[1] == s2
[1]) { /* replacement */
2323 } else if (s1
[0] == s2
[1] && s1
[1] == s2
[0]) {
2324 s1
+= 2; /* transposition */
2326 } else if (s1
[0] == s2
[1]) /* insertion */
2328 else if (s1
[1] == s2
[0]) /* deletion */
2333 dist
+= strlen(s1
) + strlen(s2
);
2334 return dist
> maxdist
? INT_MAX
: dist
;
2338 post_sh_head(POST_ARGS
)
2340 struct roff_node
*nch
;
2341 const char *goodsec
;
2342 const char *const *testsec
;
2347 * Process a new section. Sections are either "named" or
2348 * "custom". Custom sections are user-defined, while named ones
2349 * follow a conventional order and may only appear in certain
2353 sec
= mdoc
->last
->sec
;
2355 /* The NAME should be first. */
2357 if (sec
!= SEC_NAME
&& mdoc
->lastnamed
== SEC_NONE
)
2358 mandoc_vmsg(MANDOCERR_NAMESEC_FIRST
, mdoc
->parse
,
2359 mdoc
->last
->line
, mdoc
->last
->pos
, "Sh %s",
2360 sec
!= SEC_CUSTOM
? secnames
[sec
] :
2361 (nch
= mdoc
->last
->child
) == NULL
? "" :
2362 nch
->type
== ROFFT_TEXT
? nch
->string
:
2363 roff_name
[nch
->tok
]);
2365 /* The SYNOPSIS gets special attention in other areas. */
2367 if (sec
== SEC_SYNOPSIS
) {
2368 roff_setreg(mdoc
->roff
, "nS", 1, '=');
2369 mdoc
->flags
|= MDOC_SYNOPSIS
;
2371 roff_setreg(mdoc
->roff
, "nS", 0, '=');
2372 mdoc
->flags
&= ~MDOC_SYNOPSIS
;
2375 /* Mark our last section. */
2377 mdoc
->lastsec
= sec
;
2379 /* We don't care about custom sections after this. */
2381 if (sec
== SEC_CUSTOM
) {
2382 if ((nch
= mdoc
->last
->child
) == NULL
||
2383 nch
->type
!= ROFFT_TEXT
|| nch
->next
!= NULL
)
2387 for (testsec
= secnames
+ 1; *testsec
!= NULL
; testsec
++) {
2388 dist
= similar(nch
->string
, *testsec
);
2389 if (dist
< mindist
) {
2394 if (goodsec
!= NULL
)
2395 mandoc_vmsg(MANDOCERR_SEC_TYPO
, mdoc
->parse
,
2396 nch
->line
, nch
->pos
, "Sh %s instead of %s",
2397 nch
->string
, goodsec
);
2402 * Check whether our non-custom section is being repeated or is
2406 if (sec
== mdoc
->lastnamed
)
2407 mandoc_vmsg(MANDOCERR_SEC_REP
, mdoc
->parse
,
2408 mdoc
->last
->line
, mdoc
->last
->pos
,
2409 "Sh %s", secnames
[sec
]);
2411 if (sec
< mdoc
->lastnamed
)
2412 mandoc_vmsg(MANDOCERR_SEC_ORDER
, mdoc
->parse
,
2413 mdoc
->last
->line
, mdoc
->last
->pos
,
2414 "Sh %s", secnames
[sec
]);
2416 /* Mark the last named section. */
2418 mdoc
->lastnamed
= sec
;
2420 /* Check particular section/manual conventions. */
2422 if (mdoc
->meta
.msec
== NULL
)
2428 if (*mdoc
->meta
.msec
== '4')
2430 goodsec
= "2, 3, 4, 9";
2432 case SEC_RETURN_VALUES
:
2434 if (*mdoc
->meta
.msec
== '2')
2436 if (*mdoc
->meta
.msec
== '3')
2438 if (NULL
== goodsec
)
2439 goodsec
= "2, 3, 9";
2442 if (*mdoc
->meta
.msec
== '9')
2444 if (NULL
== goodsec
)
2446 mandoc_vmsg(MANDOCERR_SEC_MSEC
, mdoc
->parse
,
2447 mdoc
->last
->line
, mdoc
->last
->pos
,
2448 "Sh %s for %s only", secnames
[sec
], goodsec
);
2458 struct roff_node
*n
, *nch
;
2462 if (nch
->next
== NULL
) {
2463 mandoc_vmsg(MANDOCERR_XR_NOSEC
, mdoc
->parse
,
2464 n
->line
, n
->pos
, "Xr %s", nch
->string
);
2466 assert(nch
->next
== n
->last
);
2467 if(mandoc_xr_add(nch
->next
->string
, nch
->string
,
2468 nch
->line
, nch
->pos
))
2469 mandoc_vmsg(MANDOCERR_XR_SELF
, mdoc
->parse
,
2470 nch
->line
, nch
->pos
, "Xr %s %s",
2471 nch
->string
, nch
->next
->string
);
2473 post_delim_nb(mdoc
);
2477 post_ignpar(POST_ARGS
)
2479 struct roff_node
*np
;
2481 switch (mdoc
->last
->type
) {
2495 if ((np
= mdoc
->last
->child
) != NULL
)
2496 if (np
->tok
== MDOC_Pp
|| np
->tok
== MDOC_Lp
) {
2497 mandoc_vmsg(MANDOCERR_PAR_SKIP
,
2498 mdoc
->parse
, np
->line
, np
->pos
,
2499 "%s after %s", roff_name
[np
->tok
],
2500 roff_name
[mdoc
->last
->tok
]);
2501 roff_node_delete(mdoc
, np
);
2504 if ((np
= mdoc
->last
->last
) != NULL
)
2505 if (np
->tok
== MDOC_Pp
|| np
->tok
== MDOC_Lp
) {
2506 mandoc_vmsg(MANDOCERR_PAR_SKIP
, mdoc
->parse
,
2507 np
->line
, np
->pos
, "%s at the end of %s",
2509 roff_name
[mdoc
->last
->tok
]);
2510 roff_node_delete(mdoc
, np
);
2515 post_prevpar(POST_ARGS
)
2517 struct roff_node
*n
;
2520 if (NULL
== n
->prev
)
2522 if (n
->type
!= ROFFT_ELEM
&& n
->type
!= ROFFT_BLOCK
)
2526 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
2527 * block: `Lp', `Pp', or non-compact `Bd' or `Bl'.
2530 if (n
->prev
->tok
!= MDOC_Pp
&&
2531 n
->prev
->tok
!= MDOC_Lp
&&
2532 n
->prev
->tok
!= ROFF_br
)
2534 if (n
->tok
== MDOC_Bl
&& n
->norm
->Bl
.comp
)
2536 if (n
->tok
== MDOC_Bd
&& n
->norm
->Bd
.comp
)
2538 if (n
->tok
== MDOC_It
&& n
->parent
->norm
->Bl
.comp
)
2541 mandoc_vmsg(MANDOCERR_PAR_SKIP
, mdoc
->parse
,
2542 n
->prev
->line
, n
->prev
->pos
, "%s before %s",
2543 roff_name
[n
->prev
->tok
], roff_name
[n
->tok
]);
2544 roff_node_delete(mdoc
, n
->prev
);
2550 struct roff_node
*np
;
2553 if (np
->tok
!= ROFF_br
&& np
->tok
!= ROFF_sp
)
2556 if (np
->tok
== ROFF_sp
) {
2557 if (np
->child
!= NULL
&& np
->child
->next
!= NULL
)
2558 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, mdoc
->parse
,
2559 np
->child
->next
->line
, np
->child
->next
->pos
,
2560 "sp ... %s", np
->child
->next
->string
);
2561 } else if (np
->child
!= NULL
)
2562 mandoc_vmsg(MANDOCERR_ARG_SKIP
,
2563 mdoc
->parse
, np
->line
, np
->pos
, "%s %s",
2564 roff_name
[np
->tok
], np
->child
->string
);
2566 if ((np
= mdoc
->last
->prev
) == NULL
) {
2567 np
= mdoc
->last
->parent
;
2568 if (np
->tok
!= MDOC_Sh
&& np
->tok
!= MDOC_Ss
)
2570 } else if (np
->tok
!= MDOC_Pp
&& np
->tok
!= MDOC_Lp
&&
2571 (mdoc
->last
->tok
!= ROFF_br
||
2572 (np
->tok
!= ROFF_sp
&& np
->tok
!= ROFF_br
)))
2575 mandoc_vmsg(MANDOCERR_PAR_SKIP
, mdoc
->parse
,
2576 mdoc
->last
->line
, mdoc
->last
->pos
, "%s after %s",
2577 roff_name
[mdoc
->last
->tok
], roff_name
[np
->tok
]);
2578 roff_node_delete(mdoc
, mdoc
->last
);
2584 struct roff_node
*n
;
2588 n
->flags
|= NODE_NOPRT
;
2590 if (mdoc
->meta
.date
!= NULL
) {
2591 mandoc_msg(MANDOCERR_PROLOG_REP
, mdoc
->parse
,
2592 n
->line
, n
->pos
, "Dd");
2593 free(mdoc
->meta
.date
);
2594 } else if (mdoc
->flags
& MDOC_PBODY
)
2595 mandoc_msg(MANDOCERR_PROLOG_LATE
, mdoc
->parse
,
2596 n
->line
, n
->pos
, "Dd");
2597 else if (mdoc
->meta
.title
!= NULL
)
2598 mandoc_msg(MANDOCERR_PROLOG_ORDER
, mdoc
->parse
,
2599 n
->line
, n
->pos
, "Dd after Dt");
2600 else if (mdoc
->meta
.os
!= NULL
)
2601 mandoc_msg(MANDOCERR_PROLOG_ORDER
, mdoc
->parse
,
2602 n
->line
, n
->pos
, "Dd after Os");
2604 if (n
->child
== NULL
|| n
->child
->string
[0] == '\0') {
2605 mdoc
->meta
.date
= mdoc
->quick
? mandoc_strdup("") :
2606 mandoc_normdate(mdoc
, NULL
, n
->line
, n
->pos
);
2611 deroff(&datestr
, n
);
2613 mdoc
->meta
.date
= datestr
;
2615 mdoc
->meta
.date
= mandoc_normdate(mdoc
,
2616 datestr
, n
->line
, n
->pos
);
2624 struct roff_node
*nn
, *n
;
2629 n
->flags
|= NODE_NOPRT
;
2631 if (mdoc
->flags
& MDOC_PBODY
) {
2632 mandoc_msg(MANDOCERR_DT_LATE
, mdoc
->parse
,
2633 n
->line
, n
->pos
, "Dt");
2637 if (mdoc
->meta
.title
!= NULL
)
2638 mandoc_msg(MANDOCERR_PROLOG_REP
, mdoc
->parse
,
2639 n
->line
, n
->pos
, "Dt");
2640 else if (mdoc
->meta
.os
!= NULL
)
2641 mandoc_msg(MANDOCERR_PROLOG_ORDER
, mdoc
->parse
,
2642 n
->line
, n
->pos
, "Dt after Os");
2644 free(mdoc
->meta
.title
);
2645 free(mdoc
->meta
.msec
);
2646 free(mdoc
->meta
.vol
);
2647 free(mdoc
->meta
.arch
);
2649 mdoc
->meta
.title
= NULL
;
2650 mdoc
->meta
.msec
= NULL
;
2651 mdoc
->meta
.vol
= NULL
;
2652 mdoc
->meta
.arch
= NULL
;
2654 /* Mandatory first argument: title. */
2657 if (nn
== NULL
|| *nn
->string
== '\0') {
2658 mandoc_msg(MANDOCERR_DT_NOTITLE
,
2659 mdoc
->parse
, n
->line
, n
->pos
, "Dt");
2660 mdoc
->meta
.title
= mandoc_strdup("UNTITLED");
2662 mdoc
->meta
.title
= mandoc_strdup(nn
->string
);
2664 /* Check that all characters are uppercase. */
2666 for (p
= nn
->string
; *p
!= '\0'; p
++)
2667 if (islower((unsigned char)*p
)) {
2668 mandoc_vmsg(MANDOCERR_TITLE_CASE
,
2669 mdoc
->parse
, nn
->line
,
2670 nn
->pos
+ (p
- nn
->string
),
2671 "Dt %s", nn
->string
);
2676 /* Mandatory second argument: section. */
2682 mandoc_vmsg(MANDOCERR_MSEC_MISSING
,
2683 mdoc
->parse
, n
->line
, n
->pos
,
2684 "Dt %s", mdoc
->meta
.title
);
2685 mdoc
->meta
.vol
= mandoc_strdup("LOCAL");
2686 return; /* msec and arch remain NULL. */
2689 mdoc
->meta
.msec
= mandoc_strdup(nn
->string
);
2691 /* Infer volume title from section number. */
2693 cp
= mandoc_a2msec(nn
->string
);
2695 mandoc_vmsg(MANDOCERR_MSEC_BAD
, mdoc
->parse
,
2696 nn
->line
, nn
->pos
, "Dt ... %s", nn
->string
);
2697 mdoc
->meta
.vol
= mandoc_strdup(nn
->string
);
2699 mdoc
->meta
.vol
= mandoc_strdup(cp
);
2701 /* Optional third argument: architecture. */
2703 if ((nn
= nn
->next
) == NULL
)
2706 for (p
= nn
->string
; *p
!= '\0'; p
++)
2707 *p
= tolower((unsigned char)*p
);
2708 mdoc
->meta
.arch
= mandoc_strdup(nn
->string
);
2710 /* Ignore fourth and later arguments. */
2712 if ((nn
= nn
->next
) != NULL
)
2713 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, mdoc
->parse
,
2714 nn
->line
, nn
->pos
, "Dt ... %s", nn
->string
);
2720 struct roff_node
*n
, *nch
;
2723 post_delim_nb(mdoc
);
2729 macro
= !strcmp(nch
->string
, "Open") ? "Ox" :
2730 !strcmp(nch
->string
, "Net") ? "Nx" :
2731 !strcmp(nch
->string
, "Free") ? "Fx" :
2732 !strcmp(nch
->string
, "DragonFly") ? "Dx" : NULL
;
2734 mandoc_msg(MANDOCERR_BX
, mdoc
->parse
,
2735 n
->line
, n
->pos
, macro
);
2738 mdoc
->next
= ROFF_NEXT_SIBLING
;
2739 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Ns
);
2740 mdoc
->last
->flags
|= NODE_NOSRC
;
2741 mdoc
->next
= ROFF_NEXT_SIBLING
;
2743 mdoc
->next
= ROFF_NEXT_CHILD
;
2744 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "BSD");
2745 mdoc
->last
->flags
|= NODE_NOSRC
;
2752 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Ns
);
2753 mdoc
->last
->flags
|= NODE_NOSRC
;
2754 mdoc
->next
= ROFF_NEXT_SIBLING
;
2755 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "-");
2756 mdoc
->last
->flags
|= NODE_NOSRC
;
2757 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Ns
);
2758 mdoc
->last
->flags
|= NODE_NOSRC
;
2762 * Make `Bx's second argument always start with an uppercase
2763 * letter. Groff checks if it's an "accepted" term, but we just
2764 * uppercase blindly.
2767 *nch
->string
= (char)toupper((unsigned char)*nch
->string
);
2774 struct utsname utsname
;
2775 static char *defbuf
;
2777 struct roff_node
*n
;
2780 n
->flags
|= NODE_NOPRT
;
2782 if (mdoc
->meta
.os
!= NULL
)
2783 mandoc_msg(MANDOCERR_PROLOG_REP
, mdoc
->parse
,
2784 n
->line
, n
->pos
, "Os");
2785 else if (mdoc
->flags
& MDOC_PBODY
)
2786 mandoc_msg(MANDOCERR_PROLOG_LATE
, mdoc
->parse
,
2787 n
->line
, n
->pos
, "Os");
2792 * Set the operating system by way of the `Os' macro.
2793 * The order of precedence is:
2794 * 1. the argument of the `Os' macro, unless empty
2795 * 2. the -Ios=foo command line argument, if provided
2796 * 3. -DOSNAME="\"foo\"", if provided during compilation
2797 * 4. "sysname release" from uname(3)
2800 free(mdoc
->meta
.os
);
2801 mdoc
->meta
.os
= NULL
;
2802 deroff(&mdoc
->meta
.os
, n
);
2806 if (mdoc
->os_s
!= NULL
) {
2807 mdoc
->meta
.os
= mandoc_strdup(mdoc
->os_s
);
2812 mdoc
->meta
.os
= mandoc_strdup(OSNAME
);
2814 if (defbuf
== NULL
) {
2815 if (uname(&utsname
) == -1) {
2816 mandoc_msg(MANDOCERR_OS_UNAME
, mdoc
->parse
,
2817 n
->line
, n
->pos
, "Os");
2818 defbuf
= mandoc_strdup("UNKNOWN");
2820 mandoc_asprintf(&defbuf
, "%s %s",
2821 utsname
.sysname
, utsname
.release
);
2823 mdoc
->meta
.os
= mandoc_strdup(defbuf
);
2827 if (mdoc
->meta
.os_e
== MANDOC_OS_OTHER
) {
2828 if (strstr(mdoc
->meta
.os
, "OpenBSD") != NULL
)
2829 mdoc
->meta
.os_e
= MANDOC_OS_OPENBSD
;
2830 else if (strstr(mdoc
->meta
.os
, "NetBSD") != NULL
)
2831 mdoc
->meta
.os_e
= MANDOC_OS_NETBSD
;
2835 * This is the earliest point where we can check
2836 * Mdocdate conventions because we don't know
2837 * the operating system earlier.
2840 if (n
->child
!= NULL
)
2841 mandoc_vmsg(MANDOCERR_OS_ARG
, mdoc
->parse
,
2842 n
->child
->line
, n
->child
->pos
,
2843 "Os %s (%s)", n
->child
->string
,
2844 mdoc
->meta
.os_e
== MANDOC_OS_OPENBSD
?
2845 "OpenBSD" : "NetBSD");
2847 while (n
->tok
!= MDOC_Dd
)
2848 if ((n
= n
->prev
) == NULL
)
2850 if ((n
= n
->child
) == NULL
)
2852 if (strncmp(n
->string
, "$" "Mdocdate", 9)) {
2853 if (mdoc
->meta
.os_e
== MANDOC_OS_OPENBSD
)
2854 mandoc_vmsg(MANDOCERR_MDOCDATE_MISSING
,
2855 mdoc
->parse
, n
->line
, n
->pos
,
2856 "Dd %s (OpenBSD)", n
->string
);
2858 if (mdoc
->meta
.os_e
== MANDOC_OS_NETBSD
)
2859 mandoc_vmsg(MANDOCERR_MDOCDATE
,
2860 mdoc
->parse
, n
->line
, n
->pos
,
2861 "Dd %s (NetBSD)", n
->string
);
2866 mdoc_a2sec(const char *p
)
2870 for (i
= 0; i
< (int)SEC__MAX
; i
++)
2871 if (secnames
[i
] && 0 == strcmp(p
, secnames
[i
]))
2872 return (enum roff_sec
)i
;
2878 macro2len(enum roff_tok macro
)