head	1.30;
access;
symbols;
locks; strict;
comment	@# @;


1.30
date	2009.03.01.21.27.04;	author wiz;	state dead;
branches;
next	1.29;

1.29
date	2009.03.01.17.52.35;	author wiz;	state Exp;
branches;
next	1.28;

1.28
date	2009.01.11.18.09.09;	author wiz;	state Exp;
branches;
next	1.27;

1.27
date	2009.01.01.17.08.44;	author wiz;	state Exp;
branches;
next	1.26;

1.26
date	2008.11.22.14.47.28;	author wiz;	state Exp;
branches;
next	1.25;

1.25
date	2008.11.21.23.57.25;	author wiz;	state Exp;
branches;
next	1.24;

1.24
date	2008.11.21.22.13.55;	author wiz;	state Exp;
branches;
next	1.23;

1.23
date	2008.11.20.16.28.48;	author wiz;	state Exp;
branches;
next	1.22;

1.22
date	2008.11.03.08.49.12;	author wiz;	state Exp;
branches;
next	1.21;

1.21
date	2008.11.01.22.45.47;	author wiz;	state Exp;
branches;
next	1.20;

1.20
date	2008.11.01.17.49.19;	author wiz;	state Exp;
branches;
next	1.19;

1.19
date	2008.11.01.17.45.30;	author wiz;	state Exp;
branches;
next	1.18;

1.18
date	2008.01.05.09.03.50;	author wiz;	state Exp;
branches;
next	1.17;

1.17
date	2007.12.02.21.08.10;	author wiz;	state Exp;
branches;
next	1.16;

1.16
date	2007.01.01.23.39.15;	author wiz;	state Exp;
branches;
next	1.15;

1.15
date	2006.12.09.23.02.15;	author rillig;	state Exp;
branches;
next	1.14;

1.14
date	2006.11.18.00.35.21;	author rillig;	state Exp;
branches;
next	1.13;

1.13
date	2006.11.18.00.28.13;	author rillig;	state Exp;
branches;
next	1.12;

1.12
date	2006.01.12.20.31.22;	author wiz;	state Exp;
branches;
next	1.11;

1.11
date	2005.05.17.13.56.21;	author wiz;	state Exp;
branches;
next	1.10;

1.10
date	2005.03.26.11.30.55;	author wiz;	state Exp;
branches;
next	1.9;

1.9
date	2005.01.02.18.19.52;	author wiz;	state Exp;
branches;
next	1.8;

1.8
date	2004.01.01.03.51.06;	author wiz;	state Exp;
branches;
next	1.7;

1.7
date	2004.01.01.03.50.39;	author wiz;	state Exp;
branches;
next	1.6;

1.6
date	2003.10.19.09.13.17;	author wiz;	state Exp;
branches;
next	1.5;

1.5
date	2003.05.26.21.16.27;	author lha;	state Exp;
branches;
next	1.4;

1.4
date	2003.05.10.17.49.25;	author wiz;	state Exp;
branches;
next	1.3;

1.3
date	2003.05.09.09.03.19;	author wiz;	state Exp;
branches;
next	1.2;

1.2
date	2003.05.09.08.54.26;	author wiz;	state Exp;
branches;
next	1.1;

1.1
date	2003.05.09.08.52.17;	author wiz;	state Exp;
branches;
next	;


desc
@@


1.30
log
@Remove mdoclint -- use pkgsrc/textproc/mdoclint as main repository.
@
text
@#!/usr/pkg/bin/perl
#
# $OpenBSD: mdoclint,v 1.13 2008/12/31 10:55:11 espie Exp $
# $NetBSD: mdoclint,v 1.29 2009/03/01 17:52:35 wiz Exp $
#
# Copyright (c) 2001-2009 Thomas Klausner
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR, THOMAS KLAUSNER,
# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#

use strict;
use warnings;

$| = 1;

package Parser;
use Getopt::Std;

use constant {
	OPENBSD => 0,
	NETBSD => 1
};

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
	$opt_X $opt_x
);


my $arch=`uname -m`;
chomp($arch);
my $options="aDdeFfHhmnoPprSsvXx";

sub usage
	{
	my $default = OPENBSD ? "-aDdfHmnoPprSsXx" : "-aDdefHmnoPprSsXx";
	
	print STDERR <<"EOF";
mdoclint: verify man page correctness
usage:	mdoclint [-$options] file ...
	-a	warn about SEE ALSO section problems
	-D	warn about bad casing and archs in .Dt
	-d	warn about bad date strings (in .Dd only)
	-e	warn about unsorted errors (for functions)
	-F	fix whitespace problems (asks before overwriting)
	-f	warn about possible incorrect .Fn syntax
	-H	warn about characters that produce problems in HTML output
	-h	display this help text
	-m	warn about man pages that are not in mdoc(7) format
	-n	warn about .Nd's ending in '.'
	-o	warn about non-empty .Os strings
	-P	warn about paragraph problems
	-p	warn about punctuation problems
	-r	warn about missing RCS Id
	-S	warn about any .Sh weirdness
	-s	warn about whitespace problems
	-v	verbose output
	-X	warn about explicit mentions of FreeBSD, NetBSD, or OpenBSD
	-x	warn about cross-references with missing targets
Default is $default if no flag is specified.
EOF
	exit(0);
}


