1 /* $Id: mdoc_validate.c,v 1.352 2017/08/02 13:29:04 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"
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_text(struct roff_man
*, int, int, char *);
57 static void check_argv(struct roff_man
*,
58 struct roff_node
*, struct mdoc_argv
*);
59 static void check_args(struct roff_man
*, struct roff_node
*);
60 static void check_toptext(struct roff_man
*, int, int, const char *);
61 static int child_an(const struct roff_node
*);
62 static size_t macro2len(enum roff_tok
);
63 static void rewrite_macro2len(struct roff_man
*, char **);
64 static int similar(const char *, const char *);
66 static void post_an(POST_ARGS
);
67 static void post_an_norm(POST_ARGS
);
68 static void post_at(POST_ARGS
);
69 static void post_bd(POST_ARGS
);
70 static void post_bf(POST_ARGS
);
71 static void post_bk(POST_ARGS
);
72 static void post_bl(POST_ARGS
);
73 static void post_bl_block(POST_ARGS
);
74 static void post_bl_head(POST_ARGS
);
75 static void post_bl_norm(POST_ARGS
);
76 static void post_bx(POST_ARGS
);
77 static void post_defaults(POST_ARGS
);
78 static void post_display(POST_ARGS
);
79 static void post_dd(POST_ARGS
);
80 static void post_delim(POST_ARGS
);
81 static void post_delim_nb(POST_ARGS
);
82 static void post_dt(POST_ARGS
);
83 static void post_en(POST_ARGS
);
84 static void post_es(POST_ARGS
);
85 static void post_eoln(POST_ARGS
);
86 static void post_ex(POST_ARGS
);
87 static void post_fa(POST_ARGS
);
88 static void post_fn(POST_ARGS
);
89 static void post_fname(POST_ARGS
);
90 static void post_fo(POST_ARGS
);
91 static void post_hyph(POST_ARGS
);
92 static void post_ignpar(POST_ARGS
);
93 static void post_it(POST_ARGS
);
94 static void post_lb(POST_ARGS
);
95 static void post_nd(POST_ARGS
);
96 static void post_nm(POST_ARGS
);
97 static void post_ns(POST_ARGS
);
98 static void post_obsolete(POST_ARGS
);
99 static void post_os(POST_ARGS
);
100 static void post_par(POST_ARGS
);
101 static void post_prevpar(POST_ARGS
);
102 static void post_root(POST_ARGS
);
103 static void post_rs(POST_ARGS
);
104 static void post_rv(POST_ARGS
);
105 static void post_sh(POST_ARGS
);
106 static void post_sh_head(POST_ARGS
);
107 static void post_sh_name(POST_ARGS
);
108 static void post_sh_see_also(POST_ARGS
);
109 static void post_sh_authors(POST_ARGS
);
110 static void post_sm(POST_ARGS
);
111 static void post_st(POST_ARGS
);
112 static void post_std(POST_ARGS
);
113 static void post_sx(POST_ARGS
);
114 static void post_useless(POST_ARGS
);
115 static void post_xr(POST_ARGS
);
116 static void post_xx(POST_ARGS
);
118 static const v_post __mdoc_valids
[MDOC_MAX
- MDOC_Dd
] = {
123 post_ignpar
, /* Ss */
125 post_display
, /* D1 */
126 post_display
, /* Dl */
127 post_display
, /* Bd */
132 post_delim_nb
, /* Ad */
135 post_defaults
, /* Ar */
137 post_delim_nb
, /* Cm */
138 post_delim_nb
, /* Dv */
139 post_delim_nb
, /* Er */
140 post_delim_nb
, /* Ev */
144 post_delim_nb
, /* Fl */
146 post_delim_nb
, /* Ft */
147 post_delim_nb
, /* Ic */
148 post_delim_nb
, /* In */
149 post_defaults
, /* Li */
152 post_delim_nb
, /* Op */
153 post_obsolete
, /* Ot */
154 post_defaults
, /* Pa */
157 post_delim_nb
, /* Va */
158 post_delim_nb
, /* Vt */
161 post_hyph
, /* %B */ /* FIXME: can be used outside Rs/Re. */
169 post_hyph
, /* %T */ /* FIXME: can be used outside Rs/Re. */
172 post_delim_nb
, /* Ao */
173 post_delim_nb
, /* Aq */
177 post_delim_nb
, /* Bo */
181 post_obsolete
, /* Db */
187 post_delim_nb
, /* Em */
190 post_delim_nb
, /* Ms */
197 post_delim_nb
, /* Po */
198 post_delim_nb
, /* Pq */
200 post_delim_nb
, /* Ql */
201 post_delim_nb
, /* Qo */
202 post_delim_nb
, /* Qq */
206 post_delim_nb
, /* So */
207 post_delim_nb
, /* Sq */
210 post_delim_nb
, /* Sy */
211 post_useless
, /* Tn */
217 post_delim_nb
, /* Oo */
222 post_obsolete
, /* Hf */
223 post_obsolete
, /* Fr */
227 post_delim_nb
, /* Lk */
228 post_defaults
, /* Mt */
229 post_delim_nb
, /* Brq */
230 post_delim_nb
, /* Bro */
240 static const v_post
*const mdoc_valids
= __mdoc_valids
- MDOC_Dd
;
242 #define RSORD_MAX 14 /* Number of `Rs' blocks. */
244 static const enum roff_tok rsord
[RSORD_MAX
] = {
261 static const char * const secnames
[SEC__MAX
] = {
268 "IMPLEMENTATION NOTES",
283 "SECURITY CONSIDERATIONS",
289 mdoc_node_validate(struct roff_man
*mdoc
)
295 mdoc
->last
= mdoc
->last
->child
;
296 while (mdoc
->last
!= NULL
) {
297 mdoc_node_validate(mdoc
);
299 mdoc
->last
= mdoc
->last
->child
;
301 mdoc
->last
= mdoc
->last
->next
;
305 mdoc
->next
= ROFF_NEXT_SIBLING
;
308 if (n
->sec
!= SEC_SYNOPSIS
||
309 (n
->parent
->tok
!= MDOC_Cd
&& n
->parent
->tok
!= MDOC_Fd
))
310 check_text(mdoc
, n
->line
, n
->pos
, n
->string
);
311 if (n
->parent
->tok
== MDOC_It
||
312 (n
->parent
->type
== ROFFT_BODY
&&
313 (n
->parent
->tok
== MDOC_Sh
||
314 n
->parent
->tok
== MDOC_Ss
)))
315 check_toptext(mdoc
, n
->line
, n
->pos
, n
->string
);
324 check_args(mdoc
, mdoc
->last
);
327 * Closing delimiters are not special at the
328 * beginning of a block, opening delimiters
329 * are not special at the end.
332 if (n
->child
!= NULL
)
333 n
->child
->flags
&= ~NODE_DELIMC
;
335 n
->last
->flags
&= ~NODE_DELIMO
;
337 /* Call the macro's postprocessor. */
339 if (n
->tok
< ROFF_MAX
) {
352 assert(n
->tok
>= MDOC_Dd
&& n
->tok
< MDOC_MAX
);
353 p
= mdoc_valids
+ n
->tok
;
363 check_args(struct roff_man
*mdoc
, struct roff_node
*n
)
370 assert(n
->args
->argc
);
371 for (i
= 0; i
< (int)n
->args
->argc
; i
++)
372 check_argv(mdoc
, n
, &n
->args
->argv
[i
]);
376 check_argv(struct roff_man
*mdoc
, struct roff_node
*n
, struct mdoc_argv
*v
)
380 for (i
= 0; i
< (int)v
->sz
; i
++)
381 check_text(mdoc
, v
->line
, v
->pos
, v
->value
[i
]);
385 check_text(struct roff_man
*mdoc
, int ln
, int pos
, char *p
)
389 if (MDOC_LITERAL
& mdoc
->flags
)
392 for (cp
= p
; NULL
!= (p
= strchr(p
, '\t')); p
++)
393 mandoc_msg(MANDOCERR_FI_TAB
, mdoc
->parse
,
394 ln
, pos
+ (int)(p
- cp
), NULL
);
398 check_toptext(struct roff_man
*mdoc
, int ln
, int pos
, const char *p
)
400 const char *cp
, *cpr
;
405 if ((cp
= strstr(p
, "OpenBSD")) != NULL
)
406 mandoc_msg(MANDOCERR_BX
, mdoc
->parse
,
407 ln
, pos
+ (cp
- p
), "Ox");
408 if ((cp
= strstr(p
, "NetBSD")) != NULL
)
409 mandoc_msg(MANDOCERR_BX
, mdoc
->parse
,
410 ln
, pos
+ (cp
- p
), "Nx");
411 if ((cp
= strstr(p
, "FreeBSD")) != NULL
)
412 mandoc_msg(MANDOCERR_BX
, mdoc
->parse
,
413 ln
, pos
+ (cp
- p
), "Fx");
414 if ((cp
= strstr(p
, "DragonFly")) != NULL
)
415 mandoc_msg(MANDOCERR_BX
, mdoc
->parse
,
416 ln
, pos
+ (cp
- p
), "Dx");
419 while ((cp
= strstr(cp
+ 1, "()")) != NULL
) {
420 for (cpr
= cp
- 1; cpr
>= p
; cpr
--)
421 if (*cpr
!= '_' && !isalnum((unsigned char)*cpr
))
423 if ((cpr
< p
|| *cpr
== ' ') && cpr
+ 1 < cp
) {
425 mandoc_vmsg(MANDOCERR_FUNC
, mdoc
->parse
,
427 "%.*s()", (int)(cp
- cpr
), cpr
);
433 post_delim(POST_ARGS
)
435 const struct roff_node
*nch
;
440 tok
= mdoc
->last
->tok
;
441 nch
= mdoc
->last
->last
;
442 if (nch
== NULL
|| nch
->type
!= ROFFT_TEXT
)
444 lc
= strchr(nch
->string
, '\0') - 1;
445 if (lc
< nch
->string
)
447 delim
= mdoc_isdelim(lc
);
448 if (delim
== DELIM_NONE
|| delim
== DELIM_OPEN
)
450 if (*lc
== ')' && (tok
== MDOC_Nd
|| tok
== MDOC_Sh
||
451 tok
== MDOC_Ss
|| tok
== MDOC_Fo
))
454 mandoc_vmsg(MANDOCERR_DELIM
, mdoc
->parse
,
455 nch
->line
, nch
->pos
+ (lc
- nch
->string
),
456 "%s%s %s", roff_name
[tok
],
457 nch
== mdoc
->last
->child
? "" : " ...", nch
->string
);
461 post_delim_nb(POST_ARGS
)
463 const struct roff_node
*nch
;
470 * Find candidates: at least two bytes,
471 * the last one a closing or middle delimiter.
474 tok
= mdoc
->last
->tok
;
475 nch
= mdoc
->last
->last
;
476 if (nch
== NULL
|| nch
->type
!= ROFFT_TEXT
)
478 lc
= strchr(nch
->string
, '\0') - 1;
479 if (lc
<= nch
->string
)
481 delim
= mdoc_isdelim(lc
);
482 if (delim
== DELIM_NONE
|| delim
== DELIM_OPEN
)
486 * Reduce false positives by allowing various cases.
489 /* Escaped delimiters. */
490 if (lc
> nch
->string
+ 1 && lc
[-2] == '\\' &&
491 (lc
[-1] == '&' || lc
[-1] == 'e'))
494 /* Specific byte sequences. */
497 for (cp
= lc
; cp
>= nch
->string
; cp
--)
502 if (lc
> nch
->string
+ 1 && lc
[-2] == '.' && lc
[-1] == '.')
516 for (cp
= lc
; cp
>= nch
->string
; cp
--)
521 if (lc
== nch
->string
+ 1 && lc
[-1] == '|')
527 /* Exactly two non-alphanumeric bytes. */
528 if (lc
== nch
->string
+ 1 && !isalnum((unsigned char)lc
[-1]))
531 /* At least three alphabetic words with a sentence ending. */
532 if (strchr("!.:?", *lc
) != NULL
&& (tok
== MDOC_Em
||
533 tok
== MDOC_Li
|| tok
== MDOC_Po
|| tok
== MDOC_Pq
||
536 for (cp
= lc
- 1; cp
>= nch
->string
; cp
--) {
539 if (cp
> nch
->string
&& cp
[-1] == ',')
541 } else if (isalpha((unsigned int)*cp
)) {
549 mandoc_vmsg(MANDOCERR_DELIM_NB
, mdoc
->parse
,
550 nch
->line
, nch
->pos
+ (lc
- nch
->string
),
551 "%s%s %s", roff_name
[tok
],
552 nch
== mdoc
->last
->child
? "" : " ...", nch
->string
);
556 post_bl_norm(POST_ARGS
)
559 struct mdoc_argv
*argv
, *wa
;
561 enum mdocargt mdoclt
;
564 n
= mdoc
->last
->parent
;
565 n
->norm
->Bl
.type
= LIST__NONE
;
568 * First figure out which kind of list to use: bind ourselves to
569 * the first mentioned list type and warn about any remaining
570 * ones. If we find no list type, we default to LIST_item.
573 wa
= (n
->args
== NULL
) ? NULL
: n
->args
->argv
;
574 mdoclt
= MDOC_ARG_MAX
;
575 for (i
= 0; n
->args
&& i
< (int)n
->args
->argc
; i
++) {
576 argv
= n
->args
->argv
+ i
;
579 /* Set list types. */
613 /* Set list arguments. */
615 if (n
->norm
->Bl
.comp
)
616 mandoc_msg(MANDOCERR_ARG_REP
,
617 mdoc
->parse
, argv
->line
,
618 argv
->pos
, "Bl -compact");
619 n
->norm
->Bl
.comp
= 1;
624 mandoc_msg(MANDOCERR_ARG_EMPTY
,
625 mdoc
->parse
, argv
->line
,
626 argv
->pos
, "Bl -width");
627 n
->norm
->Bl
.width
= "0n";
630 if (NULL
!= n
->norm
->Bl
.width
)
631 mandoc_vmsg(MANDOCERR_ARG_REP
,
632 mdoc
->parse
, argv
->line
,
633 argv
->pos
, "Bl -width %s",
635 rewrite_macro2len(mdoc
, argv
->value
);
636 n
->norm
->Bl
.width
= argv
->value
[0];
640 mandoc_msg(MANDOCERR_ARG_EMPTY
,
641 mdoc
->parse
, argv
->line
,
642 argv
->pos
, "Bl -offset");
645 if (NULL
!= n
->norm
->Bl
.offs
)
646 mandoc_vmsg(MANDOCERR_ARG_REP
,
647 mdoc
->parse
, argv
->line
,
648 argv
->pos
, "Bl -offset %s",
650 rewrite_macro2len(mdoc
, argv
->value
);
651 n
->norm
->Bl
.offs
= argv
->value
[0];
656 if (LIST__NONE
== lt
)
660 /* Check: multiple list types. */
662 if (LIST__NONE
!= n
->norm
->Bl
.type
) {
663 mandoc_vmsg(MANDOCERR_BL_REP
,
664 mdoc
->parse
, n
->line
, n
->pos
,
665 "Bl -%s", mdoc_argnames
[argv
->arg
]);
669 /* The list type should come first. */
671 if (n
->norm
->Bl
.width
||
674 mandoc_vmsg(MANDOCERR_BL_LATETYPE
,
675 mdoc
->parse
, n
->line
, n
->pos
, "Bl -%s",
676 mdoc_argnames
[n
->args
->argv
[0].arg
]);
678 n
->norm
->Bl
.type
= lt
;
679 if (LIST_column
== lt
) {
680 n
->norm
->Bl
.ncols
= argv
->sz
;
681 n
->norm
->Bl
.cols
= (void *)argv
->value
;
685 /* Allow lists to default to LIST_item. */
687 if (LIST__NONE
== n
->norm
->Bl
.type
) {
688 mandoc_msg(MANDOCERR_BL_NOTYPE
, mdoc
->parse
,
689 n
->line
, n
->pos
, "Bl");
690 n
->norm
->Bl
.type
= LIST_item
;
695 * Validate the width field. Some list types don't need width
696 * types and should be warned about them. Others should have it
697 * and must also be warned. Yet others have a default and need
701 switch (n
->norm
->Bl
.type
) {
703 if (n
->norm
->Bl
.width
== NULL
)
704 mandoc_msg(MANDOCERR_BL_NOWIDTH
, mdoc
->parse
,
705 n
->line
, n
->pos
, "Bl -tag");
712 if (n
->norm
->Bl
.width
!= NULL
)
713 mandoc_vmsg(MANDOCERR_BL_SKIPW
, mdoc
->parse
,
714 wa
->line
, wa
->pos
, "Bl -%s",
715 mdoc_argnames
[mdoclt
]);
716 n
->norm
->Bl
.width
= NULL
;
721 if (n
->norm
->Bl
.width
== NULL
)
722 n
->norm
->Bl
.width
= "2n";
725 if (n
->norm
->Bl
.width
== NULL
)
726 n
->norm
->Bl
.width
= "3n";
737 struct mdoc_argv
*argv
;
742 for (i
= 0; n
->args
&& i
< (int)n
->args
->argc
; i
++) {
743 argv
= n
->args
->argv
+ i
;
763 mandoc_msg(MANDOCERR_BD_FILE
, mdoc
->parse
,
764 n
->line
, n
->pos
, NULL
);
768 mandoc_msg(MANDOCERR_ARG_EMPTY
,
769 mdoc
->parse
, argv
->line
,
770 argv
->pos
, "Bd -offset");
773 if (NULL
!= n
->norm
->Bd
.offs
)
774 mandoc_vmsg(MANDOCERR_ARG_REP
,
775 mdoc
->parse
, argv
->line
,
776 argv
->pos
, "Bd -offset %s",
778 rewrite_macro2len(mdoc
, argv
->value
);
779 n
->norm
->Bd
.offs
= argv
->value
[0];
782 if (n
->norm
->Bd
.comp
)
783 mandoc_msg(MANDOCERR_ARG_REP
,
784 mdoc
->parse
, argv
->line
,
785 argv
->pos
, "Bd -compact");
786 n
->norm
->Bd
.comp
= 1;
791 if (DISP__NONE
== dt
)
794 if (DISP__NONE
== n
->norm
->Bd
.type
)
795 n
->norm
->Bd
.type
= dt
;
797 mandoc_vmsg(MANDOCERR_BD_REP
,
798 mdoc
->parse
, n
->line
, n
->pos
,
799 "Bd -%s", mdoc_argnames
[argv
->arg
]);
802 if (DISP__NONE
== n
->norm
->Bd
.type
) {
803 mandoc_msg(MANDOCERR_BD_NOTYPE
, mdoc
->parse
,
804 n
->line
, n
->pos
, "Bd");
805 n
->norm
->Bd
.type
= DISP_ragged
;
810 * Stand-alone line macros.
814 post_an_norm(POST_ARGS
)
817 struct mdoc_argv
*argv
;
824 for (i
= 1; i
< n
->args
->argc
; i
++) {
825 argv
= n
->args
->argv
+ i
;
826 mandoc_vmsg(MANDOCERR_AN_REP
,
827 mdoc
->parse
, argv
->line
, argv
->pos
,
828 "An -%s", mdoc_argnames
[argv
->arg
]);
831 argv
= n
->args
->argv
;
832 if (argv
->arg
== MDOC_Split
)
833 n
->norm
->An
.auth
= AUTH_split
;
834 else if (argv
->arg
== MDOC_Nosplit
)
835 n
->norm
->An
.auth
= AUTH_nosplit
;
847 if (n
->child
!= NULL
)
848 mandoc_vmsg(MANDOCERR_ARG_SKIP
, mdoc
->parse
, n
->line
,
849 n
->pos
, "%s %s", roff_name
[n
->tok
], n
->child
->string
);
851 while (n
->child
!= NULL
)
852 roff_node_delete(mdoc
, n
->child
);
854 roff_word_alloc(mdoc
, n
->line
, n
->pos
, n
->tok
== MDOC_Bt
?
855 "is currently in beta test." : "currently under development.");
856 mdoc
->last
->flags
|= NODE_EOS
| NODE_NOSRC
;
861 build_list(struct roff_man
*mdoc
, int tok
)
866 n
= mdoc
->last
->next
;
867 for (ic
= 1;; ic
++) {
868 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, tok
);
869 mdoc
->last
->flags
|= NODE_NOSRC
;
870 mdoc_node_relink(mdoc
, n
);
871 n
= mdoc
->last
= mdoc
->last
->parent
;
872 mdoc
->next
= ROFF_NEXT_SIBLING
;
875 if (ic
> 1 || n
->next
->next
!= NULL
) {
876 roff_word_alloc(mdoc
, n
->line
, n
->pos
, ",");
877 mdoc
->last
->flags
|= NODE_DELIMC
| NODE_NOSRC
;
879 n
= mdoc
->last
->next
;
880 if (n
->next
== NULL
) {
881 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "and");
882 mdoc
->last
->flags
|= NODE_NOSRC
;
896 mdoc
->next
= ROFF_NEXT_CHILD
;
897 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "The");
898 mdoc
->last
->flags
|= NODE_NOSRC
;
900 if (mdoc
->last
->next
!= NULL
)
901 ic
= build_list(mdoc
, MDOC_Nm
);
902 else if (mdoc
->meta
.name
!= NULL
) {
903 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Nm
);
904 mdoc
->last
->flags
|= NODE_NOSRC
;
905 roff_word_alloc(mdoc
, n
->line
, n
->pos
, mdoc
->meta
.name
);
906 mdoc
->last
->flags
|= NODE_NOSRC
;
907 mdoc
->last
= mdoc
->last
->parent
;
908 mdoc
->next
= ROFF_NEXT_SIBLING
;
911 mandoc_msg(MANDOCERR_EX_NONAME
, mdoc
->parse
,
912 n
->line
, n
->pos
, "Ex");
916 roff_word_alloc(mdoc
, n
->line
, n
->pos
,
917 ic
> 1 ? "utilities exit\\~0" : "utility exits\\~0");
918 mdoc
->last
->flags
|= NODE_NOSRC
;
919 roff_word_alloc(mdoc
, n
->line
, n
->pos
,
920 "on success, and\\~>0 if an error occurs.");
921 mdoc
->last
->flags
|= NODE_EOS
| NODE_NOSRC
;
934 assert(n
->child
->type
== ROFFT_TEXT
);
935 mdoc
->next
= ROFF_NEXT_CHILD
;
937 if ((p
= mdoc_a2lib(n
->child
->string
)) != NULL
) {
938 n
->child
->flags
|= NODE_NOPRT
;
939 roff_word_alloc(mdoc
, n
->line
, n
->pos
, p
);
940 mdoc
->last
->flags
= NODE_NOSRC
;
945 mandoc_vmsg(MANDOCERR_LB_BAD
, mdoc
->parse
, n
->child
->line
,
946 n
->child
->pos
, "Lb %s", n
->child
->string
);
948 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "library");
949 mdoc
->last
->flags
= NODE_NOSRC
;
950 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "\\(Lq");
951 mdoc
->last
->flags
= NODE_DELIMO
| NODE_NOSRC
;
952 mdoc
->last
= mdoc
->last
->next
;
953 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "\\(Rq");
954 mdoc
->last
->flags
= NODE_DELIMC
| NODE_NOSRC
;
967 mdoc
->next
= ROFF_NEXT_CHILD
;
968 if (n
->child
!= NULL
) {
969 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "The");
970 mdoc
->last
->flags
|= NODE_NOSRC
;
971 ic
= build_list(mdoc
, MDOC_Fn
);
972 roff_word_alloc(mdoc
, n
->line
, n
->pos
,
973 ic
> 1 ? "functions return" : "function returns");
974 mdoc
->last
->flags
|= NODE_NOSRC
;
975 roff_word_alloc(mdoc
, n
->line
, n
->pos
,
976 "the value\\~0 if successful;");
978 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "Upon successful "
979 "completion, the value\\~0 is returned;");
980 mdoc
->last
->flags
|= NODE_NOSRC
;
982 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "otherwise "
983 "the value\\~\\-1 is returned and the global variable");
984 mdoc
->last
->flags
|= NODE_NOSRC
;
985 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Va
);
986 mdoc
->last
->flags
|= NODE_NOSRC
;
987 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "errno");
988 mdoc
->last
->flags
|= NODE_NOSRC
;
989 mdoc
->last
= mdoc
->last
->parent
;
990 mdoc
->next
= ROFF_NEXT_SIBLING
;
991 roff_word_alloc(mdoc
, n
->line
, n
->pos
,
992 "is set to indicate the error.");
993 mdoc
->last
->flags
|= NODE_EOS
| NODE_NOSRC
;
1000 struct roff_node
*n
;
1005 if (n
->args
&& n
->args
->argc
== 1)
1006 if (n
->args
->argv
[0].arg
== MDOC_Std
)
1009 mandoc_msg(MANDOCERR_ARG_STD
, mdoc
->parse
,
1010 n
->line
, n
->pos
, roff_name
[n
->tok
]);
1016 struct roff_node
*n
, *nch
;
1021 assert(nch
->type
== ROFFT_TEXT
);
1023 if ((p
= mdoc_a2st(nch
->string
)) == NULL
) {
1024 mandoc_vmsg(MANDOCERR_ST_BAD
, mdoc
->parse
,
1025 nch
->line
, nch
->pos
, "St %s", nch
->string
);
1026 roff_node_delete(mdoc
, n
);
1030 nch
->flags
|= NODE_NOPRT
;
1031 mdoc
->next
= ROFF_NEXT_CHILD
;
1032 roff_word_alloc(mdoc
, nch
->line
, nch
->pos
, p
);
1033 mdoc
->last
->flags
|= NODE_NOSRC
;
1038 post_obsolete(POST_ARGS
)
1040 struct roff_node
*n
;
1043 if (n
->type
== ROFFT_ELEM
|| n
->type
== ROFFT_BLOCK
)
1044 mandoc_msg(MANDOCERR_MACRO_OBS
, mdoc
->parse
,
1045 n
->line
, n
->pos
, roff_name
[n
->tok
]);
1049 post_useless(POST_ARGS
)
1051 struct roff_node
*n
;
1054 mandoc_msg(MANDOCERR_MACRO_USELESS
, mdoc
->parse
,
1055 n
->line
, n
->pos
, roff_name
[n
->tok
]);
1065 struct roff_node
*np
, *nch
;
1068 * Unlike other data pointers, these are "housed" by the HEAD
1069 * element, which contains the goods.
1073 if (np
->type
!= ROFFT_HEAD
)
1076 assert(np
->parent
->type
== ROFFT_BLOCK
);
1077 assert(np
->parent
->tok
== MDOC_Bf
);
1079 /* Check the number of arguments. */
1082 if (np
->parent
->args
== NULL
) {
1084 mandoc_msg(MANDOCERR_BF_NOFONT
, mdoc
->parse
,
1085 np
->line
, np
->pos
, "Bf");
1091 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, mdoc
->parse
,
1092 nch
->line
, nch
->pos
, "Bf ... %s", nch
->string
);
1094 /* Extract argument into data. */
1096 if (np
->parent
->args
!= NULL
) {
1097 switch (np
->parent
->args
->argv
[0].arg
) {
1099 np
->norm
->Bf
.font
= FONT_Em
;
1102 np
->norm
->Bf
.font
= FONT_Li
;
1105 np
->norm
->Bf
.font
= FONT_Sy
;
1113 /* Extract parameter into data. */
1115 if ( ! strcmp(np
->child
->string
, "Em"))
1116 np
->norm
->Bf
.font
= FONT_Em
;
1117 else if ( ! strcmp(np
->child
->string
, "Li"))
1118 np
->norm
->Bf
.font
= FONT_Li
;
1119 else if ( ! strcmp(np
->child
->string
, "Sy"))
1120 np
->norm
->Bf
.font
= FONT_Sy
;
1122 mandoc_vmsg(MANDOCERR_BF_BADFONT
, mdoc
->parse
,
1123 np
->child
->line
, np
->child
->pos
,
1124 "Bf %s", np
->child
->string
);
1128 post_fname(POST_ARGS
)
1130 const struct roff_node
*n
;
1134 n
= mdoc
->last
->child
;
1135 pos
= strcspn(n
->string
, "()");
1136 cp
= n
->string
+ pos
;
1137 if ( ! (cp
[0] == '\0' || (cp
[0] == '(' && cp
[1] == '*')))
1138 mandoc_msg(MANDOCERR_FN_PAREN
, mdoc
->parse
,
1139 n
->line
, n
->pos
+ pos
, n
->string
);
1153 const struct roff_node
*n
;
1157 if (n
->type
!= ROFFT_HEAD
)
1160 if (n
->child
== NULL
) {
1161 mandoc_msg(MANDOCERR_FO_NOHEAD
, mdoc
->parse
,
1162 n
->line
, n
->pos
, "Fo");
1165 if (n
->child
!= n
->last
) {
1166 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, mdoc
->parse
,
1167 n
->child
->next
->line
, n
->child
->next
->pos
,
1168 "Fo ... %s", n
->child
->next
->string
);
1169 while (n
->child
!= n
->last
)
1170 roff_node_delete(mdoc
, n
->last
);
1180 const struct roff_node
*n
;
1183 for (n
= mdoc
->last
->child
; n
!= NULL
; n
= n
->next
) {
1184 for (cp
= n
->string
; *cp
!= '\0'; cp
++) {
1185 /* Ignore callbacks and alterations. */
1186 if (*cp
== '(' || *cp
== '{')
1190 mandoc_msg(MANDOCERR_FA_COMMA
, mdoc
->parse
,
1191 n
->line
, n
->pos
+ (cp
- n
->string
),
1196 post_delim_nb(mdoc
);
1202 struct roff_node
*n
;
1206 if (n
->sec
== SEC_NAME
&& n
->child
!= NULL
&&
1207 n
->child
->type
== ROFFT_TEXT
&& mdoc
->meta
.msec
!= NULL
)
1208 mandoc_xr_add(mdoc
->meta
.msec
, n
->child
->string
, -1, -1);
1210 if (n
->last
!= NULL
&&
1211 (n
->last
->tok
== MDOC_Pp
||
1212 n
->last
->tok
== MDOC_Lp
))
1213 mdoc_node_relink(mdoc
, n
->last
);
1215 if (mdoc
->meta
.name
== NULL
)
1216 deroff(&mdoc
->meta
.name
, n
);
1218 if (mdoc
->meta
.name
== NULL
||
1219 (mdoc
->lastsec
== SEC_NAME
&& n
->child
== NULL
))
1220 mandoc_msg(MANDOCERR_NM_NONAME
, mdoc
->parse
,
1221 n
->line
, n
->pos
, "Nm");
1225 post_delim_nb(mdoc
);
1234 if ((n
->child
!= NULL
&& n
->child
->type
== ROFFT_TEXT
) ||
1235 mdoc
->meta
.name
== NULL
)
1238 mdoc
->next
= ROFF_NEXT_CHILD
;
1239 roff_word_alloc(mdoc
, n
->line
, n
->pos
, mdoc
->meta
.name
);
1240 mdoc
->last
->flags
|= NODE_NOSRC
;
1247 struct roff_node
*n
;
1251 if (n
->type
!= ROFFT_BODY
)
1254 if (n
->sec
!= SEC_NAME
)
1255 mandoc_msg(MANDOCERR_ND_LATE
, mdoc
->parse
,
1256 n
->line
, n
->pos
, "Nd");
1258 if (n
->child
== NULL
)
1259 mandoc_msg(MANDOCERR_ND_EMPTY
, mdoc
->parse
,
1260 n
->line
, n
->pos
, "Nd");
1268 post_display(POST_ARGS
)
1270 struct roff_node
*n
, *np
;
1275 if (n
->end
!= ENDBODY_NOT
) {
1276 if (n
->tok
== MDOC_Bd
&&
1277 n
->body
->parent
->args
== NULL
)
1278 roff_node_delete(mdoc
, n
);
1279 } else if (n
->child
== NULL
)
1280 mandoc_msg(MANDOCERR_BLK_EMPTY
, mdoc
->parse
,
1281 n
->line
, n
->pos
, roff_name
[n
->tok
]);
1282 else if (n
->tok
== MDOC_D1
)
1286 if (n
->tok
== MDOC_Bd
) {
1287 if (n
->args
== NULL
) {
1288 mandoc_msg(MANDOCERR_BD_NOARG
,
1289 mdoc
->parse
, n
->line
, n
->pos
, "Bd");
1290 mdoc
->next
= ROFF_NEXT_SIBLING
;
1291 while (n
->body
->child
!= NULL
)
1292 mdoc_node_relink(mdoc
,
1294 roff_node_delete(mdoc
, n
);
1300 for (np
= n
->parent
; np
!= NULL
; np
= np
->parent
) {
1301 if (np
->type
== ROFFT_BLOCK
&& np
->tok
== MDOC_Bd
) {
1302 mandoc_vmsg(MANDOCERR_BD_NEST
,
1303 mdoc
->parse
, n
->line
, n
->pos
,
1304 "%s in Bd", roff_name
[n
->tok
]);
1315 post_defaults(POST_ARGS
)
1317 struct roff_node
*nn
;
1319 if (mdoc
->last
->child
!= NULL
) {
1320 post_delim_nb(mdoc
);
1325 * The `Ar' defaults to "file ..." if no value is provided as an
1326 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
1327 * gets an empty string.
1333 mdoc
->next
= ROFF_NEXT_CHILD
;
1334 roff_word_alloc(mdoc
, nn
->line
, nn
->pos
, "file");
1335 mdoc
->last
->flags
|= NODE_NOSRC
;
1336 roff_word_alloc(mdoc
, nn
->line
, nn
->pos
, "...");
1337 mdoc
->last
->flags
|= NODE_NOSRC
;
1341 mdoc
->next
= ROFF_NEXT_CHILD
;
1342 roff_word_alloc(mdoc
, nn
->line
, nn
->pos
, "~");
1343 mdoc
->last
->flags
|= NODE_NOSRC
;
1354 struct roff_node
*n
, *nch
;
1361 * If we have a child, look it up in the standard keys. If a
1362 * key exist, use that instead of the child; if it doesn't,
1363 * prefix "AT&T UNIX " to the existing data.
1367 if (nch
!= NULL
&& ((att
= mdoc_a2att(nch
->string
)) == NULL
))
1368 mandoc_vmsg(MANDOCERR_AT_BAD
, mdoc
->parse
,
1369 nch
->line
, nch
->pos
, "At %s", nch
->string
);
1371 mdoc
->next
= ROFF_NEXT_CHILD
;
1373 roff_word_alloc(mdoc
, nch
->line
, nch
->pos
, att
);
1374 nch
->flags
|= NODE_NOPRT
;
1376 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "AT&T UNIX");
1377 mdoc
->last
->flags
|= NODE_NOSRC
;
1384 struct roff_node
*np
, *nch
;
1390 if (np
->norm
->An
.auth
== AUTH__NONE
) {
1392 mandoc_msg(MANDOCERR_MACRO_EMPTY
, mdoc
->parse
,
1393 np
->line
, np
->pos
, "An");
1395 post_delim_nb(mdoc
);
1396 } else if (nch
!= NULL
)
1397 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, mdoc
->parse
,
1398 nch
->line
, nch
->pos
, "An ... %s", nch
->string
);
1405 post_obsolete(mdoc
);
1406 if (mdoc
->last
->type
== ROFFT_BLOCK
)
1407 mdoc
->last
->norm
->Es
= mdoc
->last_es
;
1414 post_obsolete(mdoc
);
1415 mdoc
->last_es
= mdoc
->last
;
1421 struct roff_node
*n
;
1425 post_delim_nb(mdoc
);
1440 if (n
->child
== NULL
)
1442 v
= n
->child
->string
;
1443 if ((v
[0] != '0' && v
[0] != '1') || v
[1] != '.' ||
1444 v
[2] < '0' || v
[2] > '9' ||
1445 v
[3] < 'a' || v
[3] > 'z' || v
[4] != '\0')
1447 n
->child
->flags
|= NODE_NOPRT
;
1448 mdoc
->next
= ROFF_NEXT_CHILD
;
1449 roff_word_alloc(mdoc
, n
->child
->line
, n
->child
->pos
, v
);
1450 v
= mdoc
->last
->string
;
1451 v
[3] = toupper((unsigned char)v
[3]);
1452 mdoc
->last
->flags
|= NODE_NOSRC
;
1464 mdoc
->next
= ROFF_NEXT_CHILD
;
1465 roff_word_alloc(mdoc
, n
->line
, n
->pos
, os
);
1466 mdoc
->last
->flags
|= NODE_NOSRC
;
1473 struct roff_node
*nbl
, *nit
, *nch
;
1480 if (nit
->type
!= ROFFT_BLOCK
)
1483 nbl
= nit
->parent
->parent
;
1484 lt
= nbl
->norm
->Bl
.type
;
1492 if (nit
->head
->child
== NULL
)
1493 mandoc_vmsg(MANDOCERR_IT_NOHEAD
,
1494 mdoc
->parse
, nit
->line
, nit
->pos
,
1496 mdoc_argnames
[nbl
->args
->argv
[0].arg
]);
1502 if (nit
->body
== NULL
|| nit
->body
->child
== NULL
)
1503 mandoc_vmsg(MANDOCERR_IT_NOBODY
,
1504 mdoc
->parse
, nit
->line
, nit
->pos
,
1506 mdoc_argnames
[nbl
->args
->argv
[0].arg
]);
1509 if ((nch
= nit
->head
->child
) != NULL
)
1510 mandoc_vmsg(MANDOCERR_ARG_SKIP
, mdoc
->parse
,
1511 nit
->line
, nit
->pos
, "It %s",
1512 nch
->string
== NULL
? roff_name
[nch
->tok
] :
1516 cols
= (int)nbl
->norm
->Bl
.ncols
;
1518 assert(nit
->head
->child
== NULL
);
1520 if (nit
->head
->next
->child
== NULL
&&
1521 nit
->head
->next
->next
== NULL
) {
1522 mandoc_msg(MANDOCERR_MACRO_EMPTY
, mdoc
->parse
,
1523 nit
->line
, nit
->pos
, "It");
1524 roff_node_delete(mdoc
, nit
);
1529 for (nch
= nit
->child
; nch
!= NULL
; nch
= nch
->next
) {
1530 if (nch
->type
!= ROFFT_BODY
)
1532 if (i
++ && nch
->flags
& NODE_LINE
)
1533 mandoc_msg(MANDOCERR_TA_LINE
, mdoc
->parse
,
1534 nch
->line
, nch
->pos
, "Ta");
1536 if (i
< cols
|| i
> cols
+ 1)
1537 mandoc_vmsg(MANDOCERR_BL_COL
,
1538 mdoc
->parse
, nit
->line
, nit
->pos
,
1539 "%d columns, %d cells", cols
, i
);
1540 else if (nit
->head
->next
->child
!= NULL
&&
1541 nit
->head
->next
->child
->line
> nit
->line
)
1542 mandoc_msg(MANDOCERR_IT_NOARG
, mdoc
->parse
,
1543 nit
->line
, nit
->pos
, "Bl -column It");
1551 post_bl_block(POST_ARGS
)
1553 struct roff_node
*n
, *ni
, *nc
;
1558 for (ni
= n
->body
->child
; ni
!= NULL
; ni
= ni
->next
) {
1559 if (ni
->body
== NULL
)
1561 nc
= ni
->body
->last
;
1562 while (nc
!= NULL
) {
1572 if (ni
->next
== NULL
) {
1573 mandoc_msg(MANDOCERR_PAR_MOVE
,
1574 mdoc
->parse
, nc
->line
, nc
->pos
,
1575 roff_name
[nc
->tok
]);
1576 mdoc_node_relink(mdoc
, nc
);
1577 } else if (n
->norm
->Bl
.comp
== 0 &&
1578 n
->norm
->Bl
.type
!= LIST_column
) {
1579 mandoc_vmsg(MANDOCERR_PAR_SKIP
,
1580 mdoc
->parse
, nc
->line
, nc
->pos
,
1581 "%s before It", roff_name
[nc
->tok
]);
1582 roff_node_delete(mdoc
, nc
);
1585 nc
= ni
->body
->last
;
1591 * If the argument of -offset or -width is a macro,
1592 * replace it with the associated default width.
1595 rewrite_macro2len(struct roff_man
*mdoc
, char **arg
)
1602 else if ( ! strcmp(*arg
, "Ds"))
1604 else if ((tok
= roffhash_find(mdoc
->mdocmac
, *arg
, 0)) == TOKEN_NONE
)
1607 width
= macro2len(tok
);
1610 mandoc_asprintf(arg
, "%zun", width
);
1614 post_bl_head(POST_ARGS
)
1616 struct roff_node
*nbl
, *nh
, *nch
, *nnext
;
1617 struct mdoc_argv
*argv
;
1623 if (nh
->norm
->Bl
.type
!= LIST_column
) {
1624 if ((nch
= nh
->child
) == NULL
)
1626 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, mdoc
->parse
,
1627 nch
->line
, nch
->pos
, "Bl ... %s", nch
->string
);
1628 while (nch
!= NULL
) {
1629 roff_node_delete(mdoc
, nch
);
1636 * Append old-style lists, where the column width specifiers
1637 * trail as macro parameters, to the new-style ("normal-form")
1638 * lists where they're argument values following -column.
1641 if (nh
->child
== NULL
)
1645 for (j
= 0; j
< (int)nbl
->args
->argc
; j
++)
1646 if (nbl
->args
->argv
[j
].arg
== MDOC_Column
)
1649 assert(j
< (int)nbl
->args
->argc
);
1652 * Accommodate for new-style groff column syntax. Shuffle the
1653 * child nodes, all of which must be TEXT, as arguments for the
1654 * column field. Then, delete the head children.
1657 argv
= nbl
->args
->argv
+ j
;
1659 for (nch
= nh
->child
; nch
!= NULL
; nch
= nch
->next
)
1661 argv
->value
= mandoc_reallocarray(argv
->value
,
1662 argv
->sz
, sizeof(char *));
1664 nh
->norm
->Bl
.ncols
= argv
->sz
;
1665 nh
->norm
->Bl
.cols
= (void *)argv
->value
;
1667 for (nch
= nh
->child
; nch
!= NULL
; nch
= nnext
) {
1668 argv
->value
[i
++] = nch
->string
;
1671 roff_node_delete(NULL
, nch
);
1679 struct roff_node
*nparent
, *nprev
; /* of the Bl block */
1680 struct roff_node
*nblock
, *nbody
; /* of the Bl */
1681 struct roff_node
*nchild
, *nnext
; /* of the Bl body */
1682 const char *prev_Er
;
1686 switch (nbody
->type
) {
1688 post_bl_block(mdoc
);
1698 if (nbody
->end
!= ENDBODY_NOT
)
1701 nchild
= nbody
->child
;
1702 if (nchild
== NULL
) {
1703 mandoc_msg(MANDOCERR_BLK_EMPTY
, mdoc
->parse
,
1704 nbody
->line
, nbody
->pos
, "Bl");
1707 while (nchild
!= NULL
) {
1708 nnext
= nchild
->next
;
1709 if (nchild
->tok
== MDOC_It
||
1710 (nchild
->tok
== MDOC_Sm
&&
1711 nnext
!= NULL
&& nnext
->tok
== MDOC_It
)) {
1717 * In .Bl -column, the first rows may be implicit,
1718 * that is, they may not start with .It macros.
1719 * Such rows may be followed by nodes generated on the
1720 * roff level, for example .TS, which cannot be moved
1721 * out of the list. In that case, wrap such roff nodes
1722 * into an implicit row.
1725 if (nchild
->prev
!= NULL
) {
1726 mdoc
->last
= nchild
;
1727 mdoc
->next
= ROFF_NEXT_SIBLING
;
1728 roff_block_alloc(mdoc
, nchild
->line
,
1729 nchild
->pos
, MDOC_It
);
1730 roff_head_alloc(mdoc
, nchild
->line
,
1731 nchild
->pos
, MDOC_It
);
1732 mdoc
->next
= ROFF_NEXT_SIBLING
;
1733 roff_body_alloc(mdoc
, nchild
->line
,
1734 nchild
->pos
, MDOC_It
);
1735 while (nchild
->tok
!= MDOC_It
) {
1736 mdoc_node_relink(mdoc
, nchild
);
1737 if ((nchild
= nnext
) == NULL
)
1739 nnext
= nchild
->next
;
1740 mdoc
->next
= ROFF_NEXT_SIBLING
;
1746 mandoc_msg(MANDOCERR_BL_MOVE
, mdoc
->parse
,
1747 nchild
->line
, nchild
->pos
, roff_name
[nchild
->tok
]);
1750 * Move the node out of the Bl block.
1751 * First, collect all required node pointers.
1754 nblock
= nbody
->parent
;
1755 nprev
= nblock
->prev
;
1756 nparent
= nblock
->parent
;
1759 * Unlink this child.
1762 nbody
->child
= nnext
;
1769 * Relink this child.
1772 nchild
->parent
= nparent
;
1773 nchild
->prev
= nprev
;
1774 nchild
->next
= nblock
;
1776 nblock
->prev
= nchild
;
1778 nparent
->child
= nchild
;
1780 nprev
->next
= nchild
;
1785 if (mdoc
->meta
.os_e
!= MANDOC_OS_NETBSD
)
1789 for (nchild
= nbody
->child
; nchild
!= NULL
; nchild
= nchild
->next
) {
1790 if (nchild
->tok
!= MDOC_It
)
1792 if ((nnext
= nchild
->head
->child
) == NULL
)
1794 if (nnext
->type
== ROFFT_BLOCK
)
1795 nnext
= nnext
->body
->child
;
1796 if (nnext
== NULL
|| nnext
->tok
!= MDOC_Er
)
1798 nnext
= nnext
->child
;
1799 if (prev_Er
!= NULL
) {
1800 order
= strcmp(prev_Er
, nnext
->string
);
1802 mandoc_vmsg(MANDOCERR_ER_ORDER
,
1803 mdoc
->parse
, nnext
->line
, nnext
->pos
,
1804 "Er %s %s (NetBSD)",
1805 prev_Er
, nnext
->string
);
1806 else if (order
== 0)
1807 mandoc_vmsg(MANDOCERR_ER_REP
,
1808 mdoc
->parse
, nnext
->line
, nnext
->pos
,
1809 "Er %s (NetBSD)", prev_Er
);
1811 prev_Er
= nnext
->string
;
1818 struct roff_node
*n
;
1822 if (n
->type
== ROFFT_BLOCK
&& n
->body
->child
== NULL
) {
1823 mandoc_msg(MANDOCERR_BLK_EMPTY
,
1824 mdoc
->parse
, n
->line
, n
->pos
, "Bk");
1825 roff_node_delete(mdoc
, n
);
1832 struct roff_node
*nch
;
1834 nch
= mdoc
->last
->child
;
1837 mdoc
->flags
^= MDOC_SMOFF
;
1841 assert(nch
->type
== ROFFT_TEXT
);
1843 if ( ! strcmp(nch
->string
, "on")) {
1844 mdoc
->flags
&= ~MDOC_SMOFF
;
1847 if ( ! strcmp(nch
->string
, "off")) {
1848 mdoc
->flags
|= MDOC_SMOFF
;
1852 mandoc_vmsg(MANDOCERR_SM_BAD
,
1853 mdoc
->parse
, nch
->line
, nch
->pos
,
1854 "%s %s", roff_name
[mdoc
->last
->tok
], nch
->string
);
1855 mdoc_node_relink(mdoc
, nch
);
1860 post_root(POST_ARGS
)
1862 const char *openbsd_arch
[] = {
1863 "alpha", "amd64", "arm64", "armv7", "hppa", "i386",
1864 "landisk", "loongson", "luna88k", "macppc", "mips64",
1865 "octeon", "sgi", "socppc", "sparc64", NULL
1867 const char *netbsd_arch
[] = {
1868 "acorn26", "acorn32", "algor", "alpha", "amiga",
1870 "bebox", "cats", "cesfic", "cobalt", "dreamcast",
1871 "emips", "evbarm", "evbmips", "evbppc", "evbsh3", "evbsh5",
1872 "hp300", "hpcarm", "hpcmips", "hpcsh", "hppa",
1873 "i386", "ibmnws", "luna68k",
1874 "mac68k", "macppc", "mipsco", "mmeye", "mvme68k", "mvmeppc",
1875 "netwinder", "news68k", "newsmips", "next68k",
1876 "pc532", "playstation2", "pmax", "pmppc", "prep",
1877 "sandpoint", "sbmips", "sgimips", "shark",
1878 "sparc", "sparc64", "sun2", "sun3",
1879 "vax", "walnut", "x68k", "x86", "x86_64", "xen", NULL
1881 const char **arches
[] = { NULL
, netbsd_arch
, openbsd_arch
};
1883 struct roff_node
*n
;
1886 /* Add missing prologue data. */
1888 if (mdoc
->meta
.date
== NULL
)
1889 mdoc
->meta
.date
= mdoc
->quick
? mandoc_strdup("") :
1890 mandoc_normdate(mdoc
, NULL
, 0, 0);
1892 if (mdoc
->meta
.title
== NULL
) {
1893 mandoc_msg(MANDOCERR_DT_NOTITLE
,
1894 mdoc
->parse
, 0, 0, "EOF");
1895 mdoc
->meta
.title
= mandoc_strdup("UNTITLED");
1898 if (mdoc
->meta
.vol
== NULL
)
1899 mdoc
->meta
.vol
= mandoc_strdup("LOCAL");
1901 if (mdoc
->meta
.os
== NULL
) {
1902 mandoc_msg(MANDOCERR_OS_MISSING
,
1903 mdoc
->parse
, 0, 0, NULL
);
1904 mdoc
->meta
.os
= mandoc_strdup("");
1905 } else if (mdoc
->meta
.os_e
&&
1906 (mdoc
->meta
.rcsids
& (1 << mdoc
->meta
.os_e
)) == 0)
1907 mandoc_msg(MANDOCERR_RCS_MISSING
, mdoc
->parse
, 0, 0,
1908 mdoc
->meta
.os_e
== MANDOC_OS_OPENBSD
?
1909 "(OpenBSD)" : "(NetBSD)");
1911 if (mdoc
->meta
.arch
!= NULL
&&
1912 (arch
= arches
[mdoc
->meta
.os_e
]) != NULL
) {
1913 while (*arch
!= NULL
&& strcmp(*arch
, mdoc
->meta
.arch
))
1915 if (*arch
== NULL
) {
1916 n
= mdoc
->first
->child
;
1917 while (n
->tok
!= MDOC_Dt
)
1919 n
= n
->child
->next
->next
;
1920 mandoc_vmsg(MANDOCERR_ARCH_BAD
,
1921 mdoc
->parse
, n
->line
, n
->pos
,
1922 "Dt ... %s %s", mdoc
->meta
.arch
,
1923 mdoc
->meta
.os_e
== MANDOC_OS_OPENBSD
?
1924 "(OpenBSD)" : "(NetBSD)");
1928 /* Check that we begin with a proper `Sh'. */
1930 n
= mdoc
->first
->child
;
1931 while (n
!= NULL
&& n
->tok
>= MDOC_Dd
&&
1932 mdoc_macros
[n
->tok
].flags
& MDOC_PROLOGUE
)
1936 mandoc_msg(MANDOCERR_DOC_EMPTY
, mdoc
->parse
, 0, 0, NULL
);
1937 else if (n
->tok
!= MDOC_Sh
)
1938 mandoc_msg(MANDOCERR_SEC_BEFORE
, mdoc
->parse
,
1939 n
->line
, n
->pos
, roff_name
[n
->tok
]);
1945 struct roff_node
*np
, *nch
, *next
, *prev
;
1950 if (np
->type
!= ROFFT_BODY
)
1953 if (np
->child
== NULL
) {
1954 mandoc_msg(MANDOCERR_RS_EMPTY
, mdoc
->parse
,
1955 np
->line
, np
->pos
, "Rs");
1960 * The full `Rs' block needs special handling to order the
1961 * sub-elements according to `rsord'. Pick through each element
1962 * and correctly order it. This is an insertion sort.
1966 for (nch
= np
->child
->next
; nch
!= NULL
; nch
= next
) {
1967 /* Determine order number of this child. */
1968 for (i
= 0; i
< RSORD_MAX
; i
++)
1969 if (rsord
[i
] == nch
->tok
)
1972 if (i
== RSORD_MAX
) {
1973 mandoc_msg(MANDOCERR_RS_BAD
, mdoc
->parse
,
1974 nch
->line
, nch
->pos
, roff_name
[nch
->tok
]);
1976 } else if (nch
->tok
== MDOC__J
|| nch
->tok
== MDOC__B
)
1977 np
->norm
->Rs
.quote_T
++;
1980 * Remove this child from the chain. This somewhat
1981 * repeats roff_node_unlink(), but since we're
1982 * just re-ordering, there's no need for the
1983 * full unlink process.
1986 if ((next
= nch
->next
) != NULL
)
1987 next
->prev
= nch
->prev
;
1989 if ((prev
= nch
->prev
) != NULL
)
1990 prev
->next
= nch
->next
;
1992 nch
->prev
= nch
->next
= NULL
;
1995 * Scan back until we reach a node that's
1996 * to be ordered before this child.
1999 for ( ; prev
; prev
= prev
->prev
) {
2000 /* Determine order of `prev'. */
2001 for (j
= 0; j
< RSORD_MAX
; j
++)
2002 if (rsord
[j
] == prev
->tok
)
2012 * Set this child back into its correct place
2013 * in front of the `prev' node.
2019 np
->child
->prev
= nch
;
2020 nch
->next
= np
->child
;
2024 prev
->next
->prev
= nch
;
2025 nch
->next
= prev
->next
;
2032 * For some arguments of some macros,
2033 * convert all breakable hyphens into ASCII_HYPH.
2036 post_hyph(POST_ARGS
)
2038 struct roff_node
*nch
;
2041 for (nch
= mdoc
->last
->child
; nch
!= NULL
; nch
= nch
->next
) {
2042 if (nch
->type
!= ROFFT_TEXT
)
2047 while (*(++cp
) != '\0')
2049 isalpha((unsigned char)cp
[-1]) &&
2050 isalpha((unsigned char)cp
[1]))
2058 struct roff_node
*n
;
2061 if (n
->flags
& NODE_LINE
||
2062 (n
->next
!= NULL
&& n
->next
->flags
& NODE_DELIMC
))
2063 mandoc_msg(MANDOCERR_NS_SKIP
, mdoc
->parse
,
2064 n
->line
, n
->pos
, NULL
);
2080 switch (mdoc
->last
->type
) {
2085 switch (mdoc
->lastsec
) {
2090 post_sh_see_also(mdoc
);
2093 post_sh_authors(mdoc
);
2105 post_sh_name(POST_ARGS
)
2107 struct roff_node
*n
;
2112 for (n
= mdoc
->last
->child
; n
!= NULL
; n
= n
->next
) {
2115 if (hasnm
&& n
->child
!= NULL
)
2116 mandoc_vmsg(MANDOCERR_NAMESEC_PUNCT
,
2117 mdoc
->parse
, n
->line
, n
->pos
,
2118 "Nm %s", n
->child
->string
);
2123 if (n
->next
!= NULL
)
2124 mandoc_msg(MANDOCERR_NAMESEC_ND
,
2125 mdoc
->parse
, n
->line
, n
->pos
, NULL
);
2128 if (n
->type
== ROFFT_TEXT
&&
2129 n
->string
[0] == ',' && n
->string
[1] == '\0' &&
2130 n
->next
!= NULL
&& n
->next
->tok
== MDOC_Nm
) {
2136 mandoc_msg(MANDOCERR_NAMESEC_BAD
, mdoc
->parse
,
2137 n
->line
, n
->pos
, roff_name
[n
->tok
]);
2144 mandoc_msg(MANDOCERR_NAMESEC_NONM
, mdoc
->parse
,
2145 mdoc
->last
->line
, mdoc
->last
->pos
, NULL
);
2147 mandoc_msg(MANDOCERR_NAMESEC_NOND
, mdoc
->parse
,
2148 mdoc
->last
->line
, mdoc
->last
->pos
, NULL
);
2152 post_sh_see_also(POST_ARGS
)
2154 const struct roff_node
*n
;
2155 const char *name
, *sec
;
2156 const char *lastname
, *lastsec
, *lastpunct
;
2159 n
= mdoc
->last
->child
;
2160 lastname
= lastsec
= lastpunct
= NULL
;
2162 if (n
->tok
!= MDOC_Xr
||
2164 n
->child
->next
== NULL
)
2167 /* Process one .Xr node. */
2169 name
= n
->child
->string
;
2170 sec
= n
->child
->next
->string
;
2171 if (lastsec
!= NULL
) {
2172 if (lastpunct
[0] != ',' || lastpunct
[1] != '\0')
2173 mandoc_vmsg(MANDOCERR_XR_PUNCT
,
2174 mdoc
->parse
, n
->line
, n
->pos
,
2175 "%s before %s(%s)", lastpunct
,
2177 cmp
= strcmp(lastsec
, sec
);
2179 mandoc_vmsg(MANDOCERR_XR_ORDER
,
2180 mdoc
->parse
, n
->line
, n
->pos
,
2181 "%s(%s) after %s(%s)", name
,
2182 sec
, lastname
, lastsec
);
2183 else if (cmp
== 0 &&
2184 strcasecmp(lastname
, name
) > 0)
2185 mandoc_vmsg(MANDOCERR_XR_ORDER
,
2186 mdoc
->parse
, n
->line
, n
->pos
,
2187 "%s after %s", name
, lastname
);
2192 /* Process the following node. */
2197 if (n
->tok
== MDOC_Xr
) {
2201 if (n
->type
!= ROFFT_TEXT
)
2203 for (name
= n
->string
; *name
!= '\0'; name
++)
2204 if (isalpha((const unsigned char)*name
))
2206 lastpunct
= n
->string
;
2207 if (n
->next
== NULL
|| n
->next
->tok
== MDOC_Rs
)
2208 mandoc_vmsg(MANDOCERR_XR_PUNCT
, mdoc
->parse
,
2209 n
->line
, n
->pos
, "%s after %s(%s)",
2210 lastpunct
, lastname
, lastsec
);
2216 child_an(const struct roff_node
*n
)
2219 for (n
= n
->child
; n
!= NULL
; n
= n
->next
)
2220 if ((n
->tok
== MDOC_An
&& n
->child
!= NULL
) || child_an(n
))
2226 post_sh_authors(POST_ARGS
)
2229 if ( ! child_an(mdoc
->last
))
2230 mandoc_msg(MANDOCERR_AN_MISSING
, mdoc
->parse
,
2231 mdoc
->last
->line
, mdoc
->last
->pos
, NULL
);
2235 * Return an upper bound for the string distance (allowing
2236 * transpositions). Not a full Levenshtein implementation
2237 * because Levenshtein is quadratic in the string length
2238 * and this function is called for every standard name,
2239 * so the check for each custom name would be cubic.
2240 * The following crude heuristics is linear, resulting
2241 * in quadratic behaviour for checking one custom name,
2242 * which does not cause measurable slowdown.
2245 similar(const char *s1
, const char *s2
)
2247 const int maxdist
= 3;
2250 while (s1
[0] != '\0' && s2
[0] != '\0') {
2251 if (s1
[0] == s2
[0]) {
2256 if (++dist
> maxdist
)
2258 if (s1
[1] == s2
[1]) { /* replacement */
2261 } else if (s1
[0] == s2
[1] && s1
[1] == s2
[0]) {
2262 s1
+= 2; /* transposition */
2264 } else if (s1
[0] == s2
[1]) /* insertion */
2266 else if (s1
[1] == s2
[0]) /* deletion */
2271 dist
+= strlen(s1
) + strlen(s2
);
2272 return dist
> maxdist
? INT_MAX
: dist
;
2276 post_sh_head(POST_ARGS
)
2278 struct roff_node
*nch
;
2279 const char *goodsec
;
2280 const char *const *testsec
;
2285 * Process a new section. Sections are either "named" or
2286 * "custom". Custom sections are user-defined, while named ones
2287 * follow a conventional order and may only appear in certain
2291 sec
= mdoc
->last
->sec
;
2293 /* The NAME should be first. */
2295 if (sec
!= SEC_NAME
&& mdoc
->lastnamed
== SEC_NONE
)
2296 mandoc_vmsg(MANDOCERR_NAMESEC_FIRST
, mdoc
->parse
,
2297 mdoc
->last
->line
, mdoc
->last
->pos
, "Sh %s",
2298 sec
!= SEC_CUSTOM
? secnames
[sec
] :
2299 (nch
= mdoc
->last
->child
) == NULL
? "" :
2300 nch
->type
== ROFFT_TEXT
? nch
->string
:
2301 roff_name
[nch
->tok
]);
2303 /* The SYNOPSIS gets special attention in other areas. */
2305 if (sec
== SEC_SYNOPSIS
) {
2306 roff_setreg(mdoc
->roff
, "nS", 1, '=');
2307 mdoc
->flags
|= MDOC_SYNOPSIS
;
2309 roff_setreg(mdoc
->roff
, "nS", 0, '=');
2310 mdoc
->flags
&= ~MDOC_SYNOPSIS
;
2313 /* Mark our last section. */
2315 mdoc
->lastsec
= sec
;
2317 /* We don't care about custom sections after this. */
2319 if (sec
== SEC_CUSTOM
) {
2320 if ((nch
= mdoc
->last
->child
) == NULL
||
2321 nch
->type
!= ROFFT_TEXT
|| nch
->next
!= NULL
)
2325 for (testsec
= secnames
+ 1; *testsec
!= NULL
; testsec
++) {
2326 dist
= similar(nch
->string
, *testsec
);
2327 if (dist
< mindist
) {
2332 if (goodsec
!= NULL
)
2333 mandoc_vmsg(MANDOCERR_SEC_TYPO
, mdoc
->parse
,
2334 nch
->line
, nch
->pos
, "Sh %s instead of %s",
2335 nch
->string
, goodsec
);
2340 * Check whether our non-custom section is being repeated or is
2344 if (sec
== mdoc
->lastnamed
)
2345 mandoc_vmsg(MANDOCERR_SEC_REP
, mdoc
->parse
,
2346 mdoc
->last
->line
, mdoc
->last
->pos
,
2347 "Sh %s", secnames
[sec
]);
2349 if (sec
< mdoc
->lastnamed
)
2350 mandoc_vmsg(MANDOCERR_SEC_ORDER
, mdoc
->parse
,
2351 mdoc
->last
->line
, mdoc
->last
->pos
,
2352 "Sh %s", secnames
[sec
]);
2354 /* Mark the last named section. */
2356 mdoc
->lastnamed
= sec
;
2358 /* Check particular section/manual conventions. */
2360 if (mdoc
->meta
.msec
== NULL
)
2366 if (*mdoc
->meta
.msec
== '4')
2368 goodsec
= "2, 3, 4, 9";
2370 case SEC_RETURN_VALUES
:
2372 if (*mdoc
->meta
.msec
== '2')
2374 if (*mdoc
->meta
.msec
== '3')
2376 if (NULL
== goodsec
)
2377 goodsec
= "2, 3, 9";
2380 if (*mdoc
->meta
.msec
== '9')
2382 if (NULL
== goodsec
)
2384 mandoc_vmsg(MANDOCERR_SEC_MSEC
, mdoc
->parse
,
2385 mdoc
->last
->line
, mdoc
->last
->pos
,
2386 "Sh %s for %s only", secnames
[sec
], goodsec
);
2396 struct roff_node
*n
, *nch
;
2400 if (nch
->next
== NULL
) {
2401 mandoc_vmsg(MANDOCERR_XR_NOSEC
, mdoc
->parse
,
2402 n
->line
, n
->pos
, "Xr %s", nch
->string
);
2404 assert(nch
->next
== n
->last
);
2405 if(mandoc_xr_add(nch
->next
->string
, nch
->string
,
2406 nch
->line
, nch
->pos
))
2407 mandoc_vmsg(MANDOCERR_XR_SELF
, mdoc
->parse
,
2408 nch
->line
, nch
->pos
, "Xr %s %s",
2409 nch
->string
, nch
->next
->string
);
2411 post_delim_nb(mdoc
);
2415 post_ignpar(POST_ARGS
)
2417 struct roff_node
*np
;
2419 switch (mdoc
->last
->type
) {
2433 if ((np
= mdoc
->last
->child
) != NULL
)
2434 if (np
->tok
== MDOC_Pp
|| np
->tok
== MDOC_Lp
) {
2435 mandoc_vmsg(MANDOCERR_PAR_SKIP
,
2436 mdoc
->parse
, np
->line
, np
->pos
,
2437 "%s after %s", roff_name
[np
->tok
],
2438 roff_name
[mdoc
->last
->tok
]);
2439 roff_node_delete(mdoc
, np
);
2442 if ((np
= mdoc
->last
->last
) != NULL
)
2443 if (np
->tok
== MDOC_Pp
|| np
->tok
== MDOC_Lp
) {
2444 mandoc_vmsg(MANDOCERR_PAR_SKIP
, mdoc
->parse
,
2445 np
->line
, np
->pos
, "%s at the end of %s",
2447 roff_name
[mdoc
->last
->tok
]);
2448 roff_node_delete(mdoc
, np
);
2453 post_prevpar(POST_ARGS
)
2455 struct roff_node
*n
;
2458 if (NULL
== n
->prev
)
2460 if (n
->type
!= ROFFT_ELEM
&& n
->type
!= ROFFT_BLOCK
)
2464 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
2465 * block: `Lp', `Pp', or non-compact `Bd' or `Bl'.
2468 if (n
->prev
->tok
!= MDOC_Pp
&&
2469 n
->prev
->tok
!= MDOC_Lp
&&
2470 n
->prev
->tok
!= ROFF_br
)
2472 if (n
->tok
== MDOC_Bl
&& n
->norm
->Bl
.comp
)
2474 if (n
->tok
== MDOC_Bd
&& n
->norm
->Bd
.comp
)
2476 if (n
->tok
== MDOC_It
&& n
->parent
->norm
->Bl
.comp
)
2479 mandoc_vmsg(MANDOCERR_PAR_SKIP
, mdoc
->parse
,
2480 n
->prev
->line
, n
->prev
->pos
, "%s before %s",
2481 roff_name
[n
->prev
->tok
], roff_name
[n
->tok
]);
2482 roff_node_delete(mdoc
, n
->prev
);
2488 struct roff_node
*np
;
2491 if (np
->tok
!= ROFF_br
&& np
->tok
!= ROFF_sp
)
2494 if (np
->tok
== ROFF_sp
) {
2495 if (np
->child
!= NULL
&& np
->child
->next
!= NULL
)
2496 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, mdoc
->parse
,
2497 np
->child
->next
->line
, np
->child
->next
->pos
,
2498 "sp ... %s", np
->child
->next
->string
);
2499 } else if (np
->child
!= NULL
)
2500 mandoc_vmsg(MANDOCERR_ARG_SKIP
,
2501 mdoc
->parse
, np
->line
, np
->pos
, "%s %s",
2502 roff_name
[np
->tok
], np
->child
->string
);
2504 if ((np
= mdoc
->last
->prev
) == NULL
) {
2505 np
= mdoc
->last
->parent
;
2506 if (np
->tok
!= MDOC_Sh
&& np
->tok
!= MDOC_Ss
)
2508 } else if (np
->tok
!= MDOC_Pp
&& np
->tok
!= MDOC_Lp
&&
2509 (mdoc
->last
->tok
!= ROFF_br
||
2510 (np
->tok
!= ROFF_sp
&& np
->tok
!= ROFF_br
)))
2513 mandoc_vmsg(MANDOCERR_PAR_SKIP
, mdoc
->parse
,
2514 mdoc
->last
->line
, mdoc
->last
->pos
, "%s after %s",
2515 roff_name
[mdoc
->last
->tok
], roff_name
[np
->tok
]);
2516 roff_node_delete(mdoc
, mdoc
->last
);
2522 struct roff_node
*n
;
2526 n
->flags
|= NODE_NOPRT
;
2528 if (mdoc
->meta
.date
!= NULL
) {
2529 mandoc_msg(MANDOCERR_PROLOG_REP
, mdoc
->parse
,
2530 n
->line
, n
->pos
, "Dd");
2531 free(mdoc
->meta
.date
);
2532 } else if (mdoc
->flags
& MDOC_PBODY
)
2533 mandoc_msg(MANDOCERR_PROLOG_LATE
, mdoc
->parse
,
2534 n
->line
, n
->pos
, "Dd");
2535 else if (mdoc
->meta
.title
!= NULL
)
2536 mandoc_msg(MANDOCERR_PROLOG_ORDER
, mdoc
->parse
,
2537 n
->line
, n
->pos
, "Dd after Dt");
2538 else if (mdoc
->meta
.os
!= NULL
)
2539 mandoc_msg(MANDOCERR_PROLOG_ORDER
, mdoc
->parse
,
2540 n
->line
, n
->pos
, "Dd after Os");
2542 if (n
->child
== NULL
|| n
->child
->string
[0] == '\0') {
2543 mdoc
->meta
.date
= mdoc
->quick
? mandoc_strdup("") :
2544 mandoc_normdate(mdoc
, NULL
, n
->line
, n
->pos
);
2549 deroff(&datestr
, n
);
2551 mdoc
->meta
.date
= datestr
;
2553 mdoc
->meta
.date
= mandoc_normdate(mdoc
,
2554 datestr
, n
->line
, n
->pos
);
2562 struct roff_node
*nn
, *n
;
2567 n
->flags
|= NODE_NOPRT
;
2569 if (mdoc
->flags
& MDOC_PBODY
) {
2570 mandoc_msg(MANDOCERR_DT_LATE
, mdoc
->parse
,
2571 n
->line
, n
->pos
, "Dt");
2575 if (mdoc
->meta
.title
!= NULL
)
2576 mandoc_msg(MANDOCERR_PROLOG_REP
, mdoc
->parse
,
2577 n
->line
, n
->pos
, "Dt");
2578 else if (mdoc
->meta
.os
!= NULL
)
2579 mandoc_msg(MANDOCERR_PROLOG_ORDER
, mdoc
->parse
,
2580 n
->line
, n
->pos
, "Dt after Os");
2582 free(mdoc
->meta
.title
);
2583 free(mdoc
->meta
.msec
);
2584 free(mdoc
->meta
.vol
);
2585 free(mdoc
->meta
.arch
);
2587 mdoc
->meta
.title
= NULL
;
2588 mdoc
->meta
.msec
= NULL
;
2589 mdoc
->meta
.vol
= NULL
;
2590 mdoc
->meta
.arch
= NULL
;
2592 /* Mandatory first argument: title. */
2595 if (nn
== NULL
|| *nn
->string
== '\0') {
2596 mandoc_msg(MANDOCERR_DT_NOTITLE
,
2597 mdoc
->parse
, n
->line
, n
->pos
, "Dt");
2598 mdoc
->meta
.title
= mandoc_strdup("UNTITLED");
2600 mdoc
->meta
.title
= mandoc_strdup(nn
->string
);
2602 /* Check that all characters are uppercase. */
2604 for (p
= nn
->string
; *p
!= '\0'; p
++)
2605 if (islower((unsigned char)*p
)) {
2606 mandoc_vmsg(MANDOCERR_TITLE_CASE
,
2607 mdoc
->parse
, nn
->line
,
2608 nn
->pos
+ (p
- nn
->string
),
2609 "Dt %s", nn
->string
);
2614 /* Mandatory second argument: section. */
2620 mandoc_vmsg(MANDOCERR_MSEC_MISSING
,
2621 mdoc
->parse
, n
->line
, n
->pos
,
2622 "Dt %s", mdoc
->meta
.title
);
2623 mdoc
->meta
.vol
= mandoc_strdup("LOCAL");
2624 return; /* msec and arch remain NULL. */
2627 mdoc
->meta
.msec
= mandoc_strdup(nn
->string
);
2629 /* Infer volume title from section number. */
2631 cp
= mandoc_a2msec(nn
->string
);
2633 mandoc_vmsg(MANDOCERR_MSEC_BAD
, mdoc
->parse
,
2634 nn
->line
, nn
->pos
, "Dt ... %s", nn
->string
);
2635 mdoc
->meta
.vol
= mandoc_strdup(nn
->string
);
2637 mdoc
->meta
.vol
= mandoc_strdup(cp
);
2639 /* Optional third argument: architecture. */
2641 if ((nn
= nn
->next
) == NULL
)
2644 for (p
= nn
->string
; *p
!= '\0'; p
++)
2645 *p
= tolower((unsigned char)*p
);
2646 mdoc
->meta
.arch
= mandoc_strdup(nn
->string
);
2648 /* Ignore fourth and later arguments. */
2650 if ((nn
= nn
->next
) != NULL
)
2651 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, mdoc
->parse
,
2652 nn
->line
, nn
->pos
, "Dt ... %s", nn
->string
);
2658 struct roff_node
*n
, *nch
;
2661 post_delim_nb(mdoc
);
2667 macro
= !strcmp(nch
->string
, "Open") ? "Ox" :
2668 !strcmp(nch
->string
, "Net") ? "Nx" :
2669 !strcmp(nch
->string
, "Free") ? "Fx" :
2670 !strcmp(nch
->string
, "DragonFly") ? "Dx" : NULL
;
2672 mandoc_msg(MANDOCERR_BX
, mdoc
->parse
,
2673 n
->line
, n
->pos
, macro
);
2676 mdoc
->next
= ROFF_NEXT_SIBLING
;
2677 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Ns
);
2678 mdoc
->last
->flags
|= NODE_NOSRC
;
2679 mdoc
->next
= ROFF_NEXT_SIBLING
;
2681 mdoc
->next
= ROFF_NEXT_CHILD
;
2682 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "BSD");
2683 mdoc
->last
->flags
|= NODE_NOSRC
;
2690 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Ns
);
2691 mdoc
->last
->flags
|= NODE_NOSRC
;
2692 mdoc
->next
= ROFF_NEXT_SIBLING
;
2693 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "-");
2694 mdoc
->last
->flags
|= NODE_NOSRC
;
2695 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Ns
);
2696 mdoc
->last
->flags
|= NODE_NOSRC
;
2700 * Make `Bx's second argument always start with an uppercase
2701 * letter. Groff checks if it's an "accepted" term, but we just
2702 * uppercase blindly.
2705 *nch
->string
= (char)toupper((unsigned char)*nch
->string
);
2712 struct utsname utsname
;
2713 static char *defbuf
;
2715 struct roff_node
*n
;
2718 n
->flags
|= NODE_NOPRT
;
2720 if (mdoc
->meta
.os
!= NULL
)
2721 mandoc_msg(MANDOCERR_PROLOG_REP
, mdoc
->parse
,
2722 n
->line
, n
->pos
, "Os");
2723 else if (mdoc
->flags
& MDOC_PBODY
)
2724 mandoc_msg(MANDOCERR_PROLOG_LATE
, mdoc
->parse
,
2725 n
->line
, n
->pos
, "Os");
2730 * Set the operating system by way of the `Os' macro.
2731 * The order of precedence is:
2732 * 1. the argument of the `Os' macro, unless empty
2733 * 2. the -Ios=foo command line argument, if provided
2734 * 3. -DOSNAME="\"foo\"", if provided during compilation
2735 * 4. "sysname release" from uname(3)
2738 free(mdoc
->meta
.os
);
2739 mdoc
->meta
.os
= NULL
;
2740 deroff(&mdoc
->meta
.os
, n
);
2744 if (mdoc
->os_s
!= NULL
) {
2745 mdoc
->meta
.os
= mandoc_strdup(mdoc
->os_s
);
2750 mdoc
->meta
.os
= mandoc_strdup(OSNAME
);
2752 if (defbuf
== NULL
) {
2753 if (uname(&utsname
) == -1) {
2754 mandoc_msg(MANDOCERR_OS_UNAME
, mdoc
->parse
,
2755 n
->line
, n
->pos
, "Os");
2756 defbuf
= mandoc_strdup("UNKNOWN");
2758 mandoc_asprintf(&defbuf
, "%s %s",
2759 utsname
.sysname
, utsname
.release
);
2761 mdoc
->meta
.os
= mandoc_strdup(defbuf
);
2765 if (mdoc
->meta
.os_e
== MANDOC_OS_OTHER
) {
2766 if (strstr(mdoc
->meta
.os
, "OpenBSD") != NULL
)
2767 mdoc
->meta
.os_e
= MANDOC_OS_OPENBSD
;
2768 else if (strstr(mdoc
->meta
.os
, "NetBSD") != NULL
)
2769 mdoc
->meta
.os_e
= MANDOC_OS_NETBSD
;
2773 * This is the earliest point where we can check
2774 * Mdocdate conventions because we don't know
2775 * the operating system earlier.
2778 if (n
->child
!= NULL
)
2779 mandoc_vmsg(MANDOCERR_OS_ARG
, mdoc
->parse
,
2780 n
->child
->line
, n
->child
->pos
,
2781 "Os %s (%s)", n
->child
->string
,
2782 mdoc
->meta
.os_e
== MANDOC_OS_OPENBSD
?
2783 "OpenBSD" : "NetBSD");
2785 while (n
->tok
!= MDOC_Dd
)
2786 if ((n
= n
->prev
) == NULL
)
2788 if ((n
= n
->child
) == NULL
)
2790 if (strncmp(n
->string
, "$" "Mdocdate", 9)) {
2791 if (mdoc
->meta
.os_e
== MANDOC_OS_OPENBSD
)
2792 mandoc_vmsg(MANDOCERR_MDOCDATE_MISSING
,
2793 mdoc
->parse
, n
->line
, n
->pos
,
2794 "Dd %s (OpenBSD)", n
->string
);
2796 if (mdoc
->meta
.os_e
== MANDOC_OS_NETBSD
)
2797 mandoc_vmsg(MANDOCERR_MDOCDATE
,
2798 mdoc
->parse
, n
->line
, n
->pos
,
2799 "Dd %s (NetBSD)", n
->string
);
2804 mdoc_a2sec(const char *p
)
2808 for (i
= 0; i
< (int)SEC__MAX
; i
++)
2809 if (secnames
[i
] && 0 == strcmp(p
, secnames
[i
]))
2810 return (enum roff_sec
)i
;
2816 macro2len(enum roff_tok macro
)