1 /* $Id: mdoc_validate.c,v 1.344 2017/06/27 12:18:00 schwarze Exp $ */
3 * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2010-2017 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"
38 #include "libmandoc.h"
42 /* FIXME: .Bl -diag can't have non-text children in HEAD. */
44 #define POST_ARGS struct roff_man *mdoc
52 typedef void (*v_post
)(POST_ARGS
);
54 static int build_list(struct roff_man
*, int);
55 static void check_text(struct roff_man
*, int, int, char *);
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_toptext(struct roff_man
*, int, int, const char *);
60 static int child_an(const struct roff_node
*);
61 static size_t macro2len(enum roff_tok
);
62 static void rewrite_macro2len(struct roff_man
*, char **);
63 static int similar(const char *, const char *);
65 static void post_an(POST_ARGS
);
66 static void post_an_norm(POST_ARGS
);
67 static void post_at(POST_ARGS
);
68 static void post_bd(POST_ARGS
);
69 static void post_bf(POST_ARGS
);
70 static void post_bk(POST_ARGS
);
71 static void post_bl(POST_ARGS
);
72 static void post_bl_block(POST_ARGS
);
73 static void post_bl_head(POST_ARGS
);
74 static void post_bl_norm(POST_ARGS
);
75 static void post_bx(POST_ARGS
);
76 static void post_defaults(POST_ARGS
);
77 static void post_display(POST_ARGS
);
78 static void post_dd(POST_ARGS
);
79 static void post_delim(POST_ARGS
);
80 static void post_dt(POST_ARGS
);
81 static void post_en(POST_ARGS
);
82 static void post_es(POST_ARGS
);
83 static void post_eoln(POST_ARGS
);
84 static void post_ex(POST_ARGS
);
85 static void post_fa(POST_ARGS
);
86 static void post_fn(POST_ARGS
);
87 static void post_fname(POST_ARGS
);
88 static void post_fo(POST_ARGS
);
89 static void post_hyph(POST_ARGS
);
90 static void post_ignpar(POST_ARGS
);
91 static void post_it(POST_ARGS
);
92 static void post_lb(POST_ARGS
);
93 static void post_nd(POST_ARGS
);
94 static void post_nm(POST_ARGS
);
95 static void post_ns(POST_ARGS
);
96 static void post_obsolete(POST_ARGS
);
97 static void post_os(POST_ARGS
);
98 static void post_par(POST_ARGS
);
99 static void post_prevpar(POST_ARGS
);
100 static void post_root(POST_ARGS
);
101 static void post_rs(POST_ARGS
);
102 static void post_rv(POST_ARGS
);
103 static void post_sh(POST_ARGS
);
104 static void post_sh_head(POST_ARGS
);
105 static void post_sh_name(POST_ARGS
);
106 static void post_sh_see_also(POST_ARGS
);
107 static void post_sh_authors(POST_ARGS
);
108 static void post_sm(POST_ARGS
);
109 static void post_st(POST_ARGS
);
110 static void post_std(POST_ARGS
);
111 static void post_useless(POST_ARGS
);
112 static void post_xr(POST_ARGS
);
113 static void post_xx(POST_ARGS
);
115 static const v_post __mdoc_valids
[MDOC_MAX
- MDOC_Dd
] = {
120 post_ignpar
, /* Ss */
122 post_display
, /* D1 */
123 post_display
, /* Dl */
124 post_display
, /* Bd */
132 post_defaults
, /* Ar */
146 post_defaults
, /* Li */
150 post_obsolete
, /* Ot */
151 post_defaults
, /* Pa */
158 post_hyph
, /* %B */ /* FIXME: can be used outside Rs/Re. */
166 post_hyph
, /* %T */ /* FIXME: can be used outside Rs/Re. */
178 post_obsolete
, /* Db */
208 post_useless
, /* Tn */
219 post_obsolete
, /* Hf */
220 post_obsolete
, /* Fr */
225 post_defaults
, /* Mt */
226 post_delim
, /* Brq */
227 post_delim
, /* Bro */
237 static const v_post
*const mdoc_valids
= __mdoc_valids
- MDOC_Dd
;
239 #define RSORD_MAX 14 /* Number of `Rs' blocks. */
241 static const enum roff_tok rsord
[RSORD_MAX
] = {
258 static const char * const secnames
[SEC__MAX
] = {
265 "IMPLEMENTATION NOTES",
280 "SECURITY CONSIDERATIONS",
286 mdoc_node_validate(struct roff_man
*mdoc
)
292 mdoc
->last
= mdoc
->last
->child
;
293 while (mdoc
->last
!= NULL
) {
294 mdoc_node_validate(mdoc
);
296 mdoc
->last
= mdoc
->last
->child
;
298 mdoc
->last
= mdoc
->last
->next
;
302 mdoc
->next
= ROFF_NEXT_SIBLING
;
305 if (n
->sec
!= SEC_SYNOPSIS
||
306 (n
->parent
->tok
!= MDOC_Cd
&& n
->parent
->tok
!= MDOC_Fd
))
307 check_text(mdoc
, n
->line
, n
->pos
, n
->string
);
308 if (n
->parent
->tok
== MDOC_It
||
309 (n
->parent
->type
== ROFFT_BODY
&&
310 (n
->parent
->tok
== MDOC_Sh
||
311 n
->parent
->tok
== MDOC_Ss
)))
312 check_toptext(mdoc
, n
->line
, n
->pos
, n
->string
);
321 check_args(mdoc
, mdoc
->last
);
324 * Closing delimiters are not special at the
325 * beginning of a block, opening delimiters
326 * are not special at the end.
329 if (n
->child
!= NULL
)
330 n
->child
->flags
&= ~NODE_DELIMC
;
332 n
->last
->flags
&= ~NODE_DELIMO
;
334 /* Call the macro's postprocessor. */
336 if (n
->tok
< ROFF_MAX
) {
349 assert(n
->tok
>= MDOC_Dd
&& n
->tok
< MDOC_MAX
);
350 p
= mdoc_valids
+ n
->tok
;
360 check_args(struct roff_man
*mdoc
, struct roff_node
*n
)
367 assert(n
->args
->argc
);
368 for (i
= 0; i
< (int)n
->args
->argc
; i
++)
369 check_argv(mdoc
, n
, &n
->args
->argv
[i
]);
373 check_argv(struct roff_man
*mdoc
, struct roff_node
*n
, struct mdoc_argv
*v
)
377 for (i
= 0; i
< (int)v
->sz
; i
++)
378 check_text(mdoc
, v
->line
, v
->pos
, v
->value
[i
]);
382 check_text(struct roff_man
*mdoc
, int ln
, int pos
, char *p
)
386 if (MDOC_LITERAL
& mdoc
->flags
)
389 for (cp
= p
; NULL
!= (p
= strchr(p
, '\t')); p
++)
390 mandoc_msg(MANDOCERR_FI_TAB
, mdoc
->parse
,
391 ln
, pos
+ (int)(p
- cp
), NULL
);
395 check_toptext(struct roff_man
*mdoc
, int ln
, int pos
, const char *p
)
397 const char *cp
, *cpr
;
402 if ((cp
= strstr(p
, "OpenBSD")) != NULL
)
403 mandoc_msg(MANDOCERR_BX
, mdoc
->parse
,
404 ln
, pos
+ (cp
- p
), "Ox");
405 if ((cp
= strstr(p
, "NetBSD")) != NULL
)
406 mandoc_msg(MANDOCERR_BX
, mdoc
->parse
,
407 ln
, pos
+ (cp
- p
), "Nx");
408 if ((cp
= strstr(p
, "FreeBSD")) != NULL
)
409 mandoc_msg(MANDOCERR_BX
, mdoc
->parse
,
410 ln
, pos
+ (cp
- p
), "Fx");
411 if ((cp
= strstr(p
, "DragonFly")) != NULL
)
412 mandoc_msg(MANDOCERR_BX
, mdoc
->parse
,
413 ln
, pos
+ (cp
- p
), "Dx");
416 while ((cp
= strstr(cp
+ 1, "()")) != NULL
) {
417 for (cpr
= cp
- 1; cpr
>= p
; cpr
--)
418 if (*cpr
!= '_' && !isalnum((unsigned char)*cpr
))
420 if ((cpr
< p
|| *cpr
== ' ') && cpr
+ 1 < cp
) {
422 mandoc_vmsg(MANDOCERR_FUNC
, mdoc
->parse
,
424 "%.*s()", (int)(cp
- cpr
), cpr
);
430 post_delim(POST_ARGS
)
432 const struct roff_node
*nch
;
439 * Find candidates: at least two bytes,
440 * the last one a closing or middle delimiter.
443 tok
= mdoc
->last
->tok
;
444 nch
= mdoc
->last
->last
;
445 if (nch
== NULL
|| nch
->type
!= ROFFT_TEXT
)
447 lc
= strchr(nch
->string
, '\0') - 1;
448 if (lc
<= nch
->string
)
450 delim
= mdoc_isdelim(lc
);
451 if (delim
== DELIM_NONE
|| delim
== DELIM_OPEN
)
455 * Reduce false positives by allowing various cases.
458 /* Escaped delimiters. */
459 if (lc
> nch
->string
+ 1 && lc
[-2] == '\\' &&
460 (lc
[-1] == '&' || lc
[-1] == 'e'))
463 /* Specific byte sequences. */
466 for (cp
= lc
; cp
>= nch
->string
; cp
--)
471 if (lc
> nch
->string
+ 1 && lc
[-2] == '.' && lc
[-1] == '.')
485 for (cp
= lc
; cp
>= nch
->string
; cp
--)
490 if (lc
== nch
->string
+ 1 && lc
[-1] == '|')
496 /* Exactly two non-alphanumeric bytes. */
497 if (lc
== nch
->string
+ 1 && !isalnum((unsigned char)lc
[-1]))
500 /* At least three alphabetic words with a sentence ending. */
501 if (strchr("!.:?", *lc
) != NULL
&& (tok
== MDOC_Em
||
502 tok
== MDOC_Li
|| tok
== MDOC_Po
|| tok
== MDOC_Pq
||
505 for (cp
= lc
- 1; cp
>= nch
->string
; cp
--) {
508 if (cp
> nch
->string
&& cp
[-1] == ',')
510 } else if (isalpha((unsigned int)*cp
)) {
518 mandoc_vmsg(MANDOCERR_DELIM
, mdoc
->parse
,
519 nch
->line
, nch
->pos
+ (lc
- nch
->string
),
520 "%s%s %s", roff_name
[tok
],
521 nch
== mdoc
->last
->child
? "" : " ...", nch
->string
);
525 post_bl_norm(POST_ARGS
)
528 struct mdoc_argv
*argv
, *wa
;
530 enum mdocargt mdoclt
;
533 n
= mdoc
->last
->parent
;
534 n
->norm
->Bl
.type
= LIST__NONE
;
537 * First figure out which kind of list to use: bind ourselves to
538 * the first mentioned list type and warn about any remaining
539 * ones. If we find no list type, we default to LIST_item.
542 wa
= (n
->args
== NULL
) ? NULL
: n
->args
->argv
;
543 mdoclt
= MDOC_ARG_MAX
;
544 for (i
= 0; n
->args
&& i
< (int)n
->args
->argc
; i
++) {
545 argv
= n
->args
->argv
+ i
;
548 /* Set list types. */
582 /* Set list arguments. */
584 if (n
->norm
->Bl
.comp
)
585 mandoc_msg(MANDOCERR_ARG_REP
,
586 mdoc
->parse
, argv
->line
,
587 argv
->pos
, "Bl -compact");
588 n
->norm
->Bl
.comp
= 1;
593 mandoc_msg(MANDOCERR_ARG_EMPTY
,
594 mdoc
->parse
, argv
->line
,
595 argv
->pos
, "Bl -width");
596 n
->norm
->Bl
.width
= "0n";
599 if (NULL
!= n
->norm
->Bl
.width
)
600 mandoc_vmsg(MANDOCERR_ARG_REP
,
601 mdoc
->parse
, argv
->line
,
602 argv
->pos
, "Bl -width %s",
604 rewrite_macro2len(mdoc
, argv
->value
);
605 n
->norm
->Bl
.width
= argv
->value
[0];
609 mandoc_msg(MANDOCERR_ARG_EMPTY
,
610 mdoc
->parse
, argv
->line
,
611 argv
->pos
, "Bl -offset");
614 if (NULL
!= n
->norm
->Bl
.offs
)
615 mandoc_vmsg(MANDOCERR_ARG_REP
,
616 mdoc
->parse
, argv
->line
,
617 argv
->pos
, "Bl -offset %s",
619 rewrite_macro2len(mdoc
, argv
->value
);
620 n
->norm
->Bl
.offs
= argv
->value
[0];
625 if (LIST__NONE
== lt
)
629 /* Check: multiple list types. */
631 if (LIST__NONE
!= n
->norm
->Bl
.type
) {
632 mandoc_vmsg(MANDOCERR_BL_REP
,
633 mdoc
->parse
, n
->line
, n
->pos
,
634 "Bl -%s", mdoc_argnames
[argv
->arg
]);
638 /* The list type should come first. */
640 if (n
->norm
->Bl
.width
||
643 mandoc_vmsg(MANDOCERR_BL_LATETYPE
,
644 mdoc
->parse
, n
->line
, n
->pos
, "Bl -%s",
645 mdoc_argnames
[n
->args
->argv
[0].arg
]);
647 n
->norm
->Bl
.type
= lt
;
648 if (LIST_column
== lt
) {
649 n
->norm
->Bl
.ncols
= argv
->sz
;
650 n
->norm
->Bl
.cols
= (void *)argv
->value
;
654 /* Allow lists to default to LIST_item. */
656 if (LIST__NONE
== n
->norm
->Bl
.type
) {
657 mandoc_msg(MANDOCERR_BL_NOTYPE
, mdoc
->parse
,
658 n
->line
, n
->pos
, "Bl");
659 n
->norm
->Bl
.type
= LIST_item
;
664 * Validate the width field. Some list types don't need width
665 * types and should be warned about them. Others should have it
666 * and must also be warned. Yet others have a default and need
670 switch (n
->norm
->Bl
.type
) {
672 if (NULL
== n
->norm
->Bl
.width
)
673 mandoc_msg(MANDOCERR_BL_NOWIDTH
, mdoc
->parse
,
674 n
->line
, n
->pos
, "Bl -tag");
681 if (n
->norm
->Bl
.width
)
682 mandoc_vmsg(MANDOCERR_BL_SKIPW
, mdoc
->parse
,
683 wa
->line
, wa
->pos
, "Bl -%s",
684 mdoc_argnames
[mdoclt
]);
689 if (NULL
== n
->norm
->Bl
.width
)
690 n
->norm
->Bl
.width
= "2n";
693 if (NULL
== n
->norm
->Bl
.width
)
694 n
->norm
->Bl
.width
= "3n";
705 struct mdoc_argv
*argv
;
710 for (i
= 0; n
->args
&& i
< (int)n
->args
->argc
; i
++) {
711 argv
= n
->args
->argv
+ i
;
731 mandoc_msg(MANDOCERR_BD_FILE
, mdoc
->parse
,
732 n
->line
, n
->pos
, NULL
);
736 mandoc_msg(MANDOCERR_ARG_EMPTY
,
737 mdoc
->parse
, argv
->line
,
738 argv
->pos
, "Bd -offset");
741 if (NULL
!= n
->norm
->Bd
.offs
)
742 mandoc_vmsg(MANDOCERR_ARG_REP
,
743 mdoc
->parse
, argv
->line
,
744 argv
->pos
, "Bd -offset %s",
746 rewrite_macro2len(mdoc
, argv
->value
);
747 n
->norm
->Bd
.offs
= argv
->value
[0];
750 if (n
->norm
->Bd
.comp
)
751 mandoc_msg(MANDOCERR_ARG_REP
,
752 mdoc
->parse
, argv
->line
,
753 argv
->pos
, "Bd -compact");
754 n
->norm
->Bd
.comp
= 1;
759 if (DISP__NONE
== dt
)
762 if (DISP__NONE
== n
->norm
->Bd
.type
)
763 n
->norm
->Bd
.type
= dt
;
765 mandoc_vmsg(MANDOCERR_BD_REP
,
766 mdoc
->parse
, n
->line
, n
->pos
,
767 "Bd -%s", mdoc_argnames
[argv
->arg
]);
770 if (DISP__NONE
== n
->norm
->Bd
.type
) {
771 mandoc_msg(MANDOCERR_BD_NOTYPE
, mdoc
->parse
,
772 n
->line
, n
->pos
, "Bd");
773 n
->norm
->Bd
.type
= DISP_ragged
;
778 * Stand-alone line macros.
782 post_an_norm(POST_ARGS
)
785 struct mdoc_argv
*argv
;
792 for (i
= 1; i
< n
->args
->argc
; i
++) {
793 argv
= n
->args
->argv
+ i
;
794 mandoc_vmsg(MANDOCERR_AN_REP
,
795 mdoc
->parse
, argv
->line
, argv
->pos
,
796 "An -%s", mdoc_argnames
[argv
->arg
]);
799 argv
= n
->args
->argv
;
800 if (argv
->arg
== MDOC_Split
)
801 n
->norm
->An
.auth
= AUTH_split
;
802 else if (argv
->arg
== MDOC_Nosplit
)
803 n
->norm
->An
.auth
= AUTH_nosplit
;
815 if (n
->child
!= NULL
)
816 mandoc_vmsg(MANDOCERR_ARG_SKIP
, mdoc
->parse
, n
->line
,
817 n
->pos
, "%s %s", roff_name
[n
->tok
], n
->child
->string
);
819 while (n
->child
!= NULL
)
820 roff_node_delete(mdoc
, n
->child
);
822 roff_word_alloc(mdoc
, n
->line
, n
->pos
, n
->tok
== MDOC_Bt
?
823 "is currently in beta test." : "currently under development.");
824 mdoc
->last
->flags
|= NODE_EOS
| NODE_NOSRC
;
829 build_list(struct roff_man
*mdoc
, int tok
)
834 n
= mdoc
->last
->next
;
835 for (ic
= 1;; ic
++) {
836 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, tok
);
837 mdoc
->last
->flags
|= NODE_NOSRC
;
838 mdoc_node_relink(mdoc
, n
);
839 n
= mdoc
->last
= mdoc
->last
->parent
;
840 mdoc
->next
= ROFF_NEXT_SIBLING
;
843 if (ic
> 1 || n
->next
->next
!= NULL
) {
844 roff_word_alloc(mdoc
, n
->line
, n
->pos
, ",");
845 mdoc
->last
->flags
|= NODE_DELIMC
| NODE_NOSRC
;
847 n
= mdoc
->last
->next
;
848 if (n
->next
== NULL
) {
849 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "and");
850 mdoc
->last
->flags
|= NODE_NOSRC
;
864 mdoc
->next
= ROFF_NEXT_CHILD
;
865 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "The");
866 mdoc
->last
->flags
|= NODE_NOSRC
;
868 if (mdoc
->last
->next
!= NULL
)
869 ic
= build_list(mdoc
, MDOC_Nm
);
870 else if (mdoc
->meta
.name
!= NULL
) {
871 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Nm
);
872 mdoc
->last
->flags
|= NODE_NOSRC
;
873 roff_word_alloc(mdoc
, n
->line
, n
->pos
, mdoc
->meta
.name
);
874 mdoc
->last
->flags
|= NODE_NOSRC
;
875 mdoc
->last
= mdoc
->last
->parent
;
876 mdoc
->next
= ROFF_NEXT_SIBLING
;
879 mandoc_msg(MANDOCERR_EX_NONAME
, mdoc
->parse
,
880 n
->line
, n
->pos
, "Ex");
884 roff_word_alloc(mdoc
, n
->line
, n
->pos
,
885 ic
> 1 ? "utilities exit\\~0" : "utility exits\\~0");
886 mdoc
->last
->flags
|= NODE_NOSRC
;
887 roff_word_alloc(mdoc
, n
->line
, n
->pos
,
888 "on success, and\\~>0 if an error occurs.");
889 mdoc
->last
->flags
|= NODE_EOS
| NODE_NOSRC
;
902 assert(n
->child
->type
== ROFFT_TEXT
);
903 mdoc
->next
= ROFF_NEXT_CHILD
;
905 if ((p
= mdoc_a2lib(n
->child
->string
)) != NULL
) {
906 n
->child
->flags
|= NODE_NOPRT
;
907 roff_word_alloc(mdoc
, n
->line
, n
->pos
, p
);
908 mdoc
->last
->flags
= NODE_NOSRC
;
913 mandoc_vmsg(MANDOCERR_LB_BAD
, mdoc
->parse
, n
->child
->line
,
914 n
->child
->pos
, "Lb %s", n
->child
->string
);
916 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "library");
917 mdoc
->last
->flags
= NODE_NOSRC
;
918 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "\\(Lq");
919 mdoc
->last
->flags
= NODE_DELIMO
| NODE_NOSRC
;
920 mdoc
->last
= mdoc
->last
->next
;
921 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "\\(Rq");
922 mdoc
->last
->flags
= NODE_DELIMC
| NODE_NOSRC
;
935 mdoc
->next
= ROFF_NEXT_CHILD
;
936 if (n
->child
!= NULL
) {
937 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "The");
938 mdoc
->last
->flags
|= NODE_NOSRC
;
939 ic
= build_list(mdoc
, MDOC_Fn
);
940 roff_word_alloc(mdoc
, n
->line
, n
->pos
,
941 ic
> 1 ? "functions return" : "function returns");
942 mdoc
->last
->flags
|= NODE_NOSRC
;
943 roff_word_alloc(mdoc
, n
->line
, n
->pos
,
944 "the value\\~0 if successful;");
946 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "Upon successful "
947 "completion, the value\\~0 is returned;");
948 mdoc
->last
->flags
|= NODE_NOSRC
;
950 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "otherwise "
951 "the value\\~\\-1 is returned and the global variable");
952 mdoc
->last
->flags
|= NODE_NOSRC
;
953 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Va
);
954 mdoc
->last
->flags
|= NODE_NOSRC
;
955 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "errno");
956 mdoc
->last
->flags
|= NODE_NOSRC
;
957 mdoc
->last
= mdoc
->last
->parent
;
958 mdoc
->next
= ROFF_NEXT_SIBLING
;
959 roff_word_alloc(mdoc
, n
->line
, n
->pos
,
960 "is set to indicate the error.");
961 mdoc
->last
->flags
|= NODE_EOS
| NODE_NOSRC
;
971 if (n
->args
&& n
->args
->argc
== 1)
972 if (n
->args
->argv
[0].arg
== MDOC_Std
)
975 mandoc_msg(MANDOCERR_ARG_STD
, mdoc
->parse
,
976 n
->line
, n
->pos
, roff_name
[n
->tok
]);
982 struct roff_node
*n
, *nch
;
987 assert(nch
->type
== ROFFT_TEXT
);
989 if ((p
= mdoc_a2st(nch
->string
)) == NULL
) {
990 mandoc_vmsg(MANDOCERR_ST_BAD
, mdoc
->parse
,
991 nch
->line
, nch
->pos
, "St %s", nch
->string
);
992 roff_node_delete(mdoc
, n
);
996 nch
->flags
|= NODE_NOPRT
;
997 mdoc
->next
= ROFF_NEXT_CHILD
;
998 roff_word_alloc(mdoc
, nch
->line
, nch
->pos
, p
);
999 mdoc
->last
->flags
|= NODE_NOSRC
;
1004 post_obsolete(POST_ARGS
)
1006 struct roff_node
*n
;
1009 if (n
->type
== ROFFT_ELEM
|| n
->type
== ROFFT_BLOCK
)
1010 mandoc_msg(MANDOCERR_MACRO_OBS
, mdoc
->parse
,
1011 n
->line
, n
->pos
, roff_name
[n
->tok
]);
1015 post_useless(POST_ARGS
)
1017 struct roff_node
*n
;
1020 mandoc_msg(MANDOCERR_MACRO_USELESS
, mdoc
->parse
,
1021 n
->line
, n
->pos
, roff_name
[n
->tok
]);
1031 struct roff_node
*np
, *nch
;
1034 * Unlike other data pointers, these are "housed" by the HEAD
1035 * element, which contains the goods.
1039 if (np
->type
!= ROFFT_HEAD
)
1042 assert(np
->parent
->type
== ROFFT_BLOCK
);
1043 assert(np
->parent
->tok
== MDOC_Bf
);
1045 /* Check the number of arguments. */
1048 if (np
->parent
->args
== NULL
) {
1050 mandoc_msg(MANDOCERR_BF_NOFONT
, mdoc
->parse
,
1051 np
->line
, np
->pos
, "Bf");
1057 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, mdoc
->parse
,
1058 nch
->line
, nch
->pos
, "Bf ... %s", nch
->string
);
1060 /* Extract argument into data. */
1062 if (np
->parent
->args
!= NULL
) {
1063 switch (np
->parent
->args
->argv
[0].arg
) {
1065 np
->norm
->Bf
.font
= FONT_Em
;
1068 np
->norm
->Bf
.font
= FONT_Li
;
1071 np
->norm
->Bf
.font
= FONT_Sy
;
1079 /* Extract parameter into data. */
1081 if ( ! strcmp(np
->child
->string
, "Em"))
1082 np
->norm
->Bf
.font
= FONT_Em
;
1083 else if ( ! strcmp(np
->child
->string
, "Li"))
1084 np
->norm
->Bf
.font
= FONT_Li
;
1085 else if ( ! strcmp(np
->child
->string
, "Sy"))
1086 np
->norm
->Bf
.font
= FONT_Sy
;
1088 mandoc_vmsg(MANDOCERR_BF_BADFONT
, mdoc
->parse
,
1089 np
->child
->line
, np
->child
->pos
,
1090 "Bf %s", np
->child
->string
);
1094 post_fname(POST_ARGS
)
1096 const struct roff_node
*n
;
1100 n
= mdoc
->last
->child
;
1101 pos
= strcspn(n
->string
, "()");
1102 cp
= n
->string
+ pos
;
1103 if ( ! (cp
[0] == '\0' || (cp
[0] == '(' && cp
[1] == '*')))
1104 mandoc_msg(MANDOCERR_FN_PAREN
, mdoc
->parse
,
1105 n
->line
, n
->pos
+ pos
, n
->string
);
1119 const struct roff_node
*n
;
1123 if (n
->type
!= ROFFT_HEAD
)
1126 if (n
->child
== NULL
) {
1127 mandoc_msg(MANDOCERR_FO_NOHEAD
, mdoc
->parse
,
1128 n
->line
, n
->pos
, "Fo");
1131 if (n
->child
!= n
->last
) {
1132 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, mdoc
->parse
,
1133 n
->child
->next
->line
, n
->child
->next
->pos
,
1134 "Fo ... %s", n
->child
->next
->string
);
1135 while (n
->child
!= n
->last
)
1136 roff_node_delete(mdoc
, n
->last
);
1145 const struct roff_node
*n
;
1148 for (n
= mdoc
->last
->child
; n
!= NULL
; n
= n
->next
) {
1149 for (cp
= n
->string
; *cp
!= '\0'; cp
++) {
1150 /* Ignore callbacks and alterations. */
1151 if (*cp
== '(' || *cp
== '{')
1155 mandoc_msg(MANDOCERR_FA_COMMA
, mdoc
->parse
,
1156 n
->line
, n
->pos
+ (cp
- n
->string
),
1167 struct roff_node
*n
;
1171 if (n
->last
!= NULL
&&
1172 (n
->last
->tok
== MDOC_Pp
||
1173 n
->last
->tok
== MDOC_Lp
))
1174 mdoc_node_relink(mdoc
, n
->last
);
1176 if (mdoc
->meta
.name
== NULL
)
1177 deroff(&mdoc
->meta
.name
, n
);
1179 if (mdoc
->meta
.name
== NULL
||
1180 (mdoc
->lastsec
== SEC_NAME
&& n
->child
== NULL
))
1181 mandoc_msg(MANDOCERR_NM_NONAME
, mdoc
->parse
,
1182 n
->line
, n
->pos
, "Nm");
1184 if (n
->type
== ROFFT_ELEM
)
1187 if ((n
->type
!= ROFFT_ELEM
&& n
->type
!= ROFFT_HEAD
) ||
1188 (n
->child
!= NULL
&& n
->child
->type
== ROFFT_TEXT
) ||
1189 mdoc
->meta
.name
== NULL
)
1192 mdoc
->next
= ROFF_NEXT_CHILD
;
1193 roff_word_alloc(mdoc
, n
->line
, n
->pos
, mdoc
->meta
.name
);
1194 mdoc
->last
->flags
|= NODE_NOSRC
;
1201 struct roff_node
*n
;
1206 if (n
->type
!= ROFFT_BODY
)
1209 if (n
->sec
!= SEC_NAME
)
1210 mandoc_msg(MANDOCERR_ND_LATE
, mdoc
->parse
,
1211 n
->line
, n
->pos
, "Nd");
1213 if (n
->child
== NULL
)
1214 mandoc_msg(MANDOCERR_ND_EMPTY
, mdoc
->parse
,
1215 n
->line
, n
->pos
, "Nd");
1216 else if (n
->last
->type
== ROFFT_TEXT
&&
1217 (sz
= strlen(n
->last
->string
)) != 0 &&
1218 n
->last
->string
[sz
- 1] == '.')
1219 mandoc_msg(MANDOCERR_ND_DOT
, mdoc
->parse
,
1220 n
->last
->line
, n
->last
->pos
+ sz
- 1, NULL
);
1226 post_display(POST_ARGS
)
1228 struct roff_node
*n
, *np
;
1233 if (n
->end
!= ENDBODY_NOT
) {
1234 if (n
->tok
== MDOC_Bd
&&
1235 n
->body
->parent
->args
== NULL
)
1236 roff_node_delete(mdoc
, n
);
1237 } else if (n
->child
== NULL
)
1238 mandoc_msg(MANDOCERR_BLK_EMPTY
, mdoc
->parse
,
1239 n
->line
, n
->pos
, roff_name
[n
->tok
]);
1240 else if (n
->tok
== MDOC_D1
)
1244 if (n
->tok
== MDOC_Bd
) {
1245 if (n
->args
== NULL
) {
1246 mandoc_msg(MANDOCERR_BD_NOARG
,
1247 mdoc
->parse
, n
->line
, n
->pos
, "Bd");
1248 mdoc
->next
= ROFF_NEXT_SIBLING
;
1249 while (n
->body
->child
!= NULL
)
1250 mdoc_node_relink(mdoc
,
1252 roff_node_delete(mdoc
, n
);
1258 for (np
= n
->parent
; np
!= NULL
; np
= np
->parent
) {
1259 if (np
->type
== ROFFT_BLOCK
&& np
->tok
== MDOC_Bd
) {
1260 mandoc_vmsg(MANDOCERR_BD_NEST
,
1261 mdoc
->parse
, n
->line
, n
->pos
,
1262 "%s in Bd", roff_name
[n
->tok
]);
1273 post_defaults(POST_ARGS
)
1275 struct roff_node
*nn
;
1277 if (mdoc
->last
->child
!= NULL
) {
1283 * The `Ar' defaults to "file ..." if no value is provided as an
1284 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
1285 * gets an empty string.
1291 mdoc
->next
= ROFF_NEXT_CHILD
;
1292 roff_word_alloc(mdoc
, nn
->line
, nn
->pos
, "file");
1293 mdoc
->last
->flags
|= NODE_NOSRC
;
1294 roff_word_alloc(mdoc
, nn
->line
, nn
->pos
, "...");
1295 mdoc
->last
->flags
|= NODE_NOSRC
;
1299 mdoc
->next
= ROFF_NEXT_CHILD
;
1300 roff_word_alloc(mdoc
, nn
->line
, nn
->pos
, "~");
1301 mdoc
->last
->flags
|= NODE_NOSRC
;
1312 struct roff_node
*n
, *nch
;
1319 * If we have a child, look it up in the standard keys. If a
1320 * key exist, use that instead of the child; if it doesn't,
1321 * prefix "AT&T UNIX " to the existing data.
1325 if (nch
!= NULL
&& ((att
= mdoc_a2att(nch
->string
)) == NULL
))
1326 mandoc_vmsg(MANDOCERR_AT_BAD
, mdoc
->parse
,
1327 nch
->line
, nch
->pos
, "At %s", nch
->string
);
1329 mdoc
->next
= ROFF_NEXT_CHILD
;
1331 roff_word_alloc(mdoc
, nch
->line
, nch
->pos
, att
);
1332 nch
->flags
|= NODE_NOPRT
;
1334 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "AT&T UNIX");
1335 mdoc
->last
->flags
|= NODE_NOSRC
;
1342 struct roff_node
*np
, *nch
;
1348 if (np
->norm
->An
.auth
== AUTH__NONE
) {
1350 mandoc_msg(MANDOCERR_MACRO_EMPTY
, mdoc
->parse
,
1351 np
->line
, np
->pos
, "An");
1354 } else if (nch
!= NULL
)
1355 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, mdoc
->parse
,
1356 nch
->line
, nch
->pos
, "An ... %s", nch
->string
);
1363 post_obsolete(mdoc
);
1364 if (mdoc
->last
->type
== ROFFT_BLOCK
)
1365 mdoc
->last
->norm
->Es
= mdoc
->last_es
;
1372 post_obsolete(mdoc
);
1373 mdoc
->last_es
= mdoc
->last
;
1379 struct roff_node
*n
;
1407 mdoc
->next
= ROFF_NEXT_CHILD
;
1408 roff_word_alloc(mdoc
, n
->line
, n
->pos
, os
);
1409 mdoc
->last
->flags
|= NODE_NOSRC
;
1416 struct roff_node
*nbl
, *nit
, *nch
;
1423 if (nit
->type
!= ROFFT_BLOCK
)
1426 nbl
= nit
->parent
->parent
;
1427 lt
= nbl
->norm
->Bl
.type
;
1435 if (nit
->head
->child
== NULL
)
1436 mandoc_vmsg(MANDOCERR_IT_NOHEAD
,
1437 mdoc
->parse
, nit
->line
, nit
->pos
,
1439 mdoc_argnames
[nbl
->args
->argv
[0].arg
]);
1445 if (nit
->body
== NULL
|| nit
->body
->child
== NULL
)
1446 mandoc_vmsg(MANDOCERR_IT_NOBODY
,
1447 mdoc
->parse
, nit
->line
, nit
->pos
,
1449 mdoc_argnames
[nbl
->args
->argv
[0].arg
]);
1452 if ((nch
= nit
->head
->child
) != NULL
)
1453 mandoc_vmsg(MANDOCERR_ARG_SKIP
, mdoc
->parse
,
1454 nit
->line
, nit
->pos
, "It %s",
1455 nch
->string
== NULL
? roff_name
[nch
->tok
] :
1459 cols
= (int)nbl
->norm
->Bl
.ncols
;
1461 assert(nit
->head
->child
== NULL
);
1464 for (nch
= nit
->child
; nch
!= NULL
; nch
= nch
->next
)
1465 if (nch
->type
== ROFFT_BODY
)
1468 if (i
< cols
|| i
> cols
+ 1)
1469 mandoc_vmsg(MANDOCERR_BL_COL
,
1470 mdoc
->parse
, nit
->line
, nit
->pos
,
1471 "%d columns, %d cells", cols
, i
);
1479 post_bl_block(POST_ARGS
)
1481 struct roff_node
*n
, *ni
, *nc
;
1486 for (ni
= n
->body
->child
; ni
!= NULL
; ni
= ni
->next
) {
1487 if (ni
->body
== NULL
)
1489 nc
= ni
->body
->last
;
1490 while (nc
!= NULL
) {
1500 if (ni
->next
== NULL
) {
1501 mandoc_msg(MANDOCERR_PAR_MOVE
,
1502 mdoc
->parse
, nc
->line
, nc
->pos
,
1503 roff_name
[nc
->tok
]);
1504 mdoc_node_relink(mdoc
, nc
);
1505 } else if (n
->norm
->Bl
.comp
== 0 &&
1506 n
->norm
->Bl
.type
!= LIST_column
) {
1507 mandoc_vmsg(MANDOCERR_PAR_SKIP
,
1508 mdoc
->parse
, nc
->line
, nc
->pos
,
1509 "%s before It", roff_name
[nc
->tok
]);
1510 roff_node_delete(mdoc
, nc
);
1513 nc
= ni
->body
->last
;
1519 * If the argument of -offset or -width is a macro,
1520 * replace it with the associated default width.
1523 rewrite_macro2len(struct roff_man
*mdoc
, char **arg
)
1530 else if ( ! strcmp(*arg
, "Ds"))
1532 else if ((tok
= roffhash_find(mdoc
->mdocmac
, *arg
, 0)) == TOKEN_NONE
)
1535 width
= macro2len(tok
);
1538 mandoc_asprintf(arg
, "%zun", width
);
1542 post_bl_head(POST_ARGS
)
1544 struct roff_node
*nbl
, *nh
, *nch
, *nnext
;
1545 struct mdoc_argv
*argv
;
1551 if (nh
->norm
->Bl
.type
!= LIST_column
) {
1552 if ((nch
= nh
->child
) == NULL
)
1554 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, mdoc
->parse
,
1555 nch
->line
, nch
->pos
, "Bl ... %s", nch
->string
);
1556 while (nch
!= NULL
) {
1557 roff_node_delete(mdoc
, nch
);
1564 * Append old-style lists, where the column width specifiers
1565 * trail as macro parameters, to the new-style ("normal-form")
1566 * lists where they're argument values following -column.
1569 if (nh
->child
== NULL
)
1573 for (j
= 0; j
< (int)nbl
->args
->argc
; j
++)
1574 if (nbl
->args
->argv
[j
].arg
== MDOC_Column
)
1577 assert(j
< (int)nbl
->args
->argc
);
1580 * Accommodate for new-style groff column syntax. Shuffle the
1581 * child nodes, all of which must be TEXT, as arguments for the
1582 * column field. Then, delete the head children.
1585 argv
= nbl
->args
->argv
+ j
;
1587 for (nch
= nh
->child
; nch
!= NULL
; nch
= nch
->next
)
1589 argv
->value
= mandoc_reallocarray(argv
->value
,
1590 argv
->sz
, sizeof(char *));
1592 nh
->norm
->Bl
.ncols
= argv
->sz
;
1593 nh
->norm
->Bl
.cols
= (void *)argv
->value
;
1595 for (nch
= nh
->child
; nch
!= NULL
; nch
= nnext
) {
1596 argv
->value
[i
++] = nch
->string
;
1599 roff_node_delete(NULL
, nch
);
1607 struct roff_node
*nparent
, *nprev
; /* of the Bl block */
1608 struct roff_node
*nblock
, *nbody
; /* of the Bl */
1609 struct roff_node
*nchild
, *nnext
; /* of the Bl body */
1610 const char *prev_Er
;
1614 switch (nbody
->type
) {
1616 post_bl_block(mdoc
);
1626 if (nbody
->end
!= ENDBODY_NOT
)
1629 nchild
= nbody
->child
;
1630 if (nchild
== NULL
) {
1631 mandoc_msg(MANDOCERR_BLK_EMPTY
, mdoc
->parse
,
1632 nbody
->line
, nbody
->pos
, "Bl");
1635 while (nchild
!= NULL
) {
1636 nnext
= nchild
->next
;
1637 if (nchild
->tok
== MDOC_It
||
1638 (nchild
->tok
== MDOC_Sm
&&
1639 nnext
!= NULL
&& nnext
->tok
== MDOC_It
)) {
1645 * In .Bl -column, the first rows may be implicit,
1646 * that is, they may not start with .It macros.
1647 * Such rows may be followed by nodes generated on the
1648 * roff level, for example .TS, which cannot be moved
1649 * out of the list. In that case, wrap such roff nodes
1650 * into an implicit row.
1653 if (nchild
->prev
!= NULL
) {
1654 mdoc
->last
= nchild
;
1655 mdoc
->next
= ROFF_NEXT_SIBLING
;
1656 roff_block_alloc(mdoc
, nchild
->line
,
1657 nchild
->pos
, MDOC_It
);
1658 roff_head_alloc(mdoc
, nchild
->line
,
1659 nchild
->pos
, MDOC_It
);
1660 mdoc
->next
= ROFF_NEXT_SIBLING
;
1661 roff_body_alloc(mdoc
, nchild
->line
,
1662 nchild
->pos
, MDOC_It
);
1663 while (nchild
->tok
!= MDOC_It
) {
1664 mdoc_node_relink(mdoc
, nchild
);
1665 if ((nchild
= nnext
) == NULL
)
1667 nnext
= nchild
->next
;
1668 mdoc
->next
= ROFF_NEXT_SIBLING
;
1674 mandoc_msg(MANDOCERR_BL_MOVE
, mdoc
->parse
,
1675 nchild
->line
, nchild
->pos
, roff_name
[nchild
->tok
]);
1678 * Move the node out of the Bl block.
1679 * First, collect all required node pointers.
1682 nblock
= nbody
->parent
;
1683 nprev
= nblock
->prev
;
1684 nparent
= nblock
->parent
;
1687 * Unlink this child.
1690 nbody
->child
= nnext
;
1697 * Relink this child.
1700 nchild
->parent
= nparent
;
1701 nchild
->prev
= nprev
;
1702 nchild
->next
= nblock
;
1704 nblock
->prev
= nchild
;
1706 nparent
->child
= nchild
;
1708 nprev
->next
= nchild
;
1713 if (mdoc
->meta
.os_e
!= MANDOC_OS_NETBSD
)
1717 for (nchild
= nbody
->child
; nchild
!= NULL
; nchild
= nchild
->next
) {
1718 if (nchild
->tok
!= MDOC_It
)
1720 if ((nnext
= nchild
->head
->child
) == NULL
)
1722 if (nnext
->type
== ROFFT_BLOCK
)
1723 nnext
= nnext
->body
->child
;
1724 if (nnext
== NULL
|| nnext
->tok
!= MDOC_Er
)
1726 nnext
= nnext
->child
;
1727 if (prev_Er
!= NULL
) {
1728 order
= strcmp(prev_Er
, nnext
->string
);
1730 mandoc_vmsg(MANDOCERR_ER_ORDER
,
1731 mdoc
->parse
, nnext
->line
, nnext
->pos
,
1732 "Er %s %s (NetBSD)",
1733 prev_Er
, nnext
->string
);
1734 else if (order
== 0)
1735 mandoc_vmsg(MANDOCERR_ER_REP
,
1736 mdoc
->parse
, nnext
->line
, nnext
->pos
,
1737 "Er %s (NetBSD)", prev_Er
);
1739 prev_Er
= nnext
->string
;
1746 struct roff_node
*n
;
1750 if (n
->type
== ROFFT_BLOCK
&& n
->body
->child
== NULL
) {
1751 mandoc_msg(MANDOCERR_BLK_EMPTY
,
1752 mdoc
->parse
, n
->line
, n
->pos
, "Bk");
1753 roff_node_delete(mdoc
, n
);
1760 struct roff_node
*nch
;
1762 nch
= mdoc
->last
->child
;
1765 mdoc
->flags
^= MDOC_SMOFF
;
1769 assert(nch
->type
== ROFFT_TEXT
);
1771 if ( ! strcmp(nch
->string
, "on")) {
1772 mdoc
->flags
&= ~MDOC_SMOFF
;
1775 if ( ! strcmp(nch
->string
, "off")) {
1776 mdoc
->flags
|= MDOC_SMOFF
;
1780 mandoc_vmsg(MANDOCERR_SM_BAD
,
1781 mdoc
->parse
, nch
->line
, nch
->pos
,
1782 "%s %s", roff_name
[mdoc
->last
->tok
], nch
->string
);
1783 mdoc_node_relink(mdoc
, nch
);
1788 post_root(POST_ARGS
)
1790 const char *openbsd_arch
[] = {
1791 "alpha", "amd64", "arm64", "armv7", "hppa", "i386",
1792 "landisk", "loongson", "luna88k", "macppc", "mips64",
1793 "octeon", "sgi", "socppc", "sparc64", NULL
1795 const char *netbsd_arch
[] = {
1796 "acorn26", "acorn32", "algor", "alpha", "amiga",
1798 "bebox", "cats", "cesfic", "cobalt", "dreamcast",
1799 "emips", "evbarm", "evbmips", "evbppc", "evbsh3", "evbsh5",
1800 "hp300", "hpcarm", "hpcmips", "hpcsh", "hppa",
1801 "i386", "ibmnws", "luna68k",
1802 "mac68k", "macppc", "mipsco", "mmeye", "mvme68k", "mvmeppc",
1803 "netwinder", "news68k", "newsmips", "next68k",
1804 "pc532", "playstation2", "pmax", "pmppc", "prep",
1805 "sandpoint", "sbmips", "sgimips", "shark",
1806 "sparc", "sparc64", "sun2", "sun3",
1807 "vax", "walnut", "x68k", "x86", "x86_64", "xen", NULL
1809 const char **arches
[] = { NULL
, netbsd_arch
, openbsd_arch
};
1811 struct roff_node
*n
;
1814 /* Add missing prologue data. */
1816 if (mdoc
->meta
.date
== NULL
)
1817 mdoc
->meta
.date
= mdoc
->quick
? mandoc_strdup("") :
1818 mandoc_normdate(mdoc
, NULL
, 0, 0);
1820 if (mdoc
->meta
.title
== NULL
) {
1821 mandoc_msg(MANDOCERR_DT_NOTITLE
,
1822 mdoc
->parse
, 0, 0, "EOF");
1823 mdoc
->meta
.title
= mandoc_strdup("UNTITLED");
1826 if (mdoc
->meta
.vol
== NULL
)
1827 mdoc
->meta
.vol
= mandoc_strdup("LOCAL");
1829 if (mdoc
->meta
.os
== NULL
) {
1830 mandoc_msg(MANDOCERR_OS_MISSING
,
1831 mdoc
->parse
, 0, 0, NULL
);
1832 mdoc
->meta
.os
= mandoc_strdup("");
1833 } else if (mdoc
->meta
.os_e
&&
1834 (mdoc
->meta
.rcsids
& (1 << mdoc
->meta
.os_e
)) == 0)
1835 mandoc_msg(MANDOCERR_RCS_MISSING
, mdoc
->parse
, 0, 0,
1836 mdoc
->meta
.os_e
== MANDOC_OS_OPENBSD
?
1837 "(OpenBSD)" : "(NetBSD)");
1839 if (mdoc
->meta
.arch
!= NULL
&&
1840 (arch
= arches
[mdoc
->meta
.os_e
]) != NULL
) {
1841 while (*arch
!= NULL
&& strcmp(*arch
, mdoc
->meta
.arch
))
1843 if (*arch
== NULL
) {
1844 n
= mdoc
->first
->child
;
1845 while (n
->tok
!= MDOC_Dt
)
1847 n
= n
->child
->next
->next
;
1848 mandoc_vmsg(MANDOCERR_ARCH_BAD
,
1849 mdoc
->parse
, n
->line
, n
->pos
,
1850 "Dt ... %s %s", mdoc
->meta
.arch
,
1851 mdoc
->meta
.os_e
== MANDOC_OS_OPENBSD
?
1852 "(OpenBSD)" : "(NetBSD)");
1856 /* Check that we begin with a proper `Sh'. */
1858 n
= mdoc
->first
->child
;
1859 while (n
!= NULL
&& n
->tok
!= TOKEN_NONE
&&
1860 mdoc_macros
[n
->tok
].flags
& MDOC_PROLOGUE
)
1864 mandoc_msg(MANDOCERR_DOC_EMPTY
, mdoc
->parse
, 0, 0, NULL
);
1865 else if (n
->tok
!= MDOC_Sh
)
1866 mandoc_msg(MANDOCERR_SEC_BEFORE
, mdoc
->parse
,
1867 n
->line
, n
->pos
, roff_name
[n
->tok
]);
1873 struct roff_node
*np
, *nch
, *next
, *prev
;
1878 if (np
->type
!= ROFFT_BODY
)
1881 if (np
->child
== NULL
) {
1882 mandoc_msg(MANDOCERR_RS_EMPTY
, mdoc
->parse
,
1883 np
->line
, np
->pos
, "Rs");
1888 * The full `Rs' block needs special handling to order the
1889 * sub-elements according to `rsord'. Pick through each element
1890 * and correctly order it. This is an insertion sort.
1894 for (nch
= np
->child
->next
; nch
!= NULL
; nch
= next
) {
1895 /* Determine order number of this child. */
1896 for (i
= 0; i
< RSORD_MAX
; i
++)
1897 if (rsord
[i
] == nch
->tok
)
1900 if (i
== RSORD_MAX
) {
1901 mandoc_msg(MANDOCERR_RS_BAD
, mdoc
->parse
,
1902 nch
->line
, nch
->pos
, roff_name
[nch
->tok
]);
1904 } else if (nch
->tok
== MDOC__J
|| nch
->tok
== MDOC__B
)
1905 np
->norm
->Rs
.quote_T
++;
1908 * Remove this child from the chain. This somewhat
1909 * repeats roff_node_unlink(), but since we're
1910 * just re-ordering, there's no need for the
1911 * full unlink process.
1914 if ((next
= nch
->next
) != NULL
)
1915 next
->prev
= nch
->prev
;
1917 if ((prev
= nch
->prev
) != NULL
)
1918 prev
->next
= nch
->next
;
1920 nch
->prev
= nch
->next
= NULL
;
1923 * Scan back until we reach a node that's
1924 * to be ordered before this child.
1927 for ( ; prev
; prev
= prev
->prev
) {
1928 /* Determine order of `prev'. */
1929 for (j
= 0; j
< RSORD_MAX
; j
++)
1930 if (rsord
[j
] == prev
->tok
)
1940 * Set this child back into its correct place
1941 * in front of the `prev' node.
1947 np
->child
->prev
= nch
;
1948 nch
->next
= np
->child
;
1952 prev
->next
->prev
= nch
;
1953 nch
->next
= prev
->next
;
1960 * For some arguments of some macros,
1961 * convert all breakable hyphens into ASCII_HYPH.
1964 post_hyph(POST_ARGS
)
1966 struct roff_node
*nch
;
1969 for (nch
= mdoc
->last
->child
; nch
!= NULL
; nch
= nch
->next
) {
1970 if (nch
->type
!= ROFFT_TEXT
)
1975 while (*(++cp
) != '\0')
1977 isalpha((unsigned char)cp
[-1]) &&
1978 isalpha((unsigned char)cp
[1]))
1986 struct roff_node
*n
;
1989 if (n
->flags
& NODE_LINE
||
1990 (n
->next
!= NULL
&& n
->next
->flags
& NODE_DELIMC
))
1991 mandoc_msg(MANDOCERR_NS_SKIP
, mdoc
->parse
,
1992 n
->line
, n
->pos
, NULL
);
2001 switch (mdoc
->last
->type
) {
2006 switch (mdoc
->lastsec
) {
2011 post_sh_see_also(mdoc
);
2014 post_sh_authors(mdoc
);
2026 post_sh_name(POST_ARGS
)
2028 struct roff_node
*n
;
2033 for (n
= mdoc
->last
->child
; n
!= NULL
; n
= n
->next
) {
2036 if (hasnm
&& n
->child
!= NULL
)
2037 mandoc_vmsg(MANDOCERR_NAMESEC_PUNCT
,
2038 mdoc
->parse
, n
->line
, n
->pos
,
2039 "Nm %s", n
->child
->string
);
2044 if (n
->next
!= NULL
)
2045 mandoc_msg(MANDOCERR_NAMESEC_ND
,
2046 mdoc
->parse
, n
->line
, n
->pos
, NULL
);
2049 if (n
->type
== ROFFT_TEXT
&&
2050 n
->string
[0] == ',' && n
->string
[1] == '\0' &&
2051 n
->next
!= NULL
&& n
->next
->tok
== MDOC_Nm
) {
2057 mandoc_msg(MANDOCERR_NAMESEC_BAD
, mdoc
->parse
,
2058 n
->line
, n
->pos
, roff_name
[n
->tok
]);
2065 mandoc_msg(MANDOCERR_NAMESEC_NONM
, mdoc
->parse
,
2066 mdoc
->last
->line
, mdoc
->last
->pos
, NULL
);
2068 mandoc_msg(MANDOCERR_NAMESEC_NOND
, mdoc
->parse
,
2069 mdoc
->last
->line
, mdoc
->last
->pos
, NULL
);
2073 post_sh_see_also(POST_ARGS
)
2075 const struct roff_node
*n
;
2076 const char *name
, *sec
;
2077 const char *lastname
, *lastsec
, *lastpunct
;
2080 n
= mdoc
->last
->child
;
2081 lastname
= lastsec
= lastpunct
= NULL
;
2083 if (n
->tok
!= MDOC_Xr
||
2085 n
->child
->next
== NULL
)
2088 /* Process one .Xr node. */
2090 name
= n
->child
->string
;
2091 sec
= n
->child
->next
->string
;
2092 if (lastsec
!= NULL
) {
2093 if (lastpunct
[0] != ',' || lastpunct
[1] != '\0')
2094 mandoc_vmsg(MANDOCERR_XR_PUNCT
,
2095 mdoc
->parse
, n
->line
, n
->pos
,
2096 "%s before %s(%s)", lastpunct
,
2098 cmp
= strcmp(lastsec
, sec
);
2100 mandoc_vmsg(MANDOCERR_XR_ORDER
,
2101 mdoc
->parse
, n
->line
, n
->pos
,
2102 "%s(%s) after %s(%s)", name
,
2103 sec
, lastname
, lastsec
);
2104 else if (cmp
== 0 &&
2105 strcasecmp(lastname
, name
) > 0)
2106 mandoc_vmsg(MANDOCERR_XR_ORDER
,
2107 mdoc
->parse
, n
->line
, n
->pos
,
2108 "%s after %s", name
, lastname
);
2113 /* Process the following node. */
2118 if (n
->tok
== MDOC_Xr
) {
2122 if (n
->type
!= ROFFT_TEXT
)
2124 for (name
= n
->string
; *name
!= '\0'; name
++)
2125 if (isalpha((const unsigned char)*name
))
2127 lastpunct
= n
->string
;
2128 if (n
->next
== NULL
|| n
->next
->tok
== MDOC_Rs
)
2129 mandoc_vmsg(MANDOCERR_XR_PUNCT
, mdoc
->parse
,
2130 n
->line
, n
->pos
, "%s after %s(%s)",
2131 lastpunct
, lastname
, lastsec
);
2137 child_an(const struct roff_node
*n
)
2140 for (n
= n
->child
; n
!= NULL
; n
= n
->next
)
2141 if ((n
->tok
== MDOC_An
&& n
->child
!= NULL
) || child_an(n
))
2147 post_sh_authors(POST_ARGS
)
2150 if ( ! child_an(mdoc
->last
))
2151 mandoc_msg(MANDOCERR_AN_MISSING
, mdoc
->parse
,
2152 mdoc
->last
->line
, mdoc
->last
->pos
, NULL
);
2156 * Return an upper bound for the string distance (allowing
2157 * transpositions). Not a full Levenshtein implementation
2158 * because Levenshtein is quadratic in the string length
2159 * and this function is called for every standard name,
2160 * so the check for each custom name would be cubic.
2161 * The following crude heuristics is linear, resulting
2162 * in quadratic behaviour for checking one custom name,
2163 * which does not cause measurable slowdown.
2166 similar(const char *s1
, const char *s2
)
2168 const int maxdist
= 3;
2171 while (s1
[0] != '\0' && s2
[0] != '\0') {
2172 if (s1
[0] == s2
[0]) {
2177 if (++dist
> maxdist
)
2179 if (s1
[1] == s2
[1]) { /* replacement */
2182 } else if (s1
[0] == s2
[1] && s1
[1] == s2
[0]) {
2183 s1
+= 2; /* transposition */
2185 } else if (s1
[0] == s2
[1]) /* insertion */
2187 else if (s1
[1] == s2
[0]) /* deletion */
2192 dist
+= strlen(s1
) + strlen(s2
);
2193 return dist
> maxdist
? INT_MAX
: dist
;
2197 post_sh_head(POST_ARGS
)
2199 struct roff_node
*nch
;
2200 const char *goodsec
;
2201 const char *const *testsec
;
2206 * Process a new section. Sections are either "named" or
2207 * "custom". Custom sections are user-defined, while named ones
2208 * follow a conventional order and may only appear in certain
2212 sec
= mdoc
->last
->sec
;
2214 /* The NAME should be first. */
2216 if (sec
!= SEC_NAME
&& mdoc
->lastnamed
== SEC_NONE
)
2217 mandoc_vmsg(MANDOCERR_NAMESEC_FIRST
, mdoc
->parse
,
2218 mdoc
->last
->line
, mdoc
->last
->pos
, "Sh %s",
2219 sec
!= SEC_CUSTOM
? secnames
[sec
] :
2220 (nch
= mdoc
->last
->child
) == NULL
? "" :
2221 nch
->type
== ROFFT_TEXT
? nch
->string
:
2222 roff_name
[nch
->tok
]);
2224 /* The SYNOPSIS gets special attention in other areas. */
2226 if (sec
== SEC_SYNOPSIS
) {
2227 roff_setreg(mdoc
->roff
, "nS", 1, '=');
2228 mdoc
->flags
|= MDOC_SYNOPSIS
;
2230 roff_setreg(mdoc
->roff
, "nS", 0, '=');
2231 mdoc
->flags
&= ~MDOC_SYNOPSIS
;
2234 /* Mark our last section. */
2236 mdoc
->lastsec
= sec
;
2238 /* We don't care about custom sections after this. */
2240 if (sec
== SEC_CUSTOM
) {
2241 if ((nch
= mdoc
->last
->child
) == NULL
||
2242 nch
->type
!= ROFFT_TEXT
|| nch
->next
!= NULL
)
2246 for (testsec
= secnames
+ 1; *testsec
!= NULL
; testsec
++) {
2247 dist
= similar(nch
->string
, *testsec
);
2248 if (dist
< mindist
) {
2253 if (goodsec
!= NULL
)
2254 mandoc_vmsg(MANDOCERR_SEC_TYPO
, mdoc
->parse
,
2255 nch
->line
, nch
->pos
, "Sh %s instead of %s",
2256 nch
->string
, goodsec
);
2261 * Check whether our non-custom section is being repeated or is
2265 if (sec
== mdoc
->lastnamed
)
2266 mandoc_vmsg(MANDOCERR_SEC_REP
, mdoc
->parse
,
2267 mdoc
->last
->line
, mdoc
->last
->pos
,
2268 "Sh %s", secnames
[sec
]);
2270 if (sec
< mdoc
->lastnamed
)
2271 mandoc_vmsg(MANDOCERR_SEC_ORDER
, mdoc
->parse
,
2272 mdoc
->last
->line
, mdoc
->last
->pos
,
2273 "Sh %s", secnames
[sec
]);
2275 /* Mark the last named section. */
2277 mdoc
->lastnamed
= sec
;
2279 /* Check particular section/manual conventions. */
2281 if (mdoc
->meta
.msec
== NULL
)
2287 if (*mdoc
->meta
.msec
== '4')
2289 goodsec
= "2, 3, 4, 9";
2291 case SEC_RETURN_VALUES
:
2293 if (*mdoc
->meta
.msec
== '2')
2295 if (*mdoc
->meta
.msec
== '3')
2297 if (NULL
== goodsec
)
2298 goodsec
= "2, 3, 9";
2301 if (*mdoc
->meta
.msec
== '9')
2303 if (NULL
== goodsec
)
2305 mandoc_vmsg(MANDOCERR_SEC_MSEC
, mdoc
->parse
,
2306 mdoc
->last
->line
, mdoc
->last
->pos
,
2307 "Sh %s for %s only", secnames
[sec
], goodsec
);
2317 struct roff_node
*n
, *nch
;
2321 if (nch
->next
== NULL
) {
2322 mandoc_vmsg(MANDOCERR_XR_NOSEC
, mdoc
->parse
,
2323 n
->line
, n
->pos
, "Xr %s", nch
->string
);
2325 assert(nch
->next
== n
->last
);
2330 post_ignpar(POST_ARGS
)
2332 struct roff_node
*np
;
2334 switch (mdoc
->last
->type
) {
2347 if ((np
= mdoc
->last
->child
) != NULL
)
2348 if (np
->tok
== MDOC_Pp
|| np
->tok
== MDOC_Lp
) {
2349 mandoc_vmsg(MANDOCERR_PAR_SKIP
,
2350 mdoc
->parse
, np
->line
, np
->pos
,
2351 "%s after %s", roff_name
[np
->tok
],
2352 roff_name
[mdoc
->last
->tok
]);
2353 roff_node_delete(mdoc
, np
);
2356 if ((np
= mdoc
->last
->last
) != NULL
)
2357 if (np
->tok
== MDOC_Pp
|| np
->tok
== MDOC_Lp
) {
2358 mandoc_vmsg(MANDOCERR_PAR_SKIP
, mdoc
->parse
,
2359 np
->line
, np
->pos
, "%s at the end of %s",
2361 roff_name
[mdoc
->last
->tok
]);
2362 roff_node_delete(mdoc
, np
);
2367 post_prevpar(POST_ARGS
)
2369 struct roff_node
*n
;
2372 if (NULL
== n
->prev
)
2374 if (n
->type
!= ROFFT_ELEM
&& n
->type
!= ROFFT_BLOCK
)
2378 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
2379 * block: `Lp', `Pp', or non-compact `Bd' or `Bl'.
2382 if (n
->prev
->tok
!= MDOC_Pp
&&
2383 n
->prev
->tok
!= MDOC_Lp
&&
2384 n
->prev
->tok
!= ROFF_br
)
2386 if (n
->tok
== MDOC_Bl
&& n
->norm
->Bl
.comp
)
2388 if (n
->tok
== MDOC_Bd
&& n
->norm
->Bd
.comp
)
2390 if (n
->tok
== MDOC_It
&& n
->parent
->norm
->Bl
.comp
)
2393 mandoc_vmsg(MANDOCERR_PAR_SKIP
, mdoc
->parse
,
2394 n
->prev
->line
, n
->prev
->pos
, "%s before %s",
2395 roff_name
[n
->prev
->tok
], roff_name
[n
->tok
]);
2396 roff_node_delete(mdoc
, n
->prev
);
2402 struct roff_node
*np
;
2405 if (np
->tok
!= ROFF_br
&& np
->tok
!= ROFF_sp
)
2408 if (np
->tok
== ROFF_sp
) {
2409 if (np
->child
!= NULL
&& np
->child
->next
!= NULL
)
2410 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, mdoc
->parse
,
2411 np
->child
->next
->line
, np
->child
->next
->pos
,
2412 "sp ... %s", np
->child
->next
->string
);
2413 } else if (np
->child
!= NULL
)
2414 mandoc_vmsg(MANDOCERR_ARG_SKIP
,
2415 mdoc
->parse
, np
->line
, np
->pos
, "%s %s",
2416 roff_name
[np
->tok
], np
->child
->string
);
2418 if ((np
= mdoc
->last
->prev
) == NULL
) {
2419 np
= mdoc
->last
->parent
;
2420 if (np
->tok
!= MDOC_Sh
&& np
->tok
!= MDOC_Ss
)
2422 } else if (np
->tok
!= MDOC_Pp
&& np
->tok
!= MDOC_Lp
&&
2423 (mdoc
->last
->tok
!= ROFF_br
||
2424 (np
->tok
!= ROFF_sp
&& np
->tok
!= ROFF_br
)))
2427 mandoc_vmsg(MANDOCERR_PAR_SKIP
, mdoc
->parse
,
2428 mdoc
->last
->line
, mdoc
->last
->pos
, "%s after %s",
2429 roff_name
[mdoc
->last
->tok
], roff_name
[np
->tok
]);
2430 roff_node_delete(mdoc
, mdoc
->last
);
2436 struct roff_node
*n
;
2440 n
->flags
|= NODE_NOPRT
;
2442 if (mdoc
->meta
.date
!= NULL
) {
2443 mandoc_msg(MANDOCERR_PROLOG_REP
, mdoc
->parse
,
2444 n
->line
, n
->pos
, "Dd");
2445 free(mdoc
->meta
.date
);
2446 } else if (mdoc
->flags
& MDOC_PBODY
)
2447 mandoc_msg(MANDOCERR_PROLOG_LATE
, mdoc
->parse
,
2448 n
->line
, n
->pos
, "Dd");
2449 else if (mdoc
->meta
.title
!= NULL
)
2450 mandoc_msg(MANDOCERR_PROLOG_ORDER
, mdoc
->parse
,
2451 n
->line
, n
->pos
, "Dd after Dt");
2452 else if (mdoc
->meta
.os
!= NULL
)
2453 mandoc_msg(MANDOCERR_PROLOG_ORDER
, mdoc
->parse
,
2454 n
->line
, n
->pos
, "Dd after Os");
2456 if (n
->child
== NULL
|| n
->child
->string
[0] == '\0') {
2457 mdoc
->meta
.date
= mdoc
->quick
? mandoc_strdup("") :
2458 mandoc_normdate(mdoc
, NULL
, n
->line
, n
->pos
);
2463 deroff(&datestr
, n
);
2465 mdoc
->meta
.date
= datestr
;
2467 mdoc
->meta
.date
= mandoc_normdate(mdoc
,
2468 datestr
, n
->line
, n
->pos
);
2476 struct roff_node
*nn
, *n
;
2481 n
->flags
|= NODE_NOPRT
;
2483 if (mdoc
->flags
& MDOC_PBODY
) {
2484 mandoc_msg(MANDOCERR_DT_LATE
, mdoc
->parse
,
2485 n
->line
, n
->pos
, "Dt");
2489 if (mdoc
->meta
.title
!= NULL
)
2490 mandoc_msg(MANDOCERR_PROLOG_REP
, mdoc
->parse
,
2491 n
->line
, n
->pos
, "Dt");
2492 else if (mdoc
->meta
.os
!= NULL
)
2493 mandoc_msg(MANDOCERR_PROLOG_ORDER
, mdoc
->parse
,
2494 n
->line
, n
->pos
, "Dt after Os");
2496 free(mdoc
->meta
.title
);
2497 free(mdoc
->meta
.msec
);
2498 free(mdoc
->meta
.vol
);
2499 free(mdoc
->meta
.arch
);
2501 mdoc
->meta
.title
= NULL
;
2502 mdoc
->meta
.msec
= NULL
;
2503 mdoc
->meta
.vol
= NULL
;
2504 mdoc
->meta
.arch
= NULL
;
2506 /* Mandatory first argument: title. */
2509 if (nn
== NULL
|| *nn
->string
== '\0') {
2510 mandoc_msg(MANDOCERR_DT_NOTITLE
,
2511 mdoc
->parse
, n
->line
, n
->pos
, "Dt");
2512 mdoc
->meta
.title
= mandoc_strdup("UNTITLED");
2514 mdoc
->meta
.title
= mandoc_strdup(nn
->string
);
2516 /* Check that all characters are uppercase. */
2518 for (p
= nn
->string
; *p
!= '\0'; p
++)
2519 if (islower((unsigned char)*p
)) {
2520 mandoc_vmsg(MANDOCERR_TITLE_CASE
,
2521 mdoc
->parse
, nn
->line
,
2522 nn
->pos
+ (p
- nn
->string
),
2523 "Dt %s", nn
->string
);
2528 /* Mandatory second argument: section. */
2534 mandoc_vmsg(MANDOCERR_MSEC_MISSING
,
2535 mdoc
->parse
, n
->line
, n
->pos
,
2536 "Dt %s", mdoc
->meta
.title
);
2537 mdoc
->meta
.vol
= mandoc_strdup("LOCAL");
2538 return; /* msec and arch remain NULL. */
2541 mdoc
->meta
.msec
= mandoc_strdup(nn
->string
);
2543 /* Infer volume title from section number. */
2545 cp
= mandoc_a2msec(nn
->string
);
2547 mandoc_vmsg(MANDOCERR_MSEC_BAD
, mdoc
->parse
,
2548 nn
->line
, nn
->pos
, "Dt ... %s", nn
->string
);
2549 mdoc
->meta
.vol
= mandoc_strdup(nn
->string
);
2551 mdoc
->meta
.vol
= mandoc_strdup(cp
);
2553 /* Optional third argument: architecture. */
2555 if ((nn
= nn
->next
) == NULL
)
2558 for (p
= nn
->string
; *p
!= '\0'; p
++)
2559 *p
= tolower((unsigned char)*p
);
2560 mdoc
->meta
.arch
= mandoc_strdup(nn
->string
);
2562 /* Ignore fourth and later arguments. */
2564 if ((nn
= nn
->next
) != NULL
)
2565 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, mdoc
->parse
,
2566 nn
->line
, nn
->pos
, "Dt ... %s", nn
->string
);
2572 struct roff_node
*n
, *nch
;
2581 macro
= !strcmp(nch
->string
, "Open") ? "Ox" :
2582 !strcmp(nch
->string
, "Net") ? "Nx" :
2583 !strcmp(nch
->string
, "Free") ? "Fx" :
2584 !strcmp(nch
->string
, "DragonFly") ? "Dx" : NULL
;
2586 mandoc_msg(MANDOCERR_BX
, mdoc
->parse
,
2587 n
->line
, n
->pos
, macro
);
2590 mdoc
->next
= ROFF_NEXT_SIBLING
;
2591 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Ns
);
2592 mdoc
->last
->flags
|= NODE_NOSRC
;
2593 mdoc
->next
= ROFF_NEXT_SIBLING
;
2595 mdoc
->next
= ROFF_NEXT_CHILD
;
2596 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "BSD");
2597 mdoc
->last
->flags
|= NODE_NOSRC
;
2604 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Ns
);
2605 mdoc
->last
->flags
|= NODE_NOSRC
;
2606 mdoc
->next
= ROFF_NEXT_SIBLING
;
2607 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "-");
2608 mdoc
->last
->flags
|= NODE_NOSRC
;
2609 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Ns
);
2610 mdoc
->last
->flags
|= NODE_NOSRC
;
2614 * Make `Bx's second argument always start with an uppercase
2615 * letter. Groff checks if it's an "accepted" term, but we just
2616 * uppercase blindly.
2619 *nch
->string
= (char)toupper((unsigned char)*nch
->string
);
2626 struct utsname utsname
;
2627 static char *defbuf
;
2629 struct roff_node
*n
;
2632 n
->flags
|= NODE_NOPRT
;
2634 if (mdoc
->meta
.os
!= NULL
)
2635 mandoc_msg(MANDOCERR_PROLOG_REP
, mdoc
->parse
,
2636 n
->line
, n
->pos
, "Os");
2637 else if (mdoc
->flags
& MDOC_PBODY
)
2638 mandoc_msg(MANDOCERR_PROLOG_LATE
, mdoc
->parse
,
2639 n
->line
, n
->pos
, "Os");
2642 * Set the operating system by way of the `Os' macro.
2643 * The order of precedence is:
2644 * 1. the argument of the `Os' macro, unless empty
2645 * 2. the -Ios=foo command line argument, if provided
2646 * 3. -DOSNAME="\"foo\"", if provided during compilation
2647 * 4. "sysname release" from uname(3)
2650 free(mdoc
->meta
.os
);
2651 mdoc
->meta
.os
= NULL
;
2652 deroff(&mdoc
->meta
.os
, n
);
2656 if (mdoc
->os_s
!= NULL
) {
2657 mdoc
->meta
.os
= mandoc_strdup(mdoc
->os_s
);
2662 mdoc
->meta
.os
= mandoc_strdup(OSNAME
);
2664 if (defbuf
== NULL
) {
2665 if (uname(&utsname
) == -1) {
2666 mandoc_msg(MANDOCERR_OS_UNAME
, mdoc
->parse
,
2667 n
->line
, n
->pos
, "Os");
2668 defbuf
= mandoc_strdup("UNKNOWN");
2670 mandoc_asprintf(&defbuf
, "%s %s",
2671 utsname
.sysname
, utsname
.release
);
2673 mdoc
->meta
.os
= mandoc_strdup(defbuf
);
2677 if (mdoc
->meta
.os_e
== MANDOC_OS_OTHER
) {
2678 if (strstr(mdoc
->meta
.os
, "OpenBSD") != NULL
)
2679 mdoc
->meta
.os_e
= MANDOC_OS_OPENBSD
;
2680 else if (strstr(mdoc
->meta
.os
, "NetBSD") != NULL
)
2681 mdoc
->meta
.os_e
= MANDOC_OS_NETBSD
;
2685 * This is the earliest point where we can check
2686 * Mdocdate conventions because we don't know
2687 * the operating system earlier.
2690 if (n
->child
!= NULL
)
2691 mandoc_vmsg(MANDOCERR_OS_ARG
, mdoc
->parse
,
2692 n
->child
->line
, n
->child
->pos
,
2693 "Os %s (%s)", n
->child
->string
,
2694 mdoc
->meta
.os_e
== MANDOC_OS_OPENBSD
?
2695 "OpenBSD" : "NetBSD");
2697 while (n
->tok
!= MDOC_Dd
)
2698 if ((n
= n
->prev
) == NULL
)
2700 if ((n
= n
->child
) == NULL
)
2702 if (strncmp(n
->string
, "$" "Mdocdate", 9)) {
2703 if (mdoc
->meta
.os_e
== MANDOC_OS_OPENBSD
)
2704 mandoc_vmsg(MANDOCERR_MDOCDATE_MISSING
,
2705 mdoc
->parse
, n
->line
, n
->pos
,
2706 "Dd %s (OpenBSD)", n
->string
);
2708 if (mdoc
->meta
.os_e
== MANDOC_OS_NETBSD
)
2709 mandoc_vmsg(MANDOCERR_MDOCDATE
,
2710 mdoc
->parse
, n
->line
, n
->pos
,
2711 "Dd %s (NetBSD)", n
->string
);
2716 mdoc_a2sec(const char *p
)
2720 for (i
= 0; i
< (int)SEC__MAX
; i
++)
2721 if (secnames
[i
] && 0 == strcmp(p
, secnames
[i
]))
2722 return (enum roff_sec
)i
;
2728 macro2len(enum roff_tok macro
)