1 /* $Id: mdoc_validate.c,v 1.369 2018/12/31 08:38:21 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 ((n
->flags
& NODE_NOFILL
) == 0 &&
340 (np
->tok
!= MDOC_It
|| np
->type
!= ROFFT_HEAD
||
341 np
->parent
->parent
->norm
->Bl
.type
!= LIST_diag
))
342 check_text_em(mdoc
, n
->line
, n
->pos
, n
->string
);
343 if (np
->tok
== MDOC_It
|| (np
->type
== ROFFT_BODY
&&
344 (np
->tok
== MDOC_Sh
|| np
->tok
== MDOC_Ss
)))
345 check_toptext(mdoc
, n
->line
, n
->pos
, n
->string
);
355 check_args(mdoc
, mdoc
->last
);
358 * Closing delimiters are not special at the
359 * beginning of a block, opening delimiters
360 * are not special at the end.
363 if (n
->child
!= NULL
)
364 n
->child
->flags
&= ~NODE_DELIMC
;
366 n
->last
->flags
&= ~NODE_DELIMO
;
368 /* Call the macro's postprocessor. */
370 if (n
->tok
< ROFF_MAX
) {
375 assert(n
->tok
>= MDOC_Dd
&& n
->tok
< MDOC_MAX
);
376 p
= mdoc_valids
+ (n
->tok
- MDOC_Dd
);
386 check_args(struct roff_man
*mdoc
, struct roff_node
*n
)
393 assert(n
->args
->argc
);
394 for (i
= 0; i
< (int)n
->args
->argc
; i
++)
395 check_argv(mdoc
, n
, &n
->args
->argv
[i
]);
399 check_argv(struct roff_man
*mdoc
, struct roff_node
*n
, struct mdoc_argv
*v
)
403 for (i
= 0; i
< (int)v
->sz
; i
++)
404 check_text(mdoc
, v
->line
, v
->pos
, v
->value
[i
]);
408 check_text(struct roff_man
*mdoc
, int ln
, int pos
, char *p
)
412 if (mdoc
->last
->flags
& NODE_NOFILL
)
415 for (cp
= p
; NULL
!= (p
= strchr(p
, '\t')); p
++)
416 mandoc_msg(MANDOCERR_FI_TAB
, ln
, pos
+ (int)(p
- cp
), NULL
);
420 check_text_em(struct roff_man
*mdoc
, int ln
, int pos
, char *p
)
422 const struct roff_node
*np
, *nn
;
425 np
= mdoc
->last
->prev
;
426 nn
= mdoc
->last
->next
;
428 /* Look for em-dashes wrongly encoded as "--". */
430 for (cp
= p
; *cp
!= '\0'; cp
++) {
431 if (cp
[0] != '-' || cp
[1] != '-')
435 /* Skip input sequences of more than two '-'. */
443 /* Skip "--" directly attached to something else. */
445 if ((cp
- p
> 1 && cp
[-2] != ' ') ||
446 (cp
[1] != '\0' && cp
[1] != ' '))
449 /* Require a letter right before or right afterwards. */
452 isalpha((unsigned char)cp
[-3]) :
454 np
->type
== ROFFT_TEXT
&&
455 *np
->string
!= '\0' &&
456 isalpha((unsigned char)np
->string
[
457 strlen(np
->string
) - 1])) ||
458 (cp
[1] != '\0' && cp
[2] != '\0' ?
459 isalpha((unsigned char)cp
[2]) :
461 nn
->type
== ROFFT_TEXT
&&
462 isalpha((unsigned char)*nn
->string
))) {
463 mandoc_msg(MANDOCERR_DASHDASH
,
464 ln
, pos
+ (int)(cp
- p
) - 1, NULL
);
471 check_toptext(struct roff_man
*mdoc
, int ln
, int pos
, const char *p
)
473 const char *cp
, *cpr
;
478 if ((cp
= strstr(p
, "OpenBSD")) != NULL
)
479 mandoc_msg(MANDOCERR_BX
, ln
, pos
+ (int)(cp
- p
), "Ox");
480 if ((cp
= strstr(p
, "NetBSD")) != NULL
)
481 mandoc_msg(MANDOCERR_BX
, ln
, pos
+ (int)(cp
- p
), "Nx");
482 if ((cp
= strstr(p
, "FreeBSD")) != NULL
)
483 mandoc_msg(MANDOCERR_BX
, ln
, pos
+ (int)(cp
- p
), "Fx");
484 if ((cp
= strstr(p
, "DragonFly")) != NULL
)
485 mandoc_msg(MANDOCERR_BX
, ln
, pos
+ (int)(cp
- p
), "Dx");
488 while ((cp
= strstr(cp
+ 1, "()")) != NULL
) {
489 for (cpr
= cp
- 1; cpr
>= p
; cpr
--)
490 if (*cpr
!= '_' && !isalnum((unsigned char)*cpr
))
492 if ((cpr
< p
|| *cpr
== ' ') && cpr
+ 1 < cp
) {
494 mandoc_msg(MANDOCERR_FUNC
, ln
, pos
+ (int)(cpr
- p
),
495 "%.*s()", (int)(cp
- cpr
), cpr
);
501 post_abort(POST_ARGS
)
507 post_delim(POST_ARGS
)
509 const struct roff_node
*nch
;
514 tok
= mdoc
->last
->tok
;
515 nch
= mdoc
->last
->last
;
516 if (nch
== NULL
|| nch
->type
!= ROFFT_TEXT
)
518 lc
= strchr(nch
->string
, '\0') - 1;
519 if (lc
< nch
->string
)
521 delim
= mdoc_isdelim(lc
);
522 if (delim
== DELIM_NONE
|| delim
== DELIM_OPEN
)
524 if (*lc
== ')' && (tok
== MDOC_Nd
|| tok
== MDOC_Sh
||
525 tok
== MDOC_Ss
|| tok
== MDOC_Fo
))
528 mandoc_msg(MANDOCERR_DELIM
, nch
->line
,
529 nch
->pos
+ (int)(lc
- nch
->string
), "%s%s %s", roff_name
[tok
],
530 nch
== mdoc
->last
->child
? "" : " ...", nch
->string
);
534 post_delim_nb(POST_ARGS
)
536 const struct roff_node
*nch
;
543 * Find candidates: at least two bytes,
544 * the last one a closing or middle delimiter.
547 tok
= mdoc
->last
->tok
;
548 nch
= mdoc
->last
->last
;
549 if (nch
== NULL
|| nch
->type
!= ROFFT_TEXT
)
551 lc
= strchr(nch
->string
, '\0') - 1;
552 if (lc
<= nch
->string
)
554 delim
= mdoc_isdelim(lc
);
555 if (delim
== DELIM_NONE
|| delim
== DELIM_OPEN
)
559 * Reduce false positives by allowing various cases.
562 /* Escaped delimiters. */
563 if (lc
> nch
->string
+ 1 && lc
[-2] == '\\' &&
564 (lc
[-1] == '&' || lc
[-1] == 'e'))
567 /* Specific byte sequences. */
570 for (cp
= lc
; cp
>= nch
->string
; cp
--)
575 if (lc
> nch
->string
+ 1 && lc
[-2] == '.' && lc
[-1] == '.')
589 for (cp
= lc
; cp
>= nch
->string
; cp
--)
594 if (lc
== nch
->string
+ 1 && lc
[-1] == '|')
600 /* Exactly two non-alphanumeric bytes. */
601 if (lc
== nch
->string
+ 1 && !isalnum((unsigned char)lc
[-1]))
604 /* At least three alphabetic words with a sentence ending. */
605 if (strchr("!.:?", *lc
) != NULL
&& (tok
== MDOC_Em
||
606 tok
== MDOC_Li
|| tok
== MDOC_Pq
|| tok
== MDOC_Sy
)) {
608 for (cp
= lc
- 1; cp
>= nch
->string
; cp
--) {
611 if (cp
> nch
->string
&& cp
[-1] == ',')
613 } else if (isalpha((unsigned int)*cp
)) {
621 mandoc_msg(MANDOCERR_DELIM_NB
, nch
->line
,
622 nch
->pos
+ (int)(lc
- nch
->string
), "%s%s %s", roff_name
[tok
],
623 nch
== mdoc
->last
->child
? "" : " ...", nch
->string
);
627 post_bl_norm(POST_ARGS
)
630 struct mdoc_argv
*argv
, *wa
;
632 enum mdocargt mdoclt
;
635 n
= mdoc
->last
->parent
;
636 n
->norm
->Bl
.type
= LIST__NONE
;
639 * First figure out which kind of list to use: bind ourselves to
640 * the first mentioned list type and warn about any remaining
641 * ones. If we find no list type, we default to LIST_item.
644 wa
= (n
->args
== NULL
) ? NULL
: n
->args
->argv
;
645 mdoclt
= MDOC_ARG_MAX
;
646 for (i
= 0; n
->args
&& i
< (int)n
->args
->argc
; i
++) {
647 argv
= n
->args
->argv
+ i
;
650 /* Set list types. */
684 /* Set list arguments. */
686 if (n
->norm
->Bl
.comp
)
687 mandoc_msg(MANDOCERR_ARG_REP
,
688 argv
->line
, argv
->pos
, "Bl -compact");
689 n
->norm
->Bl
.comp
= 1;
694 mandoc_msg(MANDOCERR_ARG_EMPTY
,
695 argv
->line
, argv
->pos
, "Bl -width");
696 n
->norm
->Bl
.width
= "0n";
699 if (NULL
!= n
->norm
->Bl
.width
)
700 mandoc_msg(MANDOCERR_ARG_REP
,
701 argv
->line
, argv
->pos
,
702 "Bl -width %s", argv
->value
[0]);
703 rewrite_macro2len(mdoc
, argv
->value
);
704 n
->norm
->Bl
.width
= argv
->value
[0];
708 mandoc_msg(MANDOCERR_ARG_EMPTY
,
709 argv
->line
, argv
->pos
, "Bl -offset");
712 if (NULL
!= n
->norm
->Bl
.offs
)
713 mandoc_msg(MANDOCERR_ARG_REP
,
714 argv
->line
, argv
->pos
,
715 "Bl -offset %s", argv
->value
[0]);
716 rewrite_macro2len(mdoc
, argv
->value
);
717 n
->norm
->Bl
.offs
= argv
->value
[0];
722 if (LIST__NONE
== lt
)
726 /* Check: multiple list types. */
728 if (LIST__NONE
!= n
->norm
->Bl
.type
) {
729 mandoc_msg(MANDOCERR_BL_REP
, n
->line
, n
->pos
,
730 "Bl -%s", mdoc_argnames
[argv
->arg
]);
734 /* The list type should come first. */
736 if (n
->norm
->Bl
.width
||
739 mandoc_msg(MANDOCERR_BL_LATETYPE
,
740 n
->line
, n
->pos
, "Bl -%s",
741 mdoc_argnames
[n
->args
->argv
[0].arg
]);
743 n
->norm
->Bl
.type
= lt
;
744 if (LIST_column
== lt
) {
745 n
->norm
->Bl
.ncols
= argv
->sz
;
746 n
->norm
->Bl
.cols
= (void *)argv
->value
;
750 /* Allow lists to default to LIST_item. */
752 if (LIST__NONE
== n
->norm
->Bl
.type
) {
753 mandoc_msg(MANDOCERR_BL_NOTYPE
, n
->line
, n
->pos
, "Bl");
754 n
->norm
->Bl
.type
= LIST_item
;
759 * Validate the width field. Some list types don't need width
760 * types and should be warned about them. Others should have it
761 * and must also be warned. Yet others have a default and need
765 switch (n
->norm
->Bl
.type
) {
767 if (n
->norm
->Bl
.width
== NULL
)
768 mandoc_msg(MANDOCERR_BL_NOWIDTH
,
769 n
->line
, n
->pos
, "Bl -tag");
776 if (n
->norm
->Bl
.width
!= NULL
)
777 mandoc_msg(MANDOCERR_BL_SKIPW
, wa
->line
, wa
->pos
,
778 "Bl -%s", mdoc_argnames
[mdoclt
]);
779 n
->norm
->Bl
.width
= NULL
;
784 if (n
->norm
->Bl
.width
== NULL
)
785 n
->norm
->Bl
.width
= "2n";
788 if (n
->norm
->Bl
.width
== NULL
)
789 n
->norm
->Bl
.width
= "3n";
800 struct mdoc_argv
*argv
;
805 for (i
= 0; n
->args
&& i
< (int)n
->args
->argc
; i
++) {
806 argv
= n
->args
->argv
+ i
;
826 mandoc_msg(MANDOCERR_BD_FILE
, n
->line
, n
->pos
, NULL
);
830 mandoc_msg(MANDOCERR_ARG_EMPTY
,
831 argv
->line
, argv
->pos
, "Bd -offset");
834 if (NULL
!= n
->norm
->Bd
.offs
)
835 mandoc_msg(MANDOCERR_ARG_REP
,
836 argv
->line
, argv
->pos
,
837 "Bd -offset %s", argv
->value
[0]);
838 rewrite_macro2len(mdoc
, argv
->value
);
839 n
->norm
->Bd
.offs
= argv
->value
[0];
842 if (n
->norm
->Bd
.comp
)
843 mandoc_msg(MANDOCERR_ARG_REP
,
844 argv
->line
, argv
->pos
, "Bd -compact");
845 n
->norm
->Bd
.comp
= 1;
850 if (DISP__NONE
== dt
)
853 if (DISP__NONE
== n
->norm
->Bd
.type
)
854 n
->norm
->Bd
.type
= dt
;
856 mandoc_msg(MANDOCERR_BD_REP
, n
->line
, n
->pos
,
857 "Bd -%s", mdoc_argnames
[argv
->arg
]);
860 if (DISP__NONE
== n
->norm
->Bd
.type
) {
861 mandoc_msg(MANDOCERR_BD_NOTYPE
, n
->line
, n
->pos
, "Bd");
862 n
->norm
->Bd
.type
= DISP_ragged
;
867 * Stand-alone line macros.
871 post_an_norm(POST_ARGS
)
874 struct mdoc_argv
*argv
;
881 for (i
= 1; i
< n
->args
->argc
; i
++) {
882 argv
= n
->args
->argv
+ i
;
883 mandoc_msg(MANDOCERR_AN_REP
, argv
->line
, argv
->pos
,
884 "An -%s", mdoc_argnames
[argv
->arg
]);
887 argv
= n
->args
->argv
;
888 if (argv
->arg
== MDOC_Split
)
889 n
->norm
->An
.auth
= AUTH_split
;
890 else if (argv
->arg
== MDOC_Nosplit
)
891 n
->norm
->An
.auth
= AUTH_nosplit
;
903 if (n
->child
!= NULL
)
904 mandoc_msg(MANDOCERR_ARG_SKIP
, n
->line
,
905 n
->pos
, "%s %s", roff_name
[n
->tok
], n
->child
->string
);
907 while (n
->child
!= NULL
)
908 roff_node_delete(mdoc
, n
->child
);
910 roff_word_alloc(mdoc
, n
->line
, n
->pos
, n
->tok
== MDOC_Bt
?
911 "is currently in beta test." : "currently under development.");
912 mdoc
->last
->flags
|= NODE_EOS
| NODE_NOSRC
;
917 build_list(struct roff_man
*mdoc
, int tok
)
922 n
= mdoc
->last
->next
;
923 for (ic
= 1;; ic
++) {
924 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, tok
);
925 mdoc
->last
->flags
|= NODE_NOSRC
;
926 roff_node_relink(mdoc
, n
);
927 n
= mdoc
->last
= mdoc
->last
->parent
;
928 mdoc
->next
= ROFF_NEXT_SIBLING
;
931 if (ic
> 1 || n
->next
->next
!= NULL
) {
932 roff_word_alloc(mdoc
, n
->line
, n
->pos
, ",");
933 mdoc
->last
->flags
|= NODE_DELIMC
| NODE_NOSRC
;
935 n
= mdoc
->last
->next
;
936 if (n
->next
== NULL
) {
937 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "and");
938 mdoc
->last
->flags
|= NODE_NOSRC
;
952 mdoc
->next
= ROFF_NEXT_CHILD
;
953 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "The");
954 mdoc
->last
->flags
|= NODE_NOSRC
;
956 if (mdoc
->last
->next
!= NULL
)
957 ic
= build_list(mdoc
, MDOC_Nm
);
958 else if (mdoc
->meta
.name
!= NULL
) {
959 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Nm
);
960 mdoc
->last
->flags
|= NODE_NOSRC
;
961 roff_word_alloc(mdoc
, n
->line
, n
->pos
, mdoc
->meta
.name
);
962 mdoc
->last
->flags
|= NODE_NOSRC
;
963 mdoc
->last
= mdoc
->last
->parent
;
964 mdoc
->next
= ROFF_NEXT_SIBLING
;
967 mandoc_msg(MANDOCERR_EX_NONAME
, n
->line
, n
->pos
, "Ex");
971 roff_word_alloc(mdoc
, n
->line
, n
->pos
,
972 ic
> 1 ? "utilities exit\\~0" : "utility exits\\~0");
973 mdoc
->last
->flags
|= NODE_NOSRC
;
974 roff_word_alloc(mdoc
, n
->line
, n
->pos
,
975 "on success, and\\~>0 if an error occurs.");
976 mdoc
->last
->flags
|= NODE_EOS
| NODE_NOSRC
;
989 assert(n
->child
->type
== ROFFT_TEXT
);
990 mdoc
->next
= ROFF_NEXT_CHILD
;
992 if ((p
= mdoc_a2lib(n
->child
->string
)) != NULL
) {
993 n
->child
->flags
|= NODE_NOPRT
;
994 roff_word_alloc(mdoc
, n
->line
, n
->pos
, p
);
995 mdoc
->last
->flags
= NODE_NOSRC
;
1000 mandoc_msg(MANDOCERR_LB_BAD
, n
->child
->line
,
1001 n
->child
->pos
, "Lb %s", n
->child
->string
);
1003 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "library");
1004 mdoc
->last
->flags
= NODE_NOSRC
;
1005 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "\\(lq");
1006 mdoc
->last
->flags
= NODE_DELIMO
| NODE_NOSRC
;
1007 mdoc
->last
= mdoc
->last
->next
;
1008 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "\\(rq");
1009 mdoc
->last
->flags
= NODE_DELIMC
| NODE_NOSRC
;
1016 struct roff_node
*n
;
1022 mdoc
->next
= ROFF_NEXT_CHILD
;
1023 if (n
->child
!= NULL
) {
1024 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "The");
1025 mdoc
->last
->flags
|= NODE_NOSRC
;
1026 ic
= build_list(mdoc
, MDOC_Fn
);
1027 roff_word_alloc(mdoc
, n
->line
, n
->pos
,
1028 ic
> 1 ? "functions return" : "function returns");
1029 mdoc
->last
->flags
|= NODE_NOSRC
;
1030 roff_word_alloc(mdoc
, n
->line
, n
->pos
,
1031 "the value\\~0 if successful;");
1033 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "Upon successful "
1034 "completion, the value\\~0 is returned;");
1035 mdoc
->last
->flags
|= NODE_NOSRC
;
1037 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "otherwise "
1038 "the value\\~\\-1 is returned and the global variable");
1039 mdoc
->last
->flags
|= NODE_NOSRC
;
1040 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Va
);
1041 mdoc
->last
->flags
|= NODE_NOSRC
;
1042 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "errno");
1043 mdoc
->last
->flags
|= NODE_NOSRC
;
1044 mdoc
->last
= mdoc
->last
->parent
;
1045 mdoc
->next
= ROFF_NEXT_SIBLING
;
1046 roff_word_alloc(mdoc
, n
->line
, n
->pos
,
1047 "is set to indicate the error.");
1048 mdoc
->last
->flags
|= NODE_EOS
| NODE_NOSRC
;
1055 struct roff_node
*n
;
1060 if (n
->args
&& n
->args
->argc
== 1)
1061 if (n
->args
->argv
[0].arg
== MDOC_Std
)
1064 mandoc_msg(MANDOCERR_ARG_STD
, n
->line
, n
->pos
,
1065 "%s", roff_name
[n
->tok
]);
1071 struct roff_node
*n
, *nch
;
1076 assert(nch
->type
== ROFFT_TEXT
);
1078 if ((p
= mdoc_a2st(nch
->string
)) == NULL
) {
1079 mandoc_msg(MANDOCERR_ST_BAD
,
1080 nch
->line
, nch
->pos
, "St %s", nch
->string
);
1081 roff_node_delete(mdoc
, n
);
1085 nch
->flags
|= NODE_NOPRT
;
1086 mdoc
->next
= ROFF_NEXT_CHILD
;
1087 roff_word_alloc(mdoc
, nch
->line
, nch
->pos
, p
);
1088 mdoc
->last
->flags
|= NODE_NOSRC
;
1093 post_obsolete(POST_ARGS
)
1095 struct roff_node
*n
;
1098 if (n
->type
== ROFFT_ELEM
|| n
->type
== ROFFT_BLOCK
)
1099 mandoc_msg(MANDOCERR_MACRO_OBS
, n
->line
, n
->pos
,
1100 "%s", roff_name
[n
->tok
]);
1104 post_useless(POST_ARGS
)
1106 struct roff_node
*n
;
1109 mandoc_msg(MANDOCERR_MACRO_USELESS
, n
->line
, n
->pos
,
1110 "%s", roff_name
[n
->tok
]);
1120 struct roff_node
*np
, *nch
;
1123 * Unlike other data pointers, these are "housed" by the HEAD
1124 * element, which contains the goods.
1128 if (np
->type
!= ROFFT_HEAD
)
1131 assert(np
->parent
->type
== ROFFT_BLOCK
);
1132 assert(np
->parent
->tok
== MDOC_Bf
);
1134 /* Check the number of arguments. */
1137 if (np
->parent
->args
== NULL
) {
1139 mandoc_msg(MANDOCERR_BF_NOFONT
,
1140 np
->line
, np
->pos
, "Bf");
1146 mandoc_msg(MANDOCERR_ARG_EXCESS
,
1147 nch
->line
, nch
->pos
, "Bf ... %s", nch
->string
);
1149 /* Extract argument into data. */
1151 if (np
->parent
->args
!= NULL
) {
1152 switch (np
->parent
->args
->argv
[0].arg
) {
1154 np
->norm
->Bf
.font
= FONT_Em
;
1157 np
->norm
->Bf
.font
= FONT_Li
;
1160 np
->norm
->Bf
.font
= FONT_Sy
;
1168 /* Extract parameter into data. */
1170 if ( ! strcmp(np
->child
->string
, "Em"))
1171 np
->norm
->Bf
.font
= FONT_Em
;
1172 else if ( ! strcmp(np
->child
->string
, "Li"))
1173 np
->norm
->Bf
.font
= FONT_Li
;
1174 else if ( ! strcmp(np
->child
->string
, "Sy"))
1175 np
->norm
->Bf
.font
= FONT_Sy
;
1177 mandoc_msg(MANDOCERR_BF_BADFONT
, np
->child
->line
,
1178 np
->child
->pos
, "Bf %s", np
->child
->string
);
1182 post_fname(POST_ARGS
)
1184 const struct roff_node
*n
;
1188 n
= mdoc
->last
->child
;
1189 pos
= strcspn(n
->string
, "()");
1190 cp
= n
->string
+ pos
;
1191 if ( ! (cp
[0] == '\0' || (cp
[0] == '(' && cp
[1] == '*')))
1192 mandoc_msg(MANDOCERR_FN_PAREN
, n
->line
, n
->pos
+ pos
,
1207 const struct roff_node
*n
;
1211 if (n
->type
!= ROFFT_HEAD
)
1214 if (n
->child
== NULL
) {
1215 mandoc_msg(MANDOCERR_FO_NOHEAD
, n
->line
, n
->pos
, "Fo");
1218 if (n
->child
!= n
->last
) {
1219 mandoc_msg(MANDOCERR_ARG_EXCESS
,
1220 n
->child
->next
->line
, n
->child
->next
->pos
,
1221 "Fo ... %s", n
->child
->next
->string
);
1222 while (n
->child
!= n
->last
)
1223 roff_node_delete(mdoc
, n
->last
);
1233 const struct roff_node
*n
;
1236 for (n
= mdoc
->last
->child
; n
!= NULL
; n
= n
->next
) {
1237 for (cp
= n
->string
; *cp
!= '\0'; cp
++) {
1238 /* Ignore callbacks and alterations. */
1239 if (*cp
== '(' || *cp
== '{')
1243 mandoc_msg(MANDOCERR_FA_COMMA
, n
->line
,
1244 n
->pos
+ (int)(cp
- n
->string
), "%s", n
->string
);
1248 post_delim_nb(mdoc
);
1254 struct roff_node
*n
;
1258 if (n
->sec
== SEC_NAME
&& n
->child
!= NULL
&&
1259 n
->child
->type
== ROFFT_TEXT
&& mdoc
->meta
.msec
!= NULL
)
1260 mandoc_xr_add(mdoc
->meta
.msec
, n
->child
->string
, -1, -1);
1262 if (n
->last
!= NULL
&& n
->last
->tok
== MDOC_Pp
)
1263 roff_node_relink(mdoc
, n
->last
);
1265 if (mdoc
->meta
.name
== NULL
)
1266 deroff(&mdoc
->meta
.name
, n
);
1268 if (mdoc
->meta
.name
== NULL
||
1269 (mdoc
->lastsec
== SEC_NAME
&& n
->child
== NULL
))
1270 mandoc_msg(MANDOCERR_NM_NONAME
, n
->line
, n
->pos
, "Nm");
1274 post_delim_nb(mdoc
);
1283 if ((n
->child
!= NULL
&& n
->child
->type
== ROFFT_TEXT
) ||
1284 mdoc
->meta
.name
== NULL
)
1287 mdoc
->next
= ROFF_NEXT_CHILD
;
1288 roff_word_alloc(mdoc
, n
->line
, n
->pos
, mdoc
->meta
.name
);
1289 mdoc
->last
->flags
|= NODE_NOSRC
;
1296 struct roff_node
*n
;
1300 if (n
->type
!= ROFFT_BODY
)
1303 if (n
->sec
!= SEC_NAME
)
1304 mandoc_msg(MANDOCERR_ND_LATE
, n
->line
, n
->pos
, "Nd");
1306 if (n
->child
== NULL
)
1307 mandoc_msg(MANDOCERR_ND_EMPTY
, n
->line
, n
->pos
, "Nd");
1315 post_display(POST_ARGS
)
1317 struct roff_node
*n
, *np
;
1322 if (n
->end
!= ENDBODY_NOT
) {
1323 if (n
->tok
== MDOC_Bd
&&
1324 n
->body
->parent
->args
== NULL
)
1325 roff_node_delete(mdoc
, n
);
1326 } else if (n
->child
== NULL
)
1327 mandoc_msg(MANDOCERR_BLK_EMPTY
, n
->line
, n
->pos
,
1328 "%s", roff_name
[n
->tok
]);
1329 else if (n
->tok
== MDOC_D1
)
1333 if (n
->tok
== MDOC_Bd
) {
1334 if (n
->args
== NULL
) {
1335 mandoc_msg(MANDOCERR_BD_NOARG
,
1336 n
->line
, n
->pos
, "Bd");
1337 mdoc
->next
= ROFF_NEXT_SIBLING
;
1338 while (n
->body
->child
!= NULL
)
1339 roff_node_relink(mdoc
,
1341 roff_node_delete(mdoc
, n
);
1347 for (np
= n
->parent
; np
!= NULL
; np
= np
->parent
) {
1348 if (np
->type
== ROFFT_BLOCK
&& np
->tok
== MDOC_Bd
) {
1349 mandoc_msg(MANDOCERR_BD_NEST
, n
->line
,
1350 n
->pos
, "%s in Bd", roff_name
[n
->tok
]);
1361 post_defaults(POST_ARGS
)
1363 struct roff_node
*nn
;
1365 if (mdoc
->last
->child
!= NULL
) {
1366 post_delim_nb(mdoc
);
1371 * The `Ar' defaults to "file ..." if no value is provided as an
1372 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
1373 * gets an empty string.
1379 mdoc
->next
= ROFF_NEXT_CHILD
;
1380 roff_word_alloc(mdoc
, nn
->line
, nn
->pos
, "file");
1381 mdoc
->last
->flags
|= NODE_NOSRC
;
1382 roff_word_alloc(mdoc
, nn
->line
, nn
->pos
, "...");
1383 mdoc
->last
->flags
|= NODE_NOSRC
;
1387 mdoc
->next
= ROFF_NEXT_CHILD
;
1388 roff_word_alloc(mdoc
, nn
->line
, nn
->pos
, "~");
1389 mdoc
->last
->flags
|= NODE_NOSRC
;
1400 struct roff_node
*n
, *nch
;
1407 * If we have a child, look it up in the standard keys. If a
1408 * key exist, use that instead of the child; if it doesn't,
1409 * prefix "AT&T UNIX " to the existing data.
1413 if (nch
!= NULL
&& ((att
= mdoc_a2att(nch
->string
)) == NULL
))
1414 mandoc_msg(MANDOCERR_AT_BAD
,
1415 nch
->line
, nch
->pos
, "At %s", nch
->string
);
1417 mdoc
->next
= ROFF_NEXT_CHILD
;
1419 roff_word_alloc(mdoc
, nch
->line
, nch
->pos
, att
);
1420 nch
->flags
|= NODE_NOPRT
;
1422 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "AT&T UNIX");
1423 mdoc
->last
->flags
|= NODE_NOSRC
;
1430 struct roff_node
*np
, *nch
;
1436 if (np
->norm
->An
.auth
== AUTH__NONE
) {
1438 mandoc_msg(MANDOCERR_MACRO_EMPTY
,
1439 np
->line
, np
->pos
, "An");
1441 post_delim_nb(mdoc
);
1442 } else if (nch
!= NULL
)
1443 mandoc_msg(MANDOCERR_ARG_EXCESS
,
1444 nch
->line
, nch
->pos
, "An ... %s", nch
->string
);
1451 post_obsolete(mdoc
);
1452 if (mdoc
->last
->type
== ROFFT_BLOCK
)
1453 mdoc
->last
->norm
->Es
= mdoc
->last_es
;
1460 post_obsolete(mdoc
);
1461 mdoc
->last_es
= mdoc
->last
;
1467 struct roff_node
*n
;
1471 post_delim_nb(mdoc
);
1486 if (n
->child
== NULL
)
1488 v
= n
->child
->string
;
1489 if ((v
[0] != '0' && v
[0] != '1') || v
[1] != '.' ||
1490 v
[2] < '0' || v
[2] > '9' ||
1491 v
[3] < 'a' || v
[3] > 'z' || v
[4] != '\0')
1493 n
->child
->flags
|= NODE_NOPRT
;
1494 mdoc
->next
= ROFF_NEXT_CHILD
;
1495 roff_word_alloc(mdoc
, n
->child
->line
, n
->child
->pos
, v
);
1496 v
= mdoc
->last
->string
;
1497 v
[3] = toupper((unsigned char)v
[3]);
1498 mdoc
->last
->flags
|= NODE_NOSRC
;
1510 mdoc
->next
= ROFF_NEXT_CHILD
;
1511 roff_word_alloc(mdoc
, n
->line
, n
->pos
, os
);
1512 mdoc
->last
->flags
|= NODE_NOSRC
;
1519 struct roff_node
*nbl
, *nit
, *nch
;
1526 if (nit
->type
!= ROFFT_BLOCK
)
1529 nbl
= nit
->parent
->parent
;
1530 lt
= nbl
->norm
->Bl
.type
;
1538 if (nit
->head
->child
== NULL
)
1539 mandoc_msg(MANDOCERR_IT_NOHEAD
,
1540 nit
->line
, nit
->pos
, "Bl -%s It",
1541 mdoc_argnames
[nbl
->args
->argv
[0].arg
]);
1547 if (nit
->body
== NULL
|| nit
->body
->child
== NULL
)
1548 mandoc_msg(MANDOCERR_IT_NOBODY
,
1549 nit
->line
, nit
->pos
, "Bl -%s It",
1550 mdoc_argnames
[nbl
->args
->argv
[0].arg
]);
1553 if ((nch
= nit
->head
->child
) != NULL
)
1554 mandoc_msg(MANDOCERR_ARG_SKIP
,
1555 nit
->line
, nit
->pos
, "It %s",
1556 nch
->string
== NULL
? roff_name
[nch
->tok
] :
1560 cols
= (int)nbl
->norm
->Bl
.ncols
;
1562 assert(nit
->head
->child
== NULL
);
1564 if (nit
->head
->next
->child
== NULL
&&
1565 nit
->head
->next
->next
== NULL
) {
1566 mandoc_msg(MANDOCERR_MACRO_EMPTY
,
1567 nit
->line
, nit
->pos
, "It");
1568 roff_node_delete(mdoc
, nit
);
1573 for (nch
= nit
->child
; nch
!= NULL
; nch
= nch
->next
) {
1574 if (nch
->type
!= ROFFT_BODY
)
1576 if (i
++ && nch
->flags
& NODE_LINE
)
1577 mandoc_msg(MANDOCERR_TA_LINE
,
1578 nch
->line
, nch
->pos
, "Ta");
1580 if (i
< cols
|| i
> cols
+ 1)
1581 mandoc_msg(MANDOCERR_BL_COL
, nit
->line
, nit
->pos
,
1582 "%d columns, %d cells", cols
, i
);
1583 else if (nit
->head
->next
->child
!= NULL
&&
1584 nit
->head
->next
->child
->line
> nit
->line
)
1585 mandoc_msg(MANDOCERR_IT_NOARG
,
1586 nit
->line
, nit
->pos
, "Bl -column It");
1594 post_bl_block(POST_ARGS
)
1596 struct roff_node
*n
, *ni
, *nc
;
1601 for (ni
= n
->body
->child
; ni
!= NULL
; ni
= ni
->next
) {
1602 if (ni
->body
== NULL
)
1604 nc
= ni
->body
->last
;
1605 while (nc
!= NULL
) {
1614 if (ni
->next
== NULL
) {
1615 mandoc_msg(MANDOCERR_PAR_MOVE
, nc
->line
,
1616 nc
->pos
, "%s", roff_name
[nc
->tok
]);
1617 roff_node_relink(mdoc
, nc
);
1618 } else if (n
->norm
->Bl
.comp
== 0 &&
1619 n
->norm
->Bl
.type
!= LIST_column
) {
1620 mandoc_msg(MANDOCERR_PAR_SKIP
,
1622 "%s before It", roff_name
[nc
->tok
]);
1623 roff_node_delete(mdoc
, nc
);
1626 nc
= ni
->body
->last
;
1632 * If the argument of -offset or -width is a macro,
1633 * replace it with the associated default width.
1636 rewrite_macro2len(struct roff_man
*mdoc
, char **arg
)
1643 else if ( ! strcmp(*arg
, "Ds"))
1645 else if ((tok
= roffhash_find(mdoc
->mdocmac
, *arg
, 0)) == TOKEN_NONE
)
1648 width
= macro2len(tok
);
1651 mandoc_asprintf(arg
, "%zun", width
);
1655 post_bl_head(POST_ARGS
)
1657 struct roff_node
*nbl
, *nh
, *nch
, *nnext
;
1658 struct mdoc_argv
*argv
;
1664 if (nh
->norm
->Bl
.type
!= LIST_column
) {
1665 if ((nch
= nh
->child
) == NULL
)
1667 mandoc_msg(MANDOCERR_ARG_EXCESS
,
1668 nch
->line
, nch
->pos
, "Bl ... %s", nch
->string
);
1669 while (nch
!= NULL
) {
1670 roff_node_delete(mdoc
, nch
);
1677 * Append old-style lists, where the column width specifiers
1678 * trail as macro parameters, to the new-style ("normal-form")
1679 * lists where they're argument values following -column.
1682 if (nh
->child
== NULL
)
1686 for (j
= 0; j
< (int)nbl
->args
->argc
; j
++)
1687 if (nbl
->args
->argv
[j
].arg
== MDOC_Column
)
1690 assert(j
< (int)nbl
->args
->argc
);
1693 * Accommodate for new-style groff column syntax. Shuffle the
1694 * child nodes, all of which must be TEXT, as arguments for the
1695 * column field. Then, delete the head children.
1698 argv
= nbl
->args
->argv
+ j
;
1700 for (nch
= nh
->child
; nch
!= NULL
; nch
= nch
->next
)
1702 argv
->value
= mandoc_reallocarray(argv
->value
,
1703 argv
->sz
, sizeof(char *));
1705 nh
->norm
->Bl
.ncols
= argv
->sz
;
1706 nh
->norm
->Bl
.cols
= (void *)argv
->value
;
1708 for (nch
= nh
->child
; nch
!= NULL
; nch
= nnext
) {
1709 argv
->value
[i
++] = nch
->string
;
1712 roff_node_delete(NULL
, nch
);
1720 struct roff_node
*nparent
, *nprev
; /* of the Bl block */
1721 struct roff_node
*nblock
, *nbody
; /* of the Bl */
1722 struct roff_node
*nchild
, *nnext
; /* of the Bl body */
1723 const char *prev_Er
;
1727 switch (nbody
->type
) {
1729 post_bl_block(mdoc
);
1739 if (nbody
->end
!= ENDBODY_NOT
)
1742 nchild
= nbody
->child
;
1743 if (nchild
== NULL
) {
1744 mandoc_msg(MANDOCERR_BLK_EMPTY
,
1745 nbody
->line
, nbody
->pos
, "Bl");
1748 while (nchild
!= NULL
) {
1749 nnext
= nchild
->next
;
1750 if (nchild
->tok
== MDOC_It
||
1751 (nchild
->tok
== MDOC_Sm
&&
1752 nnext
!= NULL
&& nnext
->tok
== MDOC_It
)) {
1758 * In .Bl -column, the first rows may be implicit,
1759 * that is, they may not start with .It macros.
1760 * Such rows may be followed by nodes generated on the
1761 * roff level, for example .TS, which cannot be moved
1762 * out of the list. In that case, wrap such roff nodes
1763 * into an implicit row.
1766 if (nchild
->prev
!= NULL
) {
1767 mdoc
->last
= nchild
;
1768 mdoc
->next
= ROFF_NEXT_SIBLING
;
1769 roff_block_alloc(mdoc
, nchild
->line
,
1770 nchild
->pos
, MDOC_It
);
1771 roff_head_alloc(mdoc
, nchild
->line
,
1772 nchild
->pos
, MDOC_It
);
1773 mdoc
->next
= ROFF_NEXT_SIBLING
;
1774 roff_body_alloc(mdoc
, nchild
->line
,
1775 nchild
->pos
, MDOC_It
);
1776 while (nchild
->tok
!= MDOC_It
) {
1777 roff_node_relink(mdoc
, nchild
);
1778 if ((nchild
= nnext
) == NULL
)
1780 nnext
= nchild
->next
;
1781 mdoc
->next
= ROFF_NEXT_SIBLING
;
1787 mandoc_msg(MANDOCERR_BL_MOVE
, nchild
->line
, nchild
->pos
,
1788 "%s", roff_name
[nchild
->tok
]);
1791 * Move the node out of the Bl block.
1792 * First, collect all required node pointers.
1795 nblock
= nbody
->parent
;
1796 nprev
= nblock
->prev
;
1797 nparent
= nblock
->parent
;
1800 * Unlink this child.
1803 nbody
->child
= nnext
;
1810 * Relink this child.
1813 nchild
->parent
= nparent
;
1814 nchild
->prev
= nprev
;
1815 nchild
->next
= nblock
;
1817 nblock
->prev
= nchild
;
1819 nparent
->child
= nchild
;
1821 nprev
->next
= nchild
;
1826 if (mdoc
->meta
.os_e
!= MANDOC_OS_NETBSD
)
1830 for (nchild
= nbody
->child
; nchild
!= NULL
; nchild
= nchild
->next
) {
1831 if (nchild
->tok
!= MDOC_It
)
1833 if ((nnext
= nchild
->head
->child
) == NULL
)
1835 if (nnext
->type
== ROFFT_BLOCK
)
1836 nnext
= nnext
->body
->child
;
1837 if (nnext
== NULL
|| nnext
->tok
!= MDOC_Er
)
1839 nnext
= nnext
->child
;
1840 if (prev_Er
!= NULL
) {
1841 order
= strcmp(prev_Er
, nnext
->string
);
1843 mandoc_msg(MANDOCERR_ER_ORDER
,
1844 nnext
->line
, nnext
->pos
,
1845 "Er %s %s (NetBSD)",
1846 prev_Er
, nnext
->string
);
1847 else if (order
== 0)
1848 mandoc_msg(MANDOCERR_ER_REP
,
1849 nnext
->line
, nnext
->pos
,
1850 "Er %s (NetBSD)", prev_Er
);
1852 prev_Er
= nnext
->string
;
1859 struct roff_node
*n
;
1863 if (n
->type
== ROFFT_BLOCK
&& n
->body
->child
== NULL
) {
1864 mandoc_msg(MANDOCERR_BLK_EMPTY
, n
->line
, n
->pos
, "Bk");
1865 roff_node_delete(mdoc
, n
);
1872 struct roff_node
*nch
;
1874 nch
= mdoc
->last
->child
;
1877 mdoc
->flags
^= MDOC_SMOFF
;
1881 assert(nch
->type
== ROFFT_TEXT
);
1883 if ( ! strcmp(nch
->string
, "on")) {
1884 mdoc
->flags
&= ~MDOC_SMOFF
;
1887 if ( ! strcmp(nch
->string
, "off")) {
1888 mdoc
->flags
|= MDOC_SMOFF
;
1892 mandoc_msg(MANDOCERR_SM_BAD
, nch
->line
, nch
->pos
,
1893 "%s %s", roff_name
[mdoc
->last
->tok
], nch
->string
);
1894 roff_node_relink(mdoc
, nch
);
1899 post_root(POST_ARGS
)
1901 const char *openbsd_arch
[] = {
1902 "alpha", "amd64", "arm64", "armv7", "hppa", "i386",
1903 "landisk", "loongson", "luna88k", "macppc", "mips64",
1904 "octeon", "sgi", "socppc", "sparc64", NULL
1906 const char *netbsd_arch
[] = {
1907 "acorn26", "acorn32", "algor", "alpha", "amiga",
1909 "bebox", "cats", "cesfic", "cobalt", "dreamcast",
1910 "emips", "evbarm", "evbmips", "evbppc", "evbsh3", "evbsh5",
1911 "hp300", "hpcarm", "hpcmips", "hpcsh", "hppa",
1912 "i386", "ibmnws", "luna68k",
1913 "mac68k", "macppc", "mipsco", "mmeye", "mvme68k", "mvmeppc",
1914 "netwinder", "news68k", "newsmips", "next68k",
1915 "pc532", "playstation2", "pmax", "pmppc", "prep",
1916 "sandpoint", "sbmips", "sgimips", "shark",
1917 "sparc", "sparc64", "sun2", "sun3",
1918 "vax", "walnut", "x68k", "x86", "x86_64", "xen", NULL
1920 const char **arches
[] = { NULL
, netbsd_arch
, openbsd_arch
};
1922 struct roff_node
*n
;
1925 /* Add missing prologue data. */
1927 if (mdoc
->meta
.date
== NULL
)
1928 mdoc
->meta
.date
= mdoc
->quick
? mandoc_strdup("") :
1929 mandoc_normdate(mdoc
, NULL
, 0, 0);
1931 if (mdoc
->meta
.title
== NULL
) {
1932 mandoc_msg(MANDOCERR_DT_NOTITLE
, 0, 0, "EOF");
1933 mdoc
->meta
.title
= mandoc_strdup("UNTITLED");
1936 if (mdoc
->meta
.vol
== NULL
)
1937 mdoc
->meta
.vol
= mandoc_strdup("LOCAL");
1939 if (mdoc
->meta
.os
== NULL
) {
1940 mandoc_msg(MANDOCERR_OS_MISSING
, 0, 0, NULL
);
1941 mdoc
->meta
.os
= mandoc_strdup("");
1942 } else if (mdoc
->meta
.os_e
&&
1943 (mdoc
->meta
.rcsids
& (1 << mdoc
->meta
.os_e
)) == 0)
1944 mandoc_msg(MANDOCERR_RCS_MISSING
, 0, 0,
1945 mdoc
->meta
.os_e
== MANDOC_OS_OPENBSD
?
1946 "(OpenBSD)" : "(NetBSD)");
1948 if (mdoc
->meta
.arch
!= NULL
&&
1949 (arch
= arches
[mdoc
->meta
.os_e
]) != NULL
) {
1950 while (*arch
!= NULL
&& strcmp(*arch
, mdoc
->meta
.arch
))
1952 if (*arch
== NULL
) {
1953 n
= mdoc
->meta
.first
->child
;
1954 while (n
->tok
!= MDOC_Dt
||
1956 n
->child
->next
== NULL
||
1957 n
->child
->next
->next
== NULL
)
1959 n
= n
->child
->next
->next
;
1960 mandoc_msg(MANDOCERR_ARCH_BAD
, n
->line
, n
->pos
,
1961 "Dt ... %s %s", mdoc
->meta
.arch
,
1962 mdoc
->meta
.os_e
== MANDOC_OS_OPENBSD
?
1963 "(OpenBSD)" : "(NetBSD)");
1967 /* Check that we begin with a proper `Sh'. */
1969 n
= mdoc
->meta
.first
->child
;
1971 (n
->type
== ROFFT_COMMENT
||
1972 (n
->tok
>= MDOC_Dd
&&
1973 mdoc_macro(n
->tok
)->flags
& MDOC_PROLOGUE
)))
1977 mandoc_msg(MANDOCERR_DOC_EMPTY
, 0, 0, NULL
);
1978 else if (n
->tok
!= MDOC_Sh
)
1979 mandoc_msg(MANDOCERR_SEC_BEFORE
, n
->line
, n
->pos
,
1980 "%s", roff_name
[n
->tok
]);
1986 struct roff_node
*np
, *nch
, *next
, *prev
;
1991 if (np
->type
!= ROFFT_BODY
)
1994 if (np
->child
== NULL
) {
1995 mandoc_msg(MANDOCERR_RS_EMPTY
, np
->line
, np
->pos
, "Rs");
2000 * The full `Rs' block needs special handling to order the
2001 * sub-elements according to `rsord'. Pick through each element
2002 * and correctly order it. This is an insertion sort.
2006 for (nch
= np
->child
->next
; nch
!= NULL
; nch
= next
) {
2007 /* Determine order number of this child. */
2008 for (i
= 0; i
< RSORD_MAX
; i
++)
2009 if (rsord
[i
] == nch
->tok
)
2012 if (i
== RSORD_MAX
) {
2013 mandoc_msg(MANDOCERR_RS_BAD
, nch
->line
, nch
->pos
,
2014 "%s", roff_name
[nch
->tok
]);
2016 } else if (nch
->tok
== MDOC__J
|| nch
->tok
== MDOC__B
)
2017 np
->norm
->Rs
.quote_T
++;
2020 * Remove this child from the chain. This somewhat
2021 * repeats roff_node_unlink(), but since we're
2022 * just re-ordering, there's no need for the
2023 * full unlink process.
2026 if ((next
= nch
->next
) != NULL
)
2027 next
->prev
= nch
->prev
;
2029 if ((prev
= nch
->prev
) != NULL
)
2030 prev
->next
= nch
->next
;
2032 nch
->prev
= nch
->next
= NULL
;
2035 * Scan back until we reach a node that's
2036 * to be ordered before this child.
2039 for ( ; prev
; prev
= prev
->prev
) {
2040 /* Determine order of `prev'. */
2041 for (j
= 0; j
< RSORD_MAX
; j
++)
2042 if (rsord
[j
] == prev
->tok
)
2052 * Set this child back into its correct place
2053 * in front of the `prev' node.
2059 np
->child
->prev
= nch
;
2060 nch
->next
= np
->child
;
2064 prev
->next
->prev
= nch
;
2065 nch
->next
= prev
->next
;
2072 * For some arguments of some macros,
2073 * convert all breakable hyphens into ASCII_HYPH.
2076 post_hyph(POST_ARGS
)
2078 struct roff_node
*nch
;
2081 for (nch
= mdoc
->last
->child
; nch
!= NULL
; nch
= nch
->next
) {
2082 if (nch
->type
!= ROFFT_TEXT
)
2087 while (*(++cp
) != '\0')
2089 isalpha((unsigned char)cp
[-1]) &&
2090 isalpha((unsigned char)cp
[1]))
2098 struct roff_node
*n
;
2101 if (n
->flags
& NODE_LINE
||
2102 (n
->next
!= NULL
&& n
->next
->flags
& NODE_DELIMC
))
2103 mandoc_msg(MANDOCERR_NS_SKIP
, n
->line
, n
->pos
, NULL
);
2119 switch (mdoc
->last
->type
) {
2124 switch (mdoc
->lastsec
) {
2129 post_sh_see_also(mdoc
);
2132 post_sh_authors(mdoc
);
2144 post_sh_name(POST_ARGS
)
2146 struct roff_node
*n
;
2151 for (n
= mdoc
->last
->child
; n
!= NULL
; n
= n
->next
) {
2154 if (hasnm
&& n
->child
!= NULL
)
2155 mandoc_msg(MANDOCERR_NAMESEC_PUNCT
,
2157 "Nm %s", n
->child
->string
);
2162 if (n
->next
!= NULL
)
2163 mandoc_msg(MANDOCERR_NAMESEC_ND
,
2164 n
->line
, n
->pos
, NULL
);
2167 if (n
->type
== ROFFT_TEXT
&&
2168 n
->string
[0] == ',' && n
->string
[1] == '\0' &&
2169 n
->next
!= NULL
&& n
->next
->tok
== MDOC_Nm
) {
2175 mandoc_msg(MANDOCERR_NAMESEC_BAD
,
2176 n
->line
, n
->pos
, "%s", roff_name
[n
->tok
]);
2183 mandoc_msg(MANDOCERR_NAMESEC_NONM
,
2184 mdoc
->last
->line
, mdoc
->last
->pos
, NULL
);
2186 mandoc_msg(MANDOCERR_NAMESEC_NOND
,
2187 mdoc
->last
->line
, mdoc
->last
->pos
, NULL
);
2191 post_sh_see_also(POST_ARGS
)
2193 const struct roff_node
*n
;
2194 const char *name
, *sec
;
2195 const char *lastname
, *lastsec
, *lastpunct
;
2198 n
= mdoc
->last
->child
;
2199 lastname
= lastsec
= lastpunct
= NULL
;
2201 if (n
->tok
!= MDOC_Xr
||
2203 n
->child
->next
== NULL
)
2206 /* Process one .Xr node. */
2208 name
= n
->child
->string
;
2209 sec
= n
->child
->next
->string
;
2210 if (lastsec
!= NULL
) {
2211 if (lastpunct
[0] != ',' || lastpunct
[1] != '\0')
2212 mandoc_msg(MANDOCERR_XR_PUNCT
, n
->line
,
2213 n
->pos
, "%s before %s(%s)",
2214 lastpunct
, name
, sec
);
2215 cmp
= strcmp(lastsec
, sec
);
2217 mandoc_msg(MANDOCERR_XR_ORDER
, n
->line
,
2218 n
->pos
, "%s(%s) after %s(%s)",
2219 name
, sec
, lastname
, lastsec
);
2220 else if (cmp
== 0 &&
2221 strcasecmp(lastname
, name
) > 0)
2222 mandoc_msg(MANDOCERR_XR_ORDER
, n
->line
,
2223 n
->pos
, "%s after %s", name
, lastname
);
2228 /* Process the following node. */
2233 if (n
->tok
== MDOC_Xr
) {
2237 if (n
->type
!= ROFFT_TEXT
)
2239 for (name
= n
->string
; *name
!= '\0'; name
++)
2240 if (isalpha((const unsigned char)*name
))
2242 lastpunct
= n
->string
;
2243 if (n
->next
== NULL
|| n
->next
->tok
== MDOC_Rs
)
2244 mandoc_msg(MANDOCERR_XR_PUNCT
, n
->line
,
2245 n
->pos
, "%s after %s(%s)",
2246 lastpunct
, lastname
, lastsec
);
2252 child_an(const struct roff_node
*n
)
2255 for (n
= n
->child
; n
!= NULL
; n
= n
->next
)
2256 if ((n
->tok
== MDOC_An
&& n
->child
!= NULL
) || child_an(n
))
2262 post_sh_authors(POST_ARGS
)
2265 if ( ! child_an(mdoc
->last
))
2266 mandoc_msg(MANDOCERR_AN_MISSING
,
2267 mdoc
->last
->line
, mdoc
->last
->pos
, NULL
);
2271 * Return an upper bound for the string distance (allowing
2272 * transpositions). Not a full Levenshtein implementation
2273 * because Levenshtein is quadratic in the string length
2274 * and this function is called for every standard name,
2275 * so the check for each custom name would be cubic.
2276 * The following crude heuristics is linear, resulting
2277 * in quadratic behaviour for checking one custom name,
2278 * which does not cause measurable slowdown.
2281 similar(const char *s1
, const char *s2
)
2283 const int maxdist
= 3;
2286 while (s1
[0] != '\0' && s2
[0] != '\0') {
2287 if (s1
[0] == s2
[0]) {
2292 if (++dist
> maxdist
)
2294 if (s1
[1] == s2
[1]) { /* replacement */
2297 } else if (s1
[0] == s2
[1] && s1
[1] == s2
[0]) {
2298 s1
+= 2; /* transposition */
2300 } else if (s1
[0] == s2
[1]) /* insertion */
2302 else if (s1
[1] == s2
[0]) /* deletion */
2307 dist
+= strlen(s1
) + strlen(s2
);
2308 return dist
> maxdist
? INT_MAX
: dist
;
2312 post_sh_head(POST_ARGS
)
2314 struct roff_node
*nch
;
2315 const char *goodsec
;
2316 const char *const *testsec
;
2321 * Process a new section. Sections are either "named" or
2322 * "custom". Custom sections are user-defined, while named ones
2323 * follow a conventional order and may only appear in certain
2327 sec
= mdoc
->last
->sec
;
2329 /* The NAME should be first. */
2331 if (sec
!= SEC_NAME
&& mdoc
->lastnamed
== SEC_NONE
)
2332 mandoc_msg(MANDOCERR_NAMESEC_FIRST
,
2333 mdoc
->last
->line
, mdoc
->last
->pos
, "Sh %s",
2334 sec
!= SEC_CUSTOM
? secnames
[sec
] :
2335 (nch
= mdoc
->last
->child
) == NULL
? "" :
2336 nch
->type
== ROFFT_TEXT
? nch
->string
:
2337 roff_name
[nch
->tok
]);
2339 /* The SYNOPSIS gets special attention in other areas. */
2341 if (sec
== SEC_SYNOPSIS
) {
2342 roff_setreg(mdoc
->roff
, "nS", 1, '=');
2343 mdoc
->flags
|= MDOC_SYNOPSIS
;
2345 roff_setreg(mdoc
->roff
, "nS", 0, '=');
2346 mdoc
->flags
&= ~MDOC_SYNOPSIS
;
2349 /* Mark our last section. */
2351 mdoc
->lastsec
= sec
;
2353 /* We don't care about custom sections after this. */
2355 if (sec
== SEC_CUSTOM
) {
2356 if ((nch
= mdoc
->last
->child
) == NULL
||
2357 nch
->type
!= ROFFT_TEXT
|| nch
->next
!= NULL
)
2361 for (testsec
= secnames
+ 1; *testsec
!= NULL
; testsec
++) {
2362 dist
= similar(nch
->string
, *testsec
);
2363 if (dist
< mindist
) {
2368 if (goodsec
!= NULL
)
2369 mandoc_msg(MANDOCERR_SEC_TYPO
, nch
->line
, nch
->pos
,
2370 "Sh %s instead of %s", nch
->string
, goodsec
);
2375 * Check whether our non-custom section is being repeated or is
2379 if (sec
== mdoc
->lastnamed
)
2380 mandoc_msg(MANDOCERR_SEC_REP
, mdoc
->last
->line
,
2381 mdoc
->last
->pos
, "Sh %s", secnames
[sec
]);
2383 if (sec
< mdoc
->lastnamed
)
2384 mandoc_msg(MANDOCERR_SEC_ORDER
, mdoc
->last
->line
,
2385 mdoc
->last
->pos
, "Sh %s", secnames
[sec
]);
2387 /* Mark the last named section. */
2389 mdoc
->lastnamed
= sec
;
2391 /* Check particular section/manual conventions. */
2393 if (mdoc
->meta
.msec
== NULL
)
2399 if (*mdoc
->meta
.msec
== '4')
2401 goodsec
= "2, 3, 4, 9";
2403 case SEC_RETURN_VALUES
:
2405 if (*mdoc
->meta
.msec
== '2')
2407 if (*mdoc
->meta
.msec
== '3')
2409 if (NULL
== goodsec
)
2410 goodsec
= "2, 3, 9";
2413 if (*mdoc
->meta
.msec
== '9')
2415 if (NULL
== goodsec
)
2417 mandoc_msg(MANDOCERR_SEC_MSEC
,
2418 mdoc
->last
->line
, mdoc
->last
->pos
,
2419 "Sh %s for %s only", secnames
[sec
], goodsec
);
2429 struct roff_node
*n
, *nch
;
2433 if (nch
->next
== NULL
) {
2434 mandoc_msg(MANDOCERR_XR_NOSEC
,
2435 n
->line
, n
->pos
, "Xr %s", nch
->string
);
2437 assert(nch
->next
== n
->last
);
2438 if(mandoc_xr_add(nch
->next
->string
, nch
->string
,
2439 nch
->line
, nch
->pos
))
2440 mandoc_msg(MANDOCERR_XR_SELF
,
2441 nch
->line
, nch
->pos
, "Xr %s %s",
2442 nch
->string
, nch
->next
->string
);
2444 post_delim_nb(mdoc
);
2448 post_ignpar(POST_ARGS
)
2450 struct roff_node
*np
;
2452 switch (mdoc
->last
->type
) {
2466 if ((np
= mdoc
->last
->child
) != NULL
)
2467 if (np
->tok
== MDOC_Pp
||
2468 np
->tok
== ROFF_br
|| np
->tok
== ROFF_sp
) {
2469 mandoc_msg(MANDOCERR_PAR_SKIP
, np
->line
, np
->pos
,
2470 "%s after %s", roff_name
[np
->tok
],
2471 roff_name
[mdoc
->last
->tok
]);
2472 roff_node_delete(mdoc
, np
);
2475 if ((np
= mdoc
->last
->last
) != NULL
)
2476 if (np
->tok
== MDOC_Pp
|| np
->tok
== ROFF_br
) {
2477 mandoc_msg(MANDOCERR_PAR_SKIP
, np
->line
, np
->pos
,
2478 "%s at the end of %s", roff_name
[np
->tok
],
2479 roff_name
[mdoc
->last
->tok
]);
2480 roff_node_delete(mdoc
, np
);
2485 post_prevpar(POST_ARGS
)
2487 struct roff_node
*n
;
2490 if (NULL
== n
->prev
)
2492 if (n
->type
!= ROFFT_ELEM
&& n
->type
!= ROFFT_BLOCK
)
2496 * Don't allow `Pp' prior to a paragraph-type
2497 * block: `Pp' or non-compact `Bd' or `Bl'.
2500 if (n
->prev
->tok
!= MDOC_Pp
&& n
->prev
->tok
!= ROFF_br
)
2502 if (n
->tok
== MDOC_Bl
&& n
->norm
->Bl
.comp
)
2504 if (n
->tok
== MDOC_Bd
&& n
->norm
->Bd
.comp
)
2506 if (n
->tok
== MDOC_It
&& n
->parent
->norm
->Bl
.comp
)
2509 mandoc_msg(MANDOCERR_PAR_SKIP
, n
->prev
->line
, n
->prev
->pos
,
2510 "%s before %s", roff_name
[n
->prev
->tok
], roff_name
[n
->tok
]);
2511 roff_node_delete(mdoc
, n
->prev
);
2517 struct roff_node
*np
;
2522 if (np
->child
!= NULL
)
2523 mandoc_msg(MANDOCERR_ARG_SKIP
, np
->line
, np
->pos
,
2524 "%s %s", roff_name
[np
->tok
], np
->child
->string
);
2530 struct roff_node
*n
;
2534 n
->flags
|= NODE_NOPRT
;
2536 if (mdoc
->meta
.date
!= NULL
) {
2537 mandoc_msg(MANDOCERR_PROLOG_REP
, n
->line
, n
->pos
, "Dd");
2538 free(mdoc
->meta
.date
);
2539 } else if (mdoc
->flags
& MDOC_PBODY
)
2540 mandoc_msg(MANDOCERR_PROLOG_LATE
, n
->line
, n
->pos
, "Dd");
2541 else if (mdoc
->meta
.title
!= NULL
)
2542 mandoc_msg(MANDOCERR_PROLOG_ORDER
,
2543 n
->line
, n
->pos
, "Dd after Dt");
2544 else if (mdoc
->meta
.os
!= NULL
)
2545 mandoc_msg(MANDOCERR_PROLOG_ORDER
,
2546 n
->line
, n
->pos
, "Dd after Os");
2548 if (n
->child
== NULL
|| n
->child
->string
[0] == '\0') {
2549 mdoc
->meta
.date
= mdoc
->quick
? mandoc_strdup("") :
2550 mandoc_normdate(mdoc
, NULL
, n
->line
, n
->pos
);
2555 deroff(&datestr
, n
);
2557 mdoc
->meta
.date
= datestr
;
2559 mdoc
->meta
.date
= mandoc_normdate(mdoc
,
2560 datestr
, n
->line
, n
->pos
);
2568 struct roff_node
*nn
, *n
;
2573 n
->flags
|= NODE_NOPRT
;
2575 if (mdoc
->flags
& MDOC_PBODY
) {
2576 mandoc_msg(MANDOCERR_DT_LATE
, n
->line
, n
->pos
, "Dt");
2580 if (mdoc
->meta
.title
!= NULL
)
2581 mandoc_msg(MANDOCERR_PROLOG_REP
, n
->line
, n
->pos
, "Dt");
2582 else if (mdoc
->meta
.os
!= NULL
)
2583 mandoc_msg(MANDOCERR_PROLOG_ORDER
,
2584 n
->line
, n
->pos
, "Dt after Os");
2586 free(mdoc
->meta
.title
);
2587 free(mdoc
->meta
.msec
);
2588 free(mdoc
->meta
.vol
);
2589 free(mdoc
->meta
.arch
);
2591 mdoc
->meta
.title
= NULL
;
2592 mdoc
->meta
.msec
= NULL
;
2593 mdoc
->meta
.vol
= NULL
;
2594 mdoc
->meta
.arch
= NULL
;
2596 /* Mandatory first argument: title. */
2599 if (nn
== NULL
|| *nn
->string
== '\0') {
2600 mandoc_msg(MANDOCERR_DT_NOTITLE
, n
->line
, n
->pos
, "Dt");
2601 mdoc
->meta
.title
= mandoc_strdup("UNTITLED");
2603 mdoc
->meta
.title
= mandoc_strdup(nn
->string
);
2605 /* Check that all characters are uppercase. */
2607 for (p
= nn
->string
; *p
!= '\0'; p
++)
2608 if (islower((unsigned char)*p
)) {
2609 mandoc_msg(MANDOCERR_TITLE_CASE
, nn
->line
,
2610 nn
->pos
+ (int)(p
- nn
->string
),
2611 "Dt %s", nn
->string
);
2616 /* Mandatory second argument: section. */
2622 mandoc_msg(MANDOCERR_MSEC_MISSING
, n
->line
, n
->pos
,
2623 "Dt %s", mdoc
->meta
.title
);
2624 mdoc
->meta
.vol
= mandoc_strdup("LOCAL");
2625 return; /* msec and arch remain NULL. */
2628 mdoc
->meta
.msec
= mandoc_strdup(nn
->string
);
2630 /* Infer volume title from section number. */
2632 cp
= mandoc_a2msec(nn
->string
);
2634 mandoc_msg(MANDOCERR_MSEC_BAD
,
2635 nn
->line
, nn
->pos
, "Dt ... %s", nn
->string
);
2636 mdoc
->meta
.vol
= mandoc_strdup(nn
->string
);
2638 mdoc
->meta
.vol
= mandoc_strdup(cp
);
2640 /* Optional third argument: architecture. */
2642 if ((nn
= nn
->next
) == NULL
)
2645 for (p
= nn
->string
; *p
!= '\0'; p
++)
2646 *p
= tolower((unsigned char)*p
);
2647 mdoc
->meta
.arch
= mandoc_strdup(nn
->string
);
2649 /* Ignore fourth and later arguments. */
2651 if ((nn
= nn
->next
) != NULL
)
2652 mandoc_msg(MANDOCERR_ARG_EXCESS
,
2653 nn
->line
, nn
->pos
, "Dt ... %s", nn
->string
);
2659 struct roff_node
*n
, *nch
;
2662 post_delim_nb(mdoc
);
2668 macro
= !strcmp(nch
->string
, "Open") ? "Ox" :
2669 !strcmp(nch
->string
, "Net") ? "Nx" :
2670 !strcmp(nch
->string
, "Free") ? "Fx" :
2671 !strcmp(nch
->string
, "DragonFly") ? "Dx" : NULL
;
2673 mandoc_msg(MANDOCERR_BX
,
2674 n
->line
, n
->pos
, "%s", macro
);
2677 mdoc
->next
= ROFF_NEXT_SIBLING
;
2678 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Ns
);
2679 mdoc
->last
->flags
|= NODE_NOSRC
;
2680 mdoc
->next
= ROFF_NEXT_SIBLING
;
2682 mdoc
->next
= ROFF_NEXT_CHILD
;
2683 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "BSD");
2684 mdoc
->last
->flags
|= NODE_NOSRC
;
2691 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Ns
);
2692 mdoc
->last
->flags
|= NODE_NOSRC
;
2693 mdoc
->next
= ROFF_NEXT_SIBLING
;
2694 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "-");
2695 mdoc
->last
->flags
|= NODE_NOSRC
;
2696 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Ns
);
2697 mdoc
->last
->flags
|= NODE_NOSRC
;
2701 * Make `Bx's second argument always start with an uppercase
2702 * letter. Groff checks if it's an "accepted" term, but we just
2703 * uppercase blindly.
2706 *nch
->string
= (char)toupper((unsigned char)*nch
->string
);
2713 struct utsname utsname
;
2714 static char *defbuf
;
2716 struct roff_node
*n
;
2719 n
->flags
|= NODE_NOPRT
;
2721 if (mdoc
->meta
.os
!= NULL
)
2722 mandoc_msg(MANDOCERR_PROLOG_REP
, n
->line
, n
->pos
, "Os");
2723 else if (mdoc
->flags
& MDOC_PBODY
)
2724 mandoc_msg(MANDOCERR_PROLOG_LATE
, n
->line
, n
->pos
, "Os");
2729 * Set the operating system by way of the `Os' macro.
2730 * The order of precedence is:
2731 * 1. the argument of the `Os' macro, unless empty
2732 * 2. the -Ios=foo command line argument, if provided
2733 * 3. -DOSNAME="\"foo\"", if provided during compilation
2734 * 4. "sysname release" from uname(3)
2737 free(mdoc
->meta
.os
);
2738 mdoc
->meta
.os
= NULL
;
2739 deroff(&mdoc
->meta
.os
, n
);
2743 if (mdoc
->os_s
!= NULL
) {
2744 mdoc
->meta
.os
= mandoc_strdup(mdoc
->os_s
);
2749 mdoc
->meta
.os
= mandoc_strdup(OSNAME
);
2751 if (defbuf
== NULL
) {
2752 if (uname(&utsname
) == -1) {
2753 mandoc_msg(MANDOCERR_OS_UNAME
, n
->line
, n
->pos
, "Os");
2754 defbuf
= mandoc_strdup("UNKNOWN");
2756 mandoc_asprintf(&defbuf
, "%s %s",
2757 utsname
.sysname
, utsname
.release
);
2759 mdoc
->meta
.os
= mandoc_strdup(defbuf
);
2763 if (mdoc
->meta
.os_e
== MANDOC_OS_OTHER
) {
2764 if (strstr(mdoc
->meta
.os
, "OpenBSD") != NULL
)
2765 mdoc
->meta
.os_e
= MANDOC_OS_OPENBSD
;
2766 else if (strstr(mdoc
->meta
.os
, "NetBSD") != NULL
)
2767 mdoc
->meta
.os_e
= MANDOC_OS_NETBSD
;
2771 * This is the earliest point where we can check
2772 * Mdocdate conventions because we don't know
2773 * the operating system earlier.
2776 if (n
->child
!= NULL
)
2777 mandoc_msg(MANDOCERR_OS_ARG
, n
->child
->line
, n
->child
->pos
,
2778 "Os %s (%s)", n
->child
->string
,
2779 mdoc
->meta
.os_e
== MANDOC_OS_OPENBSD
?
2780 "OpenBSD" : "NetBSD");
2782 while (n
->tok
!= MDOC_Dd
)
2783 if ((n
= n
->prev
) == NULL
)
2785 if ((n
= n
->child
) == NULL
)
2787 if (strncmp(n
->string
, "$" "Mdocdate", 9)) {
2788 if (mdoc
->meta
.os_e
== MANDOC_OS_OPENBSD
)
2789 mandoc_msg(MANDOCERR_MDOCDATE_MISSING
, n
->line
,
2790 n
->pos
, "Dd %s (OpenBSD)", n
->string
);
2792 if (mdoc
->meta
.os_e
== MANDOC_OS_NETBSD
)
2793 mandoc_msg(MANDOCERR_MDOCDATE
, n
->line
,
2794 n
->pos
, "Dd %s (NetBSD)", n
->string
);
2799 mdoc_a2sec(const char *p
)
2803 for (i
= 0; i
< (int)SEC__MAX
; i
++)
2804 if (secnames
[i
] && 0 == strcmp(p
, secnames
[i
]))
2805 return (enum roff_sec
)i
;
2811 macro2len(enum roff_tok macro
)