my %short = (
    "Free" => ".Fx",
    "Net" => ".Nx",
    "Open" => ".Ox"
);

# 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
{
	my $self = shift;
    	print STDOUT "debug: $self->{fn}:$self->{ln}: @@_\n" if $opt_v;
}

sub warning
{
	my $self = shift;
	print STDOUT "$self->{fn}:$self->{current_section_header}:$self->{ln}: ", join('', @@_), "\n";
}

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
	    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_s = $opt_X = $opt_x = 1;
		    $opt_e = 1 if NETBSD;
	}
}


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",
	    OPENBSD ? "/usr/X11R6/man" : "/usr/X11R7/man") {
		for my $a ("", $arch) {
			for my $page ("cat$section/$a/$page.0",
			    "man$section/$a/$page.$section") {
				return 1 if -f "$dir/$page";
			}
		}
	}
	return 1 if -f "./$page.$section";

	$self->warning($pre."trailing Xref to $page($section)$post");
	return 0;
}

sub new
{
	my ($class, $fn) = @@_;

	my $o = {
		mandoc_p => 1,
		all => '',
		lastline => '',
		changes => 0,
		oxrcsidseen => 0,
		nxrcsidseen => 0,
		lastsh => 0,
		sasection => 0,
		saname => '',
		sarest => ',',
		insa => 0,
		inliteral => 0,
		shseen => {},
		last_error_name => '',
		current_section_header => '',
		fn => $fn
	};
	open my $input, '<', $fn or die "can't open input file $fn";
	$o->{file} = $input;
	$o->{ln} = 0;
	bless $o, $class;
}

sub next_line
{
	my ($self) = @@_;

	my $l = readline($self->{file});
	if (defined $l) {
		$self->{ln}++;
	}
	return $l;
}

sub close
{
	my ($self) = @@_;

	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->{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;
		return;
	}
#	if (/^.Dt\s*[\w-_".]+\s*([1-9])/) {
#	    	$section = $1;
#	}
	if ($opt_D and /^\.Dt\s+/o) {
	    if (! /^\.Dt\s+(?:[A-Z\d._-]+)\s+$sections_re(?:\s+$arches_re)?$/o)  {
		$s->warning("bad .Dt: `$_'");
	    }
	}

	if ($s->{mandoc_p}) {
		if (/^\.Sh\s+(.*)$/o) {
			$s->set_section_header($1);
			return;
		}
	} else {
		if (/^\.SH\s+(.*)$/o) {
			$s->set_section_header($1);
			return;
		}
	}

	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
			or ($s->{sasection} eq $sasection and
			    ($s->{saname} cmp $saname) > 0)) {
			$s->warning("SEE ALSO: `.Xr $s->{saname} $s->{sasection}' should "
				. "be after `.Xr $saname $sasection'");
		    }
		    if ($s->{sarest} ne ",") {
			$s->warning("SEE ALSO: .Xr not separated by ".
				"comma, but `$s->{sarest}'");
		    }
		    $s->{saname} = $saname;
		    $s->{sasection} = $sasection;
		    $s->{sarest} = $sarest;
		}
		if (/^\.Rs(?:\s+|$)/o) {
		    if ($s->{sarest} ne "") {
			$s->warning("SEE ALSO: Not necessary to separate".
				" .Xr from .Rs by `$s->{sarest}'");
		    }
		    $s->{sarest} = "";
		}
	    }
	}

	if ($opt_f and /^\.Fn.*,.+/o) {
		$s->warning("possible .Fn misuse: `$_'");
	}
	if (OPENBSD) {
		if ($opt_H and (/^(?:[<>])/o or /[^\\][<>]/o)) {
			$s->warning("use \*(Lt \*(Gt (or .Aq) ",
			    "instead of < >: `$_'");
		}
	}
	if (NETBSD) {
		if ($opt_H and (/^(?:[<>&])/o or /[^\\][<>&]/o)) {
			$s->warning("use \*[Lt] \*[Gt] (or .Aq) \*[Am] ",
			    "instead of < > &: `$_'");
		}
	}

	if ($opt_X) {
		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\s+(.+)/o) {
			$s->warning(".Os used with argument `$1'");
		}
	}

	if ($opt_n) {
		if (/^\.Nd.*\.$/o) {
			$s->warning(".Nd ends with a dot: `$_'");
		}
	}
	if ($opt_p) {
		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+($esections_re)(.*)/o) {
		    $s->debug("Xref to $1($2) found: `$_'");
		    $s->verify_xref($1, $2, "", "");
		    if ($3 =~ /^\S/o) {
			$s->warning("No space after section number in Xref: `$_'");
		    }
		} elsif ($destruct =~ /^\.Xr/o) {
		    $s->warning("Weird Xref found: `$_'");
		}
	    } else {
		my $destruct = $_;
		$destruct =~ s/\\f.//go;
		if ($destruct !~ /^\.\\\"/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*\(($sections_re)\)//o;
		    }
		}
	    }
	}
	if ($opt_d) {
		if (/^\.Dd/o and not /^\.Dd\s+$valid_date_re/o) {
			$s->warning("Invalid date found: `$_'");
		}
	}

	if ($opt_P) {
		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");
		}
	}

	# Check whether the list of possible errors for a function is
	# sorted alphabetically.
	#
	if ($opt_e) {
		# 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_header} eq "ERRORS" and
		    /^\.It\s+Bq\s+Er\s+(E[\w_]+)$/o) {
			my $current_error_name = $1;

			if ($s->{last_error_name} eq $current_error_name) {
				$s->warning("Duplicate item for "
					. "$current_error_name.");
			} elsif ($current_error_name lt $s->{last_error_name}) {
				$s->warning("$s->{last_error_name} and "
					. "$current_error_name are not in "
					. "alphabetical order.");
			}
			$s->{last_error_name} = $current_error_name;
		}
	}

	$s->{lastline} = $_;
	$s->{all} .= "$_\n";
}

