1 /* $Id: mdoc_validate.c,v 1.362 2018/08/17 20:33:38 schwarze Exp $ */
3 * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2010-2018 Ingo Schwarze <schwarze@openbsd.org>
5 * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org>
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 #include <sys/types.h>
23 #include <sys/utsname.h>
34 #include "mandoc_aux.h"
36 #include "mandoc_xr.h"
39 #include "libmandoc.h"
43 /* FIXME: .Bl -diag can't have non-text children in HEAD. */
45 #define POST_ARGS struct roff_man *mdoc
53 typedef void (*v_post
)(POST_ARGS
);
55 static int build_list(struct roff_man
*, int);
56 static void check_argv(struct roff_man
*,
57 struct roff_node
*, struct mdoc_argv
*);
58 static void check_args(struct roff_man
*, struct roff_node
*);
59 static void check_text(struct roff_man
*, int, int, char *);
60 static void check_text_em(struct roff_man
*, int, int, char *);
61 static void check_toptext(struct roff_man
*, int, int, const char *);
62 static int child_an(const struct roff_node
*);
63 static size_t macro2len(enum roff_tok
);
64 static void rewrite_macro2len(struct roff_man
*, char **);
65 static int similar(const char *, const char *);
67 static void post_an(POST_ARGS
);
68 static void post_an_norm(POST_ARGS
);
69 static void post_at(POST_ARGS
);
70 static void post_bd(POST_ARGS
);
71 static void post_bf(POST_ARGS
);
72 static void post_bk(POST_ARGS
);
73 static void post_bl(POST_ARGS
);
74 static void post_bl_block(POST_ARGS
);
75 static void post_bl_head(POST_ARGS
);
76 static void post_bl_norm(POST_ARGS
);
77 static void post_bx(POST_ARGS
);
78 static void post_defaults(POST_ARGS
);
79 static void post_display(POST_ARGS
);
80 static void post_dd(POST_ARGS
);
81 static void post_delim(POST_ARGS
);
82 static void post_delim_nb(POST_ARGS
);
83 static void post_dt(POST_ARGS
);
84 static void post_en(POST_ARGS
);
85 static void post_es(POST_ARGS
);
86 static void post_eoln(POST_ARGS
);
87 static void post_ex(POST_ARGS
);
88 static void post_fa(POST_ARGS
);
89 static void post_fn(POST_ARGS
);
90 static void post_fname(POST_ARGS
);
91 static void post_fo(POST_ARGS
);
92 static void post_hyph(POST_ARGS
);
93 static void post_ignpar(POST_ARGS
);
94 static void post_it(POST_ARGS
);
95 static void post_lb(POST_ARGS
);
96 static void post_nd(POST_ARGS
);
97 static void post_nm(POST_ARGS
);
98 static void post_ns(POST_ARGS
);
99 static void post_obsolete(POST_ARGS
);
100 static void post_os(POST_ARGS
);
101 static void post_par(POST_ARGS
);
102 static void post_prevpar(POST_ARGS
);
103 static void post_root(POST_ARGS
);
104 static void post_rs(POST_ARGS
);
105 static void post_rv(POST_ARGS
);
106 static void post_sh(POST_ARGS
);
107 static void post_sh_head(POST_ARGS
);
108 static void post_sh_name(POST_ARGS
);
109 static void post_sh_see_also(POST_ARGS
);
110 static void post_sh_authors(POST_ARGS
);
111 static void post_sm(POST_ARGS
);
112 static void post_st(POST_ARGS
);
113 static void post_std(POST_ARGS
);
114 static void post_sx(POST_ARGS
);
115 static void post_useless(POST_ARGS
);
116 static void post_xr(POST_ARGS
);
117 static void post_xx(POST_ARGS
);
119 static const v_post mdoc_valids
[MDOC_MAX
- MDOC_Dd
] = {
124 post_ignpar
, /* Ss */
126 post_display
, /* D1 */
127 post_display
, /* Dl */
128 post_display
, /* Bd */
133 post_delim_nb
, /* Ad */
136 post_defaults
, /* Ar */
138 post_delim_nb
, /* Cm */
139 post_delim_nb
, /* Dv */
140 post_delim_nb
, /* Er */
141 post_delim_nb
, /* Ev */
145 post_delim_nb
, /* Fl */
147 post_delim_nb
, /* Ft */
148 post_delim_nb
, /* Ic */
149 post_delim_nb
, /* In */
150 post_defaults
, /* Li */
153 post_delim_nb
, /* Op */
154 post_obsolete
, /* Ot */
155 post_defaults
, /* Pa */
158 post_delim_nb
, /* Va */
159 post_delim_nb
, /* Vt */
162 post_hyph
, /* %B */ /* FIXME: can be used outside Rs/Re. */
170 post_hyph
, /* %T */ /* FIXME: can be used outside Rs/Re. */
174 post_delim_nb
, /* Aq */
182 post_obsolete
, /* Db */
188 post_delim_nb
, /* Em */
191 post_delim_nb
, /* Ms */
199 post_delim_nb
, /* Pq */
201 post_delim_nb
, /* Ql */
203 post_delim_nb
, /* Qq */
208 post_delim_nb
, /* Sq */
211 post_delim_nb
, /* Sy */
212 post_useless
, /* Tn */
223 post_obsolete
, /* Hf */
224 post_obsolete
, /* Fr */
228 post_delim_nb
, /* Lk */
229 post_defaults
, /* Mt */
230 post_delim_nb
, /* Brq */
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
)
291 struct roff_node
*n
, *np
;
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
;
309 if (n
->sec
!= SEC_SYNOPSIS
||
310 (np
->tok
!= MDOC_Cd
&& np
->tok
!= MDOC_Fd
))
311 check_text(mdoc
, n
->line
, n
->pos
, n
->string
);
312 if (np
->tok
!= MDOC_Ql
&& np
->tok
!= MDOC_Dl
&&
313 (np
->tok
!= MDOC_Bd
||
314 (mdoc
->flags
& MDOC_LITERAL
) == 0) &&
315 (np
->tok
!= MDOC_It
|| np
->type
!= ROFFT_HEAD
||
316 np
->parent
->parent
->norm
->Bl
.type
!= LIST_diag
))
317 check_text_em(mdoc
, n
->line
, n
->pos
, n
->string
);
318 if (np
->tok
== MDOC_It
|| (np
->type
== ROFFT_BODY
&&
319 (np
->tok
== MDOC_Sh
|| np
->tok
== MDOC_Ss
)))
320 check_toptext(mdoc
, n
->line
, n
->pos
, n
->string
);
330 check_args(mdoc
, mdoc
->last
);
333 * Closing delimiters are not special at the
334 * beginning of a block, opening delimiters
335 * are not special at the end.
338 if (n
->child
!= NULL
)
339 n
->child
->flags
&= ~NODE_DELIMC
;
341 n
->last
->flags
&= ~NODE_DELIMO
;
343 /* Call the macro's postprocessor. */
345 if (n
->tok
< ROFF_MAX
) {
358 assert(n
->tok
>= MDOC_Dd
&& n
->tok
< MDOC_MAX
);
359 p
= mdoc_valids
+ (n
->tok
- MDOC_Dd
);
369 check_args(struct roff_man
*mdoc
, struct roff_node
*n
)
376 assert(n
->args
->argc
);
377 for (i
= 0; i
< (int)n
->args
->argc
; i
++)
378 check_argv(mdoc
, n
, &n
->args
->argv
[i
]);
382 check_argv(struct roff_man
*mdoc
, struct roff_node
*n
, struct mdoc_argv
*v
)
386 for (i
= 0; i
< (int)v
->sz
; i
++)
387 check_text(mdoc
, v
->line
, v
->pos
, v
->value
[i
]);
391 check_text(struct roff_man
*mdoc
, int ln
, int pos
, char *p
)
395 if (MDOC_LITERAL
& mdoc
->flags
)
398 for (cp
= p
; NULL
!= (p
= strchr(p
, '\t')); p
++)
399 mandoc_msg(MANDOCERR_FI_TAB
, mdoc
->parse
,
400 ln
, pos
+ (int)(p
- cp
), NULL
);
404 check_text_em(struct roff_man
*mdoc
, int ln
, int pos
, char *p
)
406 const struct roff_node
*np
, *nn
;
409 np
= mdoc
->last
->prev
;
410 nn
= mdoc
->last
->next
;
412 /* Look for em-dashes wrongly encoded as "--". */
414 for (cp
= p
; *cp
!= '\0'; cp
++) {
415 if (cp
[0] != '-' || cp
[1] != '-')
419 /* Skip input sequences of more than two '-'. */
427 /* Skip "--" directly attached to something else. */
429 if ((cp
- p
> 1 && cp
[-2] != ' ') ||
430 (cp
[1] != '\0' && cp
[1] != ' '))
433 /* Require a letter right before or right afterwards. */
436 isalpha((unsigned char)cp
[-3]) :
438 np
->type
== ROFFT_TEXT
&&
439 *np
->string
!= '\0' &&
440 isalpha((unsigned char)np
->string
[
441 strlen(np
->string
) - 1])) ||
442 (cp
[1] != '\0' && cp
[2] != '\0' ?
443 isalpha((unsigned char)cp
[2]) :
445 nn
->type
== ROFFT_TEXT
&&
446 isalpha((unsigned char)*nn
->string
))) {
447 mandoc_msg(MANDOCERR_DASHDASH
, mdoc
->parse
,
448 ln
, pos
+ (int)(cp
- p
) - 1, NULL
);
455 check_toptext(struct roff_man
*mdoc
, int ln
, int pos
, const char *p
)
457 const char *cp
, *cpr
;
462 if ((cp
= strstr(p
, "OpenBSD")) != NULL
)
463 mandoc_msg(MANDOCERR_BX
, mdoc
->parse
,
464 ln
, pos
+ (cp
- p
), "Ox");
465 if ((cp
= strstr(p
, "NetBSD")) != NULL
)
466 mandoc_msg(MANDOCERR_BX
, mdoc
->parse
,
467 ln
, pos
+ (cp
- p
), "Nx");
468 if ((cp
= strstr(p
, "FreeBSD")) != NULL
)
469 mandoc_msg(MANDOCERR_BX
, mdoc
->parse
,
470 ln
, pos
+ (cp
- p
), "Fx");
471 if ((cp
= strstr(p
, "DragonFly")) != NULL
)
472 mandoc_msg(MANDOCERR_BX
, mdoc
->parse
,
473 ln
, pos
+ (cp
- p
), "Dx");
476 while ((cp
= strstr(cp
+ 1, "()")) != NULL
) {
477 for (cpr
= cp
- 1; cpr
>= p
; cpr
--)
478 if (*cpr
!= '_' && !isalnum((unsigned char)*cpr
))
480 if ((cpr
< p
|| *cpr
== ' ') && cpr
+ 1 < cp
) {
482 mandoc_vmsg(MANDOCERR_FUNC
, mdoc
->parse
,
484 "%.*s()", (int)(cp
- cpr
), cpr
);
490 post_delim(POST_ARGS
)
492 const struct roff_node
*nch
;
497 tok
= mdoc
->last
->tok
;
498 nch
= mdoc
->last
->last
;
499 if (nch
== NULL
|| nch
->type
!= ROFFT_TEXT
)
501 lc
= strchr(nch
->string
, '\0') - 1;
502 if (lc
< nch
->string
)
504 delim
= mdoc_isdelim(lc
);
505 if (delim
== DELIM_NONE
|| delim
== DELIM_OPEN
)
507 if (*lc
== ')' && (tok
== MDOC_Nd
|| tok
== MDOC_Sh
||
508 tok
== MDOC_Ss
|| tok
== MDOC_Fo
))
511 mandoc_vmsg(MANDOCERR_DELIM
, mdoc
->parse
,
512 nch
->line
, nch
->pos
+ (lc
- nch
->string
),
513 "%s%s %s", roff_name
[tok
],
514 nch
== mdoc
->last
->child
? "" : " ...", nch
->string
);
518 post_delim_nb(POST_ARGS
)
520 const struct roff_node
*nch
;
527 * Find candidates: at least two bytes,
528 * the last one a closing or middle delimiter.
531 tok
= mdoc
->last
->tok
;
532 nch
= mdoc
->last
->last
;
533 if (nch
== NULL
|| nch
->type
!= ROFFT_TEXT
)
535 lc
= strchr(nch
->string
, '\0') - 1;
536 if (lc
<= nch
->string
)
538 delim
= mdoc_isdelim(lc
);
539 if (delim
== DELIM_NONE
|| delim
== DELIM_OPEN
)
543 * Reduce false positives by allowing various cases.
546 /* Escaped delimiters. */
547 if (lc
> nch
->string
+ 1 && lc
[-2] == '\\' &&
548 (lc
[-1] == '&' || lc
[-1] == 'e'))
551 /* Specific byte sequences. */
554 for (cp
= lc
; cp
>= nch
->string
; cp
--)
559 if (lc
> nch
->string
+ 1 && lc
[-2] == '.' && lc
[-1] == '.')
573 for (cp
= lc
; cp
>= nch
->string
; cp
--)
578 if (lc
== nch
->string
+ 1 && lc
[-1] == '|')
584 /* Exactly two non-alphanumeric bytes. */
585 if (lc
== nch
->string
+ 1 && !isalnum((unsigned char)lc
[-1]))
588 /* At least three alphabetic words with a sentence ending. */
589 if (strchr("!.:?", *lc
) != NULL
&& (tok
== MDOC_Em
||
590 tok
== MDOC_Li
|| tok
== MDOC_Pq
|| tok
== MDOC_Sy
)) {
592 for (cp
= lc
- 1; cp
>= nch
->string
; cp
--) {
595 if (cp
> nch
->string
&& cp
[-1] == ',')
597 } else if (isalpha((unsigned int)*cp
)) {
605 mandoc_vmsg(MANDOCERR_DELIM_NB
, mdoc
->parse
,
606 nch
->line
, nch
->pos
+ (lc
- nch
->string
),
607 "%s%s %s", roff_name
[tok
],
608 nch
== mdoc
->last
->child
? "" : " ...", nch
->string
);
612 post_bl_norm(POST_ARGS
)
615 struct mdoc_argv
*argv
, *wa
;
617 enum mdocargt mdoclt
;
620 n
= mdoc
->last
->parent
;
621 n
->norm
->Bl
.type
= LIST__NONE
;
624 * First figure out which kind of list to use: bind ourselves to
625 * the first mentioned list type and warn about any remaining
626 * ones. If we find no list type, we default to LIST_item.
629 wa
= (n
->args
== NULL
) ? NULL
: n
->args
->argv
;
630 mdoclt
= MDOC_ARG_MAX
;
631 for (i
= 0; n
->args
&& i
< (int)n
->args
->argc
; i
++) {
632 argv
= n
->args
->argv
+ i
;
635 /* Set list types. */
669 /* Set list arguments. */
671 if (n
->norm
->Bl
.comp
)
672 mandoc_msg(MANDOCERR_ARG_REP
,
673 mdoc
->parse
, argv
->line
,
674 argv
->pos
, "Bl -compact");
675 n
->norm
->Bl
.comp
= 1;
680 mandoc_msg(MANDOCERR_ARG_EMPTY
,
681 mdoc
->parse
, argv
->line
,
682 argv
->pos
, "Bl -width");
683 n
->norm
->Bl
.width
= "0n";
686 if (NULL
!= n
->norm
->Bl
.width
)
687 mandoc_vmsg(MANDOCERR_ARG_REP
,
688 mdoc
->parse
, argv
->line
,
689 argv
->pos
, "Bl -width %s",
691 rewrite_macro2len(mdoc
, argv
->value
);
692 n
->norm
->Bl
.width
= argv
->value
[0];
696 mandoc_msg(MANDOCERR_ARG_EMPTY
,
697 mdoc
->parse
, argv
->line
,
698 argv
->pos
, "Bl -offset");
701 if (NULL
!= n
->norm
->Bl
.offs
)
702 mandoc_vmsg(MANDOCERR_ARG_REP
,
703 mdoc
->parse
, argv
->line
,
704 argv
->pos
, "Bl -offset %s",
706 rewrite_macro2len(mdoc
, argv
->value
);
707 n
->norm
->Bl
.offs
= argv
->value
[0];
712 if (LIST__NONE
== lt
)
716 /* Check: multiple list types. */
718 if (LIST__NONE
!= n
->norm
->Bl
.type
) {
719 mandoc_vmsg(MANDOCERR_BL_REP
,
720 mdoc
->parse
, n
->line
, n
->pos
,
721 "Bl -%s", mdoc_argnames
[argv
->arg
]);
725 /* The list type should come first. */
727 if (n
->norm
->Bl
.width
||
730 mandoc_vmsg(MANDOCERR_BL_LATETYPE
,
731 mdoc
->parse
, n
->line
, n
->pos
, "Bl -%s",
732 mdoc_argnames
[n
->args
->argv
[0].arg
]);
734 n
->norm
->Bl
.type
= lt
;
735 if (LIST_column
== lt
) {
736 n
->norm
->Bl
.ncols
= argv
->sz
;
737 n
->norm
->Bl
.cols
= (void *)argv
->value
;
741 /* Allow lists to default to LIST_item. */
743 if (LIST__NONE
== n
->norm
->Bl
.type
) {
744 mandoc_msg(MANDOCERR_BL_NOTYPE
, mdoc
->parse
,
745 n
->line
, n
->pos
, "Bl");
746 n
->norm
->Bl
.type
= LIST_item
;
751 * Validate the width field. Some list types don't need width
752 * types and should be warned about them. Others should have it
753 * and must also be warned. Yet others have a default and need
757 switch (n
->norm
->Bl
.type
) {
759 if (n
->norm
->Bl
.width
== NULL
)
760 mandoc_msg(MANDOCERR_BL_NOWIDTH
, mdoc
->parse
,
761 n
->line
, n
->pos
, "Bl -tag");
768 if (n
->norm
->Bl
.width
!= NULL
)
769 mandoc_vmsg(MANDOCERR_BL_SKIPW
, mdoc
->parse
,
770 wa
->line
, wa
->pos
, "Bl -%s",
771 mdoc_argnames
[mdoclt
]);
772 n
->norm
->Bl
.width
= NULL
;
777 if (n
->norm
->Bl
.width
== NULL
)
778 n
->norm
->Bl
.width
= "2n";
781 if (n
->norm
->Bl
.width
== NULL
)
782 n
->norm
->Bl
.width
= "3n";
793 struct mdoc_argv
*argv
;
798 for (i
= 0; n
->args
&& i
< (int)n
->args
->argc
; i
++) {
799 argv
= n
->args
->argv
+ i
;
819 mandoc_msg(MANDOCERR_BD_FILE
, mdoc
->parse
,
820 n
->line
, n
->pos
, NULL
);
824 mandoc_msg(MANDOCERR_ARG_EMPTY
,
825 mdoc
->parse
, argv
->line
,
826 argv
->pos
, "Bd -offset");
829 if (NULL
!= n
->norm
->Bd
.offs
)
830 mandoc_vmsg(MANDOCERR_ARG_REP
,
831 mdoc
->parse
, argv
->line
,
832 argv
->pos
, "Bd -offset %s",
834 rewrite_macro2len(mdoc
, argv
->value
);
835 n
->norm
->Bd
.offs
= argv
->value
[0];
838 if (n
->norm
->Bd
.comp
)
839 mandoc_msg(MANDOCERR_ARG_REP
,
840 mdoc
->parse
, argv
->line
,
841 argv
->pos
, "Bd -compact");
842 n
->norm
->Bd
.comp
= 1;
847 if (DISP__NONE
== dt
)
850 if (DISP__NONE
== n
->norm
->Bd
.type
)
851 n
->norm
->Bd
.type
= dt
;
853 mandoc_vmsg(MANDOCERR_BD_REP
,
854 mdoc
->parse
, n
->line
, n
->pos
,
855 "Bd -%s", mdoc_argnames
[argv
->arg
]);
858 if (DISP__NONE
== n
->norm
->Bd
.type
) {
859 mandoc_msg(MANDOCERR_BD_NOTYPE
, mdoc
->parse
,
860 n
->line
, n
->pos
, "Bd");
861 n
->norm
->Bd
.type
= DISP_ragged
;
866 * Stand-alone line macros.
870 post_an_norm(POST_ARGS
)
873 struct mdoc_argv
*argv
;
880 for (i
= 1; i
< n
->args
->argc
; i
++) {
881 argv
= n
->args
->argv
+ i
;
882 mandoc_vmsg(MANDOCERR_AN_REP
,
883 mdoc
->parse
, argv
->line
, argv
->pos
,
884 "An -%s", mdoc_argnames
[argv
->arg
]);
887 argv
= n
->args
->argv
;
888 if (argv
->arg
== MDOC_Split
)
889 n
->norm
->An
.auth
= AUTH_split
;
890 else if (argv
->arg
== MDOC_Nosplit
)
891 n
->norm
->An
.auth
= AUTH_nosplit
;
903 if (n
->child
!= NULL
)
904 mandoc_vmsg(MANDOCERR_ARG_SKIP
, mdoc
->parse
, n
->line
,
905 n
->pos
, "%s %s", roff_name
[n
->tok
], n
->child
->string
);
907 while (n
->child
!= NULL
)
908 roff_node_delete(mdoc
, n
->child
);
910 roff_word_alloc(mdoc
, n
->line
, n
->pos
, n
->tok
== MDOC_Bt
?
911 "is currently in beta test." : "currently under development.");
912 mdoc
->last
->flags
|= NODE_EOS
| NODE_NOSRC
;
917 build_list(struct roff_man
*mdoc
, int tok
)
922 n
= mdoc
->last
->next
;
923 for (ic
= 1;; ic
++) {
924 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, tok
);
925 mdoc
->last
->flags
|= NODE_NOSRC
;
926 mdoc_node_relink(mdoc
, n
);
927 n
= mdoc
->last
= mdoc
->last
->parent
;
928 mdoc
->next
= ROFF_NEXT_SIBLING
;
931 if (ic
> 1 || n
->next
->next
!= NULL
) {
932 roff_word_alloc(mdoc
, n
->line
, n
->pos
, ",");
933 mdoc
->last
->flags
|= NODE_DELIMC
| NODE_NOSRC
;
935 n
= mdoc
->last
->next
;
936 if (n
->next
== NULL
) {
937 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "and");
938 mdoc
->last
->flags
|= NODE_NOSRC
;
952 mdoc
->next
= ROFF_NEXT_CHILD
;
953 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "The");
954 mdoc
->last
->flags
|= NODE_NOSRC
;
956 if (mdoc
->last
->next
!= NULL
)
957 ic
= build_list(mdoc
, MDOC_Nm
);
958 else if (mdoc
->meta
.name
!= NULL
) {
959 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Nm
);
960 mdoc
->last
->flags
|= NODE_NOSRC
;
961 roff_word_alloc(mdoc
, n
->line
, n
->pos
, mdoc
->meta
.name
);
962 mdoc
->last
->flags
|= NODE_NOSRC
;
963 mdoc
->last
= mdoc
->last
->parent
;
964 mdoc
->next
= ROFF_NEXT_SIBLING
;
967 mandoc_msg(MANDOCERR_EX_NONAME
, mdoc
->parse
,
968 n
->line
, n
->pos
, "Ex");
972 roff_word_alloc(mdoc
, n
->line
, n
->pos
,
973 ic
> 1 ? "utilities exit\\~0" : "utility exits\\~0");
974 mdoc
->last
->flags
|= NODE_NOSRC
;
975 roff_word_alloc(mdoc
, n
->line
, n
->pos
,
976 "on success, and\\~>0 if an error occurs.");
977 mdoc
->last
->flags
|= NODE_EOS
| NODE_NOSRC
;
990 assert(n
->child
->type
== ROFFT_TEXT
);
991 mdoc
->next
= ROFF_NEXT_CHILD
;
993 if ((p
= mdoc_a2lib(n
->child
->string
)) != NULL
) {
994 n
->child
->flags
|= NODE_NOPRT
;
995 roff_word_alloc(mdoc
, n
->line
, n
->pos
, p
);
996 mdoc
->last
->flags
= NODE_NOSRC
;
1001 mandoc_vmsg(MANDOCERR_LB_BAD
, mdoc
->parse
, n
->child
->line
,
1002 n
->child
->pos
, "Lb %s", n
->child
->string
);
1004 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "library");
1005 mdoc
->last
->flags
= NODE_NOSRC
;
1006 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "\\(lq");
1007 mdoc
->last
->flags
= NODE_DELIMO
| NODE_NOSRC
;
1008 mdoc
->last
= mdoc
->last
->next
;
1009 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "\\(rq");
1010 mdoc
->last
->flags
= NODE_DELIMC
| NODE_NOSRC
;
1017 struct roff_node
*n
;
1023 mdoc
->next
= ROFF_NEXT_CHILD
;
1024 if (n
->child
!= NULL
) {
1025 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "The");
1026 mdoc
->last
->flags
|= NODE_NOSRC
;
1027 ic
= build_list(mdoc
, MDOC_Fn
);
1028 roff_word_alloc(mdoc
, n
->line
, n
->pos
,
1029 ic
> 1 ? "functions return" : "function returns");
1030 mdoc
->last
->flags
|= NODE_NOSRC
;
1031 roff_word_alloc(mdoc
, n
->line
, n
->pos
,
1032 "the value\\~0 if successful;");
1034 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "Upon successful "
1035 "completion, the value\\~0 is returned;");
1036 mdoc
->last
->flags
|= NODE_NOSRC
;
1038 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "otherwise "
1039 "the value\\~\\-1 is returned and the global variable");
1040 mdoc
->last
->flags
|= NODE_NOSRC
;
1041 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Va
);
1042 mdoc
->last
->flags
|= NODE_NOSRC
;
1043 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "errno");
1044 mdoc
->last
->flags
|= NODE_NOSRC
;
1045 mdoc
->last
= mdoc
->last
->parent
;
1046 mdoc
->next
= ROFF_NEXT_SIBLING
;
1047 roff_word_alloc(mdoc
, n
->line
, n
->pos
,
1048 "is set to indicate the error.");
1049 mdoc
->last
->flags
|= NODE_EOS
| NODE_NOSRC
;
1056 struct roff_node
*n
;
1061 if (n
->args
&& n
->args
->argc
== 1)
1062 if (n
->args
->argv
[0].arg
== MDOC_Std
)
1065 mandoc_msg(MANDOCERR_ARG_STD
, mdoc
->parse
,
1066 n
->line
, n
->pos
, roff_name
[n
->tok
]);
1072 struct roff_node
*n
, *nch
;
1077 assert(nch
->type
== ROFFT_TEXT
);
1079 if ((p
= mdoc_a2st(nch
->string
)) == NULL
) {
1080 mandoc_vmsg(MANDOCERR_ST_BAD
, mdoc
->parse
,
1081 nch
->line
, nch
->pos
, "St %s", nch
->string
);
1082 roff_node_delete(mdoc
, n
);
1086 nch
->flags
|= NODE_NOPRT
;
1087 mdoc
->next
= ROFF_NEXT_CHILD
;
1088 roff_word_alloc(mdoc
, nch
->line
, nch
->pos
, p
);
1089 mdoc
->last
->flags
|= NODE_NOSRC
;
1094 post_obsolete(POST_ARGS
)
1096 struct roff_node
*n
;
1099 if (n
->type
== ROFFT_ELEM
|| n
->type
== ROFFT_BLOCK
)
1100 mandoc_msg(MANDOCERR_MACRO_OBS
, mdoc
->parse
,
1101 n
->line
, n
->pos
, roff_name
[n
->tok
]);
1105 post_useless(POST_ARGS
)
1107 struct roff_node
*n
;
1110 mandoc_msg(MANDOCERR_MACRO_USELESS
, mdoc
->parse
,
1111 n
->line
, n
->pos
, roff_name
[n
->tok
]);
1121 struct roff_node
*np
, *nch
;
1124 * Unlike other data pointers, these are "housed" by the HEAD
1125 * element, which contains the goods.
1129 if (np
->type
!= ROFFT_HEAD
)
1132 assert(np
->parent
->type
== ROFFT_BLOCK
);
1133 assert(np
->parent
->tok
== MDOC_Bf
);
1135 /* Check the number of arguments. */
1138 if (np
->parent
->args
== NULL
) {
1140 mandoc_msg(MANDOCERR_BF_NOFONT
, mdoc
->parse
,
1141 np
->line
, np
->pos
, "Bf");
1147 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, mdoc
->parse
,
1148 nch
->line
, nch
->pos
, "Bf ... %s", nch
->string
);
1150 /* Extract argument into data. */
1152 if (np
->parent
->args
!= NULL
) {
1153 switch (np
->parent
->args
->argv
[0].arg
) {
1155 np
->norm
->Bf
.font
= FONT_Em
;
1158 np
->norm
->Bf
.font
= FONT_Li
;
1161 np
->norm
->Bf
.font
= FONT_Sy
;
1169 /* Extract parameter into data. */
1171 if ( ! strcmp(np
->child
->string
, "Em"))
1172 np
->norm
->Bf
.font
= FONT_Em
;
1173 else if ( ! strcmp(np
->child
->string
, "Li"))
1174 np
->norm
->Bf
.font
= FONT_Li
;
1175 else if ( ! strcmp(np
->child
->string
, "Sy"))
1176 np
->norm
->Bf
.font
= FONT_Sy
;
1178 mandoc_vmsg(MANDOCERR_BF_BADFONT
, mdoc
->parse
,
1179 np
->child
->line
, np
->child
->pos
,
1180 "Bf %s", np
->child
->string
);
1184 post_fname(POST_ARGS
)
1186 const struct roff_node
*n
;
1190 n
= mdoc
->last
->child
;
1191 pos
= strcspn(n
->string
, "()");
1192 cp
= n
->string
+ pos
;
1193 if ( ! (cp
[0] == '\0' || (cp
[0] == '(' && cp
[1] == '*')))
1194 mandoc_msg(MANDOCERR_FN_PAREN
, mdoc
->parse
,
1195 n
->line
, n
->pos
+ pos
, n
->string
);
1209 const struct roff_node
*n
;
1213 if (n
->type
!= ROFFT_HEAD
)
1216 if (n
->child
== NULL
) {
1217 mandoc_msg(MANDOCERR_FO_NOHEAD
, mdoc
->parse
,
1218 n
->line
, n
->pos
, "Fo");
1221 if (n
->child
!= n
->last
) {
1222 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, mdoc
->parse
,
1223 n
->child
->next
->line
, n
->child
->next
->pos
,
1224 "Fo ... %s", n
->child
->next
->string
);
1225 while (n
->child
!= n
->last
)
1226 roff_node_delete(mdoc
, n
->last
);
1236 const struct roff_node
*n
;
1239 for (n
= mdoc
->last
->child
; n
!= NULL
; n
= n
->next
) {
1240 for (cp
= n
->string
; *cp
!= '\0'; cp
++) {
1241 /* Ignore callbacks and alterations. */
1242 if (*cp
== '(' || *cp
== '{')
1246 mandoc_msg(MANDOCERR_FA_COMMA
, mdoc
->parse
,
1247 n
->line
, n
->pos
+ (cp
- n
->string
),
1252 post_delim_nb(mdoc
);
1258 struct roff_node
*n
;
1262 if (n
->sec
== SEC_NAME
&& n
->child
!= NULL
&&
1263 n
->child
->type
== ROFFT_TEXT
&& mdoc
->meta
.msec
!= NULL
)
1264 mandoc_xr_add(mdoc
->meta
.msec
, n
->child
->string
, -1, -1);
1266 if (n
->last
!= NULL
&&
1267 (n
->last
->tok
== MDOC_Pp
||
1268 n
->last
->tok
== MDOC_Lp
))
1269 mdoc_node_relink(mdoc
, n
->last
);
1271 if (mdoc
->meta
.name
== NULL
)
1272 deroff(&mdoc
->meta
.name
, n
);
1274 if (mdoc
->meta
.name
== NULL
||
1275 (mdoc
->lastsec
== SEC_NAME
&& n
->child
== NULL
))
1276 mandoc_msg(MANDOCERR_NM_NONAME
, mdoc
->parse
,
1277 n
->line
, n
->pos
, "Nm");
1281 post_delim_nb(mdoc
);
1290 if ((n
->child
!= NULL
&& n
->child
->type
== ROFFT_TEXT
) ||
1291 mdoc
->meta
.name
== NULL
)
1294 mdoc
->next
= ROFF_NEXT_CHILD
;
1295 roff_word_alloc(mdoc
, n
->line
, n
->pos
, mdoc
->meta
.name
);
1296 mdoc
->last
->flags
|= NODE_NOSRC
;
1303 struct roff_node
*n
;
1307 if (n
->type
!= ROFFT_BODY
)
1310 if (n
->sec
!= SEC_NAME
)
1311 mandoc_msg(MANDOCERR_ND_LATE
, mdoc
->parse
,
1312 n
->line
, n
->pos
, "Nd");
1314 if (n
->child
== NULL
)
1315 mandoc_msg(MANDOCERR_ND_EMPTY
, mdoc
->parse
,
1316 n
->line
, n
->pos
, "Nd");
1324 post_display(POST_ARGS
)
1326 struct roff_node
*n
, *np
;
1331 if (n
->end
!= ENDBODY_NOT
) {
1332 if (n
->tok
== MDOC_Bd
&&
1333 n
->body
->parent
->args
== NULL
)
1334 roff_node_delete(mdoc
, n
);
1335 } else if (n
->child
== NULL
)
1336 mandoc_msg(MANDOCERR_BLK_EMPTY
, mdoc
->parse
,
1337 n
->line
, n
->pos
, roff_name
[n
->tok
]);
1338 else if (n
->tok
== MDOC_D1
)
1342 if (n
->tok
== MDOC_Bd
) {
1343 if (n
->args
== NULL
) {
1344 mandoc_msg(MANDOCERR_BD_NOARG
,
1345 mdoc
->parse
, n
->line
, n
->pos
, "Bd");
1346 mdoc
->next
= ROFF_NEXT_SIBLING
;
1347 while (n
->body
->child
!= NULL
)
1348 mdoc_node_relink(mdoc
,
1350 roff_node_delete(mdoc
, n
);
1356 for (np
= n
->parent
; np
!= NULL
; np
= np
->parent
) {
1357 if (np
->type
== ROFFT_BLOCK
&& np
->tok
== MDOC_Bd
) {
1358 mandoc_vmsg(MANDOCERR_BD_NEST
,
1359 mdoc
->parse
, n
->line
, n
->pos
,
1360 "%s in Bd", roff_name
[n
->tok
]);
1371 post_defaults(POST_ARGS
)
1373 struct roff_node
*nn
;
1375 if (mdoc
->last
->child
!= NULL
) {
1376 post_delim_nb(mdoc
);
1381 * The `Ar' defaults to "file ..." if no value is provided as an
1382 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
1383 * gets an empty string.
1389 mdoc
->next
= ROFF_NEXT_CHILD
;
1390 roff_word_alloc(mdoc
, nn
->line
, nn
->pos
, "file");
1391 mdoc
->last
->flags
|= NODE_NOSRC
;
1392 roff_word_alloc(mdoc
, nn
->line
, nn
->pos
, "...");
1393 mdoc
->last
->flags
|= NODE_NOSRC
;
1397 mdoc
->next
= ROFF_NEXT_CHILD
;
1398 roff_word_alloc(mdoc
, nn
->line
, nn
->pos
, "~");
1399 mdoc
->last
->flags
|= NODE_NOSRC
;
1410 struct roff_node
*n
, *nch
;
1417 * If we have a child, look it up in the standard keys. If a
1418 * key exist, use that instead of the child; if it doesn't,
1419 * prefix "AT&T UNIX " to the existing data.
1423 if (nch
!= NULL
&& ((att
= mdoc_a2att(nch
->string
)) == NULL
))
1424 mandoc_vmsg(MANDOCERR_AT_BAD
, mdoc
->parse
,
1425 nch
->line
, nch
->pos
, "At %s", nch
->string
);
1427 mdoc
->next
= ROFF_NEXT_CHILD
;
1429 roff_word_alloc(mdoc
, nch
->line
, nch
->pos
, att
);
1430 nch
->flags
|= NODE_NOPRT
;
1432 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "AT&T UNIX");
1433 mdoc
->last
->flags
|= NODE_NOSRC
;
1440 struct roff_node
*np
, *nch
;
1446 if (np
->norm
->An
.auth
== AUTH__NONE
) {
1448 mandoc_msg(MANDOCERR_MACRO_EMPTY
, mdoc
->parse
,
1449 np
->line
, np
->pos
, "An");
1451 post_delim_nb(mdoc
);
1452 } else if (nch
!= NULL
)
1453 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, mdoc
->parse
,
1454 nch
->line
, nch
->pos
, "An ... %s", nch
->string
);
1461 post_obsolete(mdoc
);
1462 if (mdoc
->last
->type
== ROFFT_BLOCK
)
1463 mdoc
->last
->norm
->Es
= mdoc
->last_es
;
1470 post_obsolete(mdoc
);
1471 mdoc
->last_es
= mdoc
->last
;
1477 struct roff_node
*n
;
1481 post_delim_nb(mdoc
);
1496 if (n
->child
== NULL
)
1498 v
= n
->child
->string
;
1499 if ((v
[0] != '0' && v
[0] != '1') || v
[1] != '.' ||
1500 v
[2] < '0' || v
[2] > '9' ||
1501 v
[3] < 'a' || v
[3] > 'z' || v
[4] != '\0')
1503 n
->child
->flags
|= NODE_NOPRT
;
1504 mdoc
->next
= ROFF_NEXT_CHILD
;
1505 roff_word_alloc(mdoc
, n
->child
->line
, n
->child
->pos
, v
);
1506 v
= mdoc
->last
->string
;
1507 v
[3] = toupper((unsigned char)v
[3]);
1508 mdoc
->last
->flags
|= NODE_NOSRC
;
1520 mdoc
->next
= ROFF_NEXT_CHILD
;
1521 roff_word_alloc(mdoc
, n
->line
, n
->pos
, os
);
1522 mdoc
->last
->flags
|= NODE_NOSRC
;
1529 struct roff_node
*nbl
, *nit
, *nch
;
1536 if (nit
->type
!= ROFFT_BLOCK
)
1539 nbl
= nit
->parent
->parent
;
1540 lt
= nbl
->norm
->Bl
.type
;
1548 if (nit
->head
->child
== NULL
)
1549 mandoc_vmsg(MANDOCERR_IT_NOHEAD
,
1550 mdoc
->parse
, nit
->line
, nit
->pos
,
1552 mdoc_argnames
[nbl
->args
->argv
[0].arg
]);
1558 if (nit
->body
== NULL
|| nit
->body
->child
== NULL
)
1559 mandoc_vmsg(MANDOCERR_IT_NOBODY
,
1560 mdoc
->parse
, nit
->line
, nit
->pos
,
1562 mdoc_argnames
[nbl
->args
->argv
[0].arg
]);
1565 if ((nch
= nit
->head
->child
) != NULL
)
1566 mandoc_vmsg(MANDOCERR_ARG_SKIP
, mdoc
->parse
,
1567 nit
->line
, nit
->pos
, "It %s",
1568 nch
->string
== NULL
? roff_name
[nch
->tok
] :
1572 cols
= (int)nbl
->norm
->Bl
.ncols
;
1574 assert(nit
->head
->child
== NULL
);
1576 if (nit
->head
->next
->child
== NULL
&&
1577 nit
->head
->next
->next
== NULL
) {
1578 mandoc_msg(MANDOCERR_MACRO_EMPTY
, mdoc
->parse
,
1579 nit
->line
, nit
->pos
, "It");
1580 roff_node_delete(mdoc
, nit
);
1585 for (nch
= nit
->child
; nch
!= NULL
; nch
= nch
->next
) {
1586 if (nch
->type
!= ROFFT_BODY
)
1588 if (i
++ && nch
->flags
& NODE_LINE
)
1589 mandoc_msg(MANDOCERR_TA_LINE
, mdoc
->parse
,
1590 nch
->line
, nch
->pos
, "Ta");
1592 if (i
< cols
|| i
> cols
+ 1)
1593 mandoc_vmsg(MANDOCERR_BL_COL
,
1594 mdoc
->parse
, nit
->line
, nit
->pos
,
1595 "%d columns, %d cells", cols
, i
);
1596 else if (nit
->head
->next
->child
!= NULL
&&
1597 nit
->head
->next
->child
->line
> nit
->line
)
1598 mandoc_msg(MANDOCERR_IT_NOARG
, mdoc
->parse
,
1599 nit
->line
, nit
->pos
, "Bl -column It");
1607 post_bl_block(POST_ARGS
)
1609 struct roff_node
*n
, *ni
, *nc
;
1614 for (ni
= n
->body
->child
; ni
!= NULL
; ni
= ni
->next
) {
1615 if (ni
->body
== NULL
)
1617 nc
= ni
->body
->last
;
1618 while (nc
!= NULL
) {
1628 if (ni
->next
== NULL
) {
1629 mandoc_msg(MANDOCERR_PAR_MOVE
,
1630 mdoc
->parse
, nc
->line
, nc
->pos
,
1631 roff_name
[nc
->tok
]);
1632 mdoc_node_relink(mdoc
, nc
);
1633 } else if (n
->norm
->Bl
.comp
== 0 &&
1634 n
->norm
->Bl
.type
!= LIST_column
) {
1635 mandoc_vmsg(MANDOCERR_PAR_SKIP
,
1636 mdoc
->parse
, nc
->line
, nc
->pos
,
1637 "%s before It", roff_name
[nc
->tok
]);
1638 roff_node_delete(mdoc
, nc
);
1641 nc
= ni
->body
->last
;
1647 * If the argument of -offset or -width is a macro,
1648 * replace it with the associated default width.
1651 rewrite_macro2len(struct roff_man
*mdoc
, char **arg
)
1658 else if ( ! strcmp(*arg
, "Ds"))
1660 else if ((tok
= roffhash_find(mdoc
->mdocmac
, *arg
, 0)) == TOKEN_NONE
)
1663 width
= macro2len(tok
);
1666 mandoc_asprintf(arg
, "%zun", width
);
1670 post_bl_head(POST_ARGS
)
1672 struct roff_node
*nbl
, *nh
, *nch
, *nnext
;
1673 struct mdoc_argv
*argv
;
1679 if (nh
->norm
->Bl
.type
!= LIST_column
) {
1680 if ((nch
= nh
->child
) == NULL
)
1682 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, mdoc
->parse
,
1683 nch
->line
, nch
->pos
, "Bl ... %s", nch
->string
);
1684 while (nch
!= NULL
) {
1685 roff_node_delete(mdoc
, nch
);
1692 * Append old-style lists, where the column width specifiers
1693 * trail as macro parameters, to the new-style ("normal-form")
1694 * lists where they're argument values following -column.
1697 if (nh
->child
== NULL
)
1701 for (j
= 0; j
< (int)nbl
->args
->argc
; j
++)
1702 if (nbl
->args
->argv
[j
].arg
== MDOC_Column
)
1705 assert(j
< (int)nbl
->args
->argc
);
1708 * Accommodate for new-style groff column syntax. Shuffle the
1709 * child nodes, all of which must be TEXT, as arguments for the
1710 * column field. Then, delete the head children.
1713 argv
= nbl
->args
->argv
+ j
;
1715 for (nch
= nh
->child
; nch
!= NULL
; nch
= nch
->next
)
1717 argv
->value
= mandoc_reallocarray(argv
->value
,
1718 argv
->sz
, sizeof(char *));
1720 nh
->norm
->Bl
.ncols
= argv
->sz
;
1721 nh
->norm
->Bl
.cols
= (void *)argv
->value
;
1723 for (nch
= nh
->child
; nch
!= NULL
; nch
= nnext
) {
1724 argv
->value
[i
++] = nch
->string
;
1727 roff_node_delete(NULL
, nch
);
1735 struct roff_node
*nparent
, *nprev
; /* of the Bl block */
1736 struct roff_node
*nblock
, *nbody
; /* of the Bl */
1737 struct roff_node
*nchild
, *nnext
; /* of the Bl body */
1738 const char *prev_Er
;
1742 switch (nbody
->type
) {
1744 post_bl_block(mdoc
);
1754 if (nbody
->end
!= ENDBODY_NOT
)
1757 nchild
= nbody
->child
;
1758 if (nchild
== NULL
) {
1759 mandoc_msg(MANDOCERR_BLK_EMPTY
, mdoc
->parse
,
1760 nbody
->line
, nbody
->pos
, "Bl");
1763 while (nchild
!= NULL
) {
1764 nnext
= nchild
->next
;
1765 if (nchild
->tok
== MDOC_It
||
1766 (nchild
->tok
== MDOC_Sm
&&
1767 nnext
!= NULL
&& nnext
->tok
== MDOC_It
)) {
1773 * In .Bl -column, the first rows may be implicit,
1774 * that is, they may not start with .It macros.
1775 * Such rows may be followed by nodes generated on the
1776 * roff level, for example .TS, which cannot be moved
1777 * out of the list. In that case, wrap such roff nodes
1778 * into an implicit row.
1781 if (nchild
->prev
!= NULL
) {
1782 mdoc
->last
= nchild
;
1783 mdoc
->next
= ROFF_NEXT_SIBLING
;
1784 roff_block_alloc(mdoc
, nchild
->line
,
1785 nchild
->pos
, MDOC_It
);
1786 roff_head_alloc(mdoc
, nchild
->line
,
1787 nchild
->pos
, MDOC_It
);
1788 mdoc
->next
= ROFF_NEXT_SIBLING
;
1789 roff_body_alloc(mdoc
, nchild
->line
,
1790 nchild
->pos
, MDOC_It
);
1791 while (nchild
->tok
!= MDOC_It
) {
1792 mdoc_node_relink(mdoc
, nchild
);
1793 if ((nchild
= nnext
) == NULL
)
1795 nnext
= nchild
->next
;
1796 mdoc
->next
= ROFF_NEXT_SIBLING
;
1802 mandoc_msg(MANDOCERR_BL_MOVE
, mdoc
->parse
,
1803 nchild
->line
, nchild
->pos
, roff_name
[nchild
->tok
]);
1806 * Move the node out of the Bl block.
1807 * First, collect all required node pointers.
1810 nblock
= nbody
->parent
;
1811 nprev
= nblock
->prev
;
1812 nparent
= nblock
->parent
;
1815 * Unlink this child.
1818 nbody
->child
= nnext
;
1825 * Relink this child.
1828 nchild
->parent
= nparent
;
1829 nchild
->prev
= nprev
;
1830 nchild
->next
= nblock
;
1832 nblock
->prev
= nchild
;
1834 nparent
->child
= nchild
;
1836 nprev
->next
= nchild
;
1841 if (mdoc
->meta
.os_e
!= MANDOC_OS_NETBSD
)
1845 for (nchild
= nbody
->child
; nchild
!= NULL
; nchild
= nchild
->next
) {
1846 if (nchild
->tok
!= MDOC_It
)
1848 if ((nnext
= nchild
->head
->child
) == NULL
)
1850 if (nnext
->type
== ROFFT_BLOCK
)
1851 nnext
= nnext
->body
->child
;
1852 if (nnext
== NULL
|| nnext
->tok
!= MDOC_Er
)
1854 nnext
= nnext
->child
;
1855 if (prev_Er
!= NULL
) {
1856 order
= strcmp(prev_Er
, nnext
->string
);
1858 mandoc_vmsg(MANDOCERR_ER_ORDER
,
1859 mdoc
->parse
, nnext
->line
, nnext
->pos
,
1860 "Er %s %s (NetBSD)",
1861 prev_Er
, nnext
->string
);
1862 else if (order
== 0)
1863 mandoc_vmsg(MANDOCERR_ER_REP
,
1864 mdoc
->parse
, nnext
->line
, nnext
->pos
,
1865 "Er %s (NetBSD)", prev_Er
);
1867 prev_Er
= nnext
->string
;
1874 struct roff_node
*n
;
1878 if (n
->type
== ROFFT_BLOCK
&& n
->body
->child
== NULL
) {
1879 mandoc_msg(MANDOCERR_BLK_EMPTY
,
1880 mdoc
->parse
, n
->line
, n
->pos
, "Bk");
1881 roff_node_delete(mdoc
, n
);
1888 struct roff_node
*nch
;
1890 nch
= mdoc
->last
->child
;
1893 mdoc
->flags
^= MDOC_SMOFF
;
1897 assert(nch
->type
== ROFFT_TEXT
);
1899 if ( ! strcmp(nch
->string
, "on")) {
1900 mdoc
->flags
&= ~MDOC_SMOFF
;
1903 if ( ! strcmp(nch
->string
, "off")) {
1904 mdoc
->flags
|= MDOC_SMOFF
;
1908 mandoc_vmsg(MANDOCERR_SM_BAD
,
1909 mdoc
->parse
, nch
->line
, nch
->pos
,
1910 "%s %s", roff_name
[mdoc
->last
->tok
], nch
->string
);
1911 mdoc_node_relink(mdoc
, nch
);
1916 post_root(POST_ARGS
)
1918 const char *openbsd_arch
[] = {
1919 "alpha", "amd64", "arm64", "armv7", "hppa", "i386",
1920 "landisk", "loongson", "luna88k", "macppc", "mips64",
1921 "octeon", "sgi", "socppc", "sparc64", NULL
1923 const char *netbsd_arch
[] = {
1924 "acorn26", "acorn32", "algor", "alpha", "amiga",
1926 "bebox", "cats", "cesfic", "cobalt", "dreamcast",
1927 "emips", "evbarm", "evbmips", "evbppc", "evbsh3", "evbsh5",
1928 "hp300", "hpcarm", "hpcmips", "hpcsh", "hppa",
1929 "i386", "ibmnws", "luna68k",
1930 "mac68k", "macppc", "mipsco", "mmeye", "mvme68k", "mvmeppc",
1931 "netwinder", "news68k", "newsmips", "next68k",
1932 "pc532", "playstation2", "pmax", "pmppc", "prep",
1933 "sandpoint", "sbmips", "sgimips", "shark",
1934 "sparc", "sparc64", "sun2", "sun3",
1935 "vax", "walnut", "x68k", "x86", "x86_64", "xen", NULL
1937 const char **arches
[] = { NULL
, netbsd_arch
, openbsd_arch
};
1939 struct roff_node
*n
;
1942 /* Add missing prologue data. */
1944 if (mdoc
->meta
.date
== NULL
)
1945 mdoc
->meta
.date
= mdoc
->quick
? mandoc_strdup("") :
1946 mandoc_normdate(mdoc
, NULL
, 0, 0);
1948 if (mdoc
->meta
.title
== NULL
) {
1949 mandoc_msg(MANDOCERR_DT_NOTITLE
,
1950 mdoc
->parse
, 0, 0, "EOF");
1951 mdoc
->meta
.title
= mandoc_strdup("UNTITLED");
1954 if (mdoc
->meta
.vol
== NULL
)
1955 mdoc
->meta
.vol
= mandoc_strdup("LOCAL");
1957 if (mdoc
->meta
.os
== NULL
) {
1958 mandoc_msg(MANDOCERR_OS_MISSING
,
1959 mdoc
->parse
, 0, 0, NULL
);
1960 mdoc
->meta
.os
= mandoc_strdup("");
1961 } else if (mdoc
->meta
.os_e
&&
1962 (mdoc
->meta
.rcsids
& (1 << mdoc
->meta
.os_e
)) == 0)
1963 mandoc_msg(MANDOCERR_RCS_MISSING
, mdoc
->parse
, 0, 0,
1964 mdoc
->meta
.os_e
== MANDOC_OS_OPENBSD
?
1965 "(OpenBSD)" : "(NetBSD)");
1967 if (mdoc
->meta
.arch
!= NULL
&&
1968 (arch
= arches
[mdoc
->meta
.os_e
]) != NULL
) {
1969 while (*arch
!= NULL
&& strcmp(*arch
, mdoc
->meta
.arch
))
1971 if (*arch
== NULL
) {
1972 n
= mdoc
->first
->child
;
1973 while (n
->tok
!= MDOC_Dt
||
1975 n
->child
->next
== NULL
||
1976 n
->child
->next
->next
== NULL
)
1978 n
= n
->child
->next
->next
;
1979 mandoc_vmsg(MANDOCERR_ARCH_BAD
,
1980 mdoc
->parse
, n
->line
, n
->pos
,
1981 "Dt ... %s %s", mdoc
->meta
.arch
,
1982 mdoc
->meta
.os_e
== MANDOC_OS_OPENBSD
?
1983 "(OpenBSD)" : "(NetBSD)");
1987 /* Check that we begin with a proper `Sh'. */
1989 n
= mdoc
->first
->child
;
1991 (n
->type
== ROFFT_COMMENT
||
1992 (n
->tok
>= MDOC_Dd
&&
1993 mdoc_macro(n
->tok
)->flags
& MDOC_PROLOGUE
)))
1997 mandoc_msg(MANDOCERR_DOC_EMPTY
, mdoc
->parse
, 0, 0, NULL
);
1998 else if (n
->tok
!= MDOC_Sh
)
1999 mandoc_msg(MANDOCERR_SEC_BEFORE
, mdoc
->parse
,
2000 n
->line
, n
->pos
, roff_name
[n
->tok
]);
2006 struct roff_node
*np
, *nch
, *next
, *prev
;
2011 if (np
->type
!= ROFFT_BODY
)
2014 if (np
->child
== NULL
) {
2015 mandoc_msg(MANDOCERR_RS_EMPTY
, mdoc
->parse
,
2016 np
->line
, np
->pos
, "Rs");
2021 * The full `Rs' block needs special handling to order the
2022 * sub-elements according to `rsord'. Pick through each element
2023 * and correctly order it. This is an insertion sort.
2027 for (nch
= np
->child
->next
; nch
!= NULL
; nch
= next
) {
2028 /* Determine order number of this child. */
2029 for (i
= 0; i
< RSORD_MAX
; i
++)
2030 if (rsord
[i
] == nch
->tok
)
2033 if (i
== RSORD_MAX
) {
2034 mandoc_msg(MANDOCERR_RS_BAD
, mdoc
->parse
,
2035 nch
->line
, nch
->pos
, roff_name
[nch
->tok
]);
2037 } else if (nch
->tok
== MDOC__J
|| nch
->tok
== MDOC__B
)
2038 np
->norm
->Rs
.quote_T
++;
2041 * Remove this child from the chain. This somewhat
2042 * repeats roff_node_unlink(), but since we're
2043 * just re-ordering, there's no need for the
2044 * full unlink process.
2047 if ((next
= nch
->next
) != NULL
)
2048 next
->prev
= nch
->prev
;
2050 if ((prev
= nch
->prev
) != NULL
)
2051 prev
->next
= nch
->next
;
2053 nch
->prev
= nch
->next
= NULL
;
2056 * Scan back until we reach a node that's
2057 * to be ordered before this child.
2060 for ( ; prev
; prev
= prev
->prev
) {
2061 /* Determine order of `prev'. */
2062 for (j
= 0; j
< RSORD_MAX
; j
++)
2063 if (rsord
[j
] == prev
->tok
)
2073 * Set this child back into its correct place
2074 * in front of the `prev' node.
2080 np
->child
->prev
= nch
;
2081 nch
->next
= np
->child
;
2085 prev
->next
->prev
= nch
;
2086 nch
->next
= prev
->next
;
2093 * For some arguments of some macros,
2094 * convert all breakable hyphens into ASCII_HYPH.
2097 post_hyph(POST_ARGS
)
2099 struct roff_node
*nch
;
2102 for (nch
= mdoc
->last
->child
; nch
!= NULL
; nch
= nch
->next
) {
2103 if (nch
->type
!= ROFFT_TEXT
)
2108 while (*(++cp
) != '\0')
2110 isalpha((unsigned char)cp
[-1]) &&
2111 isalpha((unsigned char)cp
[1]))
2119 struct roff_node
*n
;
2122 if (n
->flags
& NODE_LINE
||
2123 (n
->next
!= NULL
&& n
->next
->flags
& NODE_DELIMC
))
2124 mandoc_msg(MANDOCERR_NS_SKIP
, mdoc
->parse
,
2125 n
->line
, n
->pos
, NULL
);
2141 switch (mdoc
->last
->type
) {
2146 switch (mdoc
->lastsec
) {
2151 post_sh_see_also(mdoc
);
2154 post_sh_authors(mdoc
);
2166 post_sh_name(POST_ARGS
)
2168 struct roff_node
*n
;
2173 for (n
= mdoc
->last
->child
; n
!= NULL
; n
= n
->next
) {
2176 if (hasnm
&& n
->child
!= NULL
)
2177 mandoc_vmsg(MANDOCERR_NAMESEC_PUNCT
,
2178 mdoc
->parse
, n
->line
, n
->pos
,
2179 "Nm %s", n
->child
->string
);
2184 if (n
->next
!= NULL
)
2185 mandoc_msg(MANDOCERR_NAMESEC_ND
,
2186 mdoc
->parse
, n
->line
, n
->pos
, NULL
);
2189 if (n
->type
== ROFFT_TEXT
&&
2190 n
->string
[0] == ',' && n
->string
[1] == '\0' &&
2191 n
->next
!= NULL
&& n
->next
->tok
== MDOC_Nm
) {
2197 mandoc_msg(MANDOCERR_NAMESEC_BAD
, mdoc
->parse
,
2198 n
->line
, n
->pos
, roff_name
[n
->tok
]);
2205 mandoc_msg(MANDOCERR_NAMESEC_NONM
, mdoc
->parse
,
2206 mdoc
->last
->line
, mdoc
->last
->pos
, NULL
);
2208 mandoc_msg(MANDOCERR_NAMESEC_NOND
, mdoc
->parse
,
2209 mdoc
->last
->line
, mdoc
->last
->pos
, NULL
);
2213 post_sh_see_also(POST_ARGS
)
2215 const struct roff_node
*n
;
2216 const char *name
, *sec
;
2217 const char *lastname
, *lastsec
, *lastpunct
;
2220 n
= mdoc
->last
->child
;
2221 lastname
= lastsec
= lastpunct
= NULL
;
2223 if (n
->tok
!= MDOC_Xr
||
2225 n
->child
->next
== NULL
)
2228 /* Process one .Xr node. */
2230 name
= n
->child
->string
;
2231 sec
= n
->child
->next
->string
;
2232 if (lastsec
!= NULL
) {
2233 if (lastpunct
[0] != ',' || lastpunct
[1] != '\0')
2234 mandoc_vmsg(MANDOCERR_XR_PUNCT
,
2235 mdoc
->parse
, n
->line
, n
->pos
,
2236 "%s before %s(%s)", lastpunct
,
2238 cmp
= strcmp(lastsec
, sec
);
2240 mandoc_vmsg(MANDOCERR_XR_ORDER
,
2241 mdoc
->parse
, n
->line
, n
->pos
,
2242 "%s(%s) after %s(%s)", name
,
2243 sec
, lastname
, lastsec
);
2244 else if (cmp
== 0 &&
2245 strcasecmp(lastname
, name
) > 0)
2246 mandoc_vmsg(MANDOCERR_XR_ORDER
,
2247 mdoc
->parse
, n
->line
, n
->pos
,
2248 "%s after %s", name
, lastname
);
2253 /* Process the following node. */
2258 if (n
->tok
== MDOC_Xr
) {
2262 if (n
->type
!= ROFFT_TEXT
)
2264 for (name
= n
->string
; *name
!= '\0'; name
++)
2265 if (isalpha((const unsigned char)*name
))
2267 lastpunct
= n
->string
;
2268 if (n
->next
== NULL
|| n
->next
->tok
== MDOC_Rs
)
2269 mandoc_vmsg(MANDOCERR_XR_PUNCT
, mdoc
->parse
,
2270 n
->line
, n
->pos
, "%s after %s(%s)",
2271 lastpunct
, lastname
, lastsec
);
2277 child_an(const struct roff_node
*n
)
2280 for (n
= n
->child
; n
!= NULL
; n
= n
->next
)
2281 if ((n
->tok
== MDOC_An
&& n
->child
!= NULL
) || child_an(n
))
2287 post_sh_authors(POST_ARGS
)
2290 if ( ! child_an(mdoc
->last
))
2291 mandoc_msg(MANDOCERR_AN_MISSING
, mdoc
->parse
,
2292 mdoc
->last
->line
, mdoc
->last
->pos
, NULL
);
2296 * Return an upper bound for the string distance (allowing
2297 * transpositions). Not a full Levenshtein implementation
2298 * because Levenshtein is quadratic in the string length
2299 * and this function is called for every standard name,
2300 * so the check for each custom name would be cubic.
2301 * The following crude heuristics is linear, resulting
2302 * in quadratic behaviour for checking one custom name,
2303 * which does not cause measurable slowdown.
2306 similar(const char *s1
, const char *s2
)
2308 const int maxdist
= 3;
2311 while (s1
[0] != '\0' && s2
[0] != '\0') {
2312 if (s1
[0] == s2
[0]) {
2317 if (++dist
> maxdist
)
2319 if (s1
[1] == s2
[1]) { /* replacement */
2322 } else if (s1
[0] == s2
[1] && s1
[1] == s2
[0]) {
2323 s1
+= 2; /* transposition */
2325 } else if (s1
[0] == s2
[1]) /* insertion */
2327 else if (s1
[1] == s2
[0]) /* deletion */
2332 dist
+= strlen(s1
) + strlen(s2
);
2333 return dist
> maxdist
? INT_MAX
: dist
;
2337 post_sh_head(POST_ARGS
)
2339 struct roff_node
*nch
;
2340 const char *goodsec
;
2341 const char *const *testsec
;
2346 * Process a new section. Sections are either "named" or
2347 * "custom". Custom sections are user-defined, while named ones
2348 * follow a conventional order and may only appear in certain
2352 sec
= mdoc
->last
->sec
;
2354 /* The NAME should be first. */
2356 if (sec
!= SEC_NAME
&& mdoc
->lastnamed
== SEC_NONE
)
2357 mandoc_vmsg(MANDOCERR_NAMESEC_FIRST
, mdoc
->parse
,
2358 mdoc
->last
->line
, mdoc
->last
->pos
, "Sh %s",
2359 sec
!= SEC_CUSTOM
? secnames
[sec
] :
2360 (nch
= mdoc
->last
->child
) == NULL
? "" :
2361 nch
->type
== ROFFT_TEXT
? nch
->string
:
2362 roff_name
[nch
->tok
]);
2364 /* The SYNOPSIS gets special attention in other areas. */
2366 if (sec
== SEC_SYNOPSIS
) {
2367 roff_setreg(mdoc
->roff
, "nS", 1, '=');
2368 mdoc
->flags
|= MDOC_SYNOPSIS
;
2370 roff_setreg(mdoc
->roff
, "nS", 0, '=');
2371 mdoc
->flags
&= ~MDOC_SYNOPSIS
;
2374 /* Mark our last section. */
2376 mdoc
->lastsec
= sec
;
2378 /* We don't care about custom sections after this. */
2380 if (sec
== SEC_CUSTOM
) {
2381 if ((nch
= mdoc
->last
->child
) == NULL
||
2382 nch
->type
!= ROFFT_TEXT
|| nch
->next
!= NULL
)
2386 for (testsec
= secnames
+ 1; *testsec
!= NULL
; testsec
++) {
2387 dist
= similar(nch
->string
, *testsec
);
2388 if (dist
< mindist
) {
2393 if (goodsec
!= NULL
)
2394 mandoc_vmsg(MANDOCERR_SEC_TYPO
, mdoc
->parse
,
2395 nch
->line
, nch
->pos
, "Sh %s instead of %s",
2396 nch
->string
, goodsec
);
2401 * Check whether our non-custom section is being repeated or is
2405 if (sec
== mdoc
->lastnamed
)
2406 mandoc_vmsg(MANDOCERR_SEC_REP
, mdoc
->parse
,
2407 mdoc
->last
->line
, mdoc
->last
->pos
,
2408 "Sh %s", secnames
[sec
]);
2410 if (sec
< mdoc
->lastnamed
)
2411 mandoc_vmsg(MANDOCERR_SEC_ORDER
, mdoc
->parse
,
2412 mdoc
->last
->line
, mdoc
->last
->pos
,
2413 "Sh %s", secnames
[sec
]);
2415 /* Mark the last named section. */
2417 mdoc
->lastnamed
= sec
;
2419 /* Check particular section/manual conventions. */
2421 if (mdoc
->meta
.msec
== NULL
)
2427 if (*mdoc
->meta
.msec
== '4')
2429 goodsec
= "2, 3, 4, 9";
2431 case SEC_RETURN_VALUES
:
2433 if (*mdoc
->meta
.msec
== '2')
2435 if (*mdoc
->meta
.msec
== '3')
2437 if (NULL
== goodsec
)
2438 goodsec
= "2, 3, 9";
2441 if (*mdoc
->meta
.msec
== '9')
2443 if (NULL
== goodsec
)
2445 mandoc_vmsg(MANDOCERR_SEC_MSEC
, mdoc
->parse
,
2446 mdoc
->last
->line
, mdoc
->last
->pos
,
2447 "Sh %s for %s only", secnames
[sec
], goodsec
);
2457 struct roff_node
*n
, *nch
;
2461 if (nch
->next
== NULL
) {
2462 mandoc_vmsg(MANDOCERR_XR_NOSEC
, mdoc
->parse
,
2463 n
->line
, n
->pos
, "Xr %s", nch
->string
);
2465 assert(nch
->next
== n
->last
);
2466 if(mandoc_xr_add(nch
->next
->string
, nch
->string
,
2467 nch
->line
, nch
->pos
))
2468 mandoc_vmsg(MANDOCERR_XR_SELF
, mdoc
->parse
,
2469 nch
->line
, nch
->pos
, "Xr %s %s",
2470 nch
->string
, nch
->next
->string
);
2472 post_delim_nb(mdoc
);
2476 post_ignpar(POST_ARGS
)
2478 struct roff_node
*np
;
2480 switch (mdoc
->last
->type
) {
2494 if ((np
= mdoc
->last
->child
) != NULL
)
2495 if (np
->tok
== MDOC_Pp
|| np
->tok
== MDOC_Lp
) {
2496 mandoc_vmsg(MANDOCERR_PAR_SKIP
,
2497 mdoc
->parse
, np
->line
, np
->pos
,
2498 "%s after %s", roff_name
[np
->tok
],
2499 roff_name
[mdoc
->last
->tok
]);
2500 roff_node_delete(mdoc
, np
);
2503 if ((np
= mdoc
->last
->last
) != NULL
)
2504 if (np
->tok
== MDOC_Pp
|| np
->tok
== MDOC_Lp
) {
2505 mandoc_vmsg(MANDOCERR_PAR_SKIP
, mdoc
->parse
,
2506 np
->line
, np
->pos
, "%s at the end of %s",
2508 roff_name
[mdoc
->last
->tok
]);
2509 roff_node_delete(mdoc
, np
);
2514 post_prevpar(POST_ARGS
)
2516 struct roff_node
*n
;
2519 if (NULL
== n
->prev
)
2521 if (n
->type
!= ROFFT_ELEM
&& n
->type
!= ROFFT_BLOCK
)
2525 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
2526 * block: `Lp', `Pp', or non-compact `Bd' or `Bl'.
2529 if (n
->prev
->tok
!= MDOC_Pp
&&
2530 n
->prev
->tok
!= MDOC_Lp
&&
2531 n
->prev
->tok
!= ROFF_br
)
2533 if (n
->tok
== MDOC_Bl
&& n
->norm
->Bl
.comp
)
2535 if (n
->tok
== MDOC_Bd
&& n
->norm
->Bd
.comp
)
2537 if (n
->tok
== MDOC_It
&& n
->parent
->norm
->Bl
.comp
)
2540 mandoc_vmsg(MANDOCERR_PAR_SKIP
, mdoc
->parse
,
2541 n
->prev
->line
, n
->prev
->pos
, "%s before %s",
2542 roff_name
[n
->prev
->tok
], roff_name
[n
->tok
]);
2543 roff_node_delete(mdoc
, n
->prev
);
2549 struct roff_node
*np
;
2552 if (np
->tok
!= ROFF_br
&& np
->tok
!= ROFF_sp
)
2555 if (np
->tok
== ROFF_sp
) {
2556 if (np
->child
!= NULL
&& np
->child
->next
!= NULL
)
2557 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, mdoc
->parse
,
2558 np
->child
->next
->line
, np
->child
->next
->pos
,
2559 "sp ... %s", np
->child
->next
->string
);
2560 } else if (np
->child
!= NULL
)
2561 mandoc_vmsg(MANDOCERR_ARG_SKIP
,
2562 mdoc
->parse
, np
->line
, np
->pos
, "%s %s",
2563 roff_name
[np
->tok
], np
->child
->string
);
2565 if ((np
= mdoc
->last
->prev
) == NULL
) {
2566 np
= mdoc
->last
->parent
;
2567 if (np
->tok
!= MDOC_Sh
&& np
->tok
!= MDOC_Ss
)
2569 } else if (np
->tok
!= MDOC_Pp
&& np
->tok
!= MDOC_Lp
&&
2570 (mdoc
->last
->tok
!= ROFF_br
||
2571 (np
->tok
!= ROFF_sp
&& np
->tok
!= ROFF_br
)))
2574 mandoc_vmsg(MANDOCERR_PAR_SKIP
, mdoc
->parse
,
2575 mdoc
->last
->line
, mdoc
->last
->pos
, "%s after %s",
2576 roff_name
[mdoc
->last
->tok
], roff_name
[np
->tok
]);
2577 roff_node_delete(mdoc
, mdoc
->last
);
2583 struct roff_node
*n
;
2587 n
->flags
|= NODE_NOPRT
;
2589 if (mdoc
->meta
.date
!= NULL
) {
2590 mandoc_msg(MANDOCERR_PROLOG_REP
, mdoc
->parse
,
2591 n
->line
, n
->pos
, "Dd");
2592 free(mdoc
->meta
.date
);
2593 } else if (mdoc
->flags
& MDOC_PBODY
)
2594 mandoc_msg(MANDOCERR_PROLOG_LATE
, mdoc
->parse
,
2595 n
->line
, n
->pos
, "Dd");
2596 else if (mdoc
->meta
.title
!= NULL
)
2597 mandoc_msg(MANDOCERR_PROLOG_ORDER
, mdoc
->parse
,
2598 n
->line
, n
->pos
, "Dd after Dt");
2599 else if (mdoc
->meta
.os
!= NULL
)
2600 mandoc_msg(MANDOCERR_PROLOG_ORDER
, mdoc
->parse
,
2601 n
->line
, n
->pos
, "Dd after Os");
2603 if (n
->child
== NULL
|| n
->child
->string
[0] == '\0') {
2604 mdoc
->meta
.date
= mdoc
->quick
? mandoc_strdup("") :
2605 mandoc_normdate(mdoc
, NULL
, n
->line
, n
->pos
);
2610 deroff(&datestr
, n
);
2612 mdoc
->meta
.date
= datestr
;
2614 mdoc
->meta
.date
= mandoc_normdate(mdoc
,
2615 datestr
, n
->line
, n
->pos
);
2623 struct roff_node
*nn
, *n
;
2628 n
->flags
|= NODE_NOPRT
;
2630 if (mdoc
->flags
& MDOC_PBODY
) {
2631 mandoc_msg(MANDOCERR_DT_LATE
, mdoc
->parse
,
2632 n
->line
, n
->pos
, "Dt");
2636 if (mdoc
->meta
.title
!= NULL
)
2637 mandoc_msg(MANDOCERR_PROLOG_REP
, mdoc
->parse
,
2638 n
->line
, n
->pos
, "Dt");
2639 else if (mdoc
->meta
.os
!= NULL
)
2640 mandoc_msg(MANDOCERR_PROLOG_ORDER
, mdoc
->parse
,
2641 n
->line
, n
->pos
, "Dt after Os");
2643 free(mdoc
->meta
.title
);
2644 free(mdoc
->meta
.msec
);
2645 free(mdoc
->meta
.vol
);
2646 free(mdoc
->meta
.arch
);
2648 mdoc
->meta
.title
= NULL
;
2649 mdoc
->meta
.msec
= NULL
;
2650 mdoc
->meta
.vol
= NULL
;
2651 mdoc
->meta
.arch
= NULL
;
2653 /* Mandatory first argument: title. */
2656 if (nn
== NULL
|| *nn
->string
== '\0') {
2657 mandoc_msg(MANDOCERR_DT_NOTITLE
,
2658 mdoc
->parse
, n
->line
, n
->pos
, "Dt");
2659 mdoc
->meta
.title
= mandoc_strdup("UNTITLED");
2661 mdoc
->meta
.title
= mandoc_strdup(nn
->string
);
2663 /* Check that all characters are uppercase. */
2665 for (p
= nn
->string
; *p
!= '\0'; p
++)
2666 if (islower((unsigned char)*p
)) {
2667 mandoc_vmsg(MANDOCERR_TITLE_CASE
,
2668 mdoc
->parse
, nn
->line
,
2669 nn
->pos
+ (p
- nn
->string
),
2670 "Dt %s", nn
->string
);
2675 /* Mandatory second argument: section. */
2681 mandoc_vmsg(MANDOCERR_MSEC_MISSING
,
2682 mdoc
->parse
, n
->line
, n
->pos
,
2683 "Dt %s", mdoc
->meta
.title
);
2684 mdoc
->meta
.vol
= mandoc_strdup("LOCAL");
2685 return; /* msec and arch remain NULL. */
2688 mdoc
->meta
.msec
= mandoc_strdup(nn
->string
);
2690 /* Infer volume title from section number. */
2692 cp
= mandoc_a2msec(nn
->string
);
2694 mandoc_vmsg(MANDOCERR_MSEC_BAD
, mdoc
->parse
,
2695 nn
->line
, nn
->pos
, "Dt ... %s", nn
->string
);
2696 mdoc
->meta
.vol
= mandoc_strdup(nn
->string
);
2698 mdoc
->meta
.vol
= mandoc_strdup(cp
);
2700 /* Optional third argument: architecture. */
2702 if ((nn
= nn
->next
) == NULL
)
2705 for (p
= nn
->string
; *p
!= '\0'; p
++)
2706 *p
= tolower((unsigned char)*p
);
2707 mdoc
->meta
.arch
= mandoc_strdup(nn
->string
);
2709 /* Ignore fourth and later arguments. */
2711 if ((nn
= nn
->next
) != NULL
)
2712 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, mdoc
->parse
,
2713 nn
->line
, nn
->pos
, "Dt ... %s", nn
->string
);
2719 struct roff_node
*n
, *nch
;
2722 post_delim_nb(mdoc
);
2728 macro
= !strcmp(nch
->string
, "Open") ? "Ox" :
2729 !strcmp(nch
->string
, "Net") ? "Nx" :
2730 !strcmp(nch
->string
, "Free") ? "Fx" :
2731 !strcmp(nch
->string
, "DragonFly") ? "Dx" : NULL
;
2733 mandoc_msg(MANDOCERR_BX
, mdoc
->parse
,
2734 n
->line
, n
->pos
, macro
);
2737 mdoc
->next
= ROFF_NEXT_SIBLING
;
2738 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Ns
);
2739 mdoc
->last
->flags
|= NODE_NOSRC
;
2740 mdoc
->next
= ROFF_NEXT_SIBLING
;
2742 mdoc
->next
= ROFF_NEXT_CHILD
;
2743 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "BSD");
2744 mdoc
->last
->flags
|= NODE_NOSRC
;
2751 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Ns
);
2752 mdoc
->last
->flags
|= NODE_NOSRC
;
2753 mdoc
->next
= ROFF_NEXT_SIBLING
;
2754 roff_word_alloc(mdoc
, n
->line
, n
->pos
, "-");
2755 mdoc
->last
->flags
|= NODE_NOSRC
;
2756 roff_elem_alloc(mdoc
, n
->line
, n
->pos
, MDOC_Ns
);
2757 mdoc
->last
->flags
|= NODE_NOSRC
;
2761 * Make `Bx's second argument always start with an uppercase
2762 * letter. Groff checks if it's an "accepted" term, but we just
2763 * uppercase blindly.
2766 *nch
->string
= (char)toupper((unsigned char)*nch
->string
);
2773 struct utsname utsname
;
2774 static char *defbuf
;
2776 struct roff_node
*n
;
2779 n
->flags
|= NODE_NOPRT
;
2781 if (mdoc
->meta
.os
!= NULL
)
2782 mandoc_msg(MANDOCERR_PROLOG_REP
, mdoc
->parse
,
2783 n
->line
, n
->pos
, "Os");
2784 else if (mdoc
->flags
& MDOC_PBODY
)
2785 mandoc_msg(MANDOCERR_PROLOG_LATE
, mdoc
->parse
,
2786 n
->line
, n
->pos
, "Os");
2791 * Set the operating system by way of the `Os' macro.
2792 * The order of precedence is:
2793 * 1. the argument of the `Os' macro, unless empty
2794 * 2. the -Ios=foo command line argument, if provided
2795 * 3. -DOSNAME="\"foo\"", if provided during compilation
2796 * 4. "sysname release" from uname(3)
2799 free(mdoc
->meta
.os
);
2800 mdoc
->meta
.os
= NULL
;
2801 deroff(&mdoc
->meta
.os
, n
);
2805 if (mdoc
->os_s
!= NULL
) {
2806 mdoc
->meta
.os
= mandoc_strdup(mdoc
->os_s
);
2811 mdoc
->meta
.os
= mandoc_strdup(OSNAME
);
2813 if (defbuf
== NULL
) {
2814 if (uname(&utsname
) == -1) {
2815 mandoc_msg(MANDOCERR_OS_UNAME
, mdoc
->parse
,
2816 n
->line
, n
->pos
, "Os");
2817 defbuf
= mandoc_strdup("UNKNOWN");
2819 mandoc_asprintf(&defbuf
, "%s %s",
2820 utsname
.sysname
, utsname
.release
);
2822 mdoc
->meta
.os
= mandoc_strdup(defbuf
);
2826 if (mdoc
->meta
.os_e
== MANDOC_OS_OTHER
) {
2827 if (strstr(mdoc
->meta
.os
, "OpenBSD") != NULL
)
2828 mdoc
->meta
.os_e
= MANDOC_OS_OPENBSD
;
2829 else if (strstr(mdoc
->meta
.os
, "NetBSD") != NULL
)
2830 mdoc
->meta
.os_e
= MANDOC_OS_NETBSD
;
2834 * This is the earliest point where we can check
2835 * Mdocdate conventions because we don't know
2836 * the operating system earlier.
2839 if (n
->child
!= NULL
)
2840 mandoc_vmsg(MANDOCERR_OS_ARG
, mdoc
->parse
,
2841 n
->child
->line
, n
->child
->pos
,
2842 "Os %s (%s)", n
->child
->string
,
2843 mdoc
->meta
.os_e
== MANDOC_OS_OPENBSD
?
2844 "OpenBSD" : "NetBSD");
2846 while (n
->tok
!= MDOC_Dd
)
2847 if ((n
= n
->prev
) == NULL
)
2849 if ((n
= n
->child
) == NULL
)
2851 if (strncmp(n
->string
, "$" "Mdocdate", 9)) {
2852 if (mdoc
->meta
.os_e
== MANDOC_OS_OPENBSD
)
2853 mandoc_vmsg(MANDOCERR_MDOCDATE_MISSING
,
2854 mdoc
->parse
, n
->line
, n
->pos
,
2855 "Dd %s (OpenBSD)", n
->string
);
2857 if (mdoc
->meta
.os_e
== MANDOC_OS_NETBSD
)
2858 mandoc_vmsg(MANDOCERR_MDOCDATE
,
2859 mdoc
->parse
, n
->line
, n
->pos
,
2860 "Dd %s (NetBSD)", n
->string
);
2865 mdoc_a2sec(const char *p
)
2869 for (i
= 0; i
< (int)SEC__MAX
; i
++)
2870 if (secnames
[i
] && 0 == strcmp(p
, secnames
[i
]))
2871 return (enum roff_sec
)i
;
2877 macro2len(enum roff_tok macro
)