1 /* $Id: mdoc_validate.c,v 1.363 2018/12/03 21:00:11 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_abort(POST_ARGS
);
68 static void post_an(POST_ARGS
);
69 static void post_an_norm(POST_ARGS
);
70 static void post_at(POST_ARGS
);
71 static void post_bd(POST_ARGS
);
72 static void post_bf(POST_ARGS
);
73 static void post_bk(POST_ARGS
);
74 static void post_bl(POST_ARGS
);
75 static void post_bl_block(POST_ARGS
);
76 static void post_bl_head(POST_ARGS
);
77 static void post_bl_norm(POST_ARGS
);
78 static void post_bx(POST_ARGS
);
79 static void post_defaults(POST_ARGS
);
80 static void post_display(POST_ARGS
);
81 static void post_dd(POST_ARGS
);
82 static void post_delim(POST_ARGS
);
83 static void post_delim_nb(POST_ARGS
);
84 static void post_dt(POST_ARGS
);
85 static void post_en(POST_ARGS
);
86 static void post_es(POST_ARGS
);
87 static void post_eoln(POST_ARGS
);
88 static void post_ex(POST_ARGS
);
89 static void post_fa(POST_ARGS
);
90 static void post_fn(POST_ARGS
);
91 static void post_fname(POST_ARGS
);
92 static void post_fo(POST_ARGS
);
93 static void post_hyph(POST_ARGS
);
94 static void post_ignpar(POST_ARGS
);
95 static void post_it(POST_ARGS
);
96 static void post_lb(POST_ARGS
);
97 static void post_nd(POST_ARGS
);
98 static void post_nm(POST_ARGS
);
99 static void post_ns(POST_ARGS
);
100 static void post_obsolete(POST_ARGS
);
101 static void post_os(POST_ARGS
);
102 static void post_par(POST_ARGS
);
103 static void post_prevpar(POST_ARGS
);
104 static void post_root(POST_ARGS
);
105 static void post_rs(POST_ARGS
);
106 static void post_rv(POST_ARGS
);
107 static void post_sh(POST_ARGS
);
108 static void post_sh_head(POST_ARGS
);
109 static void post_sh_name(POST_ARGS
);
110 static void post_sh_see_also(POST_ARGS
);
111 static void post_sh_authors(POST_ARGS
);
112 static void post_sm(POST_ARGS
);
113 static void post_st(POST_ARGS
);
114 static void post_std(POST_ARGS
);
115 static void post_sx(POST_ARGS
);
116 static void post_useless(POST_ARGS
);
117 static void post_xr(POST_ARGS
);
118 static void post_xx(POST_ARGS
);
120 static const v_post mdoc_valids
[MDOC_MAX
- MDOC_Dd
] = {
125 post_ignpar
, /* Ss */
127 post_display
, /* D1 */
128 post_display
, /* Dl */
129 post_display
, /* Bd */
134 post_delim_nb
, /* Ad */
137 post_defaults
, /* Ar */
139 post_delim_nb
, /* Cm */
140 post_delim_nb
, /* Dv */
141 post_delim_nb
, /* Er */
142 post_delim_nb
, /* Ev */
146 post_delim_nb
, /* Fl */
148 post_delim_nb
, /* Ft */
149 post_delim_nb
, /* Ic */
150 post_delim_nb
, /* In */
151 post_defaults
, /* Li */
154 post_delim_nb
, /* Op */
156 post_defaults
, /* Pa */
159 post_delim_nb
, /* Va */
160 post_delim_nb
, /* Vt */
163 post_hyph
, /* %B */ /* FIXME: can be used outside Rs/Re. */
171 post_hyph
, /* %T */ /* FIXME: can be used outside Rs/Re. */
175 post_delim_nb
, /* Aq */
183 post_obsolete
, /* Db */
189 post_delim_nb
, /* Em */
192 post_delim_nb
, /* Ms */
200 post_delim_nb
, /* Pq */
202 post_delim_nb
, /* Ql */
204 post_delim_nb
, /* Qq */
209 post_delim_nb
, /* Sq */
212 post_delim_nb
, /* Sy */
213 post_useless
, /* Tn */
224 post_obsolete
, /* Hf */
225 post_obsolete
, /* Fr */
229 post_delim_nb
, /* Lk */
230 post_defaults
, /* Mt */
231 post_delim_nb
, /* Brq */
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",
289 /* Validate the subtree rooted at mdoc->last. */
291 mdoc_node_validate(struct roff_man
*mdoc
)
293 struct roff_node
*n
, *np
;
297 * Translate obsolete macros to modern macros first
298 * such that later code does not need to look
299 * for the obsolete versions.
316 * Iterate over all children, recursing into each one
317 * in turn, depth-first.
320 mdoc
->last
= mdoc
->last
->child
;
321 while (mdoc
->last
!= NULL
) {
322 mdoc_node_validate(mdoc
);
324 mdoc
->last
= mdoc
->last
->child
;
326 mdoc
->last
= mdoc
->last
->next
;
329 /* Finally validate the macro itself. */
332 mdoc
->next
= ROFF_NEXT_SIBLING
;
336 if (n
->sec
!= SEC_SYNOPSIS
||
337 (np
->tok
!= MDOC_Cd
&& np
->tok
!= MDOC_Fd
))
338 check_text(mdoc
, n
->line
, n
->pos
, n
->string
);
339 if (np
->tok
!= MDOC_Ql
&& np
->tok
!= MDOC_Dl
&&
340 (np
->tok
!= MDOC_Bd
||
341 (mdoc
->flags
& MDOC_LITERAL
) == 0) &&
342 (np
->tok
!= MDOC_It
|| np
->type
!= ROFFT_HEAD
||
343 np
->parent
->parent
->norm
->Bl
.type
!= LIST_diag
))
344 check_text_em(mdoc
, n
->line
, n
->pos
, n
->string
);
345 if (np
->tok
== MDOC_It
|| (np
->type
== ROFFT_BODY
&&
346 (np
->tok
== MDOC_Sh
|| np
->tok
== MDOC_Ss
)))
347 check_toptext(mdoc
, n
->line
, n
->pos
, n
->string
);
357 check_args(mdoc
, mdoc
->last
);
360 * Closing delimiters are not special at the
361 * beginning of a block, opening delimiters
362 * are not special at the end.
365 if (n
->child
!= NULL
)
366 n
->child
->flags
&= ~NODE_DELIMC
;
368 n
->last
->flags
&= ~NODE_DELIMO
;
370 /* Call the macro's postprocessor. */
372 if (n
->tok
< ROFF_MAX
) {
385 assert(n
->tok
>= MDOC_Dd
&& n
->tok
< MDOC_MAX
);
386 p
= mdoc_valids
+ (n
->tok
- MDOC_Dd
);
396 check_args(struct roff_man
*mdoc
, struct roff_node
*n
)
403 assert(n
->args
->argc
);
404 for (i
= 0; i
< (int)n
->args
->argc
; i
++)
405 check_argv(mdoc
, n
, &n
->args
->argv
[i
]);
409 check_argv(struct roff_man
*mdoc
, struct roff_node
*n
, struct mdoc_argv
*v
)
413 for (i
= 0; i
< (int)v
->sz
; i
++)
414 check_text(mdoc
, v
->line
, v
->pos
, v
->value
[i
]);
418 check_text(struct roff_man
*mdoc
, int ln
, int pos
, char *p
)
422 if (MDOC_LITERAL
& mdoc
->flags
)
425 for (cp
= p
; NULL
!= (p
= strchr(p
, '\t')); p
++)
426 mandoc_msg(MANDOCERR_FI_TAB
, mdoc
->parse
,
427 ln
, pos
+ (int)(p
- cp
), NULL
);
431 check_text_em(struct roff_man
*mdoc
, int ln
, int pos
, char *p
)
433 const struct roff_node
*np
, *nn
;
436 np
= mdoc
->last
->prev
;
437 nn
= mdoc
->last
->next
;
439 /* Look for em-dashes wrongly encoded as "--". */
441 for (cp
= p
; *cp
!= '\0'; cp
++) {
442 if (cp
[0] != '-' || cp
[1] != '-')
446 /* Skip input sequences of more than two '-'. */
454 /* Skip "--" directly attached to something else. */
456 if ((cp
- p
> 1 && cp
[-2] != ' ') ||
457 (cp
[1] != '\0' && cp
[1] != ' '))
460 /* Require a letter right before or right afterwards. */
463 isalpha((unsigned char)cp
[-3]) :
465 np
->type
== ROFFT_TEXT
&&
466 *np
->string
!= '\0' &&
467 isalpha((unsigned char)np
->string
[
468 strlen(np
->string
) - 1])) ||
469 (cp
[1] != '\0' && cp
[2] != '\0' ?
470 isalpha((unsigned char)cp
[2]) :
472 nn
->type
== ROFFT_TEXT
&&
473 isalpha((unsigned char)*nn
->string
))) {
474 mandoc_msg(MANDOCERR_DASHDASH
, mdoc
->parse
,
475 ln
, pos
+ (int)(cp
- p
) - 1, NULL
);
482 check_toptext(struct roff_man
*mdoc
, int ln
, int pos
, const char *p
)
484 const char *cp
, *cpr
;
489 if ((cp
= strstr(p
, "OpenBSD")) != NULL
)
490 mandoc_msg(MANDOCERR_BX
, mdoc
->parse
,
491 ln
, pos
+ (cp
- p
), "Ox");
492 if ((cp
= strstr(p
, "NetBSD")) != NULL
)
493 mandoc_msg(MANDOCERR_BX
, mdoc
->parse
,
494 ln
, pos
+ (cp
- p
), "Nx");
495 if ((cp
= strstr(p
, "FreeBSD")) != NULL
)
496 mandoc_msg(MANDOCERR_BX
, mdoc
->parse
,
497 ln
, pos
+ (cp
- p
), "Fx");
498 if ((cp
= strstr(p
, "DragonFly")) != NULL
)
499 mandoc_msg(MANDOCERR_BX
, mdoc
->parse
,
500 ln
, pos
+ (cp
- p
), "Dx");
503 while ((cp
= strstr(cp
+ 1, "()")) != NULL
) {
504 for (cpr
= cp
- 1; cpr
>= p
; cpr
--)
505 if (*cpr
!= '_' && !isalnum((unsigned char)*cpr
))
507 if ((cpr
< p
|| *cpr
== ' ') && cpr
+ 1 < cp
) {
509 mandoc_vmsg(MANDOCERR_FUNC
, mdoc
->parse
,
511 "%.*s()", (int)(cp
- cpr
), cpr
);
517 post_abort(POST_ARGS
)
523 post_delim(POST_ARGS
)
525 const struct roff_node
*nch
;
530 tok
= mdoc
->last
->tok
;
531 nch
= mdoc
->last
->last
;
532 if (nch
== NULL
|| nch
->type
!= ROFFT_TEXT
)
534 lc
= strchr(nch
->string
, '\0') - 1;
535 if (lc
< nch
->string
)
537 delim
= mdoc_isdelim(lc
);
538 if (delim
== DELIM_NONE
|| delim
== DELIM_OPEN
)
540 if (*lc
== ')' && (tok
== MDOC_Nd
|| tok
== MDOC_Sh
||
541 tok
== MDOC_Ss
|| tok
== MDOC_Fo
))
544 mandoc_vmsg(MANDOCERR_DELIM
, mdoc
->parse
,
545 nch
->line
, nch
->pos
+ (lc
- nch
->string
),
546 "%s%s %s", roff_name
[tok
],
547 nch
== mdoc
->last
->child
? "" : " ...", nch
->string
);
551 post_delim_nb(POST_ARGS
)
553 const struct roff_node
*nch
;
560 * Find candidates: at least two bytes,
561 * the last one a closing or middle delimiter.
564 tok
= mdoc
->last
->tok
;
565 nch
= mdoc
->last
->last
;
566 if (nch
== NULL
|| nch
->type
!= ROFFT_TEXT
)
568 lc
= strchr(nch
->string
, '\0') - 1;
569 if (lc
<= nch
->string
)
571 delim
= mdoc_isdelim(lc
);
572 if (delim
== DELIM_NONE
|| delim
== DELIM_OPEN
)
576 * Reduce false positives by allowing various cases.
579 /* Escaped delimiters. */
580 if (lc
> nch
->string
+ 1 && lc
[-2] == '\\' &&
581 (lc
[-1] == '&' || lc
[-1] == 'e'))
584 /* Specific byte sequences. */
587 for (cp
= lc
; cp
>= nch
->string
; cp
--)
592 if (lc
> nch
->string
+ 1 && lc
[-2] == '.' && lc
[-1] == '.')
606 for (cp
= lc
; cp
>= nch
->string
; cp
--)
611 if (lc
== nch
->string
+ 1 && lc
[-1] == '|')
617 /* Exactly two non-alphanumeric bytes. */
618 if (lc
== nch
->string
+ 1 && !isalnum((unsigned char)lc
[-1]))
621 /* At least three alphabetic words with a sentence ending. */
622 if (strchr("!.:?", *lc
) != NULL
&& (tok
== MDOC_Em
||
623 tok
== MDOC_Li
|| tok
== MDOC_Pq
|| tok
== MDOC_Sy
)) {
625 for (cp
= lc
- 1; cp
>= nch
->string
; cp
--) {
628 if (cp
> nch
->string
&& cp
[-1] == ',')
630 } else if (isalpha((unsigned int)*cp
)) {
638 mandoc_vmsg(MANDOCERR_DELIM_NB
, mdoc
->parse
,
639 nch
->line
, nch
->pos
+ (lc
- nch
->string
),
640 "%s%s %s", roff_name
[tok
],
641 nch
== mdoc
->last
->child
? "" : " ...", nch
->string
);
645 post_bl_norm(POST_ARGS
)
648 struct mdoc_argv
*argv
, *wa
;
650 enum mdocargt mdoclt
;
653 n
= mdoc
->last
->parent
;
654 n
->norm
->Bl
.type
= LIST__NONE
;
657 * First figure out which kind of list to use: bind ourselves to
658 * the first mentioned list type and warn about any remaining
659 * ones. If we find no list type, we default to LIST_item.
662 wa
= (n
->args
== NULL
) ? NULL
: n
->args
->argv
;
663 mdoclt
= MDOC_ARG_MAX
;
664 for (i
= 0; n
->args
&& i
< (int)n
->args
->argc
; i
++) {
665 argv
= n
->args
->argv
+ i
;
668 /* Set list types. */
702 /* Set list arguments. */
704 if (n
->norm
->Bl
.comp
)
705 mandoc_msg(MANDOCERR_ARG_REP
,
706 mdoc
->parse
, argv
->line
,
707 argv
->pos
, "Bl -compact");
708 n
->norm
->Bl
.comp
= 1;
713 mandoc_msg(MANDOCERR_ARG_EMPTY
,
714 mdoc
->parse
, argv
->line
,
715 argv
->pos
, "Bl -width");
716 n
->norm
->Bl
.width
= "0n";
719 if (NULL
!= n
->norm
->Bl
.width
)
720 mandoc_vmsg(MANDOCERR_ARG_REP
,
721 mdoc
->parse
, argv
->line
,
722 argv
->pos
, "Bl -width %s",
724 rewrite_macro2len(mdoc
, argv
->value
);
725 n
->norm
->Bl
.width
= argv
->value
[0];
729 mandoc_msg(MANDOCERR_ARG_EMPTY
,
730 mdoc
->parse
, argv
->line
,
731 argv
->pos
, "Bl -offset");
734 if (NULL
!= n
->norm
->Bl
.offs
)
735 mandoc_vmsg(MANDOCERR_ARG_REP
,
736 mdoc
->parse
, argv
->line
,
737 argv
->pos
, "Bl -offset %s",
739 rewrite_macro2len(mdoc
, argv
->value
);
740 n
->norm
->Bl
.offs
= argv
->value
[0];
745 if (LIST__NONE
== lt
)
749 /* Check: multiple list types. */
751 if (LIST__NONE
!= n
->norm
->Bl
.type
) {
752 mandoc_vmsg(MANDOCERR_BL_REP
,
753 mdoc
->parse
, n
->line
, n
->pos
,
754 "Bl -%s", mdoc_argnames
[argv
->arg
]);
758 /* The list type should come first. */
760 if (n
->norm
->Bl
.width
||
763 mandoc_vmsg(MANDOCERR_BL_LATETYPE
,
764 mdoc
->parse
, n
->line
, n
->pos
, "Bl -%s",
765 mdoc_argnames
[n
->args
->argv
[0].arg
]);
767 n
->norm
->Bl
.type
= lt
;
768 if (LIST_column
== lt
) {
769 n
->norm
->Bl
.ncols
= argv
->sz
;
770 n
->norm
->Bl
.cols
= (void *)argv
->value
;
774 /* Allow lists to default to LIST_item. */
776 if (LIST__NONE
== n
->norm
->Bl
.type
) {
777 mandoc_msg(MANDOCERR_BL_NOTYPE
, mdoc
->parse
,
778 n
->line
, n
->pos
, "Bl");
779 n
->norm
->Bl
.type
= LIST_item
;
784 * Validate the width field. Some list types don't need width
785 * types and should be warned about them. Others should have it
786 * and must also be warned. Yet others have a default and need
790 switch (n
->norm
->Bl
.type
) {
792 if (n
->norm
->Bl
.width
== NULL
)
793 mandoc_msg(MANDOCERR_BL_NOWIDTH
, mdoc
->parse
,
794 n
->line
, n
->pos
, "Bl -tag");
801 if (n
->norm
->Bl
.width
!= NULL
)
802 mandoc_vmsg(MANDOCERR_BL_SKIPW
, mdoc
->parse
,
803 wa
->line
, wa
->pos
, "Bl -%s",
804 mdoc_argnames
[mdoclt
]);
805 n
->norm
->Bl
.width
= NULL
;
810 if (n
->norm
->Bl
.width
== NULL
)
811 n
->norm
->Bl
.width
= "2n";
814 if (n
->norm
->Bl
.width
== NULL
)
815 n
->norm
->Bl
.width
= "3n";
826 struct mdoc_argv
*argv
;
831 for (i
= 0; n
->args
&& i
< (int)n
->args
->argc
; i
++) {
832 argv
= n
->args
->argv
+ i
;
852 mandoc_msg(MANDOCERR_BD_FILE
, mdoc
->parse
,
853 n
->line
, n
->pos
, NULL
);
857 mandoc_msg(MANDOCERR_ARG_EMPTY
,
858 mdoc
->parse
, argv
->line
,
859 argv
->pos
, "Bd -offset");
862 if (NULL
!= n
->norm
->Bd
.offs
)
863 mandoc_vmsg(MANDOCERR_ARG_REP
,
864 mdoc
->parse
, argv
->line
,
865 argv
->pos
, "Bd -offset %s",
867 rewrite_macro2len(mdoc
, argv
->value
);
868 n
->norm
->Bd
.offs
= argv
->value
[0];
871 if (n
->norm
->Bd
.comp
)
872 mandoc_msg(MANDOCERR_ARG_REP
,
873 mdoc
->parse
, argv
->line
,
874 argv
->pos
, "Bd -compact");
875 n
->norm
->Bd
.comp
= 1;
880 if (DISP__NONE
== dt
)
883 if (DISP__NONE
== n
->norm
->Bd
.type
)
884 n
->norm
->Bd
.type
= dt
;
886 mandoc_vmsg(MANDOCERR_BD_REP
,
887 mdoc
->parse
, n
->line
, n
->pos
,
888 "Bd -%s", mdoc_argnames
[argv
->arg
]);
891 if (DISP__NONE
== n
->norm
->Bd
.type
) {
892 mandoc_msg(MANDOCERR_BD_NOTYPE
, mdoc
->parse
,
893 n
->line
, n
->pos
, "Bd");
894 n
->norm
->Bd
.type
= DISP_ragged
;
899 * Stand-alone line macros.
903 post_an_norm(POST_ARGS
)
906 struct mdoc_argv
*argv
;
913 for (i
= 1; i
< n
->args
->argc
; i
++) {
914 argv
= n
->args
->argv
+ i
;
915 mandoc_vmsg(MANDOCERR_AN_REP
,
916 mdoc
->parse
, argv
->line
, argv
->pos
,
917 "An -%s", mdoc_argnames
[argv
->arg
]);
920 argv
= n
->args
->argv
;
921 if (argv
->arg
== MDOC_Split
)
922 n
->norm
->An
.auth
= AUTH_split
;
923 else if (argv
->arg
== MDOC_Nosplit
)
924 n
->norm
->An
.auth
= AUTH_nosplit
;
936 if (n
->child
!= NULL
)
937 mandoc_vmsg(MANDOCERR_ARG_SKIP
, mdoc
->parse
, n
->line
,
938 n
->pos
, "%s %s", roff_name
[n
->tok
], n
->child
->string
);
940 while (n
->child
!= NULL
)
941 roff_node_delete(mdoc
, n
->child
);
943 roff_word_alloc(mdoc
, n
->line
, n
->pos
, n
->tok
== MDOC_Bt
?
944 "is currently in beta test." : "currently under development.");
945 mdoc
->last
->flags
|= NODE_EOS
| NODE_NOSRC
;
950 build_list(struct roff_man
*mdoc
, int tok
)
955 n
= mdoc
->last
->next
;
956 for (ic
= 1;; ic
++) {
957 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, tok
);
958 mdoc
->last
->flags
|= NODE_NOSRC
;
959 mdoc_node_relink(mdoc
, n
);
960 n
= mdoc
->last
= mdoc
->last
->parent
;
961 mdoc
->next
= ROFF_NEXT_SIBLING
;
964 if (ic
> 1 || n
->next
->next
!= NULL
) {
965 roff_word_alloc(mdoc
, n
->line
, n
->pos
, ",");
966 mdoc
->last
->flags
|= NODE_DELIMC
| NODE_NOSRC
;
968 n
= mdoc
->last
->next
;
969 if (n
->next
== NULL
) {
970 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "and");
971 mdoc
->last
->flags
|= NODE_NOSRC
;
985 mdoc
->next
= ROFF_NEXT_CHILD
;
986 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "The");
987 mdoc
->last
->flags
|= NODE_NOSRC
;
989 if (mdoc
->last
->next
!= NULL
)
990 ic
= build_list(mdoc
, MDOC_Nm
);
991 else if (mdoc
->meta
.name
!= NULL
) {
992 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Nm
);
993 mdoc
->last
->flags
|= NODE_NOSRC
;
994 roff_word_alloc(mdoc
, n
->line
, n
->pos
, mdoc
->meta
.name
);
995 mdoc
->last
->flags
|= NODE_NOSRC
;
996 mdoc
->last
= mdoc
->last
->parent
;
997 mdoc
->next
= ROFF_NEXT_SIBLING
;
1000 mandoc_msg(MANDOCERR_EX_NONAME
, mdoc
->parse
,
1001 n
->line
, n
->pos
, "Ex");
1005 roff_word_alloc(mdoc
, n
->line
, n
->pos
,
1006 ic
> 1 ? "utilities exit\\~0" : "utility exits\\~0");
1007 mdoc
->last
->flags
|= NODE_NOSRC
;
1008 roff_word_alloc(mdoc
, n
->line
, n
->pos
,
1009 "on success, and\\~>0 if an error occurs.");
1010 mdoc
->last
->flags
|= NODE_EOS
| NODE_NOSRC
;
1017 struct roff_node
*n
;
1020 post_delim_nb(mdoc
);
1023 assert(n
->child
->type
== ROFFT_TEXT
);
1024 mdoc
->next
= ROFF_NEXT_CHILD
;
1026 if ((p
= mdoc_a2lib(n
->child
->string
)) != NULL
) {
1027 n
->child
->flags
|= NODE_NOPRT
;
1028 roff_word_alloc(mdoc
, n
->line
, n
->pos
, p
);
1029 mdoc
->last
->flags
= NODE_NOSRC
;
1034 mandoc_vmsg(MANDOCERR_LB_BAD
, mdoc
->parse
, n
->child
->line
,
1035 n
->child
->pos
, "Lb %s", n
->child
->string
);
1037 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "library");
1038 mdoc
->last
->flags
= NODE_NOSRC
;
1039 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "\\(lq");
1040 mdoc
->last
->flags
= NODE_DELIMO
| NODE_NOSRC
;
1041 mdoc
->last
= mdoc
->last
->next
;
1042 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "\\(rq");
1043 mdoc
->last
->flags
= NODE_DELIMC
| NODE_NOSRC
;
1050 struct roff_node
*n
;
1056 mdoc
->next
= ROFF_NEXT_CHILD
;
1057 if (n
->child
!= NULL
) {
1058 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "The");
1059 mdoc
->last
->flags
|= NODE_NOSRC
;
1060 ic
= build_list(mdoc
, MDOC_Fn
);
1061 roff_word_alloc(mdoc
, n
->line
, n
->pos
,
1062 ic
> 1 ? "functions return" : "function returns");
1063 mdoc
->last
->flags
|= NODE_NOSRC
;
1064 roff_word_alloc(mdoc
, n
->line
, n
->pos
,
1065 "the value\\~0 if successful;");
1067 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "Upon successful "
1068 "completion, the value\\~0 is returned;");
1069 mdoc
->last
->flags
|= NODE_NOSRC
;
1071 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "otherwise "
1072 "the value\\~\\-1 is returned and the global variable");
1073 mdoc
->last
->flags
|= NODE_NOSRC
;
1074 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Va
);
1075 mdoc
->last
->flags
|= NODE_NOSRC
;
1076 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "errno");
1077 mdoc
->last
->flags
|= NODE_NOSRC
;
1078 mdoc
->last
= mdoc
->last
->parent
;
1079 mdoc
->next
= ROFF_NEXT_SIBLING
;
1080 roff_word_alloc(mdoc
, n
->line
, n
->pos
,
1081 "is set to indicate the error.");
1082 mdoc
->last
->flags
|= NODE_EOS
| NODE_NOSRC
;
1089 struct roff_node
*n
;
1094 if (n
->args
&& n
->args
->argc
== 1)
1095 if (n
->args
->argv
[0].arg
== MDOC_Std
)
1098 mandoc_msg(MANDOCERR_ARG_STD
, mdoc
->parse
,
1099 n
->line
, n
->pos
, roff_name
[n
->tok
]);
1105 struct roff_node
*n
, *nch
;
1110 assert(nch
->type
== ROFFT_TEXT
);
1112 if ((p
= mdoc_a2st(nch
->string
)) == NULL
) {
1113 mandoc_vmsg(MANDOCERR_ST_BAD
, mdoc
->parse
,
1114 nch
->line
, nch
->pos
, "St %s", nch
->string
);
1115 roff_node_delete(mdoc
, n
);
1119 nch
->flags
|= NODE_NOPRT
;
1120 mdoc
->next
= ROFF_NEXT_CHILD
;
1121 roff_word_alloc(mdoc
, nch
->line
, nch
->pos
, p
);
1122 mdoc
->last
->flags
|= NODE_NOSRC
;
1127 post_obsolete(POST_ARGS
)
1129 struct roff_node
*n
;
1132 if (n
->type
== ROFFT_ELEM
|| n
->type
== ROFFT_BLOCK
)
1133 mandoc_msg(MANDOCERR_MACRO_OBS
, mdoc
->parse
,
1134 n
->line
, n
->pos
, roff_name
[n
->tok
]);
1138 post_useless(POST_ARGS
)
1140 struct roff_node
*n
;
1143 mandoc_msg(MANDOCERR_MACRO_USELESS
, mdoc
->parse
,
1144 n
->line
, n
->pos
, roff_name
[n
->tok
]);
1154 struct roff_node
*np
, *nch
;
1157 * Unlike other data pointers, these are "housed" by the HEAD
1158 * element, which contains the goods.
1162 if (np
->type
!= ROFFT_HEAD
)
1165 assert(np
->parent
->type
== ROFFT_BLOCK
);
1166 assert(np
->parent
->tok
== MDOC_Bf
);
1168 /* Check the number of arguments. */
1171 if (np
->parent
->args
== NULL
) {
1173 mandoc_msg(MANDOCERR_BF_NOFONT
, mdoc
->parse
,
1174 np
->line
, np
->pos
, "Bf");
1180 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, mdoc
->parse
,
1181 nch
->line
, nch
->pos
, "Bf ... %s", nch
->string
);
1183 /* Extract argument into data. */
1185 if (np
->parent
->args
!= NULL
) {
1186 switch (np
->parent
->args
->argv
[0].arg
) {
1188 np
->norm
->Bf
.font
= FONT_Em
;
1191 np
->norm
->Bf
.font
= FONT_Li
;
1194 np
->norm
->Bf
.font
= FONT_Sy
;
1202 /* Extract parameter into data. */
1204 if ( ! strcmp(np
->child
->string
, "Em"))
1205 np
->norm
->Bf
.font
= FONT_Em
;
1206 else if ( ! strcmp(np
->child
->string
, "Li"))
1207 np
->norm
->Bf
.font
= FONT_Li
;
1208 else if ( ! strcmp(np
->child
->string
, "Sy"))
1209 np
->norm
->Bf
.font
= FONT_Sy
;
1211 mandoc_vmsg(MANDOCERR_BF_BADFONT
, mdoc
->parse
,
1212 np
->child
->line
, np
->child
->pos
,
1213 "Bf %s", np
->child
->string
);
1217 post_fname(POST_ARGS
)
1219 const struct roff_node
*n
;
1223 n
= mdoc
->last
->child
;
1224 pos
= strcspn(n
->string
, "()");
1225 cp
= n
->string
+ pos
;
1226 if ( ! (cp
[0] == '\0' || (cp
[0] == '(' && cp
[1] == '*')))
1227 mandoc_msg(MANDOCERR_FN_PAREN
, mdoc
->parse
,
1228 n
->line
, n
->pos
+ pos
, n
->string
);
1242 const struct roff_node
*n
;
1246 if (n
->type
!= ROFFT_HEAD
)
1249 if (n
->child
== NULL
) {
1250 mandoc_msg(MANDOCERR_FO_NOHEAD
, mdoc
->parse
,
1251 n
->line
, n
->pos
, "Fo");
1254 if (n
->child
!= n
->last
) {
1255 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, mdoc
->parse
,
1256 n
->child
->next
->line
, n
->child
->next
->pos
,
1257 "Fo ... %s", n
->child
->next
->string
);
1258 while (n
->child
!= n
->last
)
1259 roff_node_delete(mdoc
, n
->last
);
1269 const struct roff_node
*n
;
1272 for (n
= mdoc
->last
->child
; n
!= NULL
; n
= n
->next
) {
1273 for (cp
= n
->string
; *cp
!= '\0'; cp
++) {
1274 /* Ignore callbacks and alterations. */
1275 if (*cp
== '(' || *cp
== '{')
1279 mandoc_msg(MANDOCERR_FA_COMMA
, mdoc
->parse
,
1280 n
->line
, n
->pos
+ (cp
- n
->string
),
1285 post_delim_nb(mdoc
);
1291 struct roff_node
*n
;
1295 if (n
->sec
== SEC_NAME
&& n
->child
!= NULL
&&
1296 n
->child
->type
== ROFFT_TEXT
&& mdoc
->meta
.msec
!= NULL
)
1297 mandoc_xr_add(mdoc
->meta
.msec
, n
->child
->string
, -1, -1);
1299 if (n
->last
!= NULL
&& n
->last
->tok
== MDOC_Pp
)
1300 mdoc_node_relink(mdoc
, n
->last
);
1302 if (mdoc
->meta
.name
== NULL
)
1303 deroff(&mdoc
->meta
.name
, n
);
1305 if (mdoc
->meta
.name
== NULL
||
1306 (mdoc
->lastsec
== SEC_NAME
&& n
->child
== NULL
))
1307 mandoc_msg(MANDOCERR_NM_NONAME
, mdoc
->parse
,
1308 n
->line
, n
->pos
, "Nm");
1312 post_delim_nb(mdoc
);
1321 if ((n
->child
!= NULL
&& n
->child
->type
== ROFFT_TEXT
) ||
1322 mdoc
->meta
.name
== NULL
)
1325 mdoc
->next
= ROFF_NEXT_CHILD
;
1326 roff_word_alloc(mdoc
, n
->line
, n
->pos
, mdoc
->meta
.name
);
1327 mdoc
->last
->flags
|= NODE_NOSRC
;
1334 struct roff_node
*n
;
1338 if (n
->type
!= ROFFT_BODY
)
1341 if (n
->sec
!= SEC_NAME
)
1342 mandoc_msg(MANDOCERR_ND_LATE
, mdoc
->parse
,
1343 n
->line
, n
->pos
, "Nd");
1345 if (n
->child
== NULL
)
1346 mandoc_msg(MANDOCERR_ND_EMPTY
, mdoc
->parse
,
1347 n
->line
, n
->pos
, "Nd");
1355 post_display(POST_ARGS
)
1357 struct roff_node
*n
, *np
;
1362 if (n
->end
!= ENDBODY_NOT
) {
1363 if (n
->tok
== MDOC_Bd
&&
1364 n
->body
->parent
->args
== NULL
)
1365 roff_node_delete(mdoc
, n
);
1366 } else if (n
->child
== NULL
)
1367 mandoc_msg(MANDOCERR_BLK_EMPTY
, mdoc
->parse
,
1368 n
->line
, n
->pos
, roff_name
[n
->tok
]);
1369 else if (n
->tok
== MDOC_D1
)
1373 if (n
->tok
== MDOC_Bd
) {
1374 if (n
->args
== NULL
) {
1375 mandoc_msg(MANDOCERR_BD_NOARG
,
1376 mdoc
->parse
, n
->line
, n
->pos
, "Bd");
1377 mdoc
->next
= ROFF_NEXT_SIBLING
;
1378 while (n
->body
->child
!= NULL
)
1379 mdoc_node_relink(mdoc
,
1381 roff_node_delete(mdoc
, n
);
1387 for (np
= n
->parent
; np
!= NULL
; np
= np
->parent
) {
1388 if (np
->type
== ROFFT_BLOCK
&& np
->tok
== MDOC_Bd
) {
1389 mandoc_vmsg(MANDOCERR_BD_NEST
,
1390 mdoc
->parse
, n
->line
, n
->pos
,
1391 "%s in Bd", roff_name
[n
->tok
]);
1402 post_defaults(POST_ARGS
)
1404 struct roff_node
*nn
;
1406 if (mdoc
->last
->child
!= NULL
) {
1407 post_delim_nb(mdoc
);
1412 * The `Ar' defaults to "file ..." if no value is provided as an
1413 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
1414 * gets an empty string.
1420 mdoc
->next
= ROFF_NEXT_CHILD
;
1421 roff_word_alloc(mdoc
, nn
->line
, nn
->pos
, "file");
1422 mdoc
->last
->flags
|= NODE_NOSRC
;
1423 roff_word_alloc(mdoc
, nn
->line
, nn
->pos
, "...");
1424 mdoc
->last
->flags
|= NODE_NOSRC
;
1428 mdoc
->next
= ROFF_NEXT_CHILD
;
1429 roff_word_alloc(mdoc
, nn
->line
, nn
->pos
, "~");
1430 mdoc
->last
->flags
|= NODE_NOSRC
;
1441 struct roff_node
*n
, *nch
;
1448 * If we have a child, look it up in the standard keys. If a
1449 * key exist, use that instead of the child; if it doesn't,
1450 * prefix "AT&T UNIX " to the existing data.
1454 if (nch
!= NULL
&& ((att
= mdoc_a2att(nch
->string
)) == NULL
))
1455 mandoc_vmsg(MANDOCERR_AT_BAD
, mdoc
->parse
,
1456 nch
->line
, nch
->pos
, "At %s", nch
->string
);
1458 mdoc
->next
= ROFF_NEXT_CHILD
;
1460 roff_word_alloc(mdoc
, nch
->line
, nch
->pos
, att
);
1461 nch
->flags
|= NODE_NOPRT
;
1463 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "AT&T UNIX");
1464 mdoc
->last
->flags
|= NODE_NOSRC
;
1471 struct roff_node
*np
, *nch
;
1477 if (np
->norm
->An
.auth
== AUTH__NONE
) {
1479 mandoc_msg(MANDOCERR_MACRO_EMPTY
, mdoc
->parse
,
1480 np
->line
, np
->pos
, "An");
1482 post_delim_nb(mdoc
);
1483 } else if (nch
!= NULL
)
1484 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, mdoc
->parse
,
1485 nch
->line
, nch
->pos
, "An ... %s", nch
->string
);
1492 post_obsolete(mdoc
);
1493 if (mdoc
->last
->type
== ROFFT_BLOCK
)
1494 mdoc
->last
->norm
->Es
= mdoc
->last_es
;
1501 post_obsolete(mdoc
);
1502 mdoc
->last_es
= mdoc
->last
;
1508 struct roff_node
*n
;
1512 post_delim_nb(mdoc
);
1527 if (n
->child
== NULL
)
1529 v
= n
->child
->string
;
1530 if ((v
[0] != '0' && v
[0] != '1') || v
[1] != '.' ||
1531 v
[2] < '0' || v
[2] > '9' ||
1532 v
[3] < 'a' || v
[3] > 'z' || v
[4] != '\0')
1534 n
->child
->flags
|= NODE_NOPRT
;
1535 mdoc
->next
= ROFF_NEXT_CHILD
;
1536 roff_word_alloc(mdoc
, n
->child
->line
, n
->child
->pos
, v
);
1537 v
= mdoc
->last
->string
;
1538 v
[3] = toupper((unsigned char)v
[3]);
1539 mdoc
->last
->flags
|= NODE_NOSRC
;
1551 mdoc
->next
= ROFF_NEXT_CHILD
;
1552 roff_word_alloc(mdoc
, n
->line
, n
->pos
, os
);
1553 mdoc
->last
->flags
|= NODE_NOSRC
;
1560 struct roff_node
*nbl
, *nit
, *nch
;
1567 if (nit
->type
!= ROFFT_BLOCK
)
1570 nbl
= nit
->parent
->parent
;
1571 lt
= nbl
->norm
->Bl
.type
;
1579 if (nit
->head
->child
== NULL
)
1580 mandoc_vmsg(MANDOCERR_IT_NOHEAD
,
1581 mdoc
->parse
, nit
->line
, nit
->pos
,
1583 mdoc_argnames
[nbl
->args
->argv
[0].arg
]);
1589 if (nit
->body
== NULL
|| nit
->body
->child
== NULL
)
1590 mandoc_vmsg(MANDOCERR_IT_NOBODY
,
1591 mdoc
->parse
, nit
->line
, nit
->pos
,
1593 mdoc_argnames
[nbl
->args
->argv
[0].arg
]);
1596 if ((nch
= nit
->head
->child
) != NULL
)
1597 mandoc_vmsg(MANDOCERR_ARG_SKIP
, mdoc
->parse
,
1598 nit
->line
, nit
->pos
, "It %s",
1599 nch
->string
== NULL
? roff_name
[nch
->tok
] :
1603 cols
= (int)nbl
->norm
->Bl
.ncols
;
1605 assert(nit
->head
->child
== NULL
);
1607 if (nit
->head
->next
->child
== NULL
&&
1608 nit
->head
->next
->next
== NULL
) {
1609 mandoc_msg(MANDOCERR_MACRO_EMPTY
, mdoc
->parse
,
1610 nit
->line
, nit
->pos
, "It");
1611 roff_node_delete(mdoc
, nit
);
1616 for (nch
= nit
->child
; nch
!= NULL
; nch
= nch
->next
) {
1617 if (nch
->type
!= ROFFT_BODY
)
1619 if (i
++ && nch
->flags
& NODE_LINE
)
1620 mandoc_msg(MANDOCERR_TA_LINE
, mdoc
->parse
,
1621 nch
->line
, nch
->pos
, "Ta");
1623 if (i
< cols
|| i
> cols
+ 1)
1624 mandoc_vmsg(MANDOCERR_BL_COL
,
1625 mdoc
->parse
, nit
->line
, nit
->pos
,
1626 "%d columns, %d cells", cols
, i
);
1627 else if (nit
->head
->next
->child
!= NULL
&&
1628 nit
->head
->next
->child
->line
> nit
->line
)
1629 mandoc_msg(MANDOCERR_IT_NOARG
, mdoc
->parse
,
1630 nit
->line
, nit
->pos
, "Bl -column It");
1638 post_bl_block(POST_ARGS
)
1640 struct roff_node
*n
, *ni
, *nc
;
1645 for (ni
= n
->body
->child
; ni
!= NULL
; ni
= ni
->next
) {
1646 if (ni
->body
== NULL
)
1648 nc
= ni
->body
->last
;
1649 while (nc
!= NULL
) {
1658 if (ni
->next
== NULL
) {
1659 mandoc_msg(MANDOCERR_PAR_MOVE
,
1660 mdoc
->parse
, nc
->line
, nc
->pos
,
1661 roff_name
[nc
->tok
]);
1662 mdoc_node_relink(mdoc
, nc
);
1663 } else if (n
->norm
->Bl
.comp
== 0 &&
1664 n
->norm
->Bl
.type
!= LIST_column
) {
1665 mandoc_vmsg(MANDOCERR_PAR_SKIP
,
1666 mdoc
->parse
, nc
->line
, nc
->pos
,
1667 "%s before It", roff_name
[nc
->tok
]);
1668 roff_node_delete(mdoc
, nc
);
1671 nc
= ni
->body
->last
;
1677 * If the argument of -offset or -width is a macro,
1678 * replace it with the associated default width.
1681 rewrite_macro2len(struct roff_man
*mdoc
, char **arg
)
1688 else if ( ! strcmp(*arg
, "Ds"))
1690 else if ((tok
= roffhash_find(mdoc
->mdocmac
, *arg
, 0)) == TOKEN_NONE
)
1693 width
= macro2len(tok
);
1696 mandoc_asprintf(arg
, "%zun", width
);
1700 post_bl_head(POST_ARGS
)
1702 struct roff_node
*nbl
, *nh
, *nch
, *nnext
;
1703 struct mdoc_argv
*argv
;
1709 if (nh
->norm
->Bl
.type
!= LIST_column
) {
1710 if ((nch
= nh
->child
) == NULL
)
1712 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, mdoc
->parse
,
1713 nch
->line
, nch
->pos
, "Bl ... %s", nch
->string
);
1714 while (nch
!= NULL
) {
1715 roff_node_delete(mdoc
, nch
);
1722 * Append old-style lists, where the column width specifiers
1723 * trail as macro parameters, to the new-style ("normal-form")
1724 * lists where they're argument values following -column.
1727 if (nh
->child
== NULL
)
1731 for (j
= 0; j
< (int)nbl
->args
->argc
; j
++)
1732 if (nbl
->args
->argv
[j
].arg
== MDOC_Column
)
1735 assert(j
< (int)nbl
->args
->argc
);
1738 * Accommodate for new-style groff column syntax. Shuffle the
1739 * child nodes, all of which must be TEXT, as arguments for the
1740 * column field. Then, delete the head children.
1743 argv
= nbl
->args
->argv
+ j
;
1745 for (nch
= nh
->child
; nch
!= NULL
; nch
= nch
->next
)
1747 argv
->value
= mandoc_reallocarray(argv
->value
,
1748 argv
->sz
, sizeof(char *));
1750 nh
->norm
->Bl
.ncols
= argv
->sz
;
1751 nh
->norm
->Bl
.cols
= (void *)argv
->value
;
1753 for (nch
= nh
->child
; nch
!= NULL
; nch
= nnext
) {
1754 argv
->value
[i
++] = nch
->string
;
1757 roff_node_delete(NULL
, nch
);
1765 struct roff_node
*nparent
, *nprev
; /* of the Bl block */
1766 struct roff_node
*nblock
, *nbody
; /* of the Bl */
1767 struct roff_node
*nchild
, *nnext
; /* of the Bl body */
1768 const char *prev_Er
;
1772 switch (nbody
->type
) {
1774 post_bl_block(mdoc
);
1784 if (nbody
->end
!= ENDBODY_NOT
)
1787 nchild
= nbody
->child
;
1788 if (nchild
== NULL
) {
1789 mandoc_msg(MANDOCERR_BLK_EMPTY
, mdoc
->parse
,
1790 nbody
->line
, nbody
->pos
, "Bl");
1793 while (nchild
!= NULL
) {
1794 nnext
= nchild
->next
;
1795 if (nchild
->tok
== MDOC_It
||
1796 (nchild
->tok
== MDOC_Sm
&&
1797 nnext
!= NULL
&& nnext
->tok
== MDOC_It
)) {
1803 * In .Bl -column, the first rows may be implicit,
1804 * that is, they may not start with .It macros.
1805 * Such rows may be followed by nodes generated on the
1806 * roff level, for example .TS, which cannot be moved
1807 * out of the list. In that case, wrap such roff nodes
1808 * into an implicit row.
1811 if (nchild
->prev
!= NULL
) {
1812 mdoc
->last
= nchild
;
1813 mdoc
->next
= ROFF_NEXT_SIBLING
;
1814 roff_block_alloc(mdoc
, nchild
->line
,
1815 nchild
->pos
, MDOC_It
);
1816 roff_head_alloc(mdoc
, nchild
->line
,
1817 nchild
->pos
, MDOC_It
);
1818 mdoc
->next
= ROFF_NEXT_SIBLING
;
1819 roff_body_alloc(mdoc
, nchild
->line
,
1820 nchild
->pos
, MDOC_It
);
1821 while (nchild
->tok
!= MDOC_It
) {
1822 mdoc_node_relink(mdoc
, nchild
);
1823 if ((nchild
= nnext
) == NULL
)
1825 nnext
= nchild
->next
;
1826 mdoc
->next
= ROFF_NEXT_SIBLING
;
1832 mandoc_msg(MANDOCERR_BL_MOVE
, mdoc
->parse
,
1833 nchild
->line
, nchild
->pos
, roff_name
[nchild
->tok
]);
1836 * Move the node out of the Bl block.
1837 * First, collect all required node pointers.
1840 nblock
= nbody
->parent
;
1841 nprev
= nblock
->prev
;
1842 nparent
= nblock
->parent
;
1845 * Unlink this child.
1848 nbody
->child
= nnext
;
1855 * Relink this child.
1858 nchild
->parent
= nparent
;
1859 nchild
->prev
= nprev
;
1860 nchild
->next
= nblock
;
1862 nblock
->prev
= nchild
;
1864 nparent
->child
= nchild
;
1866 nprev
->next
= nchild
;
1871 if (mdoc
->meta
.os_e
!= MANDOC_OS_NETBSD
)
1875 for (nchild
= nbody
->child
; nchild
!= NULL
; nchild
= nchild
->next
) {
1876 if (nchild
->tok
!= MDOC_It
)
1878 if ((nnext
= nchild
->head
->child
) == NULL
)
1880 if (nnext
->type
== ROFFT_BLOCK
)
1881 nnext
= nnext
->body
->child
;
1882 if (nnext
== NULL
|| nnext
->tok
!= MDOC_Er
)
1884 nnext
= nnext
->child
;
1885 if (prev_Er
!= NULL
) {
1886 order
= strcmp(prev_Er
, nnext
->string
);
1888 mandoc_vmsg(MANDOCERR_ER_ORDER
,
1889 mdoc
->parse
, nnext
->line
, nnext
->pos
,
1890 "Er %s %s (NetBSD)",
1891 prev_Er
, nnext
->string
);
1892 else if (order
== 0)
1893 mandoc_vmsg(MANDOCERR_ER_REP
,
1894 mdoc
->parse
, nnext
->line
, nnext
->pos
,
1895 "Er %s (NetBSD)", prev_Er
);
1897 prev_Er
= nnext
->string
;
1904 struct roff_node
*n
;
1908 if (n
->type
== ROFFT_BLOCK
&& n
->body
->child
== NULL
) {
1909 mandoc_msg(MANDOCERR_BLK_EMPTY
,
1910 mdoc
->parse
, n
->line
, n
->pos
, "Bk");
1911 roff_node_delete(mdoc
, n
);
1918 struct roff_node
*nch
;
1920 nch
= mdoc
->last
->child
;
1923 mdoc
->flags
^= MDOC_SMOFF
;
1927 assert(nch
->type
== ROFFT_TEXT
);
1929 if ( ! strcmp(nch
->string
, "on")) {
1930 mdoc
->flags
&= ~MDOC_SMOFF
;
1933 if ( ! strcmp(nch
->string
, "off")) {
1934 mdoc
->flags
|= MDOC_SMOFF
;
1938 mandoc_vmsg(MANDOCERR_SM_BAD
,
1939 mdoc
->parse
, nch
->line
, nch
->pos
,
1940 "%s %s", roff_name
[mdoc
->last
->tok
], nch
->string
);
1941 mdoc_node_relink(mdoc
, nch
);
1946 post_root(POST_ARGS
)
1948 const char *openbsd_arch
[] = {
1949 "alpha", "amd64", "arm64", "armv7", "hppa", "i386",
1950 "landisk", "loongson", "luna88k", "macppc", "mips64",
1951 "octeon", "sgi", "socppc", "sparc64", NULL
1953 const char *netbsd_arch
[] = {
1954 "acorn26", "acorn32", "algor", "alpha", "amiga",
1956 "bebox", "cats", "cesfic", "cobalt", "dreamcast",
1957 "emips", "evbarm", "evbmips", "evbppc", "evbsh3", "evbsh5",
1958 "hp300", "hpcarm", "hpcmips", "hpcsh", "hppa",
1959 "i386", "ibmnws", "luna68k",
1960 "mac68k", "macppc", "mipsco", "mmeye", "mvme68k", "mvmeppc",
1961 "netwinder", "news68k", "newsmips", "next68k",
1962 "pc532", "playstation2", "pmax", "pmppc", "prep",
1963 "sandpoint", "sbmips", "sgimips", "shark",
1964 "sparc", "sparc64", "sun2", "sun3",
1965 "vax", "walnut", "x68k", "x86", "x86_64", "xen", NULL
1967 const char **arches
[] = { NULL
, netbsd_arch
, openbsd_arch
};
1969 struct roff_node
*n
;
1972 /* Add missing prologue data. */
1974 if (mdoc
->meta
.date
== NULL
)
1975 mdoc
->meta
.date
= mdoc
->quick
? mandoc_strdup("") :
1976 mandoc_normdate(mdoc
, NULL
, 0, 0);
1978 if (mdoc
->meta
.title
== NULL
) {
1979 mandoc_msg(MANDOCERR_DT_NOTITLE
,
1980 mdoc
->parse
, 0, 0, "EOF");
1981 mdoc
->meta
.title
= mandoc_strdup("UNTITLED");
1984 if (mdoc
->meta
.vol
== NULL
)
1985 mdoc
->meta
.vol
= mandoc_strdup("LOCAL");
1987 if (mdoc
->meta
.os
== NULL
) {
1988 mandoc_msg(MANDOCERR_OS_MISSING
,
1989 mdoc
->parse
, 0, 0, NULL
);
1990 mdoc
->meta
.os
= mandoc_strdup("");
1991 } else if (mdoc
->meta
.os_e
&&
1992 (mdoc
->meta
.rcsids
& (1 << mdoc
->meta
.os_e
)) == 0)
1993 mandoc_msg(MANDOCERR_RCS_MISSING
, mdoc
->parse
, 0, 0,
1994 mdoc
->meta
.os_e
== MANDOC_OS_OPENBSD
?
1995 "(OpenBSD)" : "(NetBSD)");
1997 if (mdoc
->meta
.arch
!= NULL
&&
1998 (arch
= arches
[mdoc
->meta
.os_e
]) != NULL
) {
1999 while (*arch
!= NULL
&& strcmp(*arch
, mdoc
->meta
.arch
))
2001 if (*arch
== NULL
) {
2002 n
= mdoc
->first
->child
;
2003 while (n
->tok
!= MDOC_Dt
||
2005 n
->child
->next
== NULL
||
2006 n
->child
->next
->next
== NULL
)
2008 n
= n
->child
->next
->next
;
2009 mandoc_vmsg(MANDOCERR_ARCH_BAD
,
2010 mdoc
->parse
, n
->line
, n
->pos
,
2011 "Dt ... %s %s", mdoc
->meta
.arch
,
2012 mdoc
->meta
.os_e
== MANDOC_OS_OPENBSD
?
2013 "(OpenBSD)" : "(NetBSD)");
2017 /* Check that we begin with a proper `Sh'. */
2019 n
= mdoc
->first
->child
;
2021 (n
->type
== ROFFT_COMMENT
||
2022 (n
->tok
>= MDOC_Dd
&&
2023 mdoc_macro(n
->tok
)->flags
& MDOC_PROLOGUE
)))
2027 mandoc_msg(MANDOCERR_DOC_EMPTY
, mdoc
->parse
, 0, 0, NULL
);
2028 else if (n
->tok
!= MDOC_Sh
)
2029 mandoc_msg(MANDOCERR_SEC_BEFORE
, mdoc
->parse
,
2030 n
->line
, n
->pos
, roff_name
[n
->tok
]);
2036 struct roff_node
*np
, *nch
, *next
, *prev
;
2041 if (np
->type
!= ROFFT_BODY
)
2044 if (np
->child
== NULL
) {
2045 mandoc_msg(MANDOCERR_RS_EMPTY
, mdoc
->parse
,
2046 np
->line
, np
->pos
, "Rs");
2051 * The full `Rs' block needs special handling to order the
2052 * sub-elements according to `rsord'. Pick through each element
2053 * and correctly order it. This is an insertion sort.
2057 for (nch
= np
->child
->next
; nch
!= NULL
; nch
= next
) {
2058 /* Determine order number of this child. */
2059 for (i
= 0; i
< RSORD_MAX
; i
++)
2060 if (rsord
[i
] == nch
->tok
)
2063 if (i
== RSORD_MAX
) {
2064 mandoc_msg(MANDOCERR_RS_BAD
, mdoc
->parse
,
2065 nch
->line
, nch
->pos
, roff_name
[nch
->tok
]);
2067 } else if (nch
->tok
== MDOC__J
|| nch
->tok
== MDOC__B
)
2068 np
->norm
->Rs
.quote_T
++;
2071 * Remove this child from the chain. This somewhat
2072 * repeats roff_node_unlink(), but since we're
2073 * just re-ordering, there's no need for the
2074 * full unlink process.
2077 if ((next
= nch
->next
) != NULL
)
2078 next
->prev
= nch
->prev
;
2080 if ((prev
= nch
->prev
) != NULL
)
2081 prev
->next
= nch
->next
;
2083 nch
->prev
= nch
->next
= NULL
;
2086 * Scan back until we reach a node that's
2087 * to be ordered before this child.
2090 for ( ; prev
; prev
= prev
->prev
) {
2091 /* Determine order of `prev'. */
2092 for (j
= 0; j
< RSORD_MAX
; j
++)
2093 if (rsord
[j
] == prev
->tok
)
2103 * Set this child back into its correct place
2104 * in front of the `prev' node.
2110 np
->child
->prev
= nch
;
2111 nch
->next
= np
->child
;
2115 prev
->next
->prev
= nch
;
2116 nch
->next
= prev
->next
;
2123 * For some arguments of some macros,
2124 * convert all breakable hyphens into ASCII_HYPH.
2127 post_hyph(POST_ARGS
)
2129 struct roff_node
*nch
;
2132 for (nch
= mdoc
->last
->child
; nch
!= NULL
; nch
= nch
->next
) {
2133 if (nch
->type
!= ROFFT_TEXT
)
2138 while (*(++cp
) != '\0')
2140 isalpha((unsigned char)cp
[-1]) &&
2141 isalpha((unsigned char)cp
[1]))
2149 struct roff_node
*n
;
2152 if (n
->flags
& NODE_LINE
||
2153 (n
->next
!= NULL
&& n
->next
->flags
& NODE_DELIMC
))
2154 mandoc_msg(MANDOCERR_NS_SKIP
, mdoc
->parse
,
2155 n
->line
, n
->pos
, NULL
);
2171 switch (mdoc
->last
->type
) {
2176 switch (mdoc
->lastsec
) {
2181 post_sh_see_also(mdoc
);
2184 post_sh_authors(mdoc
);
2196 post_sh_name(POST_ARGS
)
2198 struct roff_node
*n
;
2203 for (n
= mdoc
->last
->child
; n
!= NULL
; n
= n
->next
) {
2206 if (hasnm
&& n
->child
!= NULL
)
2207 mandoc_vmsg(MANDOCERR_NAMESEC_PUNCT
,
2208 mdoc
->parse
, n
->line
, n
->pos
,
2209 "Nm %s", n
->child
->string
);
2214 if (n
->next
!= NULL
)
2215 mandoc_msg(MANDOCERR_NAMESEC_ND
,
2216 mdoc
->parse
, n
->line
, n
->pos
, NULL
);
2219 if (n
->type
== ROFFT_TEXT
&&
2220 n
->string
[0] == ',' && n
->string
[1] == '\0' &&
2221 n
->next
!= NULL
&& n
->next
->tok
== MDOC_Nm
) {
2227 mandoc_msg(MANDOCERR_NAMESEC_BAD
, mdoc
->parse
,
2228 n
->line
, n
->pos
, roff_name
[n
->tok
]);
2235 mandoc_msg(MANDOCERR_NAMESEC_NONM
, mdoc
->parse
,
2236 mdoc
->last
->line
, mdoc
->last
->pos
, NULL
);
2238 mandoc_msg(MANDOCERR_NAMESEC_NOND
, mdoc
->parse
,
2239 mdoc
->last
->line
, mdoc
->last
->pos
, NULL
);
2243 post_sh_see_also(POST_ARGS
)
2245 const struct roff_node
*n
;
2246 const char *name
, *sec
;
2247 const char *lastname
, *lastsec
, *lastpunct
;
2250 n
= mdoc
->last
->child
;
2251 lastname
= lastsec
= lastpunct
= NULL
;
2253 if (n
->tok
!= MDOC_Xr
||
2255 n
->child
->next
== NULL
)
2258 /* Process one .Xr node. */
2260 name
= n
->child
->string
;
2261 sec
= n
->child
->next
->string
;
2262 if (lastsec
!= NULL
) {
2263 if (lastpunct
[0] != ',' || lastpunct
[1] != '\0')
2264 mandoc_vmsg(MANDOCERR_XR_PUNCT
,
2265 mdoc
->parse
, n
->line
, n
->pos
,
2266 "%s before %s(%s)", lastpunct
,
2268 cmp
= strcmp(lastsec
, sec
);
2270 mandoc_vmsg(MANDOCERR_XR_ORDER
,
2271 mdoc
->parse
, n
->line
, n
->pos
,
2272 "%s(%s) after %s(%s)", name
,
2273 sec
, lastname
, lastsec
);
2274 else if (cmp
== 0 &&
2275 strcasecmp(lastname
, name
) > 0)
2276 mandoc_vmsg(MANDOCERR_XR_ORDER
,
2277 mdoc
->parse
, n
->line
, n
->pos
,
2278 "%s after %s", name
, lastname
);
2283 /* Process the following node. */
2288 if (n
->tok
== MDOC_Xr
) {
2292 if (n
->type
!= ROFFT_TEXT
)
2294 for (name
= n
->string
; *name
!= '\0'; name
++)
2295 if (isalpha((const unsigned char)*name
))
2297 lastpunct
= n
->string
;
2298 if (n
->next
== NULL
|| n
->next
->tok
== MDOC_Rs
)
2299 mandoc_vmsg(MANDOCERR_XR_PUNCT
, mdoc
->parse
,
2300 n
->line
, n
->pos
, "%s after %s(%s)",
2301 lastpunct
, lastname
, lastsec
);
2307 child_an(const struct roff_node
*n
)
2310 for (n
= n
->child
; n
!= NULL
; n
= n
->next
)
2311 if ((n
->tok
== MDOC_An
&& n
->child
!= NULL
) || child_an(n
))
2317 post_sh_authors(POST_ARGS
)
2320 if ( ! child_an(mdoc
->last
))
2321 mandoc_msg(MANDOCERR_AN_MISSING
, mdoc
->parse
,
2322 mdoc
->last
->line
, mdoc
->last
->pos
, NULL
);
2326 * Return an upper bound for the string distance (allowing
2327 * transpositions). Not a full Levenshtein implementation
2328 * because Levenshtein is quadratic in the string length
2329 * and this function is called for every standard name,
2330 * so the check for each custom name would be cubic.
2331 * The following crude heuristics is linear, resulting
2332 * in quadratic behaviour for checking one custom name,
2333 * which does not cause measurable slowdown.
2336 similar(const char *s1
, const char *s2
)
2338 const int maxdist
= 3;
2341 while (s1
[0] != '\0' && s2
[0] != '\0') {
2342 if (s1
[0] == s2
[0]) {
2347 if (++dist
> maxdist
)
2349 if (s1
[1] == s2
[1]) { /* replacement */
2352 } else if (s1
[0] == s2
[1] && s1
[1] == s2
[0]) {
2353 s1
+= 2; /* transposition */
2355 } else if (s1
[0] == s2
[1]) /* insertion */
2357 else if (s1
[1] == s2
[0]) /* deletion */
2362 dist
+= strlen(s1
) + strlen(s2
);
2363 return dist
> maxdist
? INT_MAX
: dist
;
2367 post_sh_head(POST_ARGS
)
2369 struct roff_node
*nch
;
2370 const char *goodsec
;
2371 const char *const *testsec
;
2376 * Process a new section. Sections are either "named" or
2377 * "custom". Custom sections are user-defined, while named ones
2378 * follow a conventional order and may only appear in certain
2382 sec
= mdoc
->last
->sec
;
2384 /* The NAME should be first. */
2386 if (sec
!= SEC_NAME
&& mdoc
->lastnamed
== SEC_NONE
)
2387 mandoc_vmsg(MANDOCERR_NAMESEC_FIRST
, mdoc
->parse
,
2388 mdoc
->last
->line
, mdoc
->last
->pos
, "Sh %s",
2389 sec
!= SEC_CUSTOM
? secnames
[sec
] :
2390 (nch
= mdoc
->last
->child
) == NULL
? "" :
2391 nch
->type
== ROFFT_TEXT
? nch
->string
:
2392 roff_name
[nch
->tok
]);
2394 /* The SYNOPSIS gets special attention in other areas. */
2396 if (sec
== SEC_SYNOPSIS
) {
2397 roff_setreg(mdoc
->roff
, "nS", 1, '=');
2398 mdoc
->flags
|= MDOC_SYNOPSIS
;
2400 roff_setreg(mdoc
->roff
, "nS", 0, '=');
2401 mdoc
->flags
&= ~MDOC_SYNOPSIS
;
2404 /* Mark our last section. */
2406 mdoc
->lastsec
= sec
;
2408 /* We don't care about custom sections after this. */
2410 if (sec
== SEC_CUSTOM
) {
2411 if ((nch
= mdoc
->last
->child
) == NULL
||
2412 nch
->type
!= ROFFT_TEXT
|| nch
->next
!= NULL
)
2416 for (testsec
= secnames
+ 1; *testsec
!= NULL
; testsec
++) {
2417 dist
= similar(nch
->string
, *testsec
);
2418 if (dist
< mindist
) {
2423 if (goodsec
!= NULL
)
2424 mandoc_vmsg(MANDOCERR_SEC_TYPO
, mdoc
->parse
,
2425 nch
->line
, nch
->pos
, "Sh %s instead of %s",
2426 nch
->string
, goodsec
);
2431 * Check whether our non-custom section is being repeated or is
2435 if (sec
== mdoc
->lastnamed
)
2436 mandoc_vmsg(MANDOCERR_SEC_REP
, mdoc
->parse
,
2437 mdoc
->last
->line
, mdoc
->last
->pos
,
2438 "Sh %s", secnames
[sec
]);
2440 if (sec
< mdoc
->lastnamed
)
2441 mandoc_vmsg(MANDOCERR_SEC_ORDER
, mdoc
->parse
,
2442 mdoc
->last
->line
, mdoc
->last
->pos
,
2443 "Sh %s", secnames
[sec
]);
2445 /* Mark the last named section. */
2447 mdoc
->lastnamed
= sec
;
2449 /* Check particular section/manual conventions. */
2451 if (mdoc
->meta
.msec
== NULL
)
2457 if (*mdoc
->meta
.msec
== '4')
2459 goodsec
= "2, 3, 4, 9";
2461 case SEC_RETURN_VALUES
:
2463 if (*mdoc
->meta
.msec
== '2')
2465 if (*mdoc
->meta
.msec
== '3')
2467 if (NULL
== goodsec
)
2468 goodsec
= "2, 3, 9";
2471 if (*mdoc
->meta
.msec
== '9')
2473 if (NULL
== goodsec
)
2475 mandoc_vmsg(MANDOCERR_SEC_MSEC
, mdoc
->parse
,
2476 mdoc
->last
->line
, mdoc
->last
->pos
,
2477 "Sh %s for %s only", secnames
[sec
], goodsec
);
2487 struct roff_node
*n
, *nch
;
2491 if (nch
->next
== NULL
) {
2492 mandoc_vmsg(MANDOCERR_XR_NOSEC
, mdoc
->parse
,
2493 n
->line
, n
->pos
, "Xr %s", nch
->string
);
2495 assert(nch
->next
== n
->last
);
2496 if(mandoc_xr_add(nch
->next
->string
, nch
->string
,
2497 nch
->line
, nch
->pos
))
2498 mandoc_vmsg(MANDOCERR_XR_SELF
, mdoc
->parse
,
2499 nch
->line
, nch
->pos
, "Xr %s %s",
2500 nch
->string
, nch
->next
->string
);
2502 post_delim_nb(mdoc
);
2506 post_ignpar(POST_ARGS
)
2508 struct roff_node
*np
;
2510 switch (mdoc
->last
->type
) {
2524 if ((np
= mdoc
->last
->child
) != NULL
)
2525 if (np
->tok
== MDOC_Pp
) {
2526 mandoc_vmsg(MANDOCERR_PAR_SKIP
,
2527 mdoc
->parse
, np
->line
, np
->pos
,
2528 "%s after %s", roff_name
[np
->tok
],
2529 roff_name
[mdoc
->last
->tok
]);
2530 roff_node_delete(mdoc
, np
);
2533 if ((np
= mdoc
->last
->last
) != NULL
)
2534 if (np
->tok
== MDOC_Pp
) {
2535 mandoc_vmsg(MANDOCERR_PAR_SKIP
, mdoc
->parse
,
2536 np
->line
, np
->pos
, "%s at the end of %s",
2538 roff_name
[mdoc
->last
->tok
]);
2539 roff_node_delete(mdoc
, np
);
2544 post_prevpar(POST_ARGS
)
2546 struct roff_node
*n
;
2549 if (NULL
== n
->prev
)
2551 if (n
->type
!= ROFFT_ELEM
&& n
->type
!= ROFFT_BLOCK
)
2555 * Don't allow `Pp' prior to a paragraph-type
2556 * block: `Pp' or non-compact `Bd' or `Bl'.
2559 if (n
->prev
->tok
!= MDOC_Pp
&& n
->prev
->tok
!= ROFF_br
)
2561 if (n
->tok
== MDOC_Bl
&& n
->norm
->Bl
.comp
)
2563 if (n
->tok
== MDOC_Bd
&& n
->norm
->Bd
.comp
)
2565 if (n
->tok
== MDOC_It
&& n
->parent
->norm
->Bl
.comp
)
2568 mandoc_vmsg(MANDOCERR_PAR_SKIP
, mdoc
->parse
,
2569 n
->prev
->line
, n
->prev
->pos
, "%s before %s",
2570 roff_name
[n
->prev
->tok
], roff_name
[n
->tok
]);
2571 roff_node_delete(mdoc
, n
->prev
);
2577 struct roff_node
*np
;
2580 if (np
->tok
!= ROFF_br
&& np
->tok
!= ROFF_sp
)
2583 if (np
->tok
== ROFF_sp
) {
2584 if (np
->child
!= NULL
&& np
->child
->next
!= NULL
)
2585 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, mdoc
->parse
,
2586 np
->child
->next
->line
, np
->child
->next
->pos
,
2587 "sp ... %s", np
->child
->next
->string
);
2588 } else if (np
->child
!= NULL
)
2589 mandoc_vmsg(MANDOCERR_ARG_SKIP
,
2590 mdoc
->parse
, np
->line
, np
->pos
, "%s %s",
2591 roff_name
[np
->tok
], np
->child
->string
);
2593 if ((np
= mdoc
->last
->prev
) == NULL
) {
2594 np
= mdoc
->last
->parent
;
2595 if (np
->tok
!= MDOC_Sh
&& np
->tok
!= MDOC_Ss
)
2597 } else if (np
->tok
!= MDOC_Pp
&&
2598 (mdoc
->last
->tok
!= ROFF_br
||
2599 (np
->tok
!= ROFF_sp
&& np
->tok
!= ROFF_br
)))
2602 mandoc_vmsg(MANDOCERR_PAR_SKIP
, mdoc
->parse
,
2603 mdoc
->last
->line
, mdoc
->last
->pos
, "%s after %s",
2604 roff_name
[mdoc
->last
->tok
], roff_name
[np
->tok
]);
2605 roff_node_delete(mdoc
, mdoc
->last
);
2611 struct roff_node
*n
;
2615 n
->flags
|= NODE_NOPRT
;
2617 if (mdoc
->meta
.date
!= NULL
) {
2618 mandoc_msg(MANDOCERR_PROLOG_REP
, mdoc
->parse
,
2619 n
->line
, n
->pos
, "Dd");
2620 free(mdoc
->meta
.date
);
2621 } else if (mdoc
->flags
& MDOC_PBODY
)
2622 mandoc_msg(MANDOCERR_PROLOG_LATE
, mdoc
->parse
,
2623 n
->line
, n
->pos
, "Dd");
2624 else if (mdoc
->meta
.title
!= NULL
)
2625 mandoc_msg(MANDOCERR_PROLOG_ORDER
, mdoc
->parse
,
2626 n
->line
, n
->pos
, "Dd after Dt");
2627 else if (mdoc
->meta
.os
!= NULL
)
2628 mandoc_msg(MANDOCERR_PROLOG_ORDER
, mdoc
->parse
,
2629 n
->line
, n
->pos
, "Dd after Os");
2631 if (n
->child
== NULL
|| n
->child
->string
[0] == '\0') {
2632 mdoc
->meta
.date
= mdoc
->quick
? mandoc_strdup("") :
2633 mandoc_normdate(mdoc
, NULL
, n
->line
, n
->pos
);
2638 deroff(&datestr
, n
);
2640 mdoc
->meta
.date
= datestr
;
2642 mdoc
->meta
.date
= mandoc_normdate(mdoc
,
2643 datestr
, n
->line
, n
->pos
);
2651 struct roff_node
*nn
, *n
;
2656 n
->flags
|= NODE_NOPRT
;
2658 if (mdoc
->flags
& MDOC_PBODY
) {
2659 mandoc_msg(MANDOCERR_DT_LATE
, mdoc
->parse
,
2660 n
->line
, n
->pos
, "Dt");
2664 if (mdoc
->meta
.title
!= NULL
)
2665 mandoc_msg(MANDOCERR_PROLOG_REP
, mdoc
->parse
,
2666 n
->line
, n
->pos
, "Dt");
2667 else if (mdoc
->meta
.os
!= NULL
)
2668 mandoc_msg(MANDOCERR_PROLOG_ORDER
, mdoc
->parse
,
2669 n
->line
, n
->pos
, "Dt after Os");
2671 free(mdoc
->meta
.title
);
2672 free(mdoc
->meta
.msec
);
2673 free(mdoc
->meta
.vol
);
2674 free(mdoc
->meta
.arch
);
2676 mdoc
->meta
.title
= NULL
;
2677 mdoc
->meta
.msec
= NULL
;
2678 mdoc
->meta
.vol
= NULL
;
2679 mdoc
->meta
.arch
= NULL
;
2681 /* Mandatory first argument: title. */
2684 if (nn
== NULL
|| *nn
->string
== '\0') {
2685 mandoc_msg(MANDOCERR_DT_NOTITLE
,
2686 mdoc
->parse
, n
->line
, n
->pos
, "Dt");
2687 mdoc
->meta
.title
= mandoc_strdup("UNTITLED");
2689 mdoc
->meta
.title
= mandoc_strdup(nn
->string
);
2691 /* Check that all characters are uppercase. */
2693 for (p
= nn
->string
; *p
!= '\0'; p
++)
2694 if (islower((unsigned char)*p
)) {
2695 mandoc_vmsg(MANDOCERR_TITLE_CASE
,
2696 mdoc
->parse
, nn
->line
,
2697 nn
->pos
+ (p
- nn
->string
),
2698 "Dt %s", nn
->string
);
2703 /* Mandatory second argument: section. */
2709 mandoc_vmsg(MANDOCERR_MSEC_MISSING
,
2710 mdoc
->parse
, n
->line
, n
->pos
,
2711 "Dt %s", mdoc
->meta
.title
);
2712 mdoc
->meta
.vol
= mandoc_strdup("LOCAL");
2713 return; /* msec and arch remain NULL. */
2716 mdoc
->meta
.msec
= mandoc_strdup(nn
->string
);
2718 /* Infer volume title from section number. */
2720 cp
= mandoc_a2msec(nn
->string
);
2722 mandoc_vmsg(MANDOCERR_MSEC_BAD
, mdoc
->parse
,
2723 nn
->line
, nn
->pos
, "Dt ... %s", nn
->string
);
2724 mdoc
->meta
.vol
= mandoc_strdup(nn
->string
);
2726 mdoc
->meta
.vol
= mandoc_strdup(cp
);
2728 /* Optional third argument: architecture. */
2730 if ((nn
= nn
->next
) == NULL
)
2733 for (p
= nn
->string
; *p
!= '\0'; p
++)
2734 *p
= tolower((unsigned char)*p
);
2735 mdoc
->meta
.arch
= mandoc_strdup(nn
->string
);
2737 /* Ignore fourth and later arguments. */
2739 if ((nn
= nn
->next
) != NULL
)
2740 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, mdoc
->parse
,
2741 nn
->line
, nn
->pos
, "Dt ... %s", nn
->string
);
2747 struct roff_node
*n
, *nch
;
2750 post_delim_nb(mdoc
);
2756 macro
= !strcmp(nch
->string
, "Open") ? "Ox" :
2757 !strcmp(nch
->string
, "Net") ? "Nx" :
2758 !strcmp(nch
->string
, "Free") ? "Fx" :
2759 !strcmp(nch
->string
, "DragonFly") ? "Dx" : NULL
;
2761 mandoc_msg(MANDOCERR_BX
, mdoc
->parse
,
2762 n
->line
, n
->pos
, macro
);
2765 mdoc
->next
= ROFF_NEXT_SIBLING
;
2766 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Ns
);
2767 mdoc
->last
->flags
|= NODE_NOSRC
;
2768 mdoc
->next
= ROFF_NEXT_SIBLING
;
2770 mdoc
->next
= ROFF_NEXT_CHILD
;
2771 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "BSD");
2772 mdoc
->last
->flags
|= NODE_NOSRC
;
2779 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Ns
);
2780 mdoc
->last
->flags
|= NODE_NOSRC
;
2781 mdoc
->next
= ROFF_NEXT_SIBLING
;
2782 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "-");
2783 mdoc
->last
->flags
|= NODE_NOSRC
;
2784 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Ns
);
2785 mdoc
->last
->flags
|= NODE_NOSRC
;
2789 * Make `Bx's second argument always start with an uppercase
2790 * letter. Groff checks if it's an "accepted" term, but we just
2791 * uppercase blindly.
2794 *nch
->string
= (char)toupper((unsigned char)*nch
->string
);
2801 struct utsname utsname
;
2802 static char *defbuf
;
2804 struct roff_node
*n
;
2807 n
->flags
|= NODE_NOPRT
;
2809 if (mdoc
->meta
.os
!= NULL
)
2810 mandoc_msg(MANDOCERR_PROLOG_REP
, mdoc
->parse
,
2811 n
->line
, n
->pos
, "Os");
2812 else if (mdoc
->flags
& MDOC_PBODY
)
2813 mandoc_msg(MANDOCERR_PROLOG_LATE
, mdoc
->parse
,
2814 n
->line
, n
->pos
, "Os");
2819 * Set the operating system by way of the `Os' macro.
2820 * The order of precedence is:
2821 * 1. the argument of the `Os' macro, unless empty
2822 * 2. the -Ios=foo command line argument, if provided
2823 * 3. -DOSNAME="\"foo\"", if provided during compilation
2824 * 4. "sysname release" from uname(3)
2827 free(mdoc
->meta
.os
);
2828 mdoc
->meta
.os
= NULL
;
2829 deroff(&mdoc
->meta
.os
, n
);
2833 if (mdoc
->os_s
!= NULL
) {
2834 mdoc
->meta
.os
= mandoc_strdup(mdoc
->os_s
);
2839 mdoc
->meta
.os
= mandoc_strdup(OSNAME
);
2841 if (defbuf
== NULL
) {
2842 if (uname(&utsname
) == -1) {
2843 mandoc_msg(MANDOCERR_OS_UNAME
, mdoc
->parse
,
2844 n
->line
, n
->pos
, "Os");
2845 defbuf
= mandoc_strdup("UNKNOWN");
2847 mandoc_asprintf(&defbuf
, "%s %s",
2848 utsname
.sysname
, utsname
.release
);
2850 mdoc
->meta
.os
= mandoc_strdup(defbuf
);
2854 if (mdoc
->meta
.os_e
== MANDOC_OS_OTHER
) {
2855 if (strstr(mdoc
->meta
.os
, "OpenBSD") != NULL
)
2856 mdoc
->meta
.os_e
= MANDOC_OS_OPENBSD
;
2857 else if (strstr(mdoc
->meta
.os
, "NetBSD") != NULL
)
2858 mdoc
->meta
.os_e
= MANDOC_OS_NETBSD
;
2862 * This is the earliest point where we can check
2863 * Mdocdate conventions because we don't know
2864 * the operating system earlier.
2867 if (n
->child
!= NULL
)
2868 mandoc_vmsg(MANDOCERR_OS_ARG
, mdoc
->parse
,
2869 n
->child
->line
, n
->child
->pos
,
2870 "Os %s (%s)", n
->child
->string
,
2871 mdoc
->meta
.os_e
== MANDOC_OS_OPENBSD
?
2872 "OpenBSD" : "NetBSD");
2874 while (n
->tok
!= MDOC_Dd
)
2875 if ((n
= n
->prev
) == NULL
)
2877 if ((n
= n
->child
) == NULL
)
2879 if (strncmp(n
->string
, "$" "Mdocdate", 9)) {
2880 if (mdoc
->meta
.os_e
== MANDOC_OS_OPENBSD
)
2881 mandoc_vmsg(MANDOCERR_MDOCDATE_MISSING
,
2882 mdoc
->parse
, n
->line
, n
->pos
,
2883 "Dd %s (OpenBSD)", n
->string
);
2885 if (mdoc
->meta
.os_e
== MANDOC_OS_NETBSD
)
2886 mandoc_vmsg(MANDOCERR_MDOCDATE
,
2887 mdoc
->parse
, n
->line
, n
->pos
,
2888 "Dd %s (NetBSD)", n
->string
);
2893 mdoc_a2sec(const char *p
)
2897 for (i
= 0; i
< (int)SEC__MAX
; i
++)
2898 if (secnames
[i
] && 0 == strcmp(p
, secnames
[i
]))
2899 return (enum roff_sec
)i
;
2905 macro2len(enum roff_tok macro
)