sub finish
{
	my ($s) = @@_;

	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) {
	    $s->warning("Paragraph problem: .Pp at EOF");
	}

	if ($opt_a and $s->{insa} > 0 and not $s->{sarest} eq "") {
	    $s->warning("unneeded characters at end of SEE ALSO: `$s->{sarest}'");
	}

#    	if (not ($fn =~ /$section$/)) {
#		$s->warning("section doesn't match (internal value: $section)");
#    	}
	if ($s->{mandoc_p} and $opt_S) {
	    foreach my $i (qw (NAME SYNOPSIS DESCRIPTION)) {
		if (not ($s->{shseen}{$i})) {
		    $s->warning("missing $i section");
		}
	    }
	}
}

package main;

sub handle_file
{
	my $parser = Parser->new($_[0]);

	while (my $_ = $parser->next_line) {
		$parser->process_line($_);
	}

	$parser->finish;
	$parser->close;
	if ($Parser::opt_F and $parser->{changes}) {
		open OUT, ">$_[0].new" or
		    die "can't open output file `$_[0].new'";
		print OUT $parser->{all};
		close OUT;
		system("mv -i $_[0].new $_[0]");
	}
}

Parser->handle_options;
foreach my $file (@@ARGV) {
    handle_file($file);
}
@


1.29
log
@Merge OpenBSD v1.13:
Lots of cleanup by Marc Espie, and much simpler to share with OpenBSD.
@
text
@d4 1
a4 1
# $NetBSD: mdoclint,v 1.26 2008/11/22 14:47:28 wiz Exp $
@


1.28
log
@Oops, allow 2008 in dates as well.
@
text
@d3 2
a4 2
# $OpenBSD: mdoclint,v 1.10 2008/11/07 20:20:33 espie Exp $
# $NetBSD: mdoclint,v 1.27 2009/01/01 17:08:44 wiz Exp $
d34 3
d39 4
a42 1
$| = 1;
d50 1
a50 3
my $fn;
my $file;
my $line;
d52 1
d55 5
a59 2
sub usage {
    print STDERR <<"EOF";
d81 1
a81 1
Default is -aDdefHmnoPprSsXx if no flag is specified.
d83 1
a83 1
    exit(0);
d86 1
d93 119
a211 54
my %sections = (
		"NAME" => 1,
		"LIBRARY" => 2,
		"SYNOPSIS" => 3,
		"DESCRIPTION" => 4,
		"EXIT STATUS" => 5,
		"RETURN VALUES" => 6,
		"ENVIRONMENT" => 7,
		"FILES" => 8,
		"EXAMPLES" => 9,
		"DIAGNOSTICS" => 10,
		"ERRORS" => 11,
		"SEE ALSO" => 12,
		"STANDARDS" => 13,
		"HISTORY" => 14,
		"AUTHORS" => 15,
		"CAVEATS" => 16,
		"BUGS" => 17,
		"SECURITY CONSIDERATIONS" => 18
		);

sub debug {
    print STDOUT "debug: $fn:$line: $_[0]\n" if $opt_v;
}

sub warning {
    print STDOUT "$fn:$line: $_[0]\n";
}

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 or $opt_S or $opt_s
	or $opt_X or $opt_x) {
	$opt_a = $opt_D = $opt_d = $opt_e = $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;
    }
}

