diff options
-rw-r--r-- | regress/usr.bin/mdoclint/mdoclint | 405 |
1 files changed, 261 insertions, 144 deletions
diff --git a/regress/usr.bin/mdoclint/mdoclint b/regress/usr.bin/mdoclint/mdoclint index 587bc67390a..838cbf46f05 100644 --- a/regress/usr.bin/mdoclint/mdoclint +++ b/regress/usr.bin/mdoclint/mdoclint @@ -1,7 +1,7 @@ #!/usr/bin/perl # -# $OpenBSD: mdoclint,v 1.12 2008/12/11 14:49:02 espie Exp $ -# $NetBSD: mdoclint,v 1.18 2008/01/05 09:03:50 wiz Exp $ +# $OpenBSD: mdoclint,v 1.13 2008/12/31 10:55:11 espie Exp $ +# $NetBSD: mdoclint,v 1.26 2008/11/22 14:47:28 wiz Exp $ # # Copyright (c) 2001-2008 Thomas Klausner # All rights reserved. @@ -36,6 +36,11 @@ $| = 1; package Parser; use Getopt::Std; +use constant { + OPENBSD => 1, + NETBSD => 0 +}; + use vars qw( $opt_a $opt_D $opt_d $opt_e $opt_F $opt_f $opt_H $opt_h $opt_m $opt_n $opt_o $opt_P $opt_p $opt_r $opt_S $opt_s $opt_v @@ -47,7 +52,10 @@ my $arch=`uname -m`; chomp($arch); my $options="aDdeFfHhmnoPprSsvXx"; -sub usage { +sub usage + { + my $default = OPENBSD ? "-aDdfHmnoPprSsXx" : "-aDdefHmnoPprSsXx"; + print STDERR <<"EOF"; mdoclint: verify man page correctness usage: mdoclint [-$options] file ... @@ -70,7 +78,7 @@ usage: mdoclint [-$options] file ... -v verbose output -X warn about explicit mentions of FreeBSD, NetBSD, or OpenBSD -x warn about cross-references with missing targets -Default is -aDdfHmnoPprSsXx if no flag is specified. +Default is $default if no flag is specified. EOF exit(0); } @@ -82,62 +90,117 @@ my %short = ( "Open" => ".Ox" ); -my %sections = ( - "NAME" => 1, - "SYNOPSIS" => 2, - "DESCRIPTION" => 3, - "RETURN VALUES" => 4, - "ENVIRONMENT" => 5, - "FILES" => 6, - "EXAMPLES" => 7, - "DIAGNOSTICS" => 8, - "ERRORS" => 9, - "SEE ALSO" => 10, - "STANDARDS" => 11, - "HISTORY" => 12, - "AUTHORS" => 13, - "CAVEATS" => 14, - "BUGS" => 15 -); +# constants to build +my %sections; +my $arches_re; +my $sections_re; +my $esections_re; +my $valid_date_re; +# and the code that builds them +{ + my @sections = ( + "NAME", + NETBSD ? "LIBRARY" : undef, + "SYNOPSIS", + "DESCRIPTION", + NETBSD ? "EXIT STATUS" : undef, + "RETURN VALUES", + "ENVIRONMENT", + "FILES", + "EXAMPLES", + "DIAGNOSTICS", + "ERRORS", + "SEE ALSO", + "STANDARDS", + "HISTORY", + "AUTHORS", + "CAVEATS", + "BUGS", + NETBSD ? "SECURITY CONSIDERATIONS" : undef + ); + + my $i = 1; + for my $sh (@sections) { + if (defined $sh) { + $sections{$sh} = $i++; + } + } + my @arches; + if (OPENBSD) { + @arches = + (qw(alpha amd64 arm armish aviion cats hp300 hppa + hppa64 i386 landisk luna88k mac68k macppc mvme68k + mvme88k sgi socppc sparc sparc64 vax zaurus)); + } + if (NETBSD) { + @arches = + (qw(acorn26 acorn32 algor alpha amiga arc atari + bebox cats cesfic cobalt dreamcast evbarm evbmips + evbsh3 evbsh5 hp300 hp700 hpcarm hpcmips hpcsh + i386 ibmnws luna68k mac68k macppc mipsco mmeye + mvme68k mvmeppc netwinder news68k newsmips next68k + pc532 playstation2 pmax pmppc prep sandpoint sbmips + sgimips shark sparc sparc64 sun2 sun3 vax walnut + x68k x86_64)); + } + my $a = join('|', @arches); + $arches_re = qr{(?:$a)}o; + if (OPENBSD) { + $sections_re = qr{(?:3p|[1-9])}o; + $esections_re = qr{(?:3p|[0-9])}o; + } + if (NETBSD) { + $sections_re = qr{[1-9]}o; + $esections_re = qr{[0-9]}o; + } + if (OPENBSD) { + $valid_date_re = qr{\$Mdocdate\b}; + } + if (NETBSD) { + $valid_date_re = qr{(?:January|February|March|April|May|June|July|August|September|October|November|December)\s*[1-9][0-9]*,\s*(?:198[0-9]|199[0-9]|200[012345678])$}o; + } +} -sub debug +sub debug { my $self = shift; print STDOUT "debug: $self->{fn}:$self->{ln}: @_\n" if $opt_v; } -sub warning +sub warning { my $self = shift; - print STDOUT "$self->{fn}:$self->{ln}: @_\n"; + print STDOUT "$self->{fn}:$self->{current_section_header}:$self->{ln}: ", join('', @_), "\n"; } -sub handle_options +sub handle_options { getopts($options); $opt_h and usage(); # default to all warnings if no flag is set - unless ($opt_a or $opt_D or $opt_d or $opt_e or $opt_f or $opt_H - or $opt_m or $opt_n or $opt_o or $opt_P or $opt_p or $opt_r + unless ($opt_a or $opt_D or $opt_d or $opt_e or $opt_f or $opt_H + or $opt_m or $opt_n or $opt_o or $opt_P or $opt_p or $opt_r or $opt_S or $opt_s or $opt_X or $opt_x) { - $opt_a = $opt_D = $opt_d = $opt_f = $opt_H = $opt_m = - $opt_n = $opt_o = $opt_P = $opt_p = $opt_r = $opt_S = + $opt_a = $opt_D = $opt_d = $opt_f = $opt_H = $opt_m = + $opt_n = $opt_o = $opt_P = $opt_p = $opt_r = $opt_S = $opt_s = $opt_X = $opt_x = 1; + $opt_e = 1 if NETBSD; } } -sub verify_xref +sub verify_xref { my ($self, $page, $section, $pre, $post) = @_; if ("$page.$section" eq $self->{fn}) { $self->warning("Xref to itself (use .Nm instead)"); } # try to find corresponding man page - for my $dir ("/usr/share/man", "/usr/X11R6/man") { + for my $dir ("/usr/share/man", + OPENBSD ? "/usr/X11R6/man" : "/usr/X11R7/man") { for my $a ("", $arch) { - for my $page ("cat$section/$a/$page.0", + for my $page ("cat$section/$a/$page.0", "man$section/$a/$page.$section") { return 1 if -f "$dir/$page"; } @@ -158,7 +221,8 @@ sub new all => '', lastline => '', changes => 0, - rcsidseen => 0, + oxrcsidseen => 0, + nxrcsidseen => 0, lastsh => 0, sasection => 0, saname => '', @@ -167,7 +231,7 @@ sub new inliteral => 0, shseen => {}, last_error_name => '', - current_section => '', + current_section_header => '', fn => $fn }; open my $input, '<', $fn or die "can't open input file $fn"; @@ -194,45 +258,115 @@ sub close close($self->{file}); } +sub parse_macro_args +{ + my ($s, $string) = @_; + my $_ = $string; + my @params = (); + while (!/^$/) { + if (s/^\"(.*?)\"\s*//) { + push(@params, $1); + } elsif (s/^(\S+)\s*//) { + push(@params, $1); + } + } + if (@params > 9 and OPENBSD) { + $s->warning("$string holds >9 parameters"); + } + return @params; +} + +sub set_section_header +{ + my ($s, $section_header) = @_; + $section_header = join(' ', $s->parse_macro_args($section_header)); + if ($section_header eq 'SEE ALSO') { + $s->{insa} = 1; + } elsif ($s->{insa} == 1) { + if (not $s->{sarest} eq "") { + $s->warning("unneeded characters at end of ", + "SEE ALSO: ", "`$s->{sarest}'") if $opt_a; + # to avoid a second warning at EOF + $s->{sarest} = ""; + } + # finished SEE ALSO section + $s->{insa} = 2; + } + if ($opt_S) { + if (not $sections{$section_header}) { + $s->warning("unknown section header: ", + "`$section_header'"); + } else { + if ($s->{lastsh} >= $sections{$section_header}) { + $s->warning("section header ", + "`$section_header' in wrong order"); + } + $s->{shseen}->{$section_header} = 1; + $s->{lastsh} = $sections{$section_header}; + } + } + if ($s->{lastline} =~ /^\.Pp/o) { + $s->warning("Paragraph problem: section header after .Pp"); + } + + $s->{current_section_header} = $section_header; +} + sub process_line { my ($s, $_) = @_; chomp; + # always cut trailing spaces + if (/\s+$/o) { + $s->warning("trailing space: `$_'") if $opt_s; + s/\s+$//o; + $s->{changes} = 1; + } if (/\$OpenBSD\b.*\$/o) { - $s->{rcsidseen} = 1; + $s->{oxrcsidseen} = 1; + # nothing else to do + return; + } + if (/\$NetBSD\b.*\$/o) { + $s->{nxrcsidseen} = 1; + # nothing else to do + return; + } + # comments + if (/^\.\\\"/) { + return; } if (/^\.TH\s+/o) { - $s->warning("not mandoc") if $opt_m; - $s->{mandoc_p} = 0; -# /^.TH\s*[\w-_".]+\s*([1-9])/; -# $section = $1; + $s->warning("not mandoc") if $opt_m; + $s->{mandoc_p} = 0; +# /^.TH\s*[\w-_".]+\s*([1-9])/; +# $section = $1; + return; } # if (/^.Dt\s*[\w-_".]+\s*([1-9])/) { -# $section = $1; +# $section = $1; # } if ($opt_D and /^\.Dt\s+/o) { - if (! /^\.Dt\s+(?:[A-Z\d._-]+)\s+[1-9](?:\s+(?:alpha|amd64|arm|armish|aviion|cats|hp300|hppa|hppa64|i386|landisk|luna88k|mac68k|macppc|mvme68k|mvme88k|sgi|socppc|sparc|sparc64|vax|zaurus))?$/o) { + if (! /^\.Dt\s+(?:[A-Z\d._-]+)\s+$sections_re(?:\s+$arches_re)?$/o) { $s->warning("bad .Dt: `$_'"); } } - if ($opt_a) { - if ($s->{insa} == 0) { - if (/^\.Sh\s+SEE ALSO$/o) { - $s->{insa} = 1; + if ($s->{mandoc_p}) { + if (/^\.Sh\s+(.*)$/o) { + $s->set_section_header($1); + return; } - } elsif ($s->{insa} == 1) { - if (/^\.Sh\s+/o) { - if (not $s->{sarest} eq "") { - $s->warning("unneeded characters at end of SEE ALSO:". - "`$s->{sarest}'"); - # to avoid a second warning at EOF - $s->{sarest} = ""; - } - # finished SEE ALSO section - $s->{insa} = 2; + } else { + if (/^\.SH\s+(.*)$/o) { + $s->set_section_header($1); + return; } - if (/^\.Xr\s+(\S+)\s+(3p|[1-9])\s?(.*)?$/o) { + } + + if ($opt_a) { + if ($s->{insa} == 1) { + if (/^\.Xr\s+(\S+)\s+($sections_re)\s?(.*)?$/o) { my ($saname, $sasection, $sarest) = ($1, $2, $3); $saname =~ s/^\\&//o; if ($s->{sasection} gt $sasection @@ -260,84 +394,66 @@ sub process_line } if ($opt_f and /^\.Fn.*,.+/o) { - $s->warning("possible .Fn misuse: `$_'"); - } - if ($opt_H and not /^\.\\\"/o and (/^(?:[<>])/o or /[^\\][<>]/o)) { - $s->warning("use \*(Lt \*(Gt (or .Aq) instead of < >: `$_'"); + $s->warning("possible .Fn misuse: `$_'"); } - if ($opt_S) { - if (/^\.Sh\s+(.*)\s*$/o) { - if (not $sections{$1}) { - $s->warning("unknown section header: `$1'"); - } else { - if ($s->{lastsh} >= $sections{$1}) { - $s->warning("section header `$1' in wrong order"); - } - $s->{shseen}->{$1} = 1; - $s->{lastsh} = $sections{$1}; + if (OPENBSD) { + if ($opt_H and (/^(?:[<>])/o or /[^\\][<>]/o)) { + $s->warning("use \*(Lt \*(Gt (or .Aq) ", + "instead of < >: `$_'"); } - } } - - if ($opt_s) { - if (/\s+$/o) { - $s->warning("trailing space: `$_'"); - if ($opt_F) { - s/\s+$//o; - $s->{changes} = 1; + if (NETBSD) { + if ($opt_H and (/^(?:[<>&])/o or /[^\\][<>&]/o)) { + $s->warning("use \*[Lt] \*[Gt] (or .Aq) \*[Am] ", + "instead of < > &: `$_'"); } - } -# this check only bought us something for phantasia.6 -# # we don't want to match at BOF -# if (/.\.\t/) { -# $s->warning("tab after dot (`.') -- probably malformatting: $_"); -# } } + if ($opt_X) { - if (not /^\.\\\"/o and /\b(Free|Net|Open)BSD\b/o - and not /\b(?:www|ftp)\.(?:Free|Net|Open)BSD\.org\b/o - and not /\bOpenBSD\::.*3p\b/o - and not /\/pub\/OpenBSD\//o - and not /\@(?:Free|Net|Open)BSD\.(?i:org)\b/o) { - $s->warning("verbose mention of `$1BSD' instead of " - . "`$short{$1}': `$_'"); - } - if (/^\./o and (/Bx (Open)/o or /Bx (Free)/o or /Bx (Net)/o)) { - $s->warning("`.Bx $1' found -- use $short{$1} instead"); - } + if (/\b(Free|Net|Open)BSD\b/o + and not /\b(?:www|ftp)\.(?:Free|Net|Open)BSD\.org\b/o + and not /\bOpenBSD\::.*3p\b/o + and not /\/pub\/OpenBSD\//o + and not /\@(?:Free|Net|Open)BSD\.(?i:org)\b/o) { + $s->warning("verbose mention of `$1BSD' instead of " + . "`$short{$1}': `$_'"); + } + if (/^\./o and (/Bx (Open)/o or /Bx (Free)/o or /Bx (Net)/o)) { + $s->warning("`.Bx $1' found -- use $short{$1} instead"); + } } if ($opt_o) { - if (/^\.Os/o && !/^\.Os\s*$/o) { - /^\.Os(.*)/o; - $s->warning(".Os used with argument `$1'"); - } + if (/^\.Os\s+(.+)/o) { + $s->warning(".Os used with argument `$1'"); + } } if ($opt_n) { - if (/^\.Nd/o and /\.\s*$/o) { - $s->warning(".Nd ends with a dot: `$_'"); - } + if (/^\.Nd.*\.$/o) { + $s->warning(".Nd ends with a dot: `$_'"); + } } if ($opt_p) { - if (not /^\.\\\"/o and /\w\w\.\s+[A-Z]/o) { - $s->warning("new sentence, new line: `$_'"); - } - if (not /^\.\\\"/o and /^\... .*[^\s][\.();,\[\]\{\}:]$/o - and not /\s\.\.\.$/o and not /\\&.$/o) { - $s->warning("punctuation in format string without space: `$_'"); - } - if (not /^\.\\\"/o and /^\./o and /Ns [\.();,\[\]\{\}:]/o) { - $s->warning("possible Ns abuse: `$_'"); - } - if (/(\w+)\(\)/o) { - $s->warning("use .Fn or .Xr for functions: `$1()'"); - } + if (/\w\w\.\s+[A-Z]/o) { + $s->warning("new sentence, new line: `$_'"); + } + if (/^\... .*[^\s][\.();,\[\]\{\}:]$/o + and not /\s\.\.\.$/o and not /\\&.$/o) { + $s->warning("punctuation in format string ", + "without space: `$_'"); + } + if (/^\./o and /Ns [\.();,\[\]\{\}:]/o) { + $s->warning("possible Ns abuse: `$_'"); + } + if (/(\w+)\(\)/o) { + $s->warning("use .Fn or .Xr for functions: `$1()'"); + } } if ($opt_x) { if ($s->{mandoc_p}) { my $destruct = $_; $destruct =~ s/\\\&([\w\.])/$1/o; - if ($destruct =~ /^\.Xr\s+([\w\:\.\-\+\/]+)\s+(3p|[0-9])(.*)/o) { + if ($destruct =~ /^\.Xr\s+([\w\:\.\-\+\/]+)\s+($esections_re)(.*)/o) { $s->debug("Xref to $1($2) found: `$_'"); $s->verify_xref($1, $2, "", ""); if ($3 =~ /^\S/o) { @@ -350,54 +466,52 @@ sub process_line my $destruct = $_; $destruct =~ s/\\f.//go; if ($destruct !~ /^\.\\\"/o) { - while ($destruct =~ s/([-\w.]+)\s*\((3p|[0-9])\)//o) { + while ($destruct =~ s/([-\w.]+)\s*\(($esections_re)\)//o) { $s->debug("possible Xref to $1($2) found: `$_'"); $s->verify_xref($1, $2, "possible ", ": `$_'"); # so that we have a chance to find more than one # per line - $destruct =~ s/(\w+)\s*\((3p|[0-9])\)//o; + $destruct =~ s/(\w+)\s*\(($sections_re)\)//o; } } } } if ($opt_d) { - if (/^\.Dd/o and not /^\.Dd [\$]Mdocdate\b/o) { - $s->warning("Invalid date found: `$_'"); - } + if (/^\.Dd/o and not /^\.Dd\s+$valid_date_re/o) { + $s->warning("Invalid date found: `$_'"); + } } if ($opt_P) { - if (/^\.Bd\b.*-literal/o) { - $s->{inliteral} = 1; - } - if ($s->{inliteral} == 1) { - if (/^\.Ed\b/o) { - $s->{inliteral} = 0; + if (/^\.Bd\b.*-literal\b/o) { + $s->{inliteral} = 1; + } + if ($s->{inliteral} == 1) { + if (/^\.Ed\b/o) { + $s->{inliteral} = 0; + } + } elsif (/^$/o) { + $s->warning("Paragraph problem: empty line -- ", + "use .Pp for paragraphs"); + } + if ($s->{lastline} =~ /^\.Pp/o and /^(\.Ss|\.Pp)/o) { + $s->warning("Paragraph problem: $1 after .Pp"); + } + if (/^\.Pp/o and $s->{lastline} =~ /^(\.S[Ssh])/o) { + $s->warning("Paragraph problem: .Pp after $1"); } - } elsif (/^$/o) { - $s->warning("Paragraph problem: empty line -- use .Pp for". - " paragraphs"); - } - if ($s->{lastline} =~ /^\.Pp/o and (/^(\.S[sh])/o or /^(\.Pp)/o)) { - $s->warning("Paragraph problem: $1 after .Pp"); - } - if (/^\.Pp/o and $s->{lastline} =~ /^(\.S[sh])/o) { - $s->warning("Paragraph problem: .Pp after $1"); - } } # Check whether the list of possible errors for a function is # sorted alphabetically. # if ($opt_e) { - /^\.Sh\s+(.*)$/o and $s->{current_section} = $1; - # Error names should not be sorted across different lists. # (see bind(2) for an example.) # /^\.Bl\s+/o and $s->{last_error_name} = ""; - if ($s->{current_section} eq "ERRORS" and + if ($s->{current_section_header} eq "ERRORS" and /^\.It\s+Bq\s+Er\s+(E[\w_]+)$/o) { my $current_error_name = $1; @@ -421,8 +535,11 @@ sub finish { my ($s) = @_; - if ($opt_r and not $s->{rcsidseen}) { - $s->warning("Missing RCS Id"); + if (NETBSD and not $s->{nxrcsidseen}) { + $s->warning("Missing RCS Id") if $opt_r; + } + if (OPENBSD and not $s->{oxrcsidseen}) { + $s->warning("Missing RCS Id") if $opt_r; } if ($opt_P and $s->{lastline} =~ /^\.Pp/o) { @@ -447,7 +564,7 @@ sub finish package main; -sub handle_file +sub handle_file { my $parser = Parser->new($_[0]); @@ -458,7 +575,7 @@ sub handle_file $parser->finish; $parser->close; if ($Parser::opt_F and $parser->{changes}) { - open OUT, ">$_[0].new" or + open OUT, ">$_[0].new" or die "can't open output file `$_[0].new'"; print OUT $parser->{all}; close OUT; |