1 /* $Id: mdoc_validate.c,v 1.357 2018/04/05 09:17:26 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
);
330 check_args(mdoc
, mdoc
->last
);
333 * Closing delimiters are not special at the
334 * beginning of a block, opening delimiters
335 * are not special at the end.
338 if (n
->child
!= NULL
)
339 n
->child
->flags
&= ~NODE_DELIMC
;
341 n
->last
->flags
&= ~NODE_DELIMO
;
343 /* Call the macro's postprocessor. */
345 if (n
->tok
< ROFF_MAX
) {
358 assert(n
->tok
>= MDOC_Dd
&& n
->tok
< MDOC_MAX
);
359 p
= mdoc_valids
+ n
->tok
;
369 check_args(struct roff_man
*mdoc
, struct roff_node
*n
)
376 assert(n
->args
->argc
);
377 for (i
= 0; i
< (int)n
->args
->argc
; i
++)
378 check_argv(mdoc
, n
, &n
->args
->argv
[i
]);
382 check_argv(struct roff_man
*mdoc
, struct roff_node
*n
, struct mdoc_argv
*v
)
386 for (i
= 0; i
< (int)v
->sz
; i
++)
387 check_text(mdoc
, v
->line
, v
->pos
, v
->value
[i
]);
391 check_text(struct roff_man
*mdoc
, int ln
, int pos
, char *p
)
395 if (MDOC_LITERAL
& mdoc
->flags
)
398 for (cp
= p
; NULL
!= (p
= strchr(p
, '\t')); p
++)
399 mandoc_msg(MANDOCERR_FI_TAB
, mdoc
->parse
,
400 ln
, pos
+ (int)(p
- cp
), NULL
);
404 check_text_em(struct roff_man
*mdoc
, int ln
, int pos
, char *p
)
406 const struct roff_node
*np
, *nn
;
409 np
= mdoc
->last
->prev
;
410 nn
= mdoc
->last
->next
;
412 /* Look for em-dashes wrongly encoded as "--". */
414 for (cp
= p
; *cp
!= '\0'; cp
++) {
415 if (cp
[0] != '-' || cp
[1] != '-')
419 /* Skip input sequences of more than two '-'. */
427 /* Skip "--" directly attached to something else. */
429 if ((cp
- p
> 1 && cp
[-2] != ' ') ||
430 (cp
[1] != '\0' && cp
[1] != ' '))
433 /* Require a letter right before or right afterwards. */
436 isalpha((unsigned char)cp
[-3]) :
438 np
->type
== ROFFT_TEXT
&&
439 np
->string
!= '\0' &&
440 isalpha((unsigned char)np
->string
[
441 strlen(np
->string
) - 1])) ||
443 isalpha((unsigned char)cp
[2]) :
445 nn
->type
== ROFFT_TEXT
&&
446 nn
->string
!= '\0' &&
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
;
1991 while (n
!= NULL
&& n
->tok
>= MDOC_Dd
&&
1992 mdoc_macros
[n
->tok
].flags
& MDOC_PROLOGUE
)
1996 mandoc_msg(MANDOCERR_DOC_EMPTY
, mdoc
->parse
, 0, 0, NULL
);
1997 else if (n
->tok
!= MDOC_Sh
)
1998 mandoc_msg(MANDOCERR_SEC_BEFORE
, mdoc
->parse
,
1999 n
->line
, n
->pos
, roff_name
[n
->tok
]);
2005 struct roff_node
*np
, *nch
, *next
, *prev
;
2010 if (np
->type
!= ROFFT_BODY
)
2013 if (np
->child
== NULL
) {
2014 mandoc_msg(MANDOCERR_RS_EMPTY
, mdoc
->parse
,
2015 np
->line
, np
->pos
, "Rs");
2020 * The full `Rs' block needs special handling to order the
2021 * sub-elements according to `rsord'. Pick through each element
2022 * and correctly order it. This is an insertion sort.
2026 for (nch
= np
->child
->next
; nch
!= NULL
; nch
= next
) {
2027 /* Determine order number of this child. */
2028 for (i
= 0; i
< RSORD_MAX
; i
++)
2029 if (rsord
[i
] == nch
->tok
)
2032 if (i
== RSORD_MAX
) {
2033 mandoc_msg(MANDOCERR_RS_BAD
, mdoc
->parse
,
2034 nch
->line
, nch
->pos
, roff_name
[nch
->tok
]);
2036 } else if (nch
->tok
== MDOC__J
|| nch
->tok
== MDOC__B
)
2037 np
->norm
->Rs
.quote_T
++;
2040 * Remove this child from the chain. This somewhat
2041 * repeats roff_node_unlink(), but since we're
2042 * just re-ordering, there's no need for the
2043 * full unlink process.
2046 if ((next
= nch
->next
) != NULL
)
2047 next
->prev
= nch
->prev
;
2049 if ((prev
= nch
->prev
) != NULL
)
2050 prev
->next
= nch
->next
;
2052 nch
->prev
= nch
->next
= NULL
;
2055 * Scan back until we reach a node that's
2056 * to be ordered before this child.
2059 for ( ; prev
; prev
= prev
->prev
) {
2060 /* Determine order of `prev'. */
2061 for (j
= 0; j
< RSORD_MAX
; j
++)
2062 if (rsord
[j
] == prev
->tok
)
2072 * Set this child back into its correct place
2073 * in front of the `prev' node.
2079 np
->child
->prev
= nch
;
2080 nch
->next
= np
->child
;
2084 prev
->next
->prev
= nch
;
2085 nch
->next
= prev
->next
;
2092 * For some arguments of some macros,
2093 * convert all breakable hyphens into ASCII_HYPH.
2096 post_hyph(POST_ARGS
)
2098 struct roff_node
*nch
;
2101 for (nch
= mdoc
->last
->child
; nch
!= NULL
; nch
= nch
->next
) {
2102 if (nch
->type
!= ROFFT_TEXT
)
2107 while (*(++cp
) != '\0')
2109 isalpha((unsigned char)cp
[-1]) &&
2110 isalpha((unsigned char)cp
[1]))
2118 struct roff_node
*n
;
2121 if (n
->flags
& NODE_LINE
||
2122 (n
->next
!= NULL
&& n
->next
->flags
& NODE_DELIMC
))
2123 mandoc_msg(MANDOCERR_NS_SKIP
, mdoc
->parse
,
2124 n
->line
, n
->pos
, NULL
);
2140 switch (mdoc
->last
->type
) {
2145 switch (mdoc
->lastsec
) {
2150 post_sh_see_also(mdoc
);
2153 post_sh_authors(mdoc
);
2165 post_sh_name(POST_ARGS
)
2167 struct roff_node
*n
;
2172 for (n
= mdoc
->last
->child
; n
!= NULL
; n
= n
->next
) {
2175 if (hasnm
&& n
->child
!= NULL
)
2176 mandoc_vmsg(MANDOCERR_NAMESEC_PUNCT
,
2177 mdoc
->parse
, n
->line
, n
->pos
,
2178 "Nm %s", n
->child
->string
);
2183 if (n
->next
!= NULL
)
2184 mandoc_msg(MANDOCERR_NAMESEC_ND
,
2185 mdoc
->parse
, n
->line
, n
->pos
, NULL
);
2188 if (n
->type
== ROFFT_TEXT
&&
2189 n
->string
[0] == ',' && n
->string
[1] == '\0' &&
2190 n
->next
!= NULL
&& n
->next
->tok
== MDOC_Nm
) {
2196 mandoc_msg(MANDOCERR_NAMESEC_BAD
, mdoc
->parse
,
2197 n
->line
, n
->pos
, roff_name
[n
->tok
]);
2204 mandoc_msg(MANDOCERR_NAMESEC_NONM
, mdoc
->parse
,
2205 mdoc
->last
->line
, mdoc
->last
->pos
, NULL
);
2207 mandoc_msg(MANDOCERR_NAMESEC_NOND
, mdoc
->parse
,
2208 mdoc
->last
->line
, mdoc
->last
->pos
, NULL
);
2212 post_sh_see_also(POST_ARGS
)
2214 const struct roff_node
*n
;
2215 const char *name
, *sec
;
2216 const char *lastname
, *lastsec
, *lastpunct
;
2219 n
= mdoc
->last
->child
;
2220 lastname
= lastsec
= lastpunct
= NULL
;
2222 if (n
->tok
!= MDOC_Xr
||
2224 n
->child
->next
== NULL
)
2227 /* Process one .Xr node. */
2229 name
= n
->child
->string
;
2230 sec
= n
->child
->next
->string
;
2231 if (lastsec
!= NULL
) {
2232 if (lastpunct
[0] != ',' || lastpunct
[1] != '\0')
2233 mandoc_vmsg(MANDOCERR_XR_PUNCT
,
2234 mdoc
->parse
, n
->line
, n
->pos
,
2235 "%s before %s(%s)", lastpunct
,
2237 cmp
= strcmp(lastsec
, sec
);
2239 mandoc_vmsg(MANDOCERR_XR_ORDER
,
2240 mdoc
->parse
, n
->line
, n
->pos
,
2241 "%s(%s) after %s(%s)", name
,
2242 sec
, lastname
, lastsec
);
2243 else if (cmp
== 0 &&
2244 strcasecmp(lastname
, name
) > 0)
2245 mandoc_vmsg(MANDOCERR_XR_ORDER
,
2246 mdoc
->parse
, n
->line
, n
->pos
,
2247 "%s after %s", name
, lastname
);
2252 /* Process the following node. */
2257 if (n
->tok
== MDOC_Xr
) {
2261 if (n
->type
!= ROFFT_TEXT
)
2263 for (name
= n
->string
; *name
!= '\0'; name
++)
2264 if (isalpha((const unsigned char)*name
))
2266 lastpunct
= n
->string
;
2267 if (n
->next
== NULL
|| n
->next
->tok
== MDOC_Rs
)
2268 mandoc_vmsg(MANDOCERR_XR_PUNCT
, mdoc
->parse
,
2269 n
->line
, n
->pos
, "%s after %s(%s)",
2270 lastpunct
, lastname
, lastsec
);
2276 child_an(const struct roff_node
*n
)
2279 for (n
= n
->child
; n
!= NULL
; n
= n
->next
)
2280 if ((n
->tok
== MDOC_An
&& n
->child
!= NULL
) || child_an(n
))
2286 post_sh_authors(POST_ARGS
)
2289 if ( ! child_an(mdoc
->last
))
2290 mandoc_msg(MANDOCERR_AN_MISSING
, mdoc
->parse
,
2291 mdoc
->last
->line
, mdoc
->last
->pos
, NULL
);
2295 * Return an upper bound for the string distance (allowing
2296 * transpositions). Not a full Levenshtein implementation
2297 * because Levenshtein is quadratic in the string length
2298 * and this function is called for every standard name,
2299 * so the check for each custom name would be cubic.
2300 * The following crude heuristics is linear, resulting
2301 * in quadratic behaviour for checking one custom name,
2302 * which does not cause measurable slowdown.
2305 similar(const char *s1
, const char *s2
)
2307 const int maxdist
= 3;
2310 while (s1
[0] != '\0' && s2
[0] != '\0') {
2311 if (s1
[0] == s2
[0]) {
2316 if (++dist
> maxdist
)
2318 if (s1
[1] == s2
[1]) { /* replacement */
2321 } else if (s1
[0] == s2
[1] && s1
[1] == s2
[0]) {
2322 s1
+= 2; /* transposition */
2324 } else if (s1
[0] == s2
[1]) /* insertion */
2326 else if (s1
[1] == s2
[0]) /* deletion */
2331 dist
+= strlen(s1
) + strlen(s2
);
2332 return dist
> maxdist
? INT_MAX
: dist
;
2336 post_sh_head(POST_ARGS
)
2338 struct roff_node
*nch
;
2339 const char *goodsec
;
2340 const char *const *testsec
;
2345 * Process a new section. Sections are either "named" or
2346 * "custom". Custom sections are user-defined, while named ones
2347 * follow a conventional order and may only appear in certain
2351 sec
= mdoc
->last
->sec
;
2353 /* The NAME should be first. */
2355 if (sec
!= SEC_NAME
&& mdoc
->lastnamed
== SEC_NONE
)
2356 mandoc_vmsg(MANDOCERR_NAMESEC_FIRST
, mdoc
->parse
,
2357 mdoc
->last
->line
, mdoc
->last
->pos
, "Sh %s",
2358 sec
!= SEC_CUSTOM
? secnames
[sec
] :
2359 (nch
= mdoc
->last
->child
) == NULL
? "" :
2360 nch
->type
== ROFFT_TEXT
? nch
->string
:
2361 roff_name
[nch
->tok
]);
2363 /* The SYNOPSIS gets special attention in other areas. */
2365 if (sec
== SEC_SYNOPSIS
) {
2366 roff_setreg(mdoc
->roff
, "nS", 1, '=');
2367 mdoc
->flags
|= MDOC_SYNOPSIS
;
2369 roff_setreg(mdoc
->roff
, "nS", 0, '=');
2370 mdoc
->flags
&= ~MDOC_SYNOPSIS
;
2373 /* Mark our last section. */
2375 mdoc
->lastsec
= sec
;
2377 /* We don't care about custom sections after this. */
2379 if (sec
== SEC_CUSTOM
) {
2380 if ((nch
= mdoc
->last
->child
) == NULL
||
2381 nch
->type
!= ROFFT_TEXT
|| nch
->next
!= NULL
)
2385 for (testsec
= secnames
+ 1; *testsec
!= NULL
; testsec
++) {
2386 dist
= similar(nch
->string
, *testsec
);
2387 if (dist
< mindist
) {
2392 if (goodsec
!= NULL
)
2393 mandoc_vmsg(MANDOCERR_SEC_TYPO
, mdoc
->parse
,
2394 nch
->line
, nch
->pos
, "Sh %s instead of %s",
2395 nch
->string
, goodsec
);
2400 * Check whether our non-custom section is being repeated or is
2404 if (sec
== mdoc
->lastnamed
)
2405 mandoc_vmsg(MANDOCERR_SEC_REP
, mdoc
->parse
,
2406 mdoc
->last
->line
, mdoc
->last
->pos
,
2407 "Sh %s", secnames
[sec
]);
2409 if (sec
< mdoc
->lastnamed
)
2410 mandoc_vmsg(MANDOCERR_SEC_ORDER
, mdoc
->parse
,
2411 mdoc
->last
->line
, mdoc
->last
->pos
,
2412 "Sh %s", secnames
[sec
]);
2414 /* Mark the last named section. */
2416 mdoc
->lastnamed
= sec
;
2418 /* Check particular section/manual conventions. */
2420 if (mdoc
->meta
.msec
== NULL
)
2426 if (*mdoc
->meta
.msec
== '4')
2428 goodsec
= "2, 3, 4, 9";
2430 case SEC_RETURN_VALUES
:
2432 if (*mdoc
->meta
.msec
== '2')
2434 if (*mdoc
->meta
.msec
== '3')
2436 if (NULL
== goodsec
)
2437 goodsec
= "2, 3, 9";
2440 if (*mdoc
->meta
.msec
== '9')
2442 if (NULL
== goodsec
)
2444 mandoc_vmsg(MANDOCERR_SEC_MSEC
, mdoc
->parse
,
2445 mdoc
->last
->line
, mdoc
->last
->pos
,
2446 "Sh %s for %s only", secnames
[sec
], goodsec
);
2456 struct roff_node
*n
, *nch
;
2460 if (nch
->next
== NULL
) {
2461 mandoc_vmsg(MANDOCERR_XR_NOSEC
, mdoc
->parse
,
2462 n
->line
, n
->pos
, "Xr %s", nch
->string
);
2464 assert(nch
->next
== n
->last
);
2465 if(mandoc_xr_add(nch
->next
->string
, nch
->string
,
2466 nch
->line
, nch
->pos
))
2467 mandoc_vmsg(MANDOCERR_XR_SELF
, mdoc
->parse
,
2468 nch
->line
, nch
->pos
, "Xr %s %s",
2469 nch
->string
, nch
->next
->string
);
2471 post_delim_nb(mdoc
);
2475 post_ignpar(POST_ARGS
)
2477 struct roff_node
*np
;
2479 switch (mdoc
->last
->type
) {
2493 if ((np
= mdoc
->last
->child
) != NULL
)
2494 if (np
->tok
== MDOC_Pp
|| np
->tok
== MDOC_Lp
) {
2495 mandoc_vmsg(MANDOCERR_PAR_SKIP
,
2496 mdoc
->parse
, np
->line
, np
->pos
,
2497 "%s after %s", roff_name
[np
->tok
],
2498 roff_name
[mdoc
->last
->tok
]);
2499 roff_node_delete(mdoc
, np
);
2502 if ((np
= mdoc
->last
->last
) != NULL
)
2503 if (np
->tok
== MDOC_Pp
|| np
->tok
== MDOC_Lp
) {
2504 mandoc_vmsg(MANDOCERR_PAR_SKIP
, mdoc
->parse
,
2505 np
->line
, np
->pos
, "%s at the end of %s",
2507 roff_name
[mdoc
->last
->tok
]);
2508 roff_node_delete(mdoc
, np
);
2513 post_prevpar(POST_ARGS
)
2515 struct roff_node
*n
;
2518 if (NULL
== n
->prev
)
2520 if (n
->type
!= ROFFT_ELEM
&& n
->type
!= ROFFT_BLOCK
)
2524 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
2525 * block: `Lp', `Pp', or non-compact `Bd' or `Bl'.
2528 if (n
->prev
->tok
!= MDOC_Pp
&&
2529 n
->prev
->tok
!= MDOC_Lp
&&
2530 n
->prev
->tok
!= ROFF_br
)
2532 if (n
->tok
== MDOC_Bl
&& n
->norm
->Bl
.comp
)
2534 if (n
->tok
== MDOC_Bd
&& n
->norm
->Bd
.comp
)
2536 if (n
->tok
== MDOC_It
&& n
->parent
->norm
->Bl
.comp
)
2539 mandoc_vmsg(MANDOCERR_PAR_SKIP
, mdoc
->parse
,
2540 n
->prev
->line
, n
->prev
->pos
, "%s before %s",
2541 roff_name
[n
->prev
->tok
], roff_name
[n
->tok
]);
2542 roff_node_delete(mdoc
, n
->prev
);
2548 struct roff_node
*np
;
2551 if (np
->tok
!= ROFF_br
&& np
->tok
!= ROFF_sp
)
2554 if (np
->tok
== ROFF_sp
) {
2555 if (np
->child
!= NULL
&& np
->child
->next
!= NULL
)
2556 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, mdoc
->parse
,
2557 np
->child
->next
->line
, np
->child
->next
->pos
,
2558 "sp ... %s", np
->child
->next
->string
);
2559 } else if (np
->child
!= NULL
)
2560 mandoc_vmsg(MANDOCERR_ARG_SKIP
,
2561 mdoc
->parse
, np
->line
, np
->pos
, "%s %s",
2562 roff_name
[np
->tok
], np
->child
->string
);
2564 if ((np
= mdoc
->last
->prev
) == NULL
) {
2565 np
= mdoc
->last
->parent
;
2566 if (np
->tok
!= MDOC_Sh
&& np
->tok
!= MDOC_Ss
)
2568 } else if (np
->tok
!= MDOC_Pp
&& np
->tok
!= MDOC_Lp
&&
2569 (mdoc
->last
->tok
!= ROFF_br
||
2570 (np
->tok
!= ROFF_sp
&& np
->tok
!= ROFF_br
)))
2573 mandoc_vmsg(MANDOCERR_PAR_SKIP
, mdoc
->parse
,
2574 mdoc
->last
->line
, mdoc
->last
->pos
, "%s after %s",
2575 roff_name
[mdoc
->last
->tok
], roff_name
[np
->tok
]);
2576 roff_node_delete(mdoc
, mdoc
->last
);
2582 struct roff_node
*n
;
2586 n
->flags
|= NODE_NOPRT
;
2588 if (mdoc
->meta
.date
!= NULL
) {
2589 mandoc_msg(MANDOCERR_PROLOG_REP
, mdoc
->parse
,
2590 n
->line
, n
->pos
, "Dd");
2591 free(mdoc
->meta
.date
);
2592 } else if (mdoc
->flags
& MDOC_PBODY
)
2593 mandoc_msg(MANDOCERR_PROLOG_LATE
, mdoc
->parse
,
2594 n
->line
, n
->pos
, "Dd");
2595 else if (mdoc
->meta
.title
!= NULL
)
2596 mandoc_msg(MANDOCERR_PROLOG_ORDER
, mdoc
->parse
,
2597 n
->line
, n
->pos
, "Dd after Dt");
2598 else if (mdoc
->meta
.os
!= NULL
)
2599 mandoc_msg(MANDOCERR_PROLOG_ORDER
, mdoc
->parse
,
2600 n
->line
, n
->pos
, "Dd after Os");
2602 if (n
->child
== NULL
|| n
->child
->string
[0] == '\0') {
2603 mdoc
->meta
.date
= mdoc
->quick
? mandoc_strdup("") :
2604 mandoc_normdate(mdoc
, NULL
, n
->line
, n
->pos
);
2609 deroff(&datestr
, n
);
2611 mdoc
->meta
.date
= datestr
;
2613 mdoc
->meta
.date
= mandoc_normdate(mdoc
,
2614 datestr
, n
->line
, n
->pos
);
2622 struct roff_node
*nn
, *n
;
2627 n
->flags
|= NODE_NOPRT
;
2629 if (mdoc
->flags
& MDOC_PBODY
) {
2630 mandoc_msg(MANDOCERR_DT_LATE
, mdoc
->parse
,
2631 n
->line
, n
->pos
, "Dt");
2635 if (mdoc
->meta
.title
!= NULL
)
2636 mandoc_msg(MANDOCERR_PROLOG_REP
, mdoc
->parse
,
2637 n
->line
, n
->pos
, "Dt");
2638 else if (mdoc
->meta
.os
!= NULL
)
2639 mandoc_msg(MANDOCERR_PROLOG_ORDER
, mdoc
->parse
,
2640 n
->line
, n
->pos
, "Dt after Os");
2642 free(mdoc
->meta
.title
);
2643 free(mdoc
->meta
.msec
);
2644 free(mdoc
->meta
.vol
);
2645 free(mdoc
->meta
.arch
);
2647 mdoc
->meta
.title
= NULL
;
2648 mdoc
->meta
.msec
= NULL
;
2649 mdoc
->meta
.vol
= NULL
;
2650 mdoc
->meta
.arch
= NULL
;
2652 /* Mandatory first argument: title. */
2655 if (nn
== NULL
|| *nn
->string
== '\0') {
2656 mandoc_msg(MANDOCERR_DT_NOTITLE
,
2657 mdoc
->parse
, n
->line
, n
->pos
, "Dt");
2658 mdoc
->meta
.title
= mandoc_strdup("UNTITLED");
2660 mdoc
->meta
.title
= mandoc_strdup(nn
->string
);
2662 /* Check that all characters are uppercase. */
2664 for (p
= nn
->string
; *p
!= '\0'; p
++)
2665 if (islower((unsigned char)*p
)) {
2666 mandoc_vmsg(MANDOCERR_TITLE_CASE
,
2667 mdoc
->parse
, nn
->line
,
2668 nn
->pos
+ (p
- nn
->string
),
2669 "Dt %s", nn
->string
);
2674 /* Mandatory second argument: section. */
2680 mandoc_vmsg(MANDOCERR_MSEC_MISSING
,
2681 mdoc
->parse
, n
->line
, n
->pos
,
2682 "Dt %s", mdoc
->meta
.title
);
2683 mdoc
->meta
.vol
= mandoc_strdup("LOCAL");
2684 return; /* msec and arch remain NULL. */
2687 mdoc
->meta
.msec
= mandoc_strdup(nn
->string
);
2689 /* Infer volume title from section number. */
2691 cp
= mandoc_a2msec(nn
->string
);
2693 mandoc_vmsg(MANDOCERR_MSEC_BAD
, mdoc
->parse
,
2694 nn
->line
, nn
->pos
, "Dt ... %s", nn
->string
);
2695 mdoc
->meta
.vol
= mandoc_strdup(nn
->string
);
2697 mdoc
->meta
.vol
= mandoc_strdup(cp
);
2699 /* Optional third argument: architecture. */
2701 if ((nn
= nn
->next
) == NULL
)
2704 for (p
= nn
->string
; *p
!= '\0'; p
++)
2705 *p
= tolower((unsigned char)*p
);
2706 mdoc
->meta
.arch
= mandoc_strdup(nn
->string
);
2708 /* Ignore fourth and later arguments. */
2710 if ((nn
= nn
->next
) != NULL
)
2711 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, mdoc
->parse
,
2712 nn
->line
, nn
->pos
, "Dt ... %s", nn
->string
);
2718 struct roff_node
*n
, *nch
;
2721 post_delim_nb(mdoc
);
2727 macro
= !strcmp(nch
->string
, "Open") ? "Ox" :
2728 !strcmp(nch
->string
, "Net") ? "Nx" :
2729 !strcmp(nch
->string
, "Free") ? "Fx" :
2730 !strcmp(nch
->string
, "DragonFly") ? "Dx" : NULL
;
2732 mandoc_msg(MANDOCERR_BX
, mdoc
->parse
,
2733 n
->line
, n
->pos
, macro
);
2736 mdoc
->next
= ROFF_NEXT_SIBLING
;
2737 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Ns
);
2738 mdoc
->last
->flags
|= NODE_NOSRC
;
2739 mdoc
->next
= ROFF_NEXT_SIBLING
;
2741 mdoc
->next
= ROFF_NEXT_CHILD
;
2742 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "BSD");
2743 mdoc
->last
->flags
|= NODE_NOSRC
;
2750 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Ns
);
2751 mdoc
->last
->flags
|= NODE_NOSRC
;
2752 mdoc
->next
= ROFF_NEXT_SIBLING
;
2753 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "-");
2754 mdoc
->last
->flags
|= NODE_NOSRC
;
2755 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Ns
);
2756 mdoc
->last
->flags
|= NODE_NOSRC
;
2760 * Make `Bx's second argument always start with an uppercase
2761 * letter. Groff checks if it's an "accepted" term, but we just
2762 * uppercase blindly.
2765 *nch
->string
= (char)toupper((unsigned char)*nch
->string
);
2772 struct utsname utsname
;
2773 static char *defbuf
;
2775 struct roff_node
*n
;
2778 n
->flags
|= NODE_NOPRT
;
2780 if (mdoc
->meta
.os
!= NULL
)
2781 mandoc_msg(MANDOCERR_PROLOG_REP
, mdoc
->parse
,
2782 n
->line
, n
->pos
, "Os");
2783 else if (mdoc
->flags
& MDOC_PBODY
)
2784 mandoc_msg(MANDOCERR_PROLOG_LATE
, mdoc
->parse
,
2785 n
->line
, n
->pos
, "Os");
2790 * Set the operating system by way of the `Os' macro.
2791 * The order of precedence is:
2792 * 1. the argument of the `Os' macro, unless empty
2793 * 2. the -Ios=foo command line argument, if provided
2794 * 3. -DOSNAME="\"foo\"", if provided during compilation
2795 * 4. "sysname release" from uname(3)
2798 free(mdoc
->meta
.os
);
2799 mdoc
->meta
.os
= NULL
;
2800 deroff(&mdoc
->meta
.os
, n
);
2804 if (mdoc
->os_s
!= NULL
) {
2805 mdoc
->meta
.os
= mandoc_strdup(mdoc
->os_s
);
2810 mdoc
->meta
.os
= mandoc_strdup(OSNAME
);
2812 if (defbuf
== NULL
) {
2813 if (uname(&utsname
) == -1) {
2814 mandoc_msg(MANDOCERR_OS_UNAME
, mdoc
->parse
,
2815 n
->line
, n
->pos
, "Os");
2816 defbuf
= mandoc_strdup("UNKNOWN");
2818 mandoc_asprintf(&defbuf
, "%s %s",
2819 utsname
.sysname
, utsname
.release
);
2821 mdoc
->meta
.os
= mandoc_strdup(defbuf
);
2825 if (mdoc
->meta
.os_e
== MANDOC_OS_OTHER
) {
2826 if (strstr(mdoc
->meta
.os
, "OpenBSD") != NULL
)
2827 mdoc
->meta
.os_e
= MANDOC_OS_OPENBSD
;
2828 else if (strstr(mdoc
->meta
.os
, "NetBSD") != NULL
)
2829 mdoc
->meta
.os_e
= MANDOC_OS_NETBSD
;
2833 * This is the earliest point where we can check
2834 * Mdocdate conventions because we don't know
2835 * the operating system earlier.
2838 if (n
->child
!= NULL
)
2839 mandoc_vmsg(MANDOCERR_OS_ARG
, mdoc
->parse
,
2840 n
->child
->line
, n
->child
->pos
,
2841 "Os %s (%s)", n
->child
->string
,
2842 mdoc
->meta
.os_e
== MANDOC_OS_OPENBSD
?
2843 "OpenBSD" : "NetBSD");
2845 while (n
->tok
!= MDOC_Dd
)
2846 if ((n
= n
->prev
) == NULL
)
2848 if ((n
= n
->child
) == NULL
)
2850 if (strncmp(n
->string
, "$" "Mdocdate", 9)) {
2851 if (mdoc
->meta
.os_e
== MANDOC_OS_OPENBSD
)
2852 mandoc_vmsg(MANDOCERR_MDOCDATE_MISSING
,
2853 mdoc
->parse
, n
->line
, n
->pos
,
2854 "Dd %s (OpenBSD)", n
->string
);
2856 if (mdoc
->meta
.os_e
== MANDOC_OS_NETBSD
)
2857 mandoc_vmsg(MANDOCERR_MDOCDATE
,
2858 mdoc
->parse
, n
->line
, n
->pos
,
2859 "Dd %s (NetBSD)", n
->string
);
2864 mdoc_a2sec(const char *p
)
2868 for (i
= 0; i
< (int)SEC__MAX
; i
++)
2869 if (secnames
[i
] && 0 == strcmp(p
, secnames
[i
]))
2870 return (enum roff_sec
)i
;
2876 macro2len(enum roff_tok macro
)