sub verify_xref {
    # try to find corresponding man page
    my $xreftarget = "/usr/share/man/man$_[2]/$_[1].$_[2]";
    my $xreftarget2 = "/usr/share/man/man$_[2]/$arch/$_[1].$_[2]";
    my $xreftarget3 = "./$_[1].$_[2]";
    if ("$_[1].$_[2]" eq $fn) {
	warning("Xref to itself (use .Nm instead)");
    }
    if (not -f $xreftarget and not -f $xreftarget2
	and not -f $xreftarget3) {
	warning("$_[0]trailing Xref to $_[1]($_[2])$_[3]");
a212 2
    }
    return 1;
d215 115
a329 25
sub handle_file {
    my $mandoc_p = 1;
    my $destruct;
    my $all = "";
    my $last = "";
    my $changes = 0;
    my $rcsidseen = 0;
#    my $section = 0;
    my $lastsh = 0;
    my $saname = "";
    my $sasection = 0;
    my $sarest = ",";
    my $insa = 0;
    my $inliteral = 0;
    my %shseen = ();
    my $last_error_name = "";
    my $current_section = "";

    $line = 0;
    $fn = $_[0];
    open INPUT, $_[0] or die "can't open input file $_[0]";

    while (<INPUT>) {
	$line++;
	chomp();
d331 7
a337 1
	    $rcsidseen = 1;
d340 5
a344 4
	    warning("not mandoc") if $opt_m;
	    $mandoc_p = 0;
#	    /^.TH\s*[\w-_".]+\s*([1-9])/;
#	    $section = $1;
d347 1
a347 1
#	    $section = $1;
d349 3
a351 3
	if ($opt_D and /^.Dt\s+/o) {
	    if (! /^.Dt\s+(?:[A-Z\d._-]+)\s+[1-9](?:\s+(?: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))?$/o) {
		warning("bad .Dt: `$_'");
d355 12
d368 9
a376 11
	    if ($insa eq 0) {
		if (/^\.Sh\s+SEE ALSO$/o) {
		    $insa = 1;
		}
	    } elsif ($insa eq 1) {
		if (/^\.Sh\s+/o) {
		    if (not $sarest eq "") {
			warning("unneeded characters at end of SEE ".
				"ALSO: `$sarest'");
			# to avoid a second warning at EOF
			$sarest = "";
d378 3
a380 11
		    # finished SEE ALSO section
		    $insa = 2;
		}
		if (/^\.Xr\s+(\S+)\s+([1-9])\s?(.*)?$/o) {
		    my ($newsaname, $newsasection, $newsarest) = ($1, $2, $3);
		    $newsaname =~ s/^\\&//o;
		    if ($sasection gt $newsasection
			or ($sasection eq $newsasection and
			    ($saname cmp $newsaname) > 0)) {
			warning("SEE ALSO: `.Xr $saname $sasection' should "
				. "be after `.Xr $newsaname $newsasection'");
d382 3
a384 7
		    if ($sarest ne ",") {
			warning("SEE ALSO: .Xr not separated by ".
				"comma, but `$sarest'");
		    }
		    $saname = $newsaname;
		    $sasection = $newsasection;
		    $sarest = $newsarest;
d387 3
a389 3
		    if ($sarest ne "") {
			warning("SEE ALSO: Not necessary to separate".
				" .Xr from .Rs by `$sarest'");
d391 1
a391 1
		    $sarest = "";
d397 1
a397 1
	    warning("possible .Fn misuse: `$_'");
d399 5
a403 2
	if ($opt_H and not /^\.\\\"/o and (/^(?:[<>&])/o or /[^\\][<>&]/o)) {
	    warning("use \*[Lt] \*[Gt] (or .Aq) \*[Am] instead of < > &: `$_'");
d405 4
a408 10
	if ($opt_S) {
	    if (/^\.Sh\s+(.*)\s*$/o) {
		if (not $sections{$1}) {
		    warning("unknown section header: `$1'");
		} else {
		    if ($lastsh >= $sections{$1}) {
			warning("section header `$1' in wrong order");
		    }
		    $shseen{$1} = 1;
		    $lastsh = $sections{$1};
a409 1
	    }
d412 11
a422 6
	if ($opt_s) {
	    if (/\s+$/o) {
		warning("trailing space: `$_'");
		if ($opt_F) {
		    s/\s+$//o;
		    $changes = 1;
a423 17
	    }
# this check only bought us something for phantasia.6
#	    # we don't want to match at BOF
#	    if (/.\.\t/) {
#		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 /\@@(?:Free|Net|Open)BSD\.(?i:org)\b/o) {
		warning("verbose mention of `$1BSD' instead of "
			. "`$short{$1}': `$_'");
	    }
	    if (/^\./o and (/Bx (Open)/o or /Bx (Free)/o or /Bx (Net)/o)) {
		warning("`.Bx $1' found -- use $short{$1} instead");
	    }
d426 3
a428 4
	    if (/^\.Os/o && !/^\.Os\s*$/o) {
		/^\.Os(.*)/o;
		warning(".Os used with argument `$1'");
	    }
d432 3
a434 3
	    if (/^\.Nd/o and /\.\s*$/o) {
		warning(".Nd ends with a dot: `$_'");
	    }
d437 14
a450 13
	    if (not /^\.\\\"/o and /\w\w\.\s+[A-Z]/o) {
		warning("new sentence, new line: `$_'");
	    }
	    if (not /^\.\\\"/o and /^\... .*[^\s][\.();,\[\]\{\}:]$/o
		and not /\s\.\.\.$/o and not /\\&.$/o) {
		warning("punctuation in format string without space: `$_'");
	    }
	    if (not /^\.\\\"/o and /^\./o and /Ns [\.();,\[\]\{\}:]/o) {
		warning("possible Ns abuse: `$_'");
	    }
	    if (/(\w+)\(\)/o) {
		warning("use .Fn or .Xr for functions: `$1()'");
	    }
d453 2
a454 2
	    if ($mandoc_p) {
		$destruct = $_;
d456 3
a458 3
		if ($destruct =~ /^\.Xr\s+([\w\.\-\+\/]+)\s+([0-9])(.*)/o) {
		    debug("Xref to $1($2) found: `$_'");
		    verify_xref("", $1, $2, "");
d460 1
a460 1
			warning("No space after section number in Xref: `$_'");
d463 1
a463 1
		    warning("Weird Xref found: `$_'");
d466 1
a466 1
		$destruct = $_;
d469 3
a471 3
		    while ($destruct =~ s/([-\w.]+)\s*\(([0-9])\)//o) {
			debug("possible Xref to $1($2) found: `$_'");
			verify_xref("possible ", $1, $2, ": `$_'");
d474 1
a474 1
			$destruct =~ s/(\w+)\s*\(([0-9])\)//o;
d480 3
a482 3
	    if (/^.Dd/o and not /^.Dd\s*(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[0123456789])$/o) {
		warning("Invalid date found: `$_'");
	    }
d486 17
a502 17
	    if (/^\.Bd\b.*-literal/o) {
		$inliteral = 1;
	    }
	    if ($inliteral eq 1) {
		if (/^\.Ed\b/o) {
		    $inliteral = 0;
		}
	    } elsif (/^$/o) {
		warning("Paragraph problem: empty line -- use .Pp for".
			" paragraphs");
	    }
	    if ($last =~ /^\.Pp/o and (/^(\.S[sh])/o or /^(\.Pp)/o)) {
		warning("Paragraph problem: $1 after .Pp");
	    }
	    if (/^\.Pp/o and $last =~ /^(\.S[sh])/o) {
		warning("Paragraph problem: .Pp after $1");
	    }
a508 2
		/^\.Sh\s+(.*)$/o and $current_section = $1;

d512 1
a512 1
		/^\.Bl\s+/o and $last_error_name = "";
d514 1
a514 1
		if ($current_section eq "ERRORS" and
d518 2
a519 2
			if ($last_error_name eq $current_error_name) {
				warning("Duplicate item for "
d521 2
a522 2
			} elsif ($current_error_name lt $last_error_name) {
				warning("$last_error_name and "
d526 1
a526 1
			$last_error_name = $current_error_name;
d530 31
a560 31
	$last = $_;
	$all .= "$_\n";
    }

    if ($opt_r and not $rcsidseen) {
	warning("Missing RCS Id");
    }

    if ($opt_P and $last =~ /^\.Pp/o) {
	warning("Paragraph problem: .Pp at EOF");
    }

    if ($opt_a and $insa > 0 and not $sarest eq "") {
	warning("unneeded characters at end of SEE ALSO: `$sarest'");
    }

#    if (not ($fn =~ /$section$/)) {
#	warning("section doesn't match (internal value: $section)");
#    }
    close INPUT;
    if ($opt_F and $changes) {
	open OUT, ">$_[0].new" or die "can't open output file `$_[0].new'";
	print OUT $all;
	close OUT;
	system("mv -i $_[0].new $_[0]");
    }

    if ($mandoc_p and $opt_S) {
	foreach my $i (qw (NAME SYNOPSIS DESCRIPTION)) {
	    if (not ($shseen{$i})) {
		warning("missing $i section");
a562 1
    }
d565 23
a587 3
chomp($arch);
handle_options();
foreach $file (@@ARGV) {
@


1.27
log
@Update for 2009.
@
text
@d4 1
a4 1
# $NetBSD: mdoclint,v 1.26 2008/11/22 14:47:28 wiz Exp $
d329 1
a329 1
	    if (/^.Dd/o and not /^.Dd\s*(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[012345679])$/o) {
@


1.26
log
@Fix typo, from Jason McIntyre.
@
text
@d4 1
a4 1
# $NetBSD: mdoclint,v 1.25 2008/11/21 23:57:25 wiz Exp $
d6 1
a6 1
# Copyright (c) 2001-2008 Thomas Klausner
d329 1
a329 1
	    if (/^.Dd/o and not /^.Dd\s*(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) {
@


1.25
log
@Follow wording suggestions from Jason McIntyre (jmc@@openbsd.org).
@
text
@d4 1
a4 1
# $NetBSD: mdoclint,v 1.24 2008/11/21 22:13:55 wiz Exp $
d288 1
a288 1
		warning("new sentences, new line: `$_'");
@


1.24
log
@Warn about sentences starting in the middle of a line.
For consistency, they should start at the beginnings of new lines, for
proper spacing afterwards.
@
text
@d4 1
a4 1
# $NetBSD: mdoclint,v 1.23 2008/11/20 16:28:48 wiz Exp $
d288 1
a288 1
		warning("sentences should start on new lines: `$_'");
@


1.23
log
@Merge with latest OpenBSD version 1.10. OpenBSD commit message:

date: 2008/11/07 20:20:33;  author: espie;  state: Exp;  lines: +49 -51
clean-up regexps, replacing some . (any char) with \. (actual dot),
adding grouping parentheses, compiling whatever's compilable with /o,
restricting words with word boundaries. /b

okay grunk@@, jmc@@ (with help from jmc@@)

#10000 !

Oddly fitting, as my first commit was concerned with manpages as well...
@
text
@d4 1
a4 1
# $NetBSD: mdoclint,v 1.21 2008/11/01 22:45:47 wiz Exp $
d287 3
@


1.22
log
@Re-add '}', lost in previous.
@
text
@d3 1
a3 1
# $OpenBSD: mdoclint,v 1.7 2008/11/01 17:59:27 grunk Exp $
d48 1
a48 1
my $options="aDdefFHhmnoPprSsvXx";
d168 1
a168 1
	if (/\$NetBSD.*\$/) {
d171 1
a171 1
	if (/^\.TH/) {
d180 2
a181 2
	if ($opt_D and /^.Dt/) {
	    if (! /^.Dt\s+([A-Z0-9._-]+)\s+[1-9](\s+(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)|)$/)  {
d188 1
a188 1
		if (/^.Sh\s+SEE ALSO$/) {
d192 1
a192 1
		if (/^.Sh\s+/) {
d202 4
a205 6
		if (/^.Xr\s+(\S+)\s+([1-9]+)\s?(.*)?$/) {
		    my $newsaname = $1;
		    my $newsasection = $2;
		    my $newsarest = $3;
		    $newsaname =~ s/^\\&//;
		    if ($sasection > $newsasection
d211 1
a211 1
		    if (not $sarest eq ",") {
d219 2
a220 2
		if (/^.Rs(\s+|$)/) {
		    if (not $sarest eq "") {
d229 1
a229 1
	if ($opt_f and /^\.Fn.*,.+/) {
d232 1
a232 1
	if ($opt_H and not /^\.\\\"/ and (/^([<>&])/ or /[^\\][<>&]/)) {
d236 1
a236 1
	    if (/^\.Sh\s+(.*)\s*$/) {
d250 1
a250 1
	    if (/\s+$/) {
d253 1
a253 1
		    s/\s+$//;
d264 3
a266 3
	    if (not /^.\\\"/ and /(Free|Net|Open)BSD/
		and not /(www|ftp).(Free|Net|Open)BSD\.org/
		and not /@@(Free|Net|Open)BSD\.[oO][rR][gG]/) {
d270 1
a270 1
	    if (/^./ and (/Bx (Open)/ or /Bx (Free)/ or /Bx (Net)/)) {
d275 2
a276 2
	    if (/^.Os/ && !/^.Os\s*$/) {
		/^.Os(.*)/;
d282 1
a282 1
	    if (/^.Nd/ and /\.\s*$/) {
d287 2
a288 2
	    if (not /^.\\\"/ and /^\... .*[^\s][\.();,\[\]\{\}:]$/
		and not /\s\.\.\.$/ and not /\\&.$/) {
d291 1
a291 1
	    if (not /^.\\\"/ and /^\./ and /Ns [\.();,\[\]\{\}:]/) {
d294 1
a294 1
	    if (/(\w+)\(\)/) {
d301 2
a302 2
		$destruct =~ s/\\\&([\w\.])/$1/;
		if ($destruct =~ /^\.Xr\s+([\w\.\-\+\/]+)\s+([0-9])(.*)/) {
d305 1
a305 1
		    if ($3 =~ /^\S/) {
d308 1
a308 1
		} elsif ($destruct =~ /^\.Xr/) {
d313 3
a315 3
		$destruct =~ s/\\f.//g;
		if (not $destruct =~ /^\.\\\"/) {
		    while ($destruct =~ s/([-\w.]+)\s*\(([0-9])\)//) {
d320 1
a320 1
			$destruct =~ s/(\w+)\s*\(([0-9])\)//;
d326 1
a326 1
	    if (/^.Dd/ and not /^.Dd\s*(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])$/) {
d332 1
a332 1
	    if (/^\.Bd.*-literal/) {
d336 1
a336 1
		if (/^\.Ed/) {
d339 1
a339 1
	    } elsif (/^$/) {
d343 1
a343 1
	    if ($last =~ /^.Pp/ and (/^(\.S[sh])/ or /^(\.Pp)/)) {
d346 1
a346 1
	    if (/^.Pp/ and $last =~ /^(\.S[sh])/) {
d355 1
a355 1
		/^\.Sh\s+(.*)$/ and $current_section = $1;
d360 1
a360 1
		/^\.Bl\s+/ and $last_error_name = "";
d363 1
a363 1
		    /^\.It\s+Bq\s+Er\s+(E[\w_]+)$/) {
d386 1
a386 1
    if ($opt_P and $last =~ /^.Pp/) {
@


1.21
log
@From Alexander von Gernler <grunk@@pestilenz.org>:
in option parsing, use a simle DeMorgan transformation, fix indentation,
and clarify the comment above.
@
text
@d4 1
a4 1
# $NetBSD: mdoclint,v 1.20 2008/11/01 17:49:19 wiz Exp $
d124 1
@


1.20
log
@From Alexander von Gernler <grunk@@pestilenz.org>:
make usage() a fatal function instead of a string.  It is just used
once, and it greatly improves code flow there.  Also, print usage
to STDERR instead of STDOUT.  Use an EOF-heredoc instead of a
clumsily escaped \t\n-string.
@
text
@d3 2
a4 1
# $NetBSD: mdoclint,v 1.19 2008/11/01 17:45:30 wiz Exp $
d73 1
a73 1
Default is -aDdfHmnoPprSsXx if no flag is specified.
d117 7
a123 11
    # default to all warnings
    if (not $opt_a and not $opt_D and not $opt_d and not $opt_e and
	not $opt_f and
	not $opt_H and not $opt_m and not $opt_n and not $opt_o and
	not $opt_P and not $opt_p and not $opt_r and not $opt_S and
	not $opt_s and not $opt_X and not $opt_x) {
	$opt_a = $opt_D = $opt_d = $opt_e = $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;
    }
@


1.19
log
@From Alexander von Gernler <grunk@@pestilenz.org>:
cleanup: zap trailing whitespaces, trim superlong lines
replace one '&&' by 'and' to not get bitten by operator
precedence later on
@
text
@d3 1
a3 1
# $NetBSD: mdoclint,v 1.18 2008/01/05 09:03:50 wiz Exp $
d49 27
a75 22
my $usage = "mdoclint: verify man page correctness\n".
    "Usage:\tmdoclint [-$options] [FILE ...]\n\n".
    "  -a\twarn about SEE ALSO section problems\n".
    "  -D\twarn about bad casing and archs in .Dt\n".
    "  -d\twarn about bad date strings (in .Dd only)\n".
    "  -e\twarn about unsorted errors (for functions)\n".
    "  -F\tfix whitespace problems (asks before overwriting)\n".
    "  -f\twarn about possible incorrect .Fn syntax\n".
    "  -H\twarn about characters that produce problems in HTML output\n".
    "  -h\tdisplay this help text\n".
    "  -m\twarn about man pages that are not in mdoc(7) format\n".
    "  -n\twarn about .Nd's ending in '.'\n".
    "  -o\twarn about non-empty .Os strings\n".
    "  -P\twarn about paragraph problems\n".
    "  -p\twarn about punctuation problems\n".
    "  -r\twarn about missing RCS Id\n".
    "  -S\twarn about any .Sh weirdness\n".
    "  -s\twarn about whitespace problems\n".
    "  -v\tverbose output\n".
    "  -X\twarn about explicit mentions of FreeBSD, NetBSD, or OpenBSD\n".
    "  -x\twarn about cross-references with missing targets\n\n".
    "Default is -aDdefHmnoPprSsXx if no flag is specified.\n";
d114 1
a114 5

    if ($opt_h) {
	print $usage;
	exit 0;
    };
@


1.18
log
@Recognize 2008.
@
text
@d3 1
a3 1
# $NetBSD: mdoclint,v 1.17 2007/12/02 21:08:10 wiz Exp $
d42 1
a42 1
	
d211 2
a212 2
			warning("SEE ALSO: `.Xr $saname $sasection' ".
				"should be after `.Xr $newsaname $newsasection'");
d251 1
a251 1
	
d270 2
a271 1
		warning("verbose mention of `$1BSD' instead of `$short{$1}': `$_'");
d365 2
a366 1
		if ($current_section eq "ERRORS" && /^\.It\s+Bq\s+Er\s+(E[\w_]+)$/) {
d370 2
a371 1
				warning("Duplicate item for $current_error_name.");
d373 3
a375 1
				warning("$last_error_name and $current_error_name are not in alphabetical order.");
d380 1
a380 1
		
d396 1
a396 1
	
@


1.17
log
@Suggest using .Fn or .Xr for functions.
@
text
@d3 1
a3 1
# $NetBSD: mdoclint,v 1.16 2007/01/01 23:39:15 wiz Exp $
d5 1
a5 1
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 Thomas Klausner
d328 1
a328 1
	    if (/^.Dd/ and not /^.Dd\s*(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[01234567])$/) {
@


1.16
log
@Accept 2007 as valid year.
@
text
@d3 1
a3 1
# $NetBSD: mdoclint,v 1.15 2006/12/09 23:02:15 rillig Exp $
d296 3
@


1.15
log
@Error names should not be sorted alphabetically across different lists.
@
text
@d3 1
a3 1
# $NetBSD: mdoclint,v 1.14 2006/11/18 00:35:21 rillig Exp $
d325 1
a325 1
	    if (/^.Dd/ and not /^.Dd\s*(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[0123456])$/) {
@


1.14
log
@Fixed the Perl warning about an "uninitialized value" being used.

Made stdout unbuffered so that it is synchronized with stderr, just in
case one wants to relate the Perl warnings to the mdoclint warnings in
the output.
@
text
@d3 1
a3 1
# $NetBSD: mdoclint,v 1.13 2006/11/18 00:28:13 rillig Exp $
d356 5
@


1.13
log
@Added "use strict" and "use warnings".

Added a test for the ERRORS section to make sure the error names are
ordered alphabetically. Currently, there does not seem to be any
consistent scheme in which the error names are ordered.
@
text
@d3 1
a3 1
# $NetBSD: mdoclint,v 1.12 2006/01/12 20:31:22 wiz Exp $
d35 2
d247 1
a248 1
		$lastsh = $sections{$1};
@


1.12
log
@Add 2006 to recognized years.
@
text
@d3 1
a3 1
# $NetBSD: mdoclint,v 1.11 2005/05/17 13:56:21 wiz Exp $
d30 3
d35 6
d45 1
a45 1
my $options="aDdfFHhmnoPprSsvXx";
d52 1
d68 1
a68 1
    "Default is -aDdfHmnoPprSsXx if no flag is specified.\n";
d114 2
a115 1
    if (not $opt_a and not $opt_D and not $opt_d and not $opt_f and
d119 3
a121 2
	$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 =
d128 3
a130 3
    $xreftarget = "/usr/share/man/man$_[2]/$_[1].$_[2]";
    $xreftarget2 = "/usr/share/man/man$_[2]/$arch/$_[1].$_[2]";
    $xreftarget3 = "./$_[1].$_[2]";
d157 2
d202 3
a204 3
		    $newsaname = $1;
		    $newsasection = $2;
		    $newsarest = $3;
d348 18
d394 1
a394 1
	foreach $i (qw (NAME SYNOPSIS DESCRIPTION)) {
@


1.11
log
@Do not warn about {www,ftp}.NetBSD.org.
@
text
@d3 1
a3 1
# $NetBSD: mdoclint,v 1.10 2005/03/26 11:30:55 wiz Exp $
d5 1
a5 1
# Copyright (c) 2001, 2002, 2003, 2004, 2005 Thomas Klausner
d309 1
a309 1
	    if (/^.Dd/ and not /^.Dd\s*(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[012345])$/) {
@


1.10
log
@-H is default too.
@
text
@d3 1
a3 1
# $NetBSD: mdoclint,v 1.9 2005/01/02 18:19:52 wiz Exp $
d251 3
a253 2
	    if (not /^.\\\"/ and /(Free|Net|Open)BSD/ and
		not /@@(Free|Net|Open)BSD\.[oO][rR][gG]/) {
@


1.9
log
@Accept 2005 in date string.
@
text
@d3 1
a3 1
# $NetBSD: mdoclint,v 1.8 2004/01/01 03:51:06 wiz Exp $
d58 1
a58 1
    "Default is -aDdfmnoPprSsXx if no flag is specified.\n";
@


1.8
log
@Add 2004 to copyright.
@
text
@d3 1
a3 1
# $NetBSD: mdoclint,v 1.7 2004/01/01 03:50:39 wiz Exp $
d5 1
a5 1
# Copyright (c) 2001, 2002, 2003, 2004 Thomas Klausner
d308 1
a308 1
	    if (/^.Dd/ and not /^.Dd\s*(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[01234])$/) {
@


1.7
log
@Accept 2004 dates and arch/name man page names.
@
text
@d3 1
a3 1
# $NetBSD: mdoclint,v 1.6 2003/10/19 09:13:17 wiz Exp $
d5 1
a5 1
# Copyright (c) 2001, 2002, 2003 Thomas Klausner
@


1.6
log
@Add ibmnws.
@
text
@d3 1
a3 1
# $NetBSD: mdoclint,v 1.5 2003/05/26 21:16:27 lha Exp $
d284 1
a284 1
		if ($destruct =~ /^\.Xr\s+([\w\.\-\+]+)\s+([0-9])(.*)/) {
d308 1
a308 1
	    if (/^.Dd/ and not /^.Dd\s*(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[0123])$/) {
@


1.5
log
@Print all error and warnings as line:number:message so mdoclint can be
used within a compile buffer in emacs. Ok'ed by wiz
@
text
@d3 1
a3 1
# $NetBSD: mdoclint,v 1.4 2003/05/10 17:49:25 wiz Exp $
d166 1
a166 1
	    if (! /^.Dt\s+([A-Z0-9._-]+)\s+[1-9](\s+(acorn26|acorn32|algor|alpha|amiga|arc|atari|bebox|cats|cesfic|cobalt|dreamcast|evbarm|evbmips|evbsh3|evbsh5|hp300|hp700|hpcarm|hpcmips|hpcsh|i386|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)|)$/)  {
@


1.4
log
@Fix: "iostat.8[209]: SEE ALSO: `.Xr nfsstat 1' should be after `.Xr \&ps 1'"
@
text
@d3 1
a3 1
# $NetBSD: mdoclint,v 1.3 2003/05/09 09:03:19 wiz Exp $
d88 1
a88 1
    print STDOUT "debug: $fn\[$line\]: $_[0]\n" if $opt_v;
d92 1
a92 1
    print STDOUT "$fn\[$line\]: $_[0]\n";
@


1.3
log
@Fix "mdoclint -d", noted broken by jmc@@openbsd.
@
text
@d3 1
a3 1
# $NetBSD: mdoclint,v 1.2 2003/05/09 08:54:26 wiz Exp $
d188 7
a194 2
		    if ($sasection > $2 or ($sasection eq $2 and
					    ($saname cmp $1) > 0)) {
d196 1
a196 1
				"should be after `.Xr $1 $2'");
d202 3
a204 3
		    $saname = $1;
		    $sasection = $2;
		    $sarest = $3;
@


1.2
log
@Some nit fixes from jmc@@openbsd.
@
text
@d3 1
a3 1
# $NetBSD: mdoclint,v 1.1 2003/05/09 08:52:17 wiz Exp $
d301 2
@


1.1
log
@Add mdoclint, a perl script to check man pages for common mistakes.
It warns a bit too much, but I prefer that to too little.
Has already proven its value for months, if not years.
@
text
@d3 1
a3 1
# $NetBSD$
d57 1
a57 1
    "  -x\twarn about cross references with missing targets\n\n".
d62 1
a62 1
    "Net" => ".Nx", 
d104 1
a104 1
    if (not $opt_a and not $opt_D and not $opt_d  and not $opt_f and
d185 1
a185 1
		    $insa = 2; 
@

