1 /* $Id: mdoc_validate.c,v 1.367 2018/12/31 04:55:47 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_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
) {
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
, ln
, pos
+ (int)(p
- cp
), NULL
);
422 check_text_em(struct roff_man
*mdoc
, int ln
, int pos
, char *p
)
424 const struct roff_node
*np
, *nn
;
427 np
= mdoc
->last
->prev
;
428 nn
= mdoc
->last
->next
;
430 /* Look for em-dashes wrongly encoded as "--". */
432 for (cp
= p
; *cp
!= '\0'; cp
++) {
433 if (cp
[0] != '-' || cp
[1] != '-')
437 /* Skip input sequences of more than two '-'. */
445 /* Skip "--" directly attached to something else. */
447 if ((cp
- p
> 1 && cp
[-2] != ' ') ||
448 (cp
[1] != '\0' && cp
[1] != ' '))
451 /* Require a letter right before or right afterwards. */
454 isalpha((unsigned char)cp
[-3]) :
456 np
->type
== ROFFT_TEXT
&&
457 *np
->string
!= '\0' &&
458 isalpha((unsigned char)np
->string
[
459 strlen(np
->string
) - 1])) ||
460 (cp
[1] != '\0' && cp
[2] != '\0' ?
461 isalpha((unsigned char)cp
[2]) :
463 nn
->type
== ROFFT_TEXT
&&
464 isalpha((unsigned char)*nn
->string
))) {
465 mandoc_msg(MANDOCERR_DASHDASH
,
466 ln
, pos
+ (int)(cp
- p
) - 1, NULL
);
473 check_toptext(struct roff_man
*mdoc
, int ln
, int pos
, const char *p
)
475 const char *cp
, *cpr
;
480 if ((cp
= strstr(p
, "OpenBSD")) != NULL
)
481 mandoc_msg(MANDOCERR_BX
, ln
, pos
+ (int)(cp
- p
), "Ox");
482 if ((cp
= strstr(p
, "NetBSD")) != NULL
)
483 mandoc_msg(MANDOCERR_BX
, ln
, pos
+ (int)(cp
- p
), "Nx");
484 if ((cp
= strstr(p
, "FreeBSD")) != NULL
)
485 mandoc_msg(MANDOCERR_BX
, ln
, pos
+ (int)(cp
- p
), "Fx");
486 if ((cp
= strstr(p
, "DragonFly")) != NULL
)
487 mandoc_msg(MANDOCERR_BX
, ln
, pos
+ (int)(cp
- p
), "Dx");
490 while ((cp
= strstr(cp
+ 1, "()")) != NULL
) {
491 for (cpr
= cp
- 1; cpr
>= p
; cpr
--)
492 if (*cpr
!= '_' && !isalnum((unsigned char)*cpr
))
494 if ((cpr
< p
|| *cpr
== ' ') && cpr
+ 1 < cp
) {
496 mandoc_msg(MANDOCERR_FUNC
, ln
, pos
+ (int)(cpr
- p
),
497 "%.*s()", (int)(cp
- cpr
), cpr
);
503 post_abort(POST_ARGS
)
509 post_delim(POST_ARGS
)
511 const struct roff_node
*nch
;
516 tok
= mdoc
->last
->tok
;
517 nch
= mdoc
->last
->last
;
518 if (nch
== NULL
|| nch
->type
!= ROFFT_TEXT
)
520 lc
= strchr(nch
->string
, '\0') - 1;
521 if (lc
< nch
->string
)
523 delim
= mdoc_isdelim(lc
);
524 if (delim
== DELIM_NONE
|| delim
== DELIM_OPEN
)
526 if (*lc
== ')' && (tok
== MDOC_Nd
|| tok
== MDOC_Sh
||
527 tok
== MDOC_Ss
|| tok
== MDOC_Fo
))
530 mandoc_msg(MANDOCERR_DELIM
, nch
->line
,
531 nch
->pos
+ (int)(lc
- nch
->string
), "%s%s %s", roff_name
[tok
],
532 nch
== mdoc
->last
->child
? "" : " ...", nch
->string
);
536 post_delim_nb(POST_ARGS
)
538 const struct roff_node
*nch
;
545 * Find candidates: at least two bytes,
546 * the last one a closing or middle delimiter.
549 tok
= mdoc
->last
->tok
;
550 nch
= mdoc
->last
->last
;
551 if (nch
== NULL
|| nch
->type
!= ROFFT_TEXT
)
553 lc
= strchr(nch
->string
, '\0') - 1;
554 if (lc
<= nch
->string
)
556 delim
= mdoc_isdelim(lc
);
557 if (delim
== DELIM_NONE
|| delim
== DELIM_OPEN
)
561 * Reduce false positives by allowing various cases.
564 /* Escaped delimiters. */
565 if (lc
> nch
->string
+ 1 && lc
[-2] == '\\' &&
566 (lc
[-1] == '&' || lc
[-1] == 'e'))
569 /* Specific byte sequences. */
572 for (cp
= lc
; cp
>= nch
->string
; cp
--)
577 if (lc
> nch
->string
+ 1 && lc
[-2] == '.' && lc
[-1] == '.')
591 for (cp
= lc
; cp
>= nch
->string
; cp
--)
596 if (lc
== nch
->string
+ 1 && lc
[-1] == '|')
602 /* Exactly two non-alphanumeric bytes. */
603 if (lc
== nch
->string
+ 1 && !isalnum((unsigned char)lc
[-1]))
606 /* At least three alphabetic words with a sentence ending. */
607 if (strchr("!.:?", *lc
) != NULL
&& (tok
== MDOC_Em
||
608 tok
== MDOC_Li
|| tok
== MDOC_Pq
|| tok
== MDOC_Sy
)) {
610 for (cp
= lc
- 1; cp
>= nch
->string
; cp
--) {
613 if (cp
> nch
->string
&& cp
[-1] == ',')
615 } else if (isalpha((unsigned int)*cp
)) {
623 mandoc_msg(MANDOCERR_DELIM_NB
, nch
->line
,
624 nch
->pos
+ (int)(lc
- nch
->string
), "%s%s %s", roff_name
[tok
],
625 nch
== mdoc
->last
->child
? "" : " ...", nch
->string
);
629 post_bl_norm(POST_ARGS
)
632 struct mdoc_argv
*argv
, *wa
;
634 enum mdocargt mdoclt
;
637 n
= mdoc
->last
->parent
;
638 n
->norm
->Bl
.type
= LIST__NONE
;
641 * First figure out which kind of list to use: bind ourselves to
642 * the first mentioned list type and warn about any remaining
643 * ones. If we find no list type, we default to LIST_item.
646 wa
= (n
->args
== NULL
) ? NULL
: n
->args
->argv
;
647 mdoclt
= MDOC_ARG_MAX
;
648 for (i
= 0; n
->args
&& i
< (int)n
->args
->argc
; i
++) {
649 argv
= n
->args
->argv
+ i
;
652 /* Set list types. */
686 /* Set list arguments. */
688 if (n
->norm
->Bl
.comp
)
689 mandoc_msg(MANDOCERR_ARG_REP
,
690 argv
->line
, argv
->pos
, "Bl -compact");
691 n
->norm
->Bl
.comp
= 1;
696 mandoc_msg(MANDOCERR_ARG_EMPTY
,
697 argv
->line
, argv
->pos
, "Bl -width");
698 n
->norm
->Bl
.width
= "0n";
701 if (NULL
!= n
->norm
->Bl
.width
)
702 mandoc_msg(MANDOCERR_ARG_REP
,
703 argv
->line
, argv
->pos
,
704 "Bl -width %s", argv
->value
[0]);
705 rewrite_macro2len(mdoc
, argv
->value
);
706 n
->norm
->Bl
.width
= argv
->value
[0];
710 mandoc_msg(MANDOCERR_ARG_EMPTY
,
711 argv
->line
, argv
->pos
, "Bl -offset");
714 if (NULL
!= n
->norm
->Bl
.offs
)
715 mandoc_msg(MANDOCERR_ARG_REP
,
716 argv
->line
, argv
->pos
,
717 "Bl -offset %s", argv
->value
[0]);
718 rewrite_macro2len(mdoc
, argv
->value
);
719 n
->norm
->Bl
.offs
= argv
->value
[0];
724 if (LIST__NONE
== lt
)
728 /* Check: multiple list types. */
730 if (LIST__NONE
!= n
->norm
->Bl
.type
) {
731 mandoc_msg(MANDOCERR_BL_REP
, n
->line
, n
->pos
,
732 "Bl -%s", mdoc_argnames
[argv
->arg
]);
736 /* The list type should come first. */
738 if (n
->norm
->Bl
.width
||
741 mandoc_msg(MANDOCERR_BL_LATETYPE
,
742 n
->line
, n
->pos
, "Bl -%s",
743 mdoc_argnames
[n
->args
->argv
[0].arg
]);
745 n
->norm
->Bl
.type
= lt
;
746 if (LIST_column
== lt
) {
747 n
->norm
->Bl
.ncols
= argv
->sz
;
748 n
->norm
->Bl
.cols
= (void *)argv
->value
;
752 /* Allow lists to default to LIST_item. */
754 if (LIST__NONE
== n
->norm
->Bl
.type
) {
755 mandoc_msg(MANDOCERR_BL_NOTYPE
, n
->line
, n
->pos
, "Bl");
756 n
->norm
->Bl
.type
= LIST_item
;
761 * Validate the width field. Some list types don't need width
762 * types and should be warned about them. Others should have it
763 * and must also be warned. Yet others have a default and need
767 switch (n
->norm
->Bl
.type
) {
769 if (n
->norm
->Bl
.width
== NULL
)
770 mandoc_msg(MANDOCERR_BL_NOWIDTH
,
771 n
->line
, n
->pos
, "Bl -tag");
778 if (n
->norm
->Bl
.width
!= NULL
)
779 mandoc_msg(MANDOCERR_BL_SKIPW
, wa
->line
, wa
->pos
,
780 "Bl -%s", mdoc_argnames
[mdoclt
]);
781 n
->norm
->Bl
.width
= NULL
;
786 if (n
->norm
->Bl
.width
== NULL
)
787 n
->norm
->Bl
.width
= "2n";
790 if (n
->norm
->Bl
.width
== NULL
)
791 n
->norm
->Bl
.width
= "3n";
802 struct mdoc_argv
*argv
;
807 for (i
= 0; n
->args
&& i
< (int)n
->args
->argc
; i
++) {
808 argv
= n
->args
->argv
+ i
;
828 mandoc_msg(MANDOCERR_BD_FILE
, n
->line
, n
->pos
, NULL
);
832 mandoc_msg(MANDOCERR_ARG_EMPTY
,
833 argv
->line
, argv
->pos
, "Bd -offset");
836 if (NULL
!= n
->norm
->Bd
.offs
)
837 mandoc_msg(MANDOCERR_ARG_REP
,
838 argv
->line
, argv
->pos
,
839 "Bd -offset %s", argv
->value
[0]);
840 rewrite_macro2len(mdoc
, argv
->value
);
841 n
->norm
->Bd
.offs
= argv
->value
[0];
844 if (n
->norm
->Bd
.comp
)
845 mandoc_msg(MANDOCERR_ARG_REP
,
846 argv
->line
, argv
->pos
, "Bd -compact");
847 n
->norm
->Bd
.comp
= 1;
852 if (DISP__NONE
== dt
)
855 if (DISP__NONE
== n
->norm
->Bd
.type
)
856 n
->norm
->Bd
.type
= dt
;
858 mandoc_msg(MANDOCERR_BD_REP
, n
->line
, n
->pos
,
859 "Bd -%s", mdoc_argnames
[argv
->arg
]);
862 if (DISP__NONE
== n
->norm
->Bd
.type
) {
863 mandoc_msg(MANDOCERR_BD_NOTYPE
, n
->line
, n
->pos
, "Bd");
864 n
->norm
->Bd
.type
= DISP_ragged
;
869 * Stand-alone line macros.
873 post_an_norm(POST_ARGS
)
876 struct mdoc_argv
*argv
;
883 for (i
= 1; i
< n
->args
->argc
; i
++) {
884 argv
= n
->args
->argv
+ i
;
885 mandoc_msg(MANDOCERR_AN_REP
, argv
->line
, argv
->pos
,
886 "An -%s", mdoc_argnames
[argv
->arg
]);
889 argv
= n
->args
->argv
;
890 if (argv
->arg
== MDOC_Split
)
891 n
->norm
->An
.auth
= AUTH_split
;
892 else if (argv
->arg
== MDOC_Nosplit
)
893 n
->norm
->An
.auth
= AUTH_nosplit
;
905 if (n
->child
!= NULL
)
906 mandoc_msg(MANDOCERR_ARG_SKIP
, n
->line
,
907 n
->pos
, "%s %s", roff_name
[n
->tok
], n
->child
->string
);
909 while (n
->child
!= NULL
)
910 roff_node_delete(mdoc
, n
->child
);
912 roff_word_alloc(mdoc
, n
->line
, n
->pos
, n
->tok
== MDOC_Bt
?
913 "is currently in beta test." : "currently under development.");
914 mdoc
->last
->flags
|= NODE_EOS
| NODE_NOSRC
;
919 build_list(struct roff_man
*mdoc
, int tok
)
924 n
= mdoc
->last
->next
;
925 for (ic
= 1;; ic
++) {
926 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, tok
);
927 mdoc
->last
->flags
|= NODE_NOSRC
;
928 roff_node_relink(mdoc
, n
);
929 n
= mdoc
->last
= mdoc
->last
->parent
;
930 mdoc
->next
= ROFF_NEXT_SIBLING
;
933 if (ic
> 1 || n
->next
->next
!= NULL
) {
934 roff_word_alloc(mdoc
, n
->line
, n
->pos
, ",");
935 mdoc
->last
->flags
|= NODE_DELIMC
| NODE_NOSRC
;
937 n
= mdoc
->last
->next
;
938 if (n
->next
== NULL
) {
939 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "and");
940 mdoc
->last
->flags
|= NODE_NOSRC
;
954 mdoc
->next
= ROFF_NEXT_CHILD
;
955 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "The");
956 mdoc
->last
->flags
|= NODE_NOSRC
;
958 if (mdoc
->last
->next
!= NULL
)
959 ic
= build_list(mdoc
, MDOC_Nm
);
960 else if (mdoc
->meta
.name
!= NULL
) {
961 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Nm
);
962 mdoc
->last
->flags
|= NODE_NOSRC
;
963 roff_word_alloc(mdoc
, n
->line
, n
->pos
, mdoc
->meta
.name
);
964 mdoc
->last
->flags
|= NODE_NOSRC
;
965 mdoc
->last
= mdoc
->last
->parent
;
966 mdoc
->next
= ROFF_NEXT_SIBLING
;
969 mandoc_msg(MANDOCERR_EX_NONAME
, n
->line
, n
->pos
, "Ex");
973 roff_word_alloc(mdoc
, n
->line
, n
->pos
,
974 ic
> 1 ? "utilities exit\\~0" : "utility exits\\~0");
975 mdoc
->last
->flags
|= NODE_NOSRC
;
976 roff_word_alloc(mdoc
, n
->line
, n
->pos
,
977 "on success, and\\~>0 if an error occurs.");
978 mdoc
->last
->flags
|= NODE_EOS
| NODE_NOSRC
;
991 assert(n
->child
->type
== ROFFT_TEXT
);
992 mdoc
->next
= ROFF_NEXT_CHILD
;
994 if ((p
= mdoc_a2lib(n
->child
->string
)) != NULL
) {
995 n
->child
->flags
|= NODE_NOPRT
;
996 roff_word_alloc(mdoc
, n
->line
, n
->pos
, p
);
997 mdoc
->last
->flags
= NODE_NOSRC
;
1002 mandoc_msg(MANDOCERR_LB_BAD
, n
->child
->line
,
1003 n
->child
->pos
, "Lb %s", n
->child
->string
);
1005 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "library");
1006 mdoc
->last
->flags
= NODE_NOSRC
;
1007 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "\\(lq");
1008 mdoc
->last
->flags
= NODE_DELIMO
| NODE_NOSRC
;
1009 mdoc
->last
= mdoc
->last
->next
;
1010 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "\\(rq");
1011 mdoc
->last
->flags
= NODE_DELIMC
| NODE_NOSRC
;
1018 struct roff_node
*n
;
1024 mdoc
->next
= ROFF_NEXT_CHILD
;
1025 if (n
->child
!= NULL
) {
1026 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "The");
1027 mdoc
->last
->flags
|= NODE_NOSRC
;
1028 ic
= build_list(mdoc
, MDOC_Fn
);
1029 roff_word_alloc(mdoc
, n
->line
, n
->pos
,
1030 ic
> 1 ? "functions return" : "function returns");
1031 mdoc
->last
->flags
|= NODE_NOSRC
;
1032 roff_word_alloc(mdoc
, n
->line
, n
->pos
,
1033 "the value\\~0 if successful;");
1035 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "Upon successful "
1036 "completion, the value\\~0 is returned;");
1037 mdoc
->last
->flags
|= NODE_NOSRC
;
1039 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "otherwise "
1040 "the value\\~\\-1 is returned and the global variable");
1041 mdoc
->last
->flags
|= NODE_NOSRC
;
1042 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Va
);
1043 mdoc
->last
->flags
|= NODE_NOSRC
;
1044 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "errno");
1045 mdoc
->last
->flags
|= NODE_NOSRC
;
1046 mdoc
->last
= mdoc
->last
->parent
;
1047 mdoc
->next
= ROFF_NEXT_SIBLING
;
1048 roff_word_alloc(mdoc
, n
->line
, n
->pos
,
1049 "is set to indicate the error.");
1050 mdoc
->last
->flags
|= NODE_EOS
| NODE_NOSRC
;
1057 struct roff_node
*n
;
1062 if (n
->args
&& n
->args
->argc
== 1)
1063 if (n
->args
->argv
[0].arg
== MDOC_Std
)
1066 mandoc_msg(MANDOCERR_ARG_STD
, n
->line
, n
->pos
,
1067 "%s", roff_name
[n
->tok
]);
1073 struct roff_node
*n
, *nch
;
1078 assert(nch
->type
== ROFFT_TEXT
);
1080 if ((p
= mdoc_a2st(nch
->string
)) == NULL
) {
1081 mandoc_msg(MANDOCERR_ST_BAD
,
1082 nch
->line
, nch
->pos
, "St %s", nch
->string
);
1083 roff_node_delete(mdoc
, n
);
1087 nch
->flags
|= NODE_NOPRT
;
1088 mdoc
->next
= ROFF_NEXT_CHILD
;
1089 roff_word_alloc(mdoc
, nch
->line
, nch
->pos
, p
);
1090 mdoc
->last
->flags
|= NODE_NOSRC
;
1095 post_obsolete(POST_ARGS
)
1097 struct roff_node
*n
;
1100 if (n
->type
== ROFFT_ELEM
|| n
->type
== ROFFT_BLOCK
)
1101 mandoc_msg(MANDOCERR_MACRO_OBS
, n
->line
, n
->pos
,
1102 "%s", roff_name
[n
->tok
]);
1106 post_useless(POST_ARGS
)
1108 struct roff_node
*n
;
1111 mandoc_msg(MANDOCERR_MACRO_USELESS
, n
->line
, n
->pos
,
1112 "%s", roff_name
[n
->tok
]);
1122 struct roff_node
*np
, *nch
;
1125 * Unlike other data pointers, these are "housed" by the HEAD
1126 * element, which contains the goods.
1130 if (np
->type
!= ROFFT_HEAD
)
1133 assert(np
->parent
->type
== ROFFT_BLOCK
);
1134 assert(np
->parent
->tok
== MDOC_Bf
);
1136 /* Check the number of arguments. */
1139 if (np
->parent
->args
== NULL
) {
1141 mandoc_msg(MANDOCERR_BF_NOFONT
,
1142 np
->line
, np
->pos
, "Bf");
1148 mandoc_msg(MANDOCERR_ARG_EXCESS
,
1149 nch
->line
, nch
->pos
, "Bf ... %s", nch
->string
);
1151 /* Extract argument into data. */
1153 if (np
->parent
->args
!= NULL
) {
1154 switch (np
->parent
->args
->argv
[0].arg
) {
1156 np
->norm
->Bf
.font
= FONT_Em
;
1159 np
->norm
->Bf
.font
= FONT_Li
;
1162 np
->norm
->Bf
.font
= FONT_Sy
;
1170 /* Extract parameter into data. */
1172 if ( ! strcmp(np
->child
->string
, "Em"))
1173 np
->norm
->Bf
.font
= FONT_Em
;
1174 else if ( ! strcmp(np
->child
->string
, "Li"))
1175 np
->norm
->Bf
.font
= FONT_Li
;
1176 else if ( ! strcmp(np
->child
->string
, "Sy"))
1177 np
->norm
->Bf
.font
= FONT_Sy
;
1179 mandoc_msg(MANDOCERR_BF_BADFONT
, np
->child
->line
,
1180 np
->child
->pos
, "Bf %s", np
->child
->string
);
1184 post_fname(POST_ARGS
)
1186 const struct roff_node
*n
;
1190 n
= mdoc
->last
->child
;
1191 pos
= strcspn(n
->string
, "()");
1192 cp
= n
->string
+ pos
;
1193 if ( ! (cp
[0] == '\0' || (cp
[0] == '(' && cp
[1] == '*')))
1194 mandoc_msg(MANDOCERR_FN_PAREN
, n
->line
, n
->pos
+ pos
,
1209 const struct roff_node
*n
;
1213 if (n
->type
!= ROFFT_HEAD
)
1216 if (n
->child
== NULL
) {
1217 mandoc_msg(MANDOCERR_FO_NOHEAD
, n
->line
, n
->pos
, "Fo");
1220 if (n
->child
!= n
->last
) {
1221 mandoc_msg(MANDOCERR_ARG_EXCESS
,
1222 n
->child
->next
->line
, n
->child
->next
->pos
,
1223 "Fo ... %s", n
->child
->next
->string
);
1224 while (n
->child
!= n
->last
)
1225 roff_node_delete(mdoc
, n
->last
);
1235 const struct roff_node
*n
;
1238 for (n
= mdoc
->last
->child
; n
!= NULL
; n
= n
->next
) {
1239 for (cp
= n
->string
; *cp
!= '\0'; cp
++) {
1240 /* Ignore callbacks and alterations. */
1241 if (*cp
== '(' || *cp
== '{')
1245 mandoc_msg(MANDOCERR_FA_COMMA
, n
->line
,
1246 n
->pos
+ (int)(cp
- n
->string
), "%s", n
->string
);
1250 post_delim_nb(mdoc
);
1256 struct roff_node
*n
;
1260 if (n
->sec
== SEC_NAME
&& n
->child
!= NULL
&&
1261 n
->child
->type
== ROFFT_TEXT
&& mdoc
->meta
.msec
!= NULL
)
1262 mandoc_xr_add(mdoc
->meta
.msec
, n
->child
->string
, -1, -1);
1264 if (n
->last
!= NULL
&& n
->last
->tok
== MDOC_Pp
)
1265 roff_node_relink(mdoc
, n
->last
);
1267 if (mdoc
->meta
.name
== NULL
)
1268 deroff(&mdoc
->meta
.name
, n
);
1270 if (mdoc
->meta
.name
== NULL
||
1271 (mdoc
->lastsec
== SEC_NAME
&& n
->child
== NULL
))
1272 mandoc_msg(MANDOCERR_NM_NONAME
, n
->line
, n
->pos
, "Nm");
1276 post_delim_nb(mdoc
);
1285 if ((n
->child
!= NULL
&& n
->child
->type
== ROFFT_TEXT
) ||
1286 mdoc
->meta
.name
== NULL
)
1289 mdoc
->next
= ROFF_NEXT_CHILD
;
1290 roff_word_alloc(mdoc
, n
->line
, n
->pos
, mdoc
->meta
.name
);
1291 mdoc
->last
->flags
|= NODE_NOSRC
;
1298 struct roff_node
*n
;
1302 if (n
->type
!= ROFFT_BODY
)
1305 if (n
->sec
!= SEC_NAME
)
1306 mandoc_msg(MANDOCERR_ND_LATE
, n
->line
, n
->pos
, "Nd");
1308 if (n
->child
== NULL
)
1309 mandoc_msg(MANDOCERR_ND_EMPTY
, n
->line
, n
->pos
, "Nd");
1317 post_display(POST_ARGS
)
1319 struct roff_node
*n
, *np
;
1324 if (n
->end
!= ENDBODY_NOT
) {
1325 if (n
->tok
== MDOC_Bd
&&
1326 n
->body
->parent
->args
== NULL
)
1327 roff_node_delete(mdoc
, n
);
1328 } else if (n
->child
== NULL
)
1329 mandoc_msg(MANDOCERR_BLK_EMPTY
, n
->line
, n
->pos
,
1330 "%s", roff_name
[n
->tok
]);
1331 else if (n
->tok
== MDOC_D1
)
1335 if (n
->tok
== MDOC_Bd
) {
1336 if (n
->args
== NULL
) {
1337 mandoc_msg(MANDOCERR_BD_NOARG
,
1338 n
->line
, n
->pos
, "Bd");
1339 mdoc
->next
= ROFF_NEXT_SIBLING
;
1340 while (n
->body
->child
!= NULL
)
1341 roff_node_relink(mdoc
,
1343 roff_node_delete(mdoc
, n
);
1349 for (np
= n
->parent
; np
!= NULL
; np
= np
->parent
) {
1350 if (np
->type
== ROFFT_BLOCK
&& np
->tok
== MDOC_Bd
) {
1351 mandoc_msg(MANDOCERR_BD_NEST
, n
->line
,
1352 n
->pos
, "%s in Bd", roff_name
[n
->tok
]);
1363 post_defaults(POST_ARGS
)
1365 struct roff_node
*nn
;
1367 if (mdoc
->last
->child
!= NULL
) {
1368 post_delim_nb(mdoc
);
1373 * The `Ar' defaults to "file ..." if no value is provided as an
1374 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
1375 * gets an empty string.
1381 mdoc
->next
= ROFF_NEXT_CHILD
;
1382 roff_word_alloc(mdoc
, nn
->line
, nn
->pos
, "file");
1383 mdoc
->last
->flags
|= NODE_NOSRC
;
1384 roff_word_alloc(mdoc
, nn
->line
, nn
->pos
, "...");
1385 mdoc
->last
->flags
|= NODE_NOSRC
;
1389 mdoc
->next
= ROFF_NEXT_CHILD
;
1390 roff_word_alloc(mdoc
, nn
->line
, nn
->pos
, "~");
1391 mdoc
->last
->flags
|= NODE_NOSRC
;
1402 struct roff_node
*n
, *nch
;
1409 * If we have a child, look it up in the standard keys. If a
1410 * key exist, use that instead of the child; if it doesn't,
1411 * prefix "AT&T UNIX " to the existing data.
1415 if (nch
!= NULL
&& ((att
= mdoc_a2att(nch
->string
)) == NULL
))
1416 mandoc_msg(MANDOCERR_AT_BAD
,
1417 nch
->line
, nch
->pos
, "At %s", nch
->string
);
1419 mdoc
->next
= ROFF_NEXT_CHILD
;
1421 roff_word_alloc(mdoc
, nch
->line
, nch
->pos
, att
);
1422 nch
->flags
|= NODE_NOPRT
;
1424 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "AT&T UNIX");
1425 mdoc
->last
->flags
|= NODE_NOSRC
;
1432 struct roff_node
*np
, *nch
;
1438 if (np
->norm
->An
.auth
== AUTH__NONE
) {
1440 mandoc_msg(MANDOCERR_MACRO_EMPTY
,
1441 np
->line
, np
->pos
, "An");
1443 post_delim_nb(mdoc
);
1444 } else if (nch
!= NULL
)
1445 mandoc_msg(MANDOCERR_ARG_EXCESS
,
1446 nch
->line
, nch
->pos
, "An ... %s", nch
->string
);
1453 post_obsolete(mdoc
);
1454 if (mdoc
->last
->type
== ROFFT_BLOCK
)
1455 mdoc
->last
->norm
->Es
= mdoc
->last_es
;
1462 post_obsolete(mdoc
);
1463 mdoc
->last_es
= mdoc
->last
;
1469 struct roff_node
*n
;
1473 post_delim_nb(mdoc
);
1488 if (n
->child
== NULL
)
1490 v
= n
->child
->string
;
1491 if ((v
[0] != '0' && v
[0] != '1') || v
[1] != '.' ||
1492 v
[2] < '0' || v
[2] > '9' ||
1493 v
[3] < 'a' || v
[3] > 'z' || v
[4] != '\0')
1495 n
->child
->flags
|= NODE_NOPRT
;
1496 mdoc
->next
= ROFF_NEXT_CHILD
;
1497 roff_word_alloc(mdoc
, n
->child
->line
, n
->child
->pos
, v
);
1498 v
= mdoc
->last
->string
;
1499 v
[3] = toupper((unsigned char)v
[3]);
1500 mdoc
->last
->flags
|= NODE_NOSRC
;
1512 mdoc
->next
= ROFF_NEXT_CHILD
;
1513 roff_word_alloc(mdoc
, n
->line
, n
->pos
, os
);
1514 mdoc
->last
->flags
|= NODE_NOSRC
;
1521 struct roff_node
*nbl
, *nit
, *nch
;
1528 if (nit
->type
!= ROFFT_BLOCK
)
1531 nbl
= nit
->parent
->parent
;
1532 lt
= nbl
->norm
->Bl
.type
;
1540 if (nit
->head
->child
== NULL
)
1541 mandoc_msg(MANDOCERR_IT_NOHEAD
,
1542 nit
->line
, nit
->pos
, "Bl -%s It",
1543 mdoc_argnames
[nbl
->args
->argv
[0].arg
]);
1549 if (nit
->body
== NULL
|| nit
->body
->child
== NULL
)
1550 mandoc_msg(MANDOCERR_IT_NOBODY
,
1551 nit
->line
, nit
->pos
, "Bl -%s It",
1552 mdoc_argnames
[nbl
->args
->argv
[0].arg
]);
1555 if ((nch
= nit
->head
->child
) != NULL
)
1556 mandoc_msg(MANDOCERR_ARG_SKIP
,
1557 nit
->line
, nit
->pos
, "It %s",
1558 nch
->string
== NULL
? roff_name
[nch
->tok
] :
1562 cols
= (int)nbl
->norm
->Bl
.ncols
;
1564 assert(nit
->head
->child
== NULL
);
1566 if (nit
->head
->next
->child
== NULL
&&
1567 nit
->head
->next
->next
== NULL
) {
1568 mandoc_msg(MANDOCERR_MACRO_EMPTY
,
1569 nit
->line
, nit
->pos
, "It");
1570 roff_node_delete(mdoc
, nit
);
1575 for (nch
= nit
->child
; nch
!= NULL
; nch
= nch
->next
) {
1576 if (nch
->type
!= ROFFT_BODY
)
1578 if (i
++ && nch
->flags
& NODE_LINE
)
1579 mandoc_msg(MANDOCERR_TA_LINE
,
1580 nch
->line
, nch
->pos
, "Ta");
1582 if (i
< cols
|| i
> cols
+ 1)
1583 mandoc_msg(MANDOCERR_BL_COL
, nit
->line
, nit
->pos
,
1584 "%d columns, %d cells", cols
, i
);
1585 else if (nit
->head
->next
->child
!= NULL
&&
1586 nit
->head
->next
->child
->line
> nit
->line
)
1587 mandoc_msg(MANDOCERR_IT_NOARG
,
1588 nit
->line
, nit
->pos
, "Bl -column It");
1596 post_bl_block(POST_ARGS
)
1598 struct roff_node
*n
, *ni
, *nc
;
1603 for (ni
= n
->body
->child
; ni
!= NULL
; ni
= ni
->next
) {
1604 if (ni
->body
== NULL
)
1606 nc
= ni
->body
->last
;
1607 while (nc
!= NULL
) {
1616 if (ni
->next
== NULL
) {
1617 mandoc_msg(MANDOCERR_PAR_MOVE
, nc
->line
,
1618 nc
->pos
, "%s", roff_name
[nc
->tok
]);
1619 roff_node_relink(mdoc
, nc
);
1620 } else if (n
->norm
->Bl
.comp
== 0 &&
1621 n
->norm
->Bl
.type
!= LIST_column
) {
1622 mandoc_msg(MANDOCERR_PAR_SKIP
,
1624 "%s before It", roff_name
[nc
->tok
]);
1625 roff_node_delete(mdoc
, nc
);
1628 nc
= ni
->body
->last
;
1634 * If the argument of -offset or -width is a macro,
1635 * replace it with the associated default width.
1638 rewrite_macro2len(struct roff_man
*mdoc
, char **arg
)
1645 else if ( ! strcmp(*arg
, "Ds"))
1647 else if ((tok
= roffhash_find(mdoc
->mdocmac
, *arg
, 0)) == TOKEN_NONE
)
1650 width
= macro2len(tok
);
1653 mandoc_asprintf(arg
, "%zun", width
);
1657 post_bl_head(POST_ARGS
)
1659 struct roff_node
*nbl
, *nh
, *nch
, *nnext
;
1660 struct mdoc_argv
*argv
;
1666 if (nh
->norm
->Bl
.type
!= LIST_column
) {
1667 if ((nch
= nh
->child
) == NULL
)
1669 mandoc_msg(MANDOCERR_ARG_EXCESS
,
1670 nch
->line
, nch
->pos
, "Bl ... %s", nch
->string
);
1671 while (nch
!= NULL
) {
1672 roff_node_delete(mdoc
, nch
);
1679 * Append old-style lists, where the column width specifiers
1680 * trail as macro parameters, to the new-style ("normal-form")
1681 * lists where they're argument values following -column.
1684 if (nh
->child
== NULL
)
1688 for (j
= 0; j
< (int)nbl
->args
->argc
; j
++)
1689 if (nbl
->args
->argv
[j
].arg
== MDOC_Column
)
1692 assert(j
< (int)nbl
->args
->argc
);
1695 * Accommodate for new-style groff column syntax. Shuffle the
1696 * child nodes, all of which must be TEXT, as arguments for the
1697 * column field. Then, delete the head children.
1700 argv
= nbl
->args
->argv
+ j
;
1702 for (nch
= nh
->child
; nch
!= NULL
; nch
= nch
->next
)
1704 argv
->value
= mandoc_reallocarray(argv
->value
,
1705 argv
->sz
, sizeof(char *));
1707 nh
->norm
->Bl
.ncols
= argv
->sz
;
1708 nh
->norm
->Bl
.cols
= (void *)argv
->value
;
1710 for (nch
= nh
->child
; nch
!= NULL
; nch
= nnext
) {
1711 argv
->value
[i
++] = nch
->string
;
1714 roff_node_delete(NULL
, nch
);
1722 struct roff_node
*nparent
, *nprev
; /* of the Bl block */
1723 struct roff_node
*nblock
, *nbody
; /* of the Bl */
1724 struct roff_node
*nchild
, *nnext
; /* of the Bl body */
1725 const char *prev_Er
;
1729 switch (nbody
->type
) {
1731 post_bl_block(mdoc
);
1741 if (nbody
->end
!= ENDBODY_NOT
)
1744 nchild
= nbody
->child
;
1745 if (nchild
== NULL
) {
1746 mandoc_msg(MANDOCERR_BLK_EMPTY
,
1747 nbody
->line
, nbody
->pos
, "Bl");
1750 while (nchild
!= NULL
) {
1751 nnext
= nchild
->next
;
1752 if (nchild
->tok
== MDOC_It
||
1753 (nchild
->tok
== MDOC_Sm
&&
1754 nnext
!= NULL
&& nnext
->tok
== MDOC_It
)) {
1760 * In .Bl -column, the first rows may be implicit,
1761 * that is, they may not start with .It macros.
1762 * Such rows may be followed by nodes generated on the
1763 * roff level, for example .TS, which cannot be moved
1764 * out of the list. In that case, wrap such roff nodes
1765 * into an implicit row.
1768 if (nchild
->prev
!= NULL
) {
1769 mdoc
->last
= nchild
;
1770 mdoc
->next
= ROFF_NEXT_SIBLING
;
1771 roff_block_alloc(mdoc
, nchild
->line
,
1772 nchild
->pos
, MDOC_It
);
1773 roff_head_alloc(mdoc
, nchild
->line
,
1774 nchild
->pos
, MDOC_It
);
1775 mdoc
->next
= ROFF_NEXT_SIBLING
;
1776 roff_body_alloc(mdoc
, nchild
->line
,
1777 nchild
->pos
, MDOC_It
);
1778 while (nchild
->tok
!= MDOC_It
) {
1779 roff_node_relink(mdoc
, nchild
);
1780 if ((nchild
= nnext
) == NULL
)
1782 nnext
= nchild
->next
;
1783 mdoc
->next
= ROFF_NEXT_SIBLING
;
1789 mandoc_msg(MANDOCERR_BL_MOVE
, nchild
->line
, nchild
->pos
,
1790 "%s", roff_name
[nchild
->tok
]);
1793 * Move the node out of the Bl block.
1794 * First, collect all required node pointers.
1797 nblock
= nbody
->parent
;
1798 nprev
= nblock
->prev
;
1799 nparent
= nblock
->parent
;
1802 * Unlink this child.
1805 nbody
->child
= nnext
;
1812 * Relink this child.
1815 nchild
->parent
= nparent
;
1816 nchild
->prev
= nprev
;
1817 nchild
->next
= nblock
;
1819 nblock
->prev
= nchild
;
1821 nparent
->child
= nchild
;
1823 nprev
->next
= nchild
;
1828 if (mdoc
->meta
.os_e
!= MANDOC_OS_NETBSD
)
1832 for (nchild
= nbody
->child
; nchild
!= NULL
; nchild
= nchild
->next
) {
1833 if (nchild
->tok
!= MDOC_It
)
1835 if ((nnext
= nchild
->head
->child
) == NULL
)
1837 if (nnext
->type
== ROFFT_BLOCK
)
1838 nnext
= nnext
->body
->child
;
1839 if (nnext
== NULL
|| nnext
->tok
!= MDOC_Er
)
1841 nnext
= nnext
->child
;
1842 if (prev_Er
!= NULL
) {
1843 order
= strcmp(prev_Er
, nnext
->string
);
1845 mandoc_msg(MANDOCERR_ER_ORDER
,
1846 nnext
->line
, nnext
->pos
,
1847 "Er %s %s (NetBSD)",
1848 prev_Er
, nnext
->string
);
1849 else if (order
== 0)
1850 mandoc_msg(MANDOCERR_ER_REP
,
1851 nnext
->line
, nnext
->pos
,
1852 "Er %s (NetBSD)", prev_Er
);
1854 prev_Er
= nnext
->string
;
1861 struct roff_node
*n
;
1865 if (n
->type
== ROFFT_BLOCK
&& n
->body
->child
== NULL
) {
1866 mandoc_msg(MANDOCERR_BLK_EMPTY
, n
->line
, n
->pos
, "Bk");
1867 roff_node_delete(mdoc
, n
);
1874 struct roff_node
*nch
;
1876 nch
= mdoc
->last
->child
;
1879 mdoc
->flags
^= MDOC_SMOFF
;
1883 assert(nch
->type
== ROFFT_TEXT
);
1885 if ( ! strcmp(nch
->string
, "on")) {
1886 mdoc
->flags
&= ~MDOC_SMOFF
;
1889 if ( ! strcmp(nch
->string
, "off")) {
1890 mdoc
->flags
|= MDOC_SMOFF
;
1894 mandoc_msg(MANDOCERR_SM_BAD
, nch
->line
, nch
->pos
,
1895 "%s %s", roff_name
[mdoc
->last
->tok
], nch
->string
);
1896 roff_node_relink(mdoc
, nch
);
1901 post_root(POST_ARGS
)
1903 const char *openbsd_arch
[] = {
1904 "alpha", "amd64", "arm64", "armv7", "hppa", "i386",
1905 "landisk", "loongson", "luna88k", "macppc", "mips64",
1906 "octeon", "sgi", "socppc", "sparc64", NULL
1908 const char *netbsd_arch
[] = {
1909 "acorn26", "acorn32", "algor", "alpha", "amiga",
1911 "bebox", "cats", "cesfic", "cobalt", "dreamcast",
1912 "emips", "evbarm", "evbmips", "evbppc", "evbsh3", "evbsh5",
1913 "hp300", "hpcarm", "hpcmips", "hpcsh", "hppa",
1914 "i386", "ibmnws", "luna68k",
1915 "mac68k", "macppc", "mipsco", "mmeye", "mvme68k", "mvmeppc",
1916 "netwinder", "news68k", "newsmips", "next68k",
1917 "pc532", "playstation2", "pmax", "pmppc", "prep",
1918 "sandpoint", "sbmips", "sgimips", "shark",
1919 "sparc", "sparc64", "sun2", "sun3",
1920 "vax", "walnut", "x68k", "x86", "x86_64", "xen", NULL
1922 const char **arches
[] = { NULL
, netbsd_arch
, openbsd_arch
};
1924 struct roff_node
*n
;
1927 /* Add missing prologue data. */
1929 if (mdoc
->meta
.date
== NULL
)
1930 mdoc
->meta
.date
= mdoc
->quick
? mandoc_strdup("") :
1931 mandoc_normdate(mdoc
, NULL
, 0, 0);
1933 if (mdoc
->meta
.title
== NULL
) {
1934 mandoc_msg(MANDOCERR_DT_NOTITLE
, 0, 0, "EOF");
1935 mdoc
->meta
.title
= mandoc_strdup("UNTITLED");
1938 if (mdoc
->meta
.vol
== NULL
)
1939 mdoc
->meta
.vol
= mandoc_strdup("LOCAL");
1941 if (mdoc
->meta
.os
== NULL
) {
1942 mandoc_msg(MANDOCERR_OS_MISSING
, 0, 0, NULL
);
1943 mdoc
->meta
.os
= mandoc_strdup("");
1944 } else if (mdoc
->meta
.os_e
&&
1945 (mdoc
->meta
.rcsids
& (1 << mdoc
->meta
.os_e
)) == 0)
1946 mandoc_msg(MANDOCERR_RCS_MISSING
, 0, 0,
1947 mdoc
->meta
.os_e
== MANDOC_OS_OPENBSD
?
1948 "(OpenBSD)" : "(NetBSD)");
1950 if (mdoc
->meta
.arch
!= NULL
&&
1951 (arch
= arches
[mdoc
->meta
.os_e
]) != NULL
) {
1952 while (*arch
!= NULL
&& strcmp(*arch
, mdoc
->meta
.arch
))
1954 if (*arch
== NULL
) {
1955 n
= mdoc
->meta
.first
->child
;
1956 while (n
->tok
!= MDOC_Dt
||
1958 n
->child
->next
== NULL
||
1959 n
->child
->next
->next
== NULL
)
1961 n
= n
->child
->next
->next
;
1962 mandoc_msg(MANDOCERR_ARCH_BAD
, n
->line
, n
->pos
,
1963 "Dt ... %s %s", mdoc
->meta
.arch
,
1964 mdoc
->meta
.os_e
== MANDOC_OS_OPENBSD
?
1965 "(OpenBSD)" : "(NetBSD)");
1969 /* Check that we begin with a proper `Sh'. */
1971 n
= mdoc
->meta
.first
->child
;
1973 (n
->type
== ROFFT_COMMENT
||
1974 (n
->tok
>= MDOC_Dd
&&
1975 mdoc_macro(n
->tok
)->flags
& MDOC_PROLOGUE
)))
1979 mandoc_msg(MANDOCERR_DOC_EMPTY
, 0, 0, NULL
);
1980 else if (n
->tok
!= MDOC_Sh
)
1981 mandoc_msg(MANDOCERR_SEC_BEFORE
, n
->line
, n
->pos
,
1982 "%s", roff_name
[n
->tok
]);
1988 struct roff_node
*np
, *nch
, *next
, *prev
;
1993 if (np
->type
!= ROFFT_BODY
)
1996 if (np
->child
== NULL
) {
1997 mandoc_msg(MANDOCERR_RS_EMPTY
, np
->line
, np
->pos
, "Rs");
2002 * The full `Rs' block needs special handling to order the
2003 * sub-elements according to `rsord'. Pick through each element
2004 * and correctly order it. This is an insertion sort.
2008 for (nch
= np
->child
->next
; nch
!= NULL
; nch
= next
) {
2009 /* Determine order number of this child. */
2010 for (i
= 0; i
< RSORD_MAX
; i
++)
2011 if (rsord
[i
] == nch
->tok
)
2014 if (i
== RSORD_MAX
) {
2015 mandoc_msg(MANDOCERR_RS_BAD
, nch
->line
, nch
->pos
,
2016 "%s", roff_name
[nch
->tok
]);
2018 } else if (nch
->tok
== MDOC__J
|| nch
->tok
== MDOC__B
)
2019 np
->norm
->Rs
.quote_T
++;
2022 * Remove this child from the chain. This somewhat
2023 * repeats roff_node_unlink(), but since we're
2024 * just re-ordering, there's no need for the
2025 * full unlink process.
2028 if ((next
= nch
->next
) != NULL
)
2029 next
->prev
= nch
->prev
;
2031 if ((prev
= nch
->prev
) != NULL
)
2032 prev
->next
= nch
->next
;
2034 nch
->prev
= nch
->next
= NULL
;
2037 * Scan back until we reach a node that's
2038 * to be ordered before this child.
2041 for ( ; prev
; prev
= prev
->prev
) {
2042 /* Determine order of `prev'. */
2043 for (j
= 0; j
< RSORD_MAX
; j
++)
2044 if (rsord
[j
] == prev
->tok
)
2054 * Set this child back into its correct place
2055 * in front of the `prev' node.
2061 np
->child
->prev
= nch
;
2062 nch
->next
= np
->child
;
2066 prev
->next
->prev
= nch
;
2067 nch
->next
= prev
->next
;
2074 * For some arguments of some macros,
2075 * convert all breakable hyphens into ASCII_HYPH.
2078 post_hyph(POST_ARGS
)
2080 struct roff_node
*nch
;
2083 for (nch
= mdoc
->last
->child
; nch
!= NULL
; nch
= nch
->next
) {
2084 if (nch
->type
!= ROFFT_TEXT
)
2089 while (*(++cp
) != '\0')
2091 isalpha((unsigned char)cp
[-1]) &&
2092 isalpha((unsigned char)cp
[1]))
2100 struct roff_node
*n
;
2103 if (n
->flags
& NODE_LINE
||
2104 (n
->next
!= NULL
&& n
->next
->flags
& NODE_DELIMC
))
2105 mandoc_msg(MANDOCERR_NS_SKIP
, n
->line
, n
->pos
, NULL
);
2121 switch (mdoc
->last
->type
) {
2126 switch (mdoc
->lastsec
) {
2131 post_sh_see_also(mdoc
);
2134 post_sh_authors(mdoc
);
2146 post_sh_name(POST_ARGS
)
2148 struct roff_node
*n
;
2153 for (n
= mdoc
->last
->child
; n
!= NULL
; n
= n
->next
) {
2156 if (hasnm
&& n
->child
!= NULL
)
2157 mandoc_msg(MANDOCERR_NAMESEC_PUNCT
,
2159 "Nm %s", n
->child
->string
);
2164 if (n
->next
!= NULL
)
2165 mandoc_msg(MANDOCERR_NAMESEC_ND
,
2166 n
->line
, n
->pos
, NULL
);
2169 if (n
->type
== ROFFT_TEXT
&&
2170 n
->string
[0] == ',' && n
->string
[1] == '\0' &&
2171 n
->next
!= NULL
&& n
->next
->tok
== MDOC_Nm
) {
2177 mandoc_msg(MANDOCERR_NAMESEC_BAD
,
2178 n
->line
, n
->pos
, "%s", roff_name
[n
->tok
]);
2185 mandoc_msg(MANDOCERR_NAMESEC_NONM
,
2186 mdoc
->last
->line
, mdoc
->last
->pos
, NULL
);
2188 mandoc_msg(MANDOCERR_NAMESEC_NOND
,
2189 mdoc
->last
->line
, mdoc
->last
->pos
, NULL
);
2193 post_sh_see_also(POST_ARGS
)
2195 const struct roff_node
*n
;
2196 const char *name
, *sec
;
2197 const char *lastname
, *lastsec
, *lastpunct
;
2200 n
= mdoc
->last
->child
;
2201 lastname
= lastsec
= lastpunct
= NULL
;
2203 if (n
->tok
!= MDOC_Xr
||
2205 n
->child
->next
== NULL
)
2208 /* Process one .Xr node. */
2210 name
= n
->child
->string
;
2211 sec
= n
->child
->next
->string
;
2212 if (lastsec
!= NULL
) {
2213 if (lastpunct
[0] != ',' || lastpunct
[1] != '\0')
2214 mandoc_msg(MANDOCERR_XR_PUNCT
, n
->line
,
2215 n
->pos
, "%s before %s(%s)",
2216 lastpunct
, name
, sec
);
2217 cmp
= strcmp(lastsec
, sec
);
2219 mandoc_msg(MANDOCERR_XR_ORDER
, n
->line
,
2220 n
->pos
, "%s(%s) after %s(%s)",
2221 name
, sec
, lastname
, lastsec
);
2222 else if (cmp
== 0 &&
2223 strcasecmp(lastname
, name
) > 0)
2224 mandoc_msg(MANDOCERR_XR_ORDER
, n
->line
,
2225 n
->pos
, "%s after %s", name
, lastname
);
2230 /* Process the following node. */
2235 if (n
->tok
== MDOC_Xr
) {
2239 if (n
->type
!= ROFFT_TEXT
)
2241 for (name
= n
->string
; *name
!= '\0'; name
++)
2242 if (isalpha((const unsigned char)*name
))
2244 lastpunct
= n
->string
;
2245 if (n
->next
== NULL
|| n
->next
->tok
== MDOC_Rs
)
2246 mandoc_msg(MANDOCERR_XR_PUNCT
, n
->line
,
2247 n
->pos
, "%s after %s(%s)",
2248 lastpunct
, lastname
, lastsec
);
2254 child_an(const struct roff_node
*n
)
2257 for (n
= n
->child
; n
!= NULL
; n
= n
->next
)
2258 if ((n
->tok
== MDOC_An
&& n
->child
!= NULL
) || child_an(n
))
2264 post_sh_authors(POST_ARGS
)
2267 if ( ! child_an(mdoc
->last
))
2268 mandoc_msg(MANDOCERR_AN_MISSING
,
2269 mdoc
->last
->line
, mdoc
->last
->pos
, NULL
);
2273 * Return an upper bound for the string distance (allowing
2274 * transpositions). Not a full Levenshtein implementation
2275 * because Levenshtein is quadratic in the string length
2276 * and this function is called for every standard name,
2277 * so the check for each custom name would be cubic.
2278 * The following crude heuristics is linear, resulting
2279 * in quadratic behaviour for checking one custom name,
2280 * which does not cause measurable slowdown.
2283 similar(const char *s1
, const char *s2
)
2285 const int maxdist
= 3;
2288 while (s1
[0] != '\0' && s2
[0] != '\0') {
2289 if (s1
[0] == s2
[0]) {
2294 if (++dist
> maxdist
)
2296 if (s1
[1] == s2
[1]) { /* replacement */
2299 } else if (s1
[0] == s2
[1] && s1
[1] == s2
[0]) {
2300 s1
+= 2; /* transposition */
2302 } else if (s1
[0] == s2
[1]) /* insertion */
2304 else if (s1
[1] == s2
[0]) /* deletion */
2309 dist
+= strlen(s1
) + strlen(s2
);
2310 return dist
> maxdist
? INT_MAX
: dist
;
2314 post_sh_head(POST_ARGS
)
2316 struct roff_node
*nch
;
2317 const char *goodsec
;
2318 const char *const *testsec
;
2323 * Process a new section. Sections are either "named" or
2324 * "custom". Custom sections are user-defined, while named ones
2325 * follow a conventional order and may only appear in certain
2329 sec
= mdoc
->last
->sec
;
2331 /* The NAME should be first. */
2333 if (sec
!= SEC_NAME
&& mdoc
->lastnamed
== SEC_NONE
)
2334 mandoc_msg(MANDOCERR_NAMESEC_FIRST
,
2335 mdoc
->last
->line
, mdoc
->last
->pos
, "Sh %s",
2336 sec
!= SEC_CUSTOM
? secnames
[sec
] :
2337 (nch
= mdoc
->last
->child
) == NULL
? "" :
2338 nch
->type
== ROFFT_TEXT
? nch
->string
:
2339 roff_name
[nch
->tok
]);
2341 /* The SYNOPSIS gets special attention in other areas. */
2343 if (sec
== SEC_SYNOPSIS
) {
2344 roff_setreg(mdoc
->roff
, "nS", 1, '=');
2345 mdoc
->flags
|= MDOC_SYNOPSIS
;
2347 roff_setreg(mdoc
->roff
, "nS", 0, '=');
2348 mdoc
->flags
&= ~MDOC_SYNOPSIS
;
2351 /* Mark our last section. */
2353 mdoc
->lastsec
= sec
;
2355 /* We don't care about custom sections after this. */
2357 if (sec
== SEC_CUSTOM
) {
2358 if ((nch
= mdoc
->last
->child
) == NULL
||
2359 nch
->type
!= ROFFT_TEXT
|| nch
->next
!= NULL
)
2363 for (testsec
= secnames
+ 1; *testsec
!= NULL
; testsec
++) {
2364 dist
= similar(nch
->string
, *testsec
);
2365 if (dist
< mindist
) {
2370 if (goodsec
!= NULL
)
2371 mandoc_msg(MANDOCERR_SEC_TYPO
, nch
->line
, nch
->pos
,
2372 "Sh %s instead of %s", nch
->string
, goodsec
);
2377 * Check whether our non-custom section is being repeated or is
2381 if (sec
== mdoc
->lastnamed
)
2382 mandoc_msg(MANDOCERR_SEC_REP
, mdoc
->last
->line
,
2383 mdoc
->last
->pos
, "Sh %s", secnames
[sec
]);
2385 if (sec
< mdoc
->lastnamed
)
2386 mandoc_msg(MANDOCERR_SEC_ORDER
, mdoc
->last
->line
,
2387 mdoc
->last
->pos
, "Sh %s", secnames
[sec
]);
2389 /* Mark the last named section. */
2391 mdoc
->lastnamed
= sec
;
2393 /* Check particular section/manual conventions. */
2395 if (mdoc
->meta
.msec
== NULL
)
2401 if (*mdoc
->meta
.msec
== '4')
2403 goodsec
= "2, 3, 4, 9";
2405 case SEC_RETURN_VALUES
:
2407 if (*mdoc
->meta
.msec
== '2')
2409 if (*mdoc
->meta
.msec
== '3')
2411 if (NULL
== goodsec
)
2412 goodsec
= "2, 3, 9";
2415 if (*mdoc
->meta
.msec
== '9')
2417 if (NULL
== goodsec
)
2419 mandoc_msg(MANDOCERR_SEC_MSEC
,
2420 mdoc
->last
->line
, mdoc
->last
->pos
,
2421 "Sh %s for %s only", secnames
[sec
], goodsec
);
2431 struct roff_node
*n
, *nch
;
2435 if (nch
->next
== NULL
) {
2436 mandoc_msg(MANDOCERR_XR_NOSEC
,
2437 n
->line
, n
->pos
, "Xr %s", nch
->string
);
2439 assert(nch
->next
== n
->last
);
2440 if(mandoc_xr_add(nch
->next
->string
, nch
->string
,
2441 nch
->line
, nch
->pos
))
2442 mandoc_msg(MANDOCERR_XR_SELF
,
2443 nch
->line
, nch
->pos
, "Xr %s %s",
2444 nch
->string
, nch
->next
->string
);
2446 post_delim_nb(mdoc
);
2450 post_ignpar(POST_ARGS
)
2452 struct roff_node
*np
;
2454 switch (mdoc
->last
->type
) {
2468 if ((np
= mdoc
->last
->child
) != NULL
)
2469 if (np
->tok
== MDOC_Pp
||
2470 np
->tok
== ROFF_br
|| np
->tok
== ROFF_sp
) {
2471 mandoc_msg(MANDOCERR_PAR_SKIP
, np
->line
, np
->pos
,
2472 "%s after %s", roff_name
[np
->tok
],
2473 roff_name
[mdoc
->last
->tok
]);
2474 roff_node_delete(mdoc
, np
);
2477 if ((np
= mdoc
->last
->last
) != NULL
)
2478 if (np
->tok
== MDOC_Pp
|| np
->tok
== ROFF_br
) {
2479 mandoc_msg(MANDOCERR_PAR_SKIP
, np
->line
, np
->pos
,
2480 "%s at the end of %s", roff_name
[np
->tok
],
2481 roff_name
[mdoc
->last
->tok
]);
2482 roff_node_delete(mdoc
, np
);
2487 post_prevpar(POST_ARGS
)
2489 struct roff_node
*n
;
2492 if (NULL
== n
->prev
)
2494 if (n
->type
!= ROFFT_ELEM
&& n
->type
!= ROFFT_BLOCK
)
2498 * Don't allow `Pp' prior to a paragraph-type
2499 * block: `Pp' or non-compact `Bd' or `Bl'.
2502 if (n
->prev
->tok
!= MDOC_Pp
&& n
->prev
->tok
!= ROFF_br
)
2504 if (n
->tok
== MDOC_Bl
&& n
->norm
->Bl
.comp
)
2506 if (n
->tok
== MDOC_Bd
&& n
->norm
->Bd
.comp
)
2508 if (n
->tok
== MDOC_It
&& n
->parent
->norm
->Bl
.comp
)
2511 mandoc_msg(MANDOCERR_PAR_SKIP
, n
->prev
->line
, n
->prev
->pos
,
2512 "%s before %s", roff_name
[n
->prev
->tok
], roff_name
[n
->tok
]);
2513 roff_node_delete(mdoc
, n
->prev
);
2519 struct roff_node
*np
;
2524 if (np
->child
!= NULL
)
2525 mandoc_msg(MANDOCERR_ARG_SKIP
, np
->line
, np
->pos
,
2526 "%s %s", roff_name
[np
->tok
], np
->child
->string
);
2532 struct roff_node
*n
;
2536 n
->flags
|= NODE_NOPRT
;
2538 if (mdoc
->meta
.date
!= NULL
) {
2539 mandoc_msg(MANDOCERR_PROLOG_REP
, n
->line
, n
->pos
, "Dd");
2540 free(mdoc
->meta
.date
);
2541 } else if (mdoc
->flags
& MDOC_PBODY
)
2542 mandoc_msg(MANDOCERR_PROLOG_LATE
, n
->line
, n
->pos
, "Dd");
2543 else if (mdoc
->meta
.title
!= NULL
)
2544 mandoc_msg(MANDOCERR_PROLOG_ORDER
,
2545 n
->line
, n
->pos
, "Dd after Dt");
2546 else if (mdoc
->meta
.os
!= NULL
)
2547 mandoc_msg(MANDOCERR_PROLOG_ORDER
,
2548 n
->line
, n
->pos
, "Dd after Os");
2550 if (n
->child
== NULL
|| n
->child
->string
[0] == '\0') {
2551 mdoc
->meta
.date
= mdoc
->quick
? mandoc_strdup("") :
2552 mandoc_normdate(mdoc
, NULL
, n
->line
, n
->pos
);
2557 deroff(&datestr
, n
);
2559 mdoc
->meta
.date
= datestr
;
2561 mdoc
->meta
.date
= mandoc_normdate(mdoc
,
2562 datestr
, n
->line
, n
->pos
);
2570 struct roff_node
*nn
, *n
;
2575 n
->flags
|= NODE_NOPRT
;
2577 if (mdoc
->flags
& MDOC_PBODY
) {
2578 mandoc_msg(MANDOCERR_DT_LATE
, n
->line
, n
->pos
, "Dt");
2582 if (mdoc
->meta
.title
!= NULL
)
2583 mandoc_msg(MANDOCERR_PROLOG_REP
, n
->line
, n
->pos
, "Dt");
2584 else if (mdoc
->meta
.os
!= NULL
)
2585 mandoc_msg(MANDOCERR_PROLOG_ORDER
,
2586 n
->line
, n
->pos
, "Dt after Os");
2588 free(mdoc
->meta
.title
);
2589 free(mdoc
->meta
.msec
);
2590 free(mdoc
->meta
.vol
);
2591 free(mdoc
->meta
.arch
);
2593 mdoc
->meta
.title
= NULL
;
2594 mdoc
->meta
.msec
= NULL
;
2595 mdoc
->meta
.vol
= NULL
;
2596 mdoc
->meta
.arch
= NULL
;
2598 /* Mandatory first argument: title. */
2601 if (nn
== NULL
|| *nn
->string
== '\0') {
2602 mandoc_msg(MANDOCERR_DT_NOTITLE
, n
->line
, n
->pos
, "Dt");
2603 mdoc
->meta
.title
= mandoc_strdup("UNTITLED");
2605 mdoc
->meta
.title
= mandoc_strdup(nn
->string
);
2607 /* Check that all characters are uppercase. */
2609 for (p
= nn
->string
; *p
!= '\0'; p
++)
2610 if (islower((unsigned char)*p
)) {
2611 mandoc_msg(MANDOCERR_TITLE_CASE
, nn
->line
,
2612 nn
->pos
+ (int)(p
- nn
->string
),
2613 "Dt %s", nn
->string
);
2618 /* Mandatory second argument: section. */
2624 mandoc_msg(MANDOCERR_MSEC_MISSING
, n
->line
, n
->pos
,
2625 "Dt %s", mdoc
->meta
.title
);
2626 mdoc
->meta
.vol
= mandoc_strdup("LOCAL");
2627 return; /* msec and arch remain NULL. */
2630 mdoc
->meta
.msec
= mandoc_strdup(nn
->string
);
2632 /* Infer volume title from section number. */
2634 cp
= mandoc_a2msec(nn
->string
);
2636 mandoc_msg(MANDOCERR_MSEC_BAD
,
2637 nn
->line
, nn
->pos
, "Dt ... %s", nn
->string
);
2638 mdoc
->meta
.vol
= mandoc_strdup(nn
->string
);
2640 mdoc
->meta
.vol
= mandoc_strdup(cp
);
2642 /* Optional third argument: architecture. */
2644 if ((nn
= nn
->next
) == NULL
)
2647 for (p
= nn
->string
; *p
!= '\0'; p
++)
2648 *p
= tolower((unsigned char)*p
);
2649 mdoc
->meta
.arch
= mandoc_strdup(nn
->string
);
2651 /* Ignore fourth and later arguments. */
2653 if ((nn
= nn
->next
) != NULL
)
2654 mandoc_msg(MANDOCERR_ARG_EXCESS
,
2655 nn
->line
, nn
->pos
, "Dt ... %s", nn
->string
);
2661 struct roff_node
*n
, *nch
;
2664 post_delim_nb(mdoc
);
2670 macro
= !strcmp(nch
->string
, "Open") ? "Ox" :
2671 !strcmp(nch
->string
, "Net") ? "Nx" :
2672 !strcmp(nch
->string
, "Free") ? "Fx" :
2673 !strcmp(nch
->string
, "DragonFly") ? "Dx" : NULL
;
2675 mandoc_msg(MANDOCERR_BX
,
2676 n
->line
, n
->pos
, "%s", macro
);
2679 mdoc
->next
= ROFF_NEXT_SIBLING
;
2680 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Ns
);
2681 mdoc
->last
->flags
|= NODE_NOSRC
;
2682 mdoc
->next
= ROFF_NEXT_SIBLING
;
2684 mdoc
->next
= ROFF_NEXT_CHILD
;
2685 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "BSD");
2686 mdoc
->last
->flags
|= NODE_NOSRC
;
2693 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Ns
);
2694 mdoc
->last
->flags
|= NODE_NOSRC
;
2695 mdoc
->next
= ROFF_NEXT_SIBLING
;
2696 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "-");
2697 mdoc
->last
->flags
|= NODE_NOSRC
;
2698 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Ns
);
2699 mdoc
->last
->flags
|= NODE_NOSRC
;
2703 * Make `Bx's second argument always start with an uppercase
2704 * letter. Groff checks if it's an "accepted" term, but we just
2705 * uppercase blindly.
2708 *nch
->string
= (char)toupper((unsigned char)*nch
->string
);
2715 struct utsname utsname
;
2716 static char *defbuf
;
2718 struct roff_node
*n
;
2721 n
->flags
|= NODE_NOPRT
;
2723 if (mdoc
->meta
.os
!= NULL
)
2724 mandoc_msg(MANDOCERR_PROLOG_REP
, n
->line
, n
->pos
, "Os");
2725 else if (mdoc
->flags
& MDOC_PBODY
)
2726 mandoc_msg(MANDOCERR_PROLOG_LATE
, n
->line
, n
->pos
, "Os");
2731 * Set the operating system by way of the `Os' macro.
2732 * The order of precedence is:
2733 * 1. the argument of the `Os' macro, unless empty
2734 * 2. the -Ios=foo command line argument, if provided
2735 * 3. -DOSNAME="\"foo\"", if provided during compilation
2736 * 4. "sysname release" from uname(3)
2739 free(mdoc
->meta
.os
);
2740 mdoc
->meta
.os
= NULL
;
2741 deroff(&mdoc
->meta
.os
, n
);
2745 if (mdoc
->os_s
!= NULL
) {
2746 mdoc
->meta
.os
= mandoc_strdup(mdoc
->os_s
);
2751 mdoc
->meta
.os
= mandoc_strdup(OSNAME
);
2753 if (defbuf
== NULL
) {
2754 if (uname(&utsname
) == -1) {
2755 mandoc_msg(MANDOCERR_OS_UNAME
, n
->line
, n
->pos
, "Os");
2756 defbuf
= mandoc_strdup("UNKNOWN");
2758 mandoc_asprintf(&defbuf
, "%s %s",
2759 utsname
.sysname
, utsname
.release
);
2761 mdoc
->meta
.os
= mandoc_strdup(defbuf
);
2765 if (mdoc
->meta
.os_e
== MANDOC_OS_OTHER
) {
2766 if (strstr(mdoc
->meta
.os
, "OpenBSD") != NULL
)
2767 mdoc
->meta
.os_e
= MANDOC_OS_OPENBSD
;
2768 else if (strstr(mdoc
->meta
.os
, "NetBSD") != NULL
)
2769 mdoc
->meta
.os_e
= MANDOC_OS_NETBSD
;
2773 * This is the earliest point where we can check
2774 * Mdocdate conventions because we don't know
2775 * the operating system earlier.
2778 if (n
->child
!= NULL
)
2779 mandoc_msg(MANDOCERR_OS_ARG
, n
->child
->line
, n
->child
->pos
,
2780 "Os %s (%s)", n
->child
->string
,
2781 mdoc
->meta
.os_e
== MANDOC_OS_OPENBSD
?
2782 "OpenBSD" : "NetBSD");
2784 while (n
->tok
!= MDOC_Dd
)
2785 if ((n
= n
->prev
) == NULL
)
2787 if ((n
= n
->child
) == NULL
)
2789 if (strncmp(n
->string
, "$" "Mdocdate", 9)) {
2790 if (mdoc
->meta
.os_e
== MANDOC_OS_OPENBSD
)
2791 mandoc_msg(MANDOCERR_MDOCDATE_MISSING
, n
->line
,
2792 n
->pos
, "Dd %s (OpenBSD)", n
->string
);
2794 if (mdoc
->meta
.os_e
== MANDOC_OS_NETBSD
)
2795 mandoc_msg(MANDOCERR_MDOCDATE
, n
->line
,
2796 n
->pos
, "Dd %s (NetBSD)", n
->string
);
2801 mdoc_a2sec(const char *p
)
2805 for (i
= 0; i
< (int)SEC__MAX
; i
++)
2806 if (secnames
[i
] && 0 == strcmp(p
, secnames
[i
]))
2807 return (enum roff_sec
)i
;
2813 macro2len(enum roff_tok macro
)