1 /* $Id: mdoc_validate.c,v 1.364 2018/12/04 02:53:51 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
) {
377 assert(n
->tok
>= MDOC_Dd
&& n
->tok
< MDOC_MAX
);
378 p
= mdoc_valids
+ (n
->tok
- MDOC_Dd
);
388 check_args(struct roff_man
*mdoc
, struct roff_node
*n
)
395 assert(n
->args
->argc
);
396 for (i
= 0; i
< (int)n
->args
->argc
; i
++)
397 check_argv(mdoc
, n
, &n
->args
->argv
[i
]);
401 check_argv(struct roff_man
*mdoc
, struct roff_node
*n
, struct mdoc_argv
*v
)
405 for (i
= 0; i
< (int)v
->sz
; i
++)
406 check_text(mdoc
, v
->line
, v
->pos
, v
->value
[i
]);
410 check_text(struct roff_man
*mdoc
, int ln
, int pos
, char *p
)
414 if (MDOC_LITERAL
& mdoc
->flags
)
417 for (cp
= p
; NULL
!= (p
= strchr(p
, '\t')); p
++)
418 mandoc_msg(MANDOCERR_FI_TAB
, mdoc
->parse
,
419 ln
, pos
+ (int)(p
- cp
), NULL
);
423 check_text_em(struct roff_man
*mdoc
, int ln
, int pos
, char *p
)
425 const struct roff_node
*np
, *nn
;
428 np
= mdoc
->last
->prev
;
429 nn
= mdoc
->last
->next
;
431 /* Look for em-dashes wrongly encoded as "--". */
433 for (cp
= p
; *cp
!= '\0'; cp
++) {
434 if (cp
[0] != '-' || cp
[1] != '-')
438 /* Skip input sequences of more than two '-'. */
446 /* Skip "--" directly attached to something else. */
448 if ((cp
- p
> 1 && cp
[-2] != ' ') ||
449 (cp
[1] != '\0' && cp
[1] != ' '))
452 /* Require a letter right before or right afterwards. */
455 isalpha((unsigned char)cp
[-3]) :
457 np
->type
== ROFFT_TEXT
&&
458 *np
->string
!= '\0' &&
459 isalpha((unsigned char)np
->string
[
460 strlen(np
->string
) - 1])) ||
461 (cp
[1] != '\0' && cp
[2] != '\0' ?
462 isalpha((unsigned char)cp
[2]) :
464 nn
->type
== ROFFT_TEXT
&&
465 isalpha((unsigned char)*nn
->string
))) {
466 mandoc_msg(MANDOCERR_DASHDASH
, mdoc
->parse
,
467 ln
, pos
+ (int)(cp
- p
) - 1, NULL
);
474 check_toptext(struct roff_man
*mdoc
, int ln
, int pos
, const char *p
)
476 const char *cp
, *cpr
;
481 if ((cp
= strstr(p
, "OpenBSD")) != NULL
)
482 mandoc_msg(MANDOCERR_BX
, mdoc
->parse
,
483 ln
, pos
+ (cp
- p
), "Ox");
484 if ((cp
= strstr(p
, "NetBSD")) != NULL
)
485 mandoc_msg(MANDOCERR_BX
, mdoc
->parse
,
486 ln
, pos
+ (cp
- p
), "Nx");
487 if ((cp
= strstr(p
, "FreeBSD")) != NULL
)
488 mandoc_msg(MANDOCERR_BX
, mdoc
->parse
,
489 ln
, pos
+ (cp
- p
), "Fx");
490 if ((cp
= strstr(p
, "DragonFly")) != NULL
)
491 mandoc_msg(MANDOCERR_BX
, mdoc
->parse
,
492 ln
, pos
+ (cp
- p
), "Dx");
495 while ((cp
= strstr(cp
+ 1, "()")) != NULL
) {
496 for (cpr
= cp
- 1; cpr
>= p
; cpr
--)
497 if (*cpr
!= '_' && !isalnum((unsigned char)*cpr
))
499 if ((cpr
< p
|| *cpr
== ' ') && cpr
+ 1 < cp
) {
501 mandoc_vmsg(MANDOCERR_FUNC
, mdoc
->parse
,
503 "%.*s()", (int)(cp
- cpr
), cpr
);
509 post_abort(POST_ARGS
)
515 post_delim(POST_ARGS
)
517 const struct roff_node
*nch
;
522 tok
= mdoc
->last
->tok
;
523 nch
= mdoc
->last
->last
;
524 if (nch
== NULL
|| nch
->type
!= ROFFT_TEXT
)
526 lc
= strchr(nch
->string
, '\0') - 1;
527 if (lc
< nch
->string
)
529 delim
= mdoc_isdelim(lc
);
530 if (delim
== DELIM_NONE
|| delim
== DELIM_OPEN
)
532 if (*lc
== ')' && (tok
== MDOC_Nd
|| tok
== MDOC_Sh
||
533 tok
== MDOC_Ss
|| tok
== MDOC_Fo
))
536 mandoc_vmsg(MANDOCERR_DELIM
, mdoc
->parse
,
537 nch
->line
, nch
->pos
+ (lc
- nch
->string
),
538 "%s%s %s", roff_name
[tok
],
539 nch
== mdoc
->last
->child
? "" : " ...", nch
->string
);
543 post_delim_nb(POST_ARGS
)
545 const struct roff_node
*nch
;
552 * Find candidates: at least two bytes,
553 * the last one a closing or middle delimiter.
556 tok
= mdoc
->last
->tok
;
557 nch
= mdoc
->last
->last
;
558 if (nch
== NULL
|| nch
->type
!= ROFFT_TEXT
)
560 lc
= strchr(nch
->string
, '\0') - 1;
561 if (lc
<= nch
->string
)
563 delim
= mdoc_isdelim(lc
);
564 if (delim
== DELIM_NONE
|| delim
== DELIM_OPEN
)
568 * Reduce false positives by allowing various cases.
571 /* Escaped delimiters. */
572 if (lc
> nch
->string
+ 1 && lc
[-2] == '\\' &&
573 (lc
[-1] == '&' || lc
[-1] == 'e'))
576 /* Specific byte sequences. */
579 for (cp
= lc
; cp
>= nch
->string
; cp
--)
584 if (lc
> nch
->string
+ 1 && lc
[-2] == '.' && lc
[-1] == '.')
598 for (cp
= lc
; cp
>= nch
->string
; cp
--)
603 if (lc
== nch
->string
+ 1 && lc
[-1] == '|')
609 /* Exactly two non-alphanumeric bytes. */
610 if (lc
== nch
->string
+ 1 && !isalnum((unsigned char)lc
[-1]))
613 /* At least three alphabetic words with a sentence ending. */
614 if (strchr("!.:?", *lc
) != NULL
&& (tok
== MDOC_Em
||
615 tok
== MDOC_Li
|| tok
== MDOC_Pq
|| tok
== MDOC_Sy
)) {
617 for (cp
= lc
- 1; cp
>= nch
->string
; cp
--) {
620 if (cp
> nch
->string
&& cp
[-1] == ',')
622 } else if (isalpha((unsigned int)*cp
)) {
630 mandoc_vmsg(MANDOCERR_DELIM_NB
, mdoc
->parse
,
631 nch
->line
, nch
->pos
+ (lc
- nch
->string
),
632 "%s%s %s", roff_name
[tok
],
633 nch
== mdoc
->last
->child
? "" : " ...", nch
->string
);
637 post_bl_norm(POST_ARGS
)
640 struct mdoc_argv
*argv
, *wa
;
642 enum mdocargt mdoclt
;
645 n
= mdoc
->last
->parent
;
646 n
->norm
->Bl
.type
= LIST__NONE
;
649 * First figure out which kind of list to use: bind ourselves to
650 * the first mentioned list type and warn about any remaining
651 * ones. If we find no list type, we default to LIST_item.
654 wa
= (n
->args
== NULL
) ? NULL
: n
->args
->argv
;
655 mdoclt
= MDOC_ARG_MAX
;
656 for (i
= 0; n
->args
&& i
< (int)n
->args
->argc
; i
++) {
657 argv
= n
->args
->argv
+ i
;
660 /* Set list types. */
694 /* Set list arguments. */
696 if (n
->norm
->Bl
.comp
)
697 mandoc_msg(MANDOCERR_ARG_REP
,
698 mdoc
->parse
, argv
->line
,
699 argv
->pos
, "Bl -compact");
700 n
->norm
->Bl
.comp
= 1;
705 mandoc_msg(MANDOCERR_ARG_EMPTY
,
706 mdoc
->parse
, argv
->line
,
707 argv
->pos
, "Bl -width");
708 n
->norm
->Bl
.width
= "0n";
711 if (NULL
!= n
->norm
->Bl
.width
)
712 mandoc_vmsg(MANDOCERR_ARG_REP
,
713 mdoc
->parse
, argv
->line
,
714 argv
->pos
, "Bl -width %s",
716 rewrite_macro2len(mdoc
, argv
->value
);
717 n
->norm
->Bl
.width
= argv
->value
[0];
721 mandoc_msg(MANDOCERR_ARG_EMPTY
,
722 mdoc
->parse
, argv
->line
,
723 argv
->pos
, "Bl -offset");
726 if (NULL
!= n
->norm
->Bl
.offs
)
727 mandoc_vmsg(MANDOCERR_ARG_REP
,
728 mdoc
->parse
, argv
->line
,
729 argv
->pos
, "Bl -offset %s",
731 rewrite_macro2len(mdoc
, argv
->value
);
732 n
->norm
->Bl
.offs
= argv
->value
[0];
737 if (LIST__NONE
== lt
)
741 /* Check: multiple list types. */
743 if (LIST__NONE
!= n
->norm
->Bl
.type
) {
744 mandoc_vmsg(MANDOCERR_BL_REP
,
745 mdoc
->parse
, n
->line
, n
->pos
,
746 "Bl -%s", mdoc_argnames
[argv
->arg
]);
750 /* The list type should come first. */
752 if (n
->norm
->Bl
.width
||
755 mandoc_vmsg(MANDOCERR_BL_LATETYPE
,
756 mdoc
->parse
, n
->line
, n
->pos
, "Bl -%s",
757 mdoc_argnames
[n
->args
->argv
[0].arg
]);
759 n
->norm
->Bl
.type
= lt
;
760 if (LIST_column
== lt
) {
761 n
->norm
->Bl
.ncols
= argv
->sz
;
762 n
->norm
->Bl
.cols
= (void *)argv
->value
;
766 /* Allow lists to default to LIST_item. */
768 if (LIST__NONE
== n
->norm
->Bl
.type
) {
769 mandoc_msg(MANDOCERR_BL_NOTYPE
, mdoc
->parse
,
770 n
->line
, n
->pos
, "Bl");
771 n
->norm
->Bl
.type
= LIST_item
;
776 * Validate the width field. Some list types don't need width
777 * types and should be warned about them. Others should have it
778 * and must also be warned. Yet others have a default and need
782 switch (n
->norm
->Bl
.type
) {
784 if (n
->norm
->Bl
.width
== NULL
)
785 mandoc_msg(MANDOCERR_BL_NOWIDTH
, mdoc
->parse
,
786 n
->line
, n
->pos
, "Bl -tag");
793 if (n
->norm
->Bl
.width
!= NULL
)
794 mandoc_vmsg(MANDOCERR_BL_SKIPW
, mdoc
->parse
,
795 wa
->line
, wa
->pos
, "Bl -%s",
796 mdoc_argnames
[mdoclt
]);
797 n
->norm
->Bl
.width
= NULL
;
802 if (n
->norm
->Bl
.width
== NULL
)
803 n
->norm
->Bl
.width
= "2n";
806 if (n
->norm
->Bl
.width
== NULL
)
807 n
->norm
->Bl
.width
= "3n";
818 struct mdoc_argv
*argv
;
823 for (i
= 0; n
->args
&& i
< (int)n
->args
->argc
; i
++) {
824 argv
= n
->args
->argv
+ i
;
844 mandoc_msg(MANDOCERR_BD_FILE
, mdoc
->parse
,
845 n
->line
, n
->pos
, NULL
);
849 mandoc_msg(MANDOCERR_ARG_EMPTY
,
850 mdoc
->parse
, argv
->line
,
851 argv
->pos
, "Bd -offset");
854 if (NULL
!= n
->norm
->Bd
.offs
)
855 mandoc_vmsg(MANDOCERR_ARG_REP
,
856 mdoc
->parse
, argv
->line
,
857 argv
->pos
, "Bd -offset %s",
859 rewrite_macro2len(mdoc
, argv
->value
);
860 n
->norm
->Bd
.offs
= argv
->value
[0];
863 if (n
->norm
->Bd
.comp
)
864 mandoc_msg(MANDOCERR_ARG_REP
,
865 mdoc
->parse
, argv
->line
,
866 argv
->pos
, "Bd -compact");
867 n
->norm
->Bd
.comp
= 1;
872 if (DISP__NONE
== dt
)
875 if (DISP__NONE
== n
->norm
->Bd
.type
)
876 n
->norm
->Bd
.type
= dt
;
878 mandoc_vmsg(MANDOCERR_BD_REP
,
879 mdoc
->parse
, n
->line
, n
->pos
,
880 "Bd -%s", mdoc_argnames
[argv
->arg
]);
883 if (DISP__NONE
== n
->norm
->Bd
.type
) {
884 mandoc_msg(MANDOCERR_BD_NOTYPE
, mdoc
->parse
,
885 n
->line
, n
->pos
, "Bd");
886 n
->norm
->Bd
.type
= DISP_ragged
;
891 * Stand-alone line macros.
895 post_an_norm(POST_ARGS
)
898 struct mdoc_argv
*argv
;
905 for (i
= 1; i
< n
->args
->argc
; i
++) {
906 argv
= n
->args
->argv
+ i
;
907 mandoc_vmsg(MANDOCERR_AN_REP
,
908 mdoc
->parse
, argv
->line
, argv
->pos
,
909 "An -%s", mdoc_argnames
[argv
->arg
]);
912 argv
= n
->args
->argv
;
913 if (argv
->arg
== MDOC_Split
)
914 n
->norm
->An
.auth
= AUTH_split
;
915 else if (argv
->arg
== MDOC_Nosplit
)
916 n
->norm
->An
.auth
= AUTH_nosplit
;
928 if (n
->child
!= NULL
)
929 mandoc_vmsg(MANDOCERR_ARG_SKIP
, mdoc
->parse
, n
->line
,
930 n
->pos
, "%s %s", roff_name
[n
->tok
], n
->child
->string
);
932 while (n
->child
!= NULL
)
933 roff_node_delete(mdoc
, n
->child
);
935 roff_word_alloc(mdoc
, n
->line
, n
->pos
, n
->tok
== MDOC_Bt
?
936 "is currently in beta test." : "currently under development.");
937 mdoc
->last
->flags
|= NODE_EOS
| NODE_NOSRC
;
942 build_list(struct roff_man
*mdoc
, int tok
)
947 n
= mdoc
->last
->next
;
948 for (ic
= 1;; ic
++) {
949 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, tok
);
950 mdoc
->last
->flags
|= NODE_NOSRC
;
951 roff_node_relink(mdoc
, n
);
952 n
= mdoc
->last
= mdoc
->last
->parent
;
953 mdoc
->next
= ROFF_NEXT_SIBLING
;
956 if (ic
> 1 || n
->next
->next
!= NULL
) {
957 roff_word_alloc(mdoc
, n
->line
, n
->pos
, ",");
958 mdoc
->last
->flags
|= NODE_DELIMC
| NODE_NOSRC
;
960 n
= mdoc
->last
->next
;
961 if (n
->next
== NULL
) {
962 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "and");
963 mdoc
->last
->flags
|= NODE_NOSRC
;
977 mdoc
->next
= ROFF_NEXT_CHILD
;
978 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "The");
979 mdoc
->last
->flags
|= NODE_NOSRC
;
981 if (mdoc
->last
->next
!= NULL
)
982 ic
= build_list(mdoc
, MDOC_Nm
);
983 else if (mdoc
->meta
.name
!= NULL
) {
984 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Nm
);
985 mdoc
->last
->flags
|= NODE_NOSRC
;
986 roff_word_alloc(mdoc
, n
->line
, n
->pos
, mdoc
->meta
.name
);
987 mdoc
->last
->flags
|= NODE_NOSRC
;
988 mdoc
->last
= mdoc
->last
->parent
;
989 mdoc
->next
= ROFF_NEXT_SIBLING
;
992 mandoc_msg(MANDOCERR_EX_NONAME
, mdoc
->parse
,
993 n
->line
, n
->pos
, "Ex");
997 roff_word_alloc(mdoc
, n
->line
, n
->pos
,
998 ic
> 1 ? "utilities exit\\~0" : "utility exits\\~0");
999 mdoc
->last
->flags
|= NODE_NOSRC
;
1000 roff_word_alloc(mdoc
, n
->line
, n
->pos
,
1001 "on success, and\\~>0 if an error occurs.");
1002 mdoc
->last
->flags
|= NODE_EOS
| NODE_NOSRC
;
1009 struct roff_node
*n
;
1012 post_delim_nb(mdoc
);
1015 assert(n
->child
->type
== ROFFT_TEXT
);
1016 mdoc
->next
= ROFF_NEXT_CHILD
;
1018 if ((p
= mdoc_a2lib(n
->child
->string
)) != NULL
) {
1019 n
->child
->flags
|= NODE_NOPRT
;
1020 roff_word_alloc(mdoc
, n
->line
, n
->pos
, p
);
1021 mdoc
->last
->flags
= NODE_NOSRC
;
1026 mandoc_vmsg(MANDOCERR_LB_BAD
, mdoc
->parse
, n
->child
->line
,
1027 n
->child
->pos
, "Lb %s", n
->child
->string
);
1029 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "library");
1030 mdoc
->last
->flags
= NODE_NOSRC
;
1031 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "\\(lq");
1032 mdoc
->last
->flags
= NODE_DELIMO
| NODE_NOSRC
;
1033 mdoc
->last
= mdoc
->last
->next
;
1034 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "\\(rq");
1035 mdoc
->last
->flags
= NODE_DELIMC
| NODE_NOSRC
;
1042 struct roff_node
*n
;
1048 mdoc
->next
= ROFF_NEXT_CHILD
;
1049 if (n
->child
!= NULL
) {
1050 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "The");
1051 mdoc
->last
->flags
|= NODE_NOSRC
;
1052 ic
= build_list(mdoc
, MDOC_Fn
);
1053 roff_word_alloc(mdoc
, n
->line
, n
->pos
,
1054 ic
> 1 ? "functions return" : "function returns");
1055 mdoc
->last
->flags
|= NODE_NOSRC
;
1056 roff_word_alloc(mdoc
, n
->line
, n
->pos
,
1057 "the value\\~0 if successful;");
1059 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "Upon successful "
1060 "completion, the value\\~0 is returned;");
1061 mdoc
->last
->flags
|= NODE_NOSRC
;
1063 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "otherwise "
1064 "the value\\~\\-1 is returned and the global variable");
1065 mdoc
->last
->flags
|= NODE_NOSRC
;
1066 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Va
);
1067 mdoc
->last
->flags
|= NODE_NOSRC
;
1068 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "errno");
1069 mdoc
->last
->flags
|= NODE_NOSRC
;
1070 mdoc
->last
= mdoc
->last
->parent
;
1071 mdoc
->next
= ROFF_NEXT_SIBLING
;
1072 roff_word_alloc(mdoc
, n
->line
, n
->pos
,
1073 "is set to indicate the error.");
1074 mdoc
->last
->flags
|= NODE_EOS
| NODE_NOSRC
;
1081 struct roff_node
*n
;
1086 if (n
->args
&& n
->args
->argc
== 1)
1087 if (n
->args
->argv
[0].arg
== MDOC_Std
)
1090 mandoc_msg(MANDOCERR_ARG_STD
, mdoc
->parse
,
1091 n
->line
, n
->pos
, roff_name
[n
->tok
]);
1097 struct roff_node
*n
, *nch
;
1102 assert(nch
->type
== ROFFT_TEXT
);
1104 if ((p
= mdoc_a2st(nch
->string
)) == NULL
) {
1105 mandoc_vmsg(MANDOCERR_ST_BAD
, mdoc
->parse
,
1106 nch
->line
, nch
->pos
, "St %s", nch
->string
);
1107 roff_node_delete(mdoc
, n
);
1111 nch
->flags
|= NODE_NOPRT
;
1112 mdoc
->next
= ROFF_NEXT_CHILD
;
1113 roff_word_alloc(mdoc
, nch
->line
, nch
->pos
, p
);
1114 mdoc
->last
->flags
|= NODE_NOSRC
;
1119 post_obsolete(POST_ARGS
)
1121 struct roff_node
*n
;
1124 if (n
->type
== ROFFT_ELEM
|| n
->type
== ROFFT_BLOCK
)
1125 mandoc_msg(MANDOCERR_MACRO_OBS
, mdoc
->parse
,
1126 n
->line
, n
->pos
, roff_name
[n
->tok
]);
1130 post_useless(POST_ARGS
)
1132 struct roff_node
*n
;
1135 mandoc_msg(MANDOCERR_MACRO_USELESS
, mdoc
->parse
,
1136 n
->line
, n
->pos
, roff_name
[n
->tok
]);
1146 struct roff_node
*np
, *nch
;
1149 * Unlike other data pointers, these are "housed" by the HEAD
1150 * element, which contains the goods.
1154 if (np
->type
!= ROFFT_HEAD
)
1157 assert(np
->parent
->type
== ROFFT_BLOCK
);
1158 assert(np
->parent
->tok
== MDOC_Bf
);
1160 /* Check the number of arguments. */
1163 if (np
->parent
->args
== NULL
) {
1165 mandoc_msg(MANDOCERR_BF_NOFONT
, mdoc
->parse
,
1166 np
->line
, np
->pos
, "Bf");
1172 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, mdoc
->parse
,
1173 nch
->line
, nch
->pos
, "Bf ... %s", nch
->string
);
1175 /* Extract argument into data. */
1177 if (np
->parent
->args
!= NULL
) {
1178 switch (np
->parent
->args
->argv
[0].arg
) {
1180 np
->norm
->Bf
.font
= FONT_Em
;
1183 np
->norm
->Bf
.font
= FONT_Li
;
1186 np
->norm
->Bf
.font
= FONT_Sy
;
1194 /* Extract parameter into data. */
1196 if ( ! strcmp(np
->child
->string
, "Em"))
1197 np
->norm
->Bf
.font
= FONT_Em
;
1198 else if ( ! strcmp(np
->child
->string
, "Li"))
1199 np
->norm
->Bf
.font
= FONT_Li
;
1200 else if ( ! strcmp(np
->child
->string
, "Sy"))
1201 np
->norm
->Bf
.font
= FONT_Sy
;
1203 mandoc_vmsg(MANDOCERR_BF_BADFONT
, mdoc
->parse
,
1204 np
->child
->line
, np
->child
->pos
,
1205 "Bf %s", np
->child
->string
);
1209 post_fname(POST_ARGS
)
1211 const struct roff_node
*n
;
1215 n
= mdoc
->last
->child
;
1216 pos
= strcspn(n
->string
, "()");
1217 cp
= n
->string
+ pos
;
1218 if ( ! (cp
[0] == '\0' || (cp
[0] == '(' && cp
[1] == '*')))
1219 mandoc_msg(MANDOCERR_FN_PAREN
, mdoc
->parse
,
1220 n
->line
, n
->pos
+ pos
, n
->string
);
1234 const struct roff_node
*n
;
1238 if (n
->type
!= ROFFT_HEAD
)
1241 if (n
->child
== NULL
) {
1242 mandoc_msg(MANDOCERR_FO_NOHEAD
, mdoc
->parse
,
1243 n
->line
, n
->pos
, "Fo");
1246 if (n
->child
!= n
->last
) {
1247 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, mdoc
->parse
,
1248 n
->child
->next
->line
, n
->child
->next
->pos
,
1249 "Fo ... %s", n
->child
->next
->string
);
1250 while (n
->child
!= n
->last
)
1251 roff_node_delete(mdoc
, n
->last
);
1261 const struct roff_node
*n
;
1264 for (n
= mdoc
->last
->child
; n
!= NULL
; n
= n
->next
) {
1265 for (cp
= n
->string
; *cp
!= '\0'; cp
++) {
1266 /* Ignore callbacks and alterations. */
1267 if (*cp
== '(' || *cp
== '{')
1271 mandoc_msg(MANDOCERR_FA_COMMA
, mdoc
->parse
,
1272 n
->line
, n
->pos
+ (cp
- n
->string
),
1277 post_delim_nb(mdoc
);
1283 struct roff_node
*n
;
1287 if (n
->sec
== SEC_NAME
&& n
->child
!= NULL
&&
1288 n
->child
->type
== ROFFT_TEXT
&& mdoc
->meta
.msec
!= NULL
)
1289 mandoc_xr_add(mdoc
->meta
.msec
, n
->child
->string
, -1, -1);
1291 if (n
->last
!= NULL
&& n
->last
->tok
== MDOC_Pp
)
1292 roff_node_relink(mdoc
, n
->last
);
1294 if (mdoc
->meta
.name
== NULL
)
1295 deroff(&mdoc
->meta
.name
, n
);
1297 if (mdoc
->meta
.name
== NULL
||
1298 (mdoc
->lastsec
== SEC_NAME
&& n
->child
== NULL
))
1299 mandoc_msg(MANDOCERR_NM_NONAME
, mdoc
->parse
,
1300 n
->line
, n
->pos
, "Nm");
1304 post_delim_nb(mdoc
);
1313 if ((n
->child
!= NULL
&& n
->child
->type
== ROFFT_TEXT
) ||
1314 mdoc
->meta
.name
== NULL
)
1317 mdoc
->next
= ROFF_NEXT_CHILD
;
1318 roff_word_alloc(mdoc
, n
->line
, n
->pos
, mdoc
->meta
.name
);
1319 mdoc
->last
->flags
|= NODE_NOSRC
;
1326 struct roff_node
*n
;
1330 if (n
->type
!= ROFFT_BODY
)
1333 if (n
->sec
!= SEC_NAME
)
1334 mandoc_msg(MANDOCERR_ND_LATE
, mdoc
->parse
,
1335 n
->line
, n
->pos
, "Nd");
1337 if (n
->child
== NULL
)
1338 mandoc_msg(MANDOCERR_ND_EMPTY
, mdoc
->parse
,
1339 n
->line
, n
->pos
, "Nd");
1347 post_display(POST_ARGS
)
1349 struct roff_node
*n
, *np
;
1354 if (n
->end
!= ENDBODY_NOT
) {
1355 if (n
->tok
== MDOC_Bd
&&
1356 n
->body
->parent
->args
== NULL
)
1357 roff_node_delete(mdoc
, n
);
1358 } else if (n
->child
== NULL
)
1359 mandoc_msg(MANDOCERR_BLK_EMPTY
, mdoc
->parse
,
1360 n
->line
, n
->pos
, roff_name
[n
->tok
]);
1361 else if (n
->tok
== MDOC_D1
)
1365 if (n
->tok
== MDOC_Bd
) {
1366 if (n
->args
== NULL
) {
1367 mandoc_msg(MANDOCERR_BD_NOARG
,
1368 mdoc
->parse
, n
->line
, n
->pos
, "Bd");
1369 mdoc
->next
= ROFF_NEXT_SIBLING
;
1370 while (n
->body
->child
!= NULL
)
1371 roff_node_relink(mdoc
,
1373 roff_node_delete(mdoc
, n
);
1379 for (np
= n
->parent
; np
!= NULL
; np
= np
->parent
) {
1380 if (np
->type
== ROFFT_BLOCK
&& np
->tok
== MDOC_Bd
) {
1381 mandoc_vmsg(MANDOCERR_BD_NEST
,
1382 mdoc
->parse
, n
->line
, n
->pos
,
1383 "%s in Bd", roff_name
[n
->tok
]);
1394 post_defaults(POST_ARGS
)
1396 struct roff_node
*nn
;
1398 if (mdoc
->last
->child
!= NULL
) {
1399 post_delim_nb(mdoc
);
1404 * The `Ar' defaults to "file ..." if no value is provided as an
1405 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
1406 * gets an empty string.
1412 mdoc
->next
= ROFF_NEXT_CHILD
;
1413 roff_word_alloc(mdoc
, nn
->line
, nn
->pos
, "file");
1414 mdoc
->last
->flags
|= NODE_NOSRC
;
1415 roff_word_alloc(mdoc
, nn
->line
, nn
->pos
, "...");
1416 mdoc
->last
->flags
|= NODE_NOSRC
;
1420 mdoc
->next
= ROFF_NEXT_CHILD
;
1421 roff_word_alloc(mdoc
, nn
->line
, nn
->pos
, "~");
1422 mdoc
->last
->flags
|= NODE_NOSRC
;
1433 struct roff_node
*n
, *nch
;
1440 * If we have a child, look it up in the standard keys. If a
1441 * key exist, use that instead of the child; if it doesn't,
1442 * prefix "AT&T UNIX " to the existing data.
1446 if (nch
!= NULL
&& ((att
= mdoc_a2att(nch
->string
)) == NULL
))
1447 mandoc_vmsg(MANDOCERR_AT_BAD
, mdoc
->parse
,
1448 nch
->line
, nch
->pos
, "At %s", nch
->string
);
1450 mdoc
->next
= ROFF_NEXT_CHILD
;
1452 roff_word_alloc(mdoc
, nch
->line
, nch
->pos
, att
);
1453 nch
->flags
|= NODE_NOPRT
;
1455 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "AT&T UNIX");
1456 mdoc
->last
->flags
|= NODE_NOSRC
;
1463 struct roff_node
*np
, *nch
;
1469 if (np
->norm
->An
.auth
== AUTH__NONE
) {
1471 mandoc_msg(MANDOCERR_MACRO_EMPTY
, mdoc
->parse
,
1472 np
->line
, np
->pos
, "An");
1474 post_delim_nb(mdoc
);
1475 } else if (nch
!= NULL
)
1476 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, mdoc
->parse
,
1477 nch
->line
, nch
->pos
, "An ... %s", nch
->string
);
1484 post_obsolete(mdoc
);
1485 if (mdoc
->last
->type
== ROFFT_BLOCK
)
1486 mdoc
->last
->norm
->Es
= mdoc
->last_es
;
1493 post_obsolete(mdoc
);
1494 mdoc
->last_es
= mdoc
->last
;
1500 struct roff_node
*n
;
1504 post_delim_nb(mdoc
);
1519 if (n
->child
== NULL
)
1521 v
= n
->child
->string
;
1522 if ((v
[0] != '0' && v
[0] != '1') || v
[1] != '.' ||
1523 v
[2] < '0' || v
[2] > '9' ||
1524 v
[3] < 'a' || v
[3] > 'z' || v
[4] != '\0')
1526 n
->child
->flags
|= NODE_NOPRT
;
1527 mdoc
->next
= ROFF_NEXT_CHILD
;
1528 roff_word_alloc(mdoc
, n
->child
->line
, n
->child
->pos
, v
);
1529 v
= mdoc
->last
->string
;
1530 v
[3] = toupper((unsigned char)v
[3]);
1531 mdoc
->last
->flags
|= NODE_NOSRC
;
1543 mdoc
->next
= ROFF_NEXT_CHILD
;
1544 roff_word_alloc(mdoc
, n
->line
, n
->pos
, os
);
1545 mdoc
->last
->flags
|= NODE_NOSRC
;
1552 struct roff_node
*nbl
, *nit
, *nch
;
1559 if (nit
->type
!= ROFFT_BLOCK
)
1562 nbl
= nit
->parent
->parent
;
1563 lt
= nbl
->norm
->Bl
.type
;
1571 if (nit
->head
->child
== NULL
)
1572 mandoc_vmsg(MANDOCERR_IT_NOHEAD
,
1573 mdoc
->parse
, nit
->line
, nit
->pos
,
1575 mdoc_argnames
[nbl
->args
->argv
[0].arg
]);
1581 if (nit
->body
== NULL
|| nit
->body
->child
== NULL
)
1582 mandoc_vmsg(MANDOCERR_IT_NOBODY
,
1583 mdoc
->parse
, nit
->line
, nit
->pos
,
1585 mdoc_argnames
[nbl
->args
->argv
[0].arg
]);
1588 if ((nch
= nit
->head
->child
) != NULL
)
1589 mandoc_vmsg(MANDOCERR_ARG_SKIP
, mdoc
->parse
,
1590 nit
->line
, nit
->pos
, "It %s",
1591 nch
->string
== NULL
? roff_name
[nch
->tok
] :
1595 cols
= (int)nbl
->norm
->Bl
.ncols
;
1597 assert(nit
->head
->child
== NULL
);
1599 if (nit
->head
->next
->child
== NULL
&&
1600 nit
->head
->next
->next
== NULL
) {
1601 mandoc_msg(MANDOCERR_MACRO_EMPTY
, mdoc
->parse
,
1602 nit
->line
, nit
->pos
, "It");
1603 roff_node_delete(mdoc
, nit
);
1608 for (nch
= nit
->child
; nch
!= NULL
; nch
= nch
->next
) {
1609 if (nch
->type
!= ROFFT_BODY
)
1611 if (i
++ && nch
->flags
& NODE_LINE
)
1612 mandoc_msg(MANDOCERR_TA_LINE
, mdoc
->parse
,
1613 nch
->line
, nch
->pos
, "Ta");
1615 if (i
< cols
|| i
> cols
+ 1)
1616 mandoc_vmsg(MANDOCERR_BL_COL
,
1617 mdoc
->parse
, nit
->line
, nit
->pos
,
1618 "%d columns, %d cells", cols
, i
);
1619 else if (nit
->head
->next
->child
!= NULL
&&
1620 nit
->head
->next
->child
->line
> nit
->line
)
1621 mandoc_msg(MANDOCERR_IT_NOARG
, mdoc
->parse
,
1622 nit
->line
, nit
->pos
, "Bl -column It");
1630 post_bl_block(POST_ARGS
)
1632 struct roff_node
*n
, *ni
, *nc
;
1637 for (ni
= n
->body
->child
; ni
!= NULL
; ni
= ni
->next
) {
1638 if (ni
->body
== NULL
)
1640 nc
= ni
->body
->last
;
1641 while (nc
!= NULL
) {
1650 if (ni
->next
== NULL
) {
1651 mandoc_msg(MANDOCERR_PAR_MOVE
,
1652 mdoc
->parse
, nc
->line
, nc
->pos
,
1653 roff_name
[nc
->tok
]);
1654 roff_node_relink(mdoc
, nc
);
1655 } else if (n
->norm
->Bl
.comp
== 0 &&
1656 n
->norm
->Bl
.type
!= LIST_column
) {
1657 mandoc_vmsg(MANDOCERR_PAR_SKIP
,
1658 mdoc
->parse
, nc
->line
, nc
->pos
,
1659 "%s before It", roff_name
[nc
->tok
]);
1660 roff_node_delete(mdoc
, nc
);
1663 nc
= ni
->body
->last
;
1669 * If the argument of -offset or -width is a macro,
1670 * replace it with the associated default width.
1673 rewrite_macro2len(struct roff_man
*mdoc
, char **arg
)
1680 else if ( ! strcmp(*arg
, "Ds"))
1682 else if ((tok
= roffhash_find(mdoc
->mdocmac
, *arg
, 0)) == TOKEN_NONE
)
1685 width
= macro2len(tok
);
1688 mandoc_asprintf(arg
, "%zun", width
);
1692 post_bl_head(POST_ARGS
)
1694 struct roff_node
*nbl
, *nh
, *nch
, *nnext
;
1695 struct mdoc_argv
*argv
;
1701 if (nh
->norm
->Bl
.type
!= LIST_column
) {
1702 if ((nch
= nh
->child
) == NULL
)
1704 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, mdoc
->parse
,
1705 nch
->line
, nch
->pos
, "Bl ... %s", nch
->string
);
1706 while (nch
!= NULL
) {
1707 roff_node_delete(mdoc
, nch
);
1714 * Append old-style lists, where the column width specifiers
1715 * trail as macro parameters, to the new-style ("normal-form")
1716 * lists where they're argument values following -column.
1719 if (nh
->child
== NULL
)
1723 for (j
= 0; j
< (int)nbl
->args
->argc
; j
++)
1724 if (nbl
->args
->argv
[j
].arg
== MDOC_Column
)
1727 assert(j
< (int)nbl
->args
->argc
);
1730 * Accommodate for new-style groff column syntax. Shuffle the
1731 * child nodes, all of which must be TEXT, as arguments for the
1732 * column field. Then, delete the head children.
1735 argv
= nbl
->args
->argv
+ j
;
1737 for (nch
= nh
->child
; nch
!= NULL
; nch
= nch
->next
)
1739 argv
->value
= mandoc_reallocarray(argv
->value
,
1740 argv
->sz
, sizeof(char *));
1742 nh
->norm
->Bl
.ncols
= argv
->sz
;
1743 nh
->norm
->Bl
.cols
= (void *)argv
->value
;
1745 for (nch
= nh
->child
; nch
!= NULL
; nch
= nnext
) {
1746 argv
->value
[i
++] = nch
->string
;
1749 roff_node_delete(NULL
, nch
);
1757 struct roff_node
*nparent
, *nprev
; /* of the Bl block */
1758 struct roff_node
*nblock
, *nbody
; /* of the Bl */
1759 struct roff_node
*nchild
, *nnext
; /* of the Bl body */
1760 const char *prev_Er
;
1764 switch (nbody
->type
) {
1766 post_bl_block(mdoc
);
1776 if (nbody
->end
!= ENDBODY_NOT
)
1779 nchild
= nbody
->child
;
1780 if (nchild
== NULL
) {
1781 mandoc_msg(MANDOCERR_BLK_EMPTY
, mdoc
->parse
,
1782 nbody
->line
, nbody
->pos
, "Bl");
1785 while (nchild
!= NULL
) {
1786 nnext
= nchild
->next
;
1787 if (nchild
->tok
== MDOC_It
||
1788 (nchild
->tok
== MDOC_Sm
&&
1789 nnext
!= NULL
&& nnext
->tok
== MDOC_It
)) {
1795 * In .Bl -column, the first rows may be implicit,
1796 * that is, they may not start with .It macros.
1797 * Such rows may be followed by nodes generated on the
1798 * roff level, for example .TS, which cannot be moved
1799 * out of the list. In that case, wrap such roff nodes
1800 * into an implicit row.
1803 if (nchild
->prev
!= NULL
) {
1804 mdoc
->last
= nchild
;
1805 mdoc
->next
= ROFF_NEXT_SIBLING
;
1806 roff_block_alloc(mdoc
, nchild
->line
,
1807 nchild
->pos
, MDOC_It
);
1808 roff_head_alloc(mdoc
, nchild
->line
,
1809 nchild
->pos
, MDOC_It
);
1810 mdoc
->next
= ROFF_NEXT_SIBLING
;
1811 roff_body_alloc(mdoc
, nchild
->line
,
1812 nchild
->pos
, MDOC_It
);
1813 while (nchild
->tok
!= MDOC_It
) {
1814 roff_node_relink(mdoc
, nchild
);
1815 if ((nchild
= nnext
) == NULL
)
1817 nnext
= nchild
->next
;
1818 mdoc
->next
= ROFF_NEXT_SIBLING
;
1824 mandoc_msg(MANDOCERR_BL_MOVE
, mdoc
->parse
,
1825 nchild
->line
, nchild
->pos
, roff_name
[nchild
->tok
]);
1828 * Move the node out of the Bl block.
1829 * First, collect all required node pointers.
1832 nblock
= nbody
->parent
;
1833 nprev
= nblock
->prev
;
1834 nparent
= nblock
->parent
;
1837 * Unlink this child.
1840 nbody
->child
= nnext
;
1847 * Relink this child.
1850 nchild
->parent
= nparent
;
1851 nchild
->prev
= nprev
;
1852 nchild
->next
= nblock
;
1854 nblock
->prev
= nchild
;
1856 nparent
->child
= nchild
;
1858 nprev
->next
= nchild
;
1863 if (mdoc
->meta
.os_e
!= MANDOC_OS_NETBSD
)
1867 for (nchild
= nbody
->child
; nchild
!= NULL
; nchild
= nchild
->next
) {
1868 if (nchild
->tok
!= MDOC_It
)
1870 if ((nnext
= nchild
->head
->child
) == NULL
)
1872 if (nnext
->type
== ROFFT_BLOCK
)
1873 nnext
= nnext
->body
->child
;
1874 if (nnext
== NULL
|| nnext
->tok
!= MDOC_Er
)
1876 nnext
= nnext
->child
;
1877 if (prev_Er
!= NULL
) {
1878 order
= strcmp(prev_Er
, nnext
->string
);
1880 mandoc_vmsg(MANDOCERR_ER_ORDER
,
1881 mdoc
->parse
, nnext
->line
, nnext
->pos
,
1882 "Er %s %s (NetBSD)",
1883 prev_Er
, nnext
->string
);
1884 else if (order
== 0)
1885 mandoc_vmsg(MANDOCERR_ER_REP
,
1886 mdoc
->parse
, nnext
->line
, nnext
->pos
,
1887 "Er %s (NetBSD)", prev_Er
);
1889 prev_Er
= nnext
->string
;
1896 struct roff_node
*n
;
1900 if (n
->type
== ROFFT_BLOCK
&& n
->body
->child
== NULL
) {
1901 mandoc_msg(MANDOCERR_BLK_EMPTY
,
1902 mdoc
->parse
, n
->line
, n
->pos
, "Bk");
1903 roff_node_delete(mdoc
, n
);
1910 struct roff_node
*nch
;
1912 nch
= mdoc
->last
->child
;
1915 mdoc
->flags
^= MDOC_SMOFF
;
1919 assert(nch
->type
== ROFFT_TEXT
);
1921 if ( ! strcmp(nch
->string
, "on")) {
1922 mdoc
->flags
&= ~MDOC_SMOFF
;
1925 if ( ! strcmp(nch
->string
, "off")) {
1926 mdoc
->flags
|= MDOC_SMOFF
;
1930 mandoc_vmsg(MANDOCERR_SM_BAD
,
1931 mdoc
->parse
, nch
->line
, nch
->pos
,
1932 "%s %s", roff_name
[mdoc
->last
->tok
], nch
->string
);
1933 roff_node_relink(mdoc
, nch
);
1938 post_root(POST_ARGS
)
1940 const char *openbsd_arch
[] = {
1941 "alpha", "amd64", "arm64", "armv7", "hppa", "i386",
1942 "landisk", "loongson", "luna88k", "macppc", "mips64",
1943 "octeon", "sgi", "socppc", "sparc64", NULL
1945 const char *netbsd_arch
[] = {
1946 "acorn26", "acorn32", "algor", "alpha", "amiga",
1948 "bebox", "cats", "cesfic", "cobalt", "dreamcast",
1949 "emips", "evbarm", "evbmips", "evbppc", "evbsh3", "evbsh5",
1950 "hp300", "hpcarm", "hpcmips", "hpcsh", "hppa",
1951 "i386", "ibmnws", "luna68k",
1952 "mac68k", "macppc", "mipsco", "mmeye", "mvme68k", "mvmeppc",
1953 "netwinder", "news68k", "newsmips", "next68k",
1954 "pc532", "playstation2", "pmax", "pmppc", "prep",
1955 "sandpoint", "sbmips", "sgimips", "shark",
1956 "sparc", "sparc64", "sun2", "sun3",
1957 "vax", "walnut", "x68k", "x86", "x86_64", "xen", NULL
1959 const char **arches
[] = { NULL
, netbsd_arch
, openbsd_arch
};
1961 struct roff_node
*n
;
1964 /* Add missing prologue data. */
1966 if (mdoc
->meta
.date
== NULL
)
1967 mdoc
->meta
.date
= mdoc
->quick
? mandoc_strdup("") :
1968 mandoc_normdate(mdoc
, NULL
, 0, 0);
1970 if (mdoc
->meta
.title
== NULL
) {
1971 mandoc_msg(MANDOCERR_DT_NOTITLE
,
1972 mdoc
->parse
, 0, 0, "EOF");
1973 mdoc
->meta
.title
= mandoc_strdup("UNTITLED");
1976 if (mdoc
->meta
.vol
== NULL
)
1977 mdoc
->meta
.vol
= mandoc_strdup("LOCAL");
1979 if (mdoc
->meta
.os
== NULL
) {
1980 mandoc_msg(MANDOCERR_OS_MISSING
,
1981 mdoc
->parse
, 0, 0, NULL
);
1982 mdoc
->meta
.os
= mandoc_strdup("");
1983 } else if (mdoc
->meta
.os_e
&&
1984 (mdoc
->meta
.rcsids
& (1 << mdoc
->meta
.os_e
)) == 0)
1985 mandoc_msg(MANDOCERR_RCS_MISSING
, mdoc
->parse
, 0, 0,
1986 mdoc
->meta
.os_e
== MANDOC_OS_OPENBSD
?
1987 "(OpenBSD)" : "(NetBSD)");
1989 if (mdoc
->meta
.arch
!= NULL
&&
1990 (arch
= arches
[mdoc
->meta
.os_e
]) != NULL
) {
1991 while (*arch
!= NULL
&& strcmp(*arch
, mdoc
->meta
.arch
))
1993 if (*arch
== NULL
) {
1994 n
= mdoc
->first
->child
;
1995 while (n
->tok
!= MDOC_Dt
||
1997 n
->child
->next
== NULL
||
1998 n
->child
->next
->next
== NULL
)
2000 n
= n
->child
->next
->next
;
2001 mandoc_vmsg(MANDOCERR_ARCH_BAD
,
2002 mdoc
->parse
, n
->line
, n
->pos
,
2003 "Dt ... %s %s", mdoc
->meta
.arch
,
2004 mdoc
->meta
.os_e
== MANDOC_OS_OPENBSD
?
2005 "(OpenBSD)" : "(NetBSD)");
2009 /* Check that we begin with a proper `Sh'. */
2011 n
= mdoc
->first
->child
;
2013 (n
->type
== ROFFT_COMMENT
||
2014 (n
->tok
>= MDOC_Dd
&&
2015 mdoc_macro(n
->tok
)->flags
& MDOC_PROLOGUE
)))
2019 mandoc_msg(MANDOCERR_DOC_EMPTY
, mdoc
->parse
, 0, 0, NULL
);
2020 else if (n
->tok
!= MDOC_Sh
)
2021 mandoc_msg(MANDOCERR_SEC_BEFORE
, mdoc
->parse
,
2022 n
->line
, n
->pos
, roff_name
[n
->tok
]);
2028 struct roff_node
*np
, *nch
, *next
, *prev
;
2033 if (np
->type
!= ROFFT_BODY
)
2036 if (np
->child
== NULL
) {
2037 mandoc_msg(MANDOCERR_RS_EMPTY
, mdoc
->parse
,
2038 np
->line
, np
->pos
, "Rs");
2043 * The full `Rs' block needs special handling to order the
2044 * sub-elements according to `rsord'. Pick through each element
2045 * and correctly order it. This is an insertion sort.
2049 for (nch
= np
->child
->next
; nch
!= NULL
; nch
= next
) {
2050 /* Determine order number of this child. */
2051 for (i
= 0; i
< RSORD_MAX
; i
++)
2052 if (rsord
[i
] == nch
->tok
)
2055 if (i
== RSORD_MAX
) {
2056 mandoc_msg(MANDOCERR_RS_BAD
, mdoc
->parse
,
2057 nch
->line
, nch
->pos
, roff_name
[nch
->tok
]);
2059 } else if (nch
->tok
== MDOC__J
|| nch
->tok
== MDOC__B
)
2060 np
->norm
->Rs
.quote_T
++;
2063 * Remove this child from the chain. This somewhat
2064 * repeats roff_node_unlink(), but since we're
2065 * just re-ordering, there's no need for the
2066 * full unlink process.
2069 if ((next
= nch
->next
) != NULL
)
2070 next
->prev
= nch
->prev
;
2072 if ((prev
= nch
->prev
) != NULL
)
2073 prev
->next
= nch
->next
;
2075 nch
->prev
= nch
->next
= NULL
;
2078 * Scan back until we reach a node that's
2079 * to be ordered before this child.
2082 for ( ; prev
; prev
= prev
->prev
) {
2083 /* Determine order of `prev'. */
2084 for (j
= 0; j
< RSORD_MAX
; j
++)
2085 if (rsord
[j
] == prev
->tok
)
2095 * Set this child back into its correct place
2096 * in front of the `prev' node.
2102 np
->child
->prev
= nch
;
2103 nch
->next
= np
->child
;
2107 prev
->next
->prev
= nch
;
2108 nch
->next
= prev
->next
;
2115 * For some arguments of some macros,
2116 * convert all breakable hyphens into ASCII_HYPH.
2119 post_hyph(POST_ARGS
)
2121 struct roff_node
*nch
;
2124 for (nch
= mdoc
->last
->child
; nch
!= NULL
; nch
= nch
->next
) {
2125 if (nch
->type
!= ROFFT_TEXT
)
2130 while (*(++cp
) != '\0')
2132 isalpha((unsigned char)cp
[-1]) &&
2133 isalpha((unsigned char)cp
[1]))
2141 struct roff_node
*n
;
2144 if (n
->flags
& NODE_LINE
||
2145 (n
->next
!= NULL
&& n
->next
->flags
& NODE_DELIMC
))
2146 mandoc_msg(MANDOCERR_NS_SKIP
, mdoc
->parse
,
2147 n
->line
, n
->pos
, NULL
);
2163 switch (mdoc
->last
->type
) {
2168 switch (mdoc
->lastsec
) {
2173 post_sh_see_also(mdoc
);
2176 post_sh_authors(mdoc
);
2188 post_sh_name(POST_ARGS
)
2190 struct roff_node
*n
;
2195 for (n
= mdoc
->last
->child
; n
!= NULL
; n
= n
->next
) {
2198 if (hasnm
&& n
->child
!= NULL
)
2199 mandoc_vmsg(MANDOCERR_NAMESEC_PUNCT
,
2200 mdoc
->parse
, n
->line
, n
->pos
,
2201 "Nm %s", n
->child
->string
);
2206 if (n
->next
!= NULL
)
2207 mandoc_msg(MANDOCERR_NAMESEC_ND
,
2208 mdoc
->parse
, n
->line
, n
->pos
, NULL
);
2211 if (n
->type
== ROFFT_TEXT
&&
2212 n
->string
[0] == ',' && n
->string
[1] == '\0' &&
2213 n
->next
!= NULL
&& n
->next
->tok
== MDOC_Nm
) {
2219 mandoc_msg(MANDOCERR_NAMESEC_BAD
, mdoc
->parse
,
2220 n
->line
, n
->pos
, roff_name
[n
->tok
]);
2227 mandoc_msg(MANDOCERR_NAMESEC_NONM
, mdoc
->parse
,
2228 mdoc
->last
->line
, mdoc
->last
->pos
, NULL
);
2230 mandoc_msg(MANDOCERR_NAMESEC_NOND
, mdoc
->parse
,
2231 mdoc
->last
->line
, mdoc
->last
->pos
, NULL
);
2235 post_sh_see_also(POST_ARGS
)
2237 const struct roff_node
*n
;
2238 const char *name
, *sec
;
2239 const char *lastname
, *lastsec
, *lastpunct
;
2242 n
= mdoc
->last
->child
;
2243 lastname
= lastsec
= lastpunct
= NULL
;
2245 if (n
->tok
!= MDOC_Xr
||
2247 n
->child
->next
== NULL
)
2250 /* Process one .Xr node. */
2252 name
= n
->child
->string
;
2253 sec
= n
->child
->next
->string
;
2254 if (lastsec
!= NULL
) {
2255 if (lastpunct
[0] != ',' || lastpunct
[1] != '\0')
2256 mandoc_vmsg(MANDOCERR_XR_PUNCT
,
2257 mdoc
->parse
, n
->line
, n
->pos
,
2258 "%s before %s(%s)", lastpunct
,
2260 cmp
= strcmp(lastsec
, sec
);
2262 mandoc_vmsg(MANDOCERR_XR_ORDER
,
2263 mdoc
->parse
, n
->line
, n
->pos
,
2264 "%s(%s) after %s(%s)", name
,
2265 sec
, lastname
, lastsec
);
2266 else if (cmp
== 0 &&
2267 strcasecmp(lastname
, name
) > 0)
2268 mandoc_vmsg(MANDOCERR_XR_ORDER
,
2269 mdoc
->parse
, n
->line
, n
->pos
,
2270 "%s after %s", name
, lastname
);
2275 /* Process the following node. */
2280 if (n
->tok
== MDOC_Xr
) {
2284 if (n
->type
!= ROFFT_TEXT
)
2286 for (name
= n
->string
; *name
!= '\0'; name
++)
2287 if (isalpha((const unsigned char)*name
))
2289 lastpunct
= n
->string
;
2290 if (n
->next
== NULL
|| n
->next
->tok
== MDOC_Rs
)
2291 mandoc_vmsg(MANDOCERR_XR_PUNCT
, mdoc
->parse
,
2292 n
->line
, n
->pos
, "%s after %s(%s)",
2293 lastpunct
, lastname
, lastsec
);
2299 child_an(const struct roff_node
*n
)
2302 for (n
= n
->child
; n
!= NULL
; n
= n
->next
)
2303 if ((n
->tok
== MDOC_An
&& n
->child
!= NULL
) || child_an(n
))
2309 post_sh_authors(POST_ARGS
)
2312 if ( ! child_an(mdoc
->last
))
2313 mandoc_msg(MANDOCERR_AN_MISSING
, mdoc
->parse
,
2314 mdoc
->last
->line
, mdoc
->last
->pos
, NULL
);
2318 * Return an upper bound for the string distance (allowing
2319 * transpositions). Not a full Levenshtein implementation
2320 * because Levenshtein is quadratic in the string length
2321 * and this function is called for every standard name,
2322 * so the check for each custom name would be cubic.
2323 * The following crude heuristics is linear, resulting
2324 * in quadratic behaviour for checking one custom name,
2325 * which does not cause measurable slowdown.
2328 similar(const char *s1
, const char *s2
)
2330 const int maxdist
= 3;
2333 while (s1
[0] != '\0' && s2
[0] != '\0') {
2334 if (s1
[0] == s2
[0]) {
2339 if (++dist
> maxdist
)
2341 if (s1
[1] == s2
[1]) { /* replacement */
2344 } else if (s1
[0] == s2
[1] && s1
[1] == s2
[0]) {
2345 s1
+= 2; /* transposition */
2347 } else if (s1
[0] == s2
[1]) /* insertion */
2349 else if (s1
[1] == s2
[0]) /* deletion */
2354 dist
+= strlen(s1
) + strlen(s2
);
2355 return dist
> maxdist
? INT_MAX
: dist
;
2359 post_sh_head(POST_ARGS
)
2361 struct roff_node
*nch
;
2362 const char *goodsec
;
2363 const char *const *testsec
;
2368 * Process a new section. Sections are either "named" or
2369 * "custom". Custom sections are user-defined, while named ones
2370 * follow a conventional order and may only appear in certain
2374 sec
= mdoc
->last
->sec
;
2376 /* The NAME should be first. */
2378 if (sec
!= SEC_NAME
&& mdoc
->lastnamed
== SEC_NONE
)
2379 mandoc_vmsg(MANDOCERR_NAMESEC_FIRST
, mdoc
->parse
,
2380 mdoc
->last
->line
, mdoc
->last
->pos
, "Sh %s",
2381 sec
!= SEC_CUSTOM
? secnames
[sec
] :
2382 (nch
= mdoc
->last
->child
) == NULL
? "" :
2383 nch
->type
== ROFFT_TEXT
? nch
->string
:
2384 roff_name
[nch
->tok
]);
2386 /* The SYNOPSIS gets special attention in other areas. */
2388 if (sec
== SEC_SYNOPSIS
) {
2389 roff_setreg(mdoc
->roff
, "nS", 1, '=');
2390 mdoc
->flags
|= MDOC_SYNOPSIS
;
2392 roff_setreg(mdoc
->roff
, "nS", 0, '=');
2393 mdoc
->flags
&= ~MDOC_SYNOPSIS
;
2396 /* Mark our last section. */
2398 mdoc
->lastsec
= sec
;
2400 /* We don't care about custom sections after this. */
2402 if (sec
== SEC_CUSTOM
) {
2403 if ((nch
= mdoc
->last
->child
) == NULL
||
2404 nch
->type
!= ROFFT_TEXT
|| nch
->next
!= NULL
)
2408 for (testsec
= secnames
+ 1; *testsec
!= NULL
; testsec
++) {
2409 dist
= similar(nch
->string
, *testsec
);
2410 if (dist
< mindist
) {
2415 if (goodsec
!= NULL
)
2416 mandoc_vmsg(MANDOCERR_SEC_TYPO
, mdoc
->parse
,
2417 nch
->line
, nch
->pos
, "Sh %s instead of %s",
2418 nch
->string
, goodsec
);
2423 * Check whether our non-custom section is being repeated or is
2427 if (sec
== mdoc
->lastnamed
)
2428 mandoc_vmsg(MANDOCERR_SEC_REP
, mdoc
->parse
,
2429 mdoc
->last
->line
, mdoc
->last
->pos
,
2430 "Sh %s", secnames
[sec
]);
2432 if (sec
< mdoc
->lastnamed
)
2433 mandoc_vmsg(MANDOCERR_SEC_ORDER
, mdoc
->parse
,
2434 mdoc
->last
->line
, mdoc
->last
->pos
,
2435 "Sh %s", secnames
[sec
]);
2437 /* Mark the last named section. */
2439 mdoc
->lastnamed
= sec
;
2441 /* Check particular section/manual conventions. */
2443 if (mdoc
->meta
.msec
== NULL
)
2449 if (*mdoc
->meta
.msec
== '4')
2451 goodsec
= "2, 3, 4, 9";
2453 case SEC_RETURN_VALUES
:
2455 if (*mdoc
->meta
.msec
== '2')
2457 if (*mdoc
->meta
.msec
== '3')
2459 if (NULL
== goodsec
)
2460 goodsec
= "2, 3, 9";
2463 if (*mdoc
->meta
.msec
== '9')
2465 if (NULL
== goodsec
)
2467 mandoc_vmsg(MANDOCERR_SEC_MSEC
, mdoc
->parse
,
2468 mdoc
->last
->line
, mdoc
->last
->pos
,
2469 "Sh %s for %s only", secnames
[sec
], goodsec
);
2479 struct roff_node
*n
, *nch
;
2483 if (nch
->next
== NULL
) {
2484 mandoc_vmsg(MANDOCERR_XR_NOSEC
, mdoc
->parse
,
2485 n
->line
, n
->pos
, "Xr %s", nch
->string
);
2487 assert(nch
->next
== n
->last
);
2488 if(mandoc_xr_add(nch
->next
->string
, nch
->string
,
2489 nch
->line
, nch
->pos
))
2490 mandoc_vmsg(MANDOCERR_XR_SELF
, mdoc
->parse
,
2491 nch
->line
, nch
->pos
, "Xr %s %s",
2492 nch
->string
, nch
->next
->string
);
2494 post_delim_nb(mdoc
);
2498 post_ignpar(POST_ARGS
)
2500 struct roff_node
*np
;
2502 switch (mdoc
->last
->type
) {
2516 if ((np
= mdoc
->last
->child
) != NULL
)
2517 if (np
->tok
== MDOC_Pp
||
2518 np
->tok
== ROFF_br
|| np
->tok
== ROFF_sp
) {
2519 mandoc_vmsg(MANDOCERR_PAR_SKIP
,
2520 mdoc
->parse
, np
->line
, np
->pos
,
2521 "%s after %s", roff_name
[np
->tok
],
2522 roff_name
[mdoc
->last
->tok
]);
2523 roff_node_delete(mdoc
, np
);
2526 if ((np
= mdoc
->last
->last
) != NULL
)
2527 if (np
->tok
== MDOC_Pp
|| np
->tok
== ROFF_br
) {
2528 mandoc_vmsg(MANDOCERR_PAR_SKIP
, mdoc
->parse
,
2529 np
->line
, np
->pos
, "%s at the end of %s",
2531 roff_name
[mdoc
->last
->tok
]);
2532 roff_node_delete(mdoc
, np
);
2537 post_prevpar(POST_ARGS
)
2539 struct roff_node
*n
;
2542 if (NULL
== n
->prev
)
2544 if (n
->type
!= ROFFT_ELEM
&& n
->type
!= ROFFT_BLOCK
)
2548 * Don't allow `Pp' prior to a paragraph-type
2549 * block: `Pp' or non-compact `Bd' or `Bl'.
2552 if (n
->prev
->tok
!= MDOC_Pp
&& n
->prev
->tok
!= ROFF_br
)
2554 if (n
->tok
== MDOC_Bl
&& n
->norm
->Bl
.comp
)
2556 if (n
->tok
== MDOC_Bd
&& n
->norm
->Bd
.comp
)
2558 if (n
->tok
== MDOC_It
&& n
->parent
->norm
->Bl
.comp
)
2561 mandoc_vmsg(MANDOCERR_PAR_SKIP
, mdoc
->parse
,
2562 n
->prev
->line
, n
->prev
->pos
, "%s before %s",
2563 roff_name
[n
->prev
->tok
], roff_name
[n
->tok
]);
2564 roff_node_delete(mdoc
, n
->prev
);
2570 struct roff_node
*np
;
2575 if (np
->child
!= NULL
)
2576 mandoc_vmsg(MANDOCERR_ARG_SKIP
,
2577 mdoc
->parse
, np
->line
, np
->pos
, "%s %s",
2578 roff_name
[np
->tok
], np
->child
->string
);
2584 struct roff_node
*n
;
2588 n
->flags
|= NODE_NOPRT
;
2590 if (mdoc
->meta
.date
!= NULL
) {
2591 mandoc_msg(MANDOCERR_PROLOG_REP
, mdoc
->parse
,
2592 n
->line
, n
->pos
, "Dd");
2593 free(mdoc
->meta
.date
);
2594 } else if (mdoc
->flags
& MDOC_PBODY
)
2595 mandoc_msg(MANDOCERR_PROLOG_LATE
, mdoc
->parse
,
2596 n
->line
, n
->pos
, "Dd");
2597 else if (mdoc
->meta
.title
!= NULL
)
2598 mandoc_msg(MANDOCERR_PROLOG_ORDER
, mdoc
->parse
,
2599 n
->line
, n
->pos
, "Dd after Dt");
2600 else if (mdoc
->meta
.os
!= NULL
)
2601 mandoc_msg(MANDOCERR_PROLOG_ORDER
, mdoc
->parse
,
2602 n
->line
, n
->pos
, "Dd after Os");
2604 if (n
->child
== NULL
|| n
->child
->string
[0] == '\0') {
2605 mdoc
->meta
.date
= mdoc
->quick
? mandoc_strdup("") :
2606 mandoc_normdate(mdoc
, NULL
, n
->line
, n
->pos
);
2611 deroff(&datestr
, n
);
2613 mdoc
->meta
.date
= datestr
;
2615 mdoc
->meta
.date
= mandoc_normdate(mdoc
,
2616 datestr
, n
->line
, n
->pos
);
2624 struct roff_node
*nn
, *n
;
2629 n
->flags
|= NODE_NOPRT
;
2631 if (mdoc
->flags
& MDOC_PBODY
) {
2632 mandoc_msg(MANDOCERR_DT_LATE
, mdoc
->parse
,
2633 n
->line
, n
->pos
, "Dt");
2637 if (mdoc
->meta
.title
!= NULL
)
2638 mandoc_msg(MANDOCERR_PROLOG_REP
, mdoc
->parse
,
2639 n
->line
, n
->pos
, "Dt");
2640 else if (mdoc
->meta
.os
!= NULL
)
2641 mandoc_msg(MANDOCERR_PROLOG_ORDER
, mdoc
->parse
,
2642 n
->line
, n
->pos
, "Dt after Os");
2644 free(mdoc
->meta
.title
);
2645 free(mdoc
->meta
.msec
);
2646 free(mdoc
->meta
.vol
);
2647 free(mdoc
->meta
.arch
);
2649 mdoc
->meta
.title
= NULL
;
2650 mdoc
->meta
.msec
= NULL
;
2651 mdoc
->meta
.vol
= NULL
;
2652 mdoc
->meta
.arch
= NULL
;
2654 /* Mandatory first argument: title. */
2657 if (nn
== NULL
|| *nn
->string
== '\0') {
2658 mandoc_msg(MANDOCERR_DT_NOTITLE
,
2659 mdoc
->parse
, n
->line
, n
->pos
, "Dt");
2660 mdoc
->meta
.title
= mandoc_strdup("UNTITLED");
2662 mdoc
->meta
.title
= mandoc_strdup(nn
->string
);
2664 /* Check that all characters are uppercase. */
2666 for (p
= nn
->string
; *p
!= '\0'; p
++)
2667 if (islower((unsigned char)*p
)) {
2668 mandoc_vmsg(MANDOCERR_TITLE_CASE
,
2669 mdoc
->parse
, nn
->line
,
2670 nn
->pos
+ (p
- nn
->string
),
2671 "Dt %s", nn
->string
);
2676 /* Mandatory second argument: section. */
2682 mandoc_vmsg(MANDOCERR_MSEC_MISSING
,
2683 mdoc
->parse
, n
->line
, n
->pos
,
2684 "Dt %s", mdoc
->meta
.title
);
2685 mdoc
->meta
.vol
= mandoc_strdup("LOCAL");
2686 return; /* msec and arch remain NULL. */
2689 mdoc
->meta
.msec
= mandoc_strdup(nn
->string
);
2691 /* Infer volume title from section number. */
2693 cp
= mandoc_a2msec(nn
->string
);
2695 mandoc_vmsg(MANDOCERR_MSEC_BAD
, mdoc
->parse
,
2696 nn
->line
, nn
->pos
, "Dt ... %s", nn
->string
);
2697 mdoc
->meta
.vol
= mandoc_strdup(nn
->string
);
2699 mdoc
->meta
.vol
= mandoc_strdup(cp
);
2701 /* Optional third argument: architecture. */
2703 if ((nn
= nn
->next
) == NULL
)
2706 for (p
= nn
->string
; *p
!= '\0'; p
++)
2707 *p
= tolower((unsigned char)*p
);
2708 mdoc
->meta
.arch
= mandoc_strdup(nn
->string
);
2710 /* Ignore fourth and later arguments. */
2712 if ((nn
= nn
->next
) != NULL
)
2713 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, mdoc
->parse
,
2714 nn
->line
, nn
->pos
, "Dt ... %s", nn
->string
);
2720 struct roff_node
*n
, *nch
;
2723 post_delim_nb(mdoc
);
2729 macro
= !strcmp(nch
->string
, "Open") ? "Ox" :
2730 !strcmp(nch
->string
, "Net") ? "Nx" :
2731 !strcmp(nch
->string
, "Free") ? "Fx" :
2732 !strcmp(nch
->string
, "DragonFly") ? "Dx" : NULL
;
2734 mandoc_msg(MANDOCERR_BX
, mdoc
->parse
,
2735 n
->line
, n
->pos
, macro
);
2738 mdoc
->next
= ROFF_NEXT_SIBLING
;
2739 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Ns
);
2740 mdoc
->last
->flags
|= NODE_NOSRC
;
2741 mdoc
->next
= ROFF_NEXT_SIBLING
;
2743 mdoc
->next
= ROFF_NEXT_CHILD
;
2744 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "BSD");
2745 mdoc
->last
->flags
|= NODE_NOSRC
;
2752 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Ns
);
2753 mdoc
->last
->flags
|= NODE_NOSRC
;
2754 mdoc
->next
= ROFF_NEXT_SIBLING
;
2755 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "-");
2756 mdoc
->last
->flags
|= NODE_NOSRC
;
2757 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Ns
);
2758 mdoc
->last
->flags
|= NODE_NOSRC
;
2762 * Make `Bx's second argument always start with an uppercase
2763 * letter. Groff checks if it's an "accepted" term, but we just
2764 * uppercase blindly.
2767 *nch
->string
= (char)toupper((unsigned char)*nch
->string
);
2774 struct utsname utsname
;
2775 static char *defbuf
;
2777 struct roff_node
*n
;
2780 n
->flags
|= NODE_NOPRT
;
2782 if (mdoc
->meta
.os
!= NULL
)
2783 mandoc_msg(MANDOCERR_PROLOG_REP
, mdoc
->parse
,
2784 n
->line
, n
->pos
, "Os");
2785 else if (mdoc
->flags
& MDOC_PBODY
)
2786 mandoc_msg(MANDOCERR_PROLOG_LATE
, mdoc
->parse
,
2787 n
->line
, n
->pos
, "Os");
2792 * Set the operating system by way of the `Os' macro.
2793 * The order of precedence is:
2794 * 1. the argument of the `Os' macro, unless empty
2795 * 2. the -Ios=foo command line argument, if provided
2796 * 3. -DOSNAME="\"foo\"", if provided during compilation
2797 * 4. "sysname release" from uname(3)
2800 free(mdoc
->meta
.os
);
2801 mdoc
->meta
.os
= NULL
;
2802 deroff(&mdoc
->meta
.os
, n
);
2806 if (mdoc
->os_s
!= NULL
) {
2807 mdoc
->meta
.os
= mandoc_strdup(mdoc
->os_s
);
2812 mdoc
->meta
.os
= mandoc_strdup(OSNAME
);
2814 if (defbuf
== NULL
) {
2815 if (uname(&utsname
) == -1) {
2816 mandoc_msg(MANDOCERR_OS_UNAME
, mdoc
->parse
,
2817 n
->line
, n
->pos
, "Os");
2818 defbuf
= mandoc_strdup("UNKNOWN");
2820 mandoc_asprintf(&defbuf
, "%s %s",
2821 utsname
.sysname
, utsname
.release
);
2823 mdoc
->meta
.os
= mandoc_strdup(defbuf
);
2827 if (mdoc
->meta
.os_e
== MANDOC_OS_OTHER
) {
2828 if (strstr(mdoc
->meta
.os
, "OpenBSD") != NULL
)
2829 mdoc
->meta
.os_e
= MANDOC_OS_OPENBSD
;
2830 else if (strstr(mdoc
->meta
.os
, "NetBSD") != NULL
)
2831 mdoc
->meta
.os_e
= MANDOC_OS_NETBSD
;
2835 * This is the earliest point where we can check
2836 * Mdocdate conventions because we don't know
2837 * the operating system earlier.
2840 if (n
->child
!= NULL
)
2841 mandoc_vmsg(MANDOCERR_OS_ARG
, mdoc
->parse
,
2842 n
->child
->line
, n
->child
->pos
,
2843 "Os %s (%s)", n
->child
->string
,
2844 mdoc
->meta
.os_e
== MANDOC_OS_OPENBSD
?
2845 "OpenBSD" : "NetBSD");
2847 while (n
->tok
!= MDOC_Dd
)
2848 if ((n
= n
->prev
) == NULL
)
2850 if ((n
= n
->child
) == NULL
)
2852 if (strncmp(n
->string
, "$" "Mdocdate", 9)) {
2853 if (mdoc
->meta
.os_e
== MANDOC_OS_OPENBSD
)
2854 mandoc_vmsg(MANDOCERR_MDOCDATE_MISSING
,
2855 mdoc
->parse
, n
->line
, n
->pos
,
2856 "Dd %s (OpenBSD)", n
->string
);
2858 if (mdoc
->meta
.os_e
== MANDOC_OS_NETBSD
)
2859 mandoc_vmsg(MANDOCERR_MDOCDATE
,
2860 mdoc
->parse
, n
->line
, n
->pos
,
2861 "Dd %s (NetBSD)", n
->string
);
2866 mdoc_a2sec(const char *p
)
2870 for (i
= 0; i
< (int)SEC__MAX
; i
++)
2871 if (secnames
[i
] && 0 == strcmp(p
, secnames
[i
]))
2872 return (enum roff_sec
)i
;
2878 macro2len(enum roff_tok macro
)