: # use perl                                  -*- mode: Perl; -*-
	eval 'exec perl -S $0 "$@"'
		if $running_under_some_shell;

#
# weblint - pick fluff off WWW pages (html).
#
# Copyright (C) 1994, 1995, 1996 Neil Bowers.  All rights reserved.
#
# See README for additional blurb.
# Bugs, comments, suggestions welcome: neilb@cre.canon.co.uk
#
# Latest version is available as:
#	ftp://ftp.cre.canon.co.uk/pub/weblint/weblint.tar.gz
#
#
# Modified by Masayasu Ishikawa, 1997-03-08.  No right reserved :-).
#   Main changes are:
#     * Default spec is changed from HTML 3.0 to 2.0 (RFC 1866).
#     * Added support for internationalization (i18n); handle ISO-2022-* and
#       Unicode. Code conversion library is `htmli18n.pl'.
#     * Added support for new standards, that is, HTML 3.2 and i18n (RFC 2070).
#     * Added support for W3C's experimental spec, `Cougar',
#       Netscape Navigator 3.0 and Microsoft Internet Explorer 3.x features.
#     * Separated codes related to HTML extensions from main script,
#         and put it in `html-ext.pl', for future flexibility.
#
# Bug REPORTs, comments, suggestions about my modifications
#     are welcome: mimasa@aichi-u.ac.jp
# No bug is welcome :-p
#

$VERSION        = '0.12 [1997-03-08]';
($PROGRAM       = $0) =~ s@.*/@@;
@TMPDIR_OPTIONS	= ('/usr/tmp', '/tmp', '/var/tmp', '/temp');
$TMPDIR         = &PickTmpdir(@TMPDIR_OPTIONS);
$SITE_DIR       = '';
$USER_RCFILE    = $ENV{'WEBLINTRC'} || "$ENV{'HOME'}/.weblintrc";
$SITE_RCFILE	= $SITE_DIR.'/global.weblintrc' if $SITE_DIR;

#------------------------------------------------------------------------
# $version - the string which is displayed with -v or -version
#------------------------------------------------------------------------
$versionString=<<EofVersion;
	This is jweblint 97, version $VERSION (EUC-JP)
		based on Weblint version 1.017

	Copyright 1994,1995,1996 Neil Bowers
	Modified by Masayasu Ishikawa

	Weblint may be used and copied only under the terms of the Artistic
	License, which may be found in the Weblint source kit, or at:
        	http://www.cre.canon.co.uk/~neilb/weblint/artistic.html
EofVersion


#------------------------------------------------------------------------
# $usage - usage string displayed with the -U command-line switch
#------------------------------------------------------------------------
$usage=<<EofUsage;
  jweblint 97 v$VERSION -  web ڡ (HTML) θŦ߼
      -d            : ꤵ줿ٹ̵ˤ (ƷٹϥޤǶڤ)
      -e            : ꤵ줿ٹͭˤ (ƷٹϥޤǶڤ)
      -f filename   : configuration file 
      -stderr       : ٹ STDOUT ǤϤʤ STDERR ˽Ϥ
      -i            : ȥ case (ʸʸ) ζ̵̤뤹
      -l            : directory Ƶ˥ܥå󥯤̵뤹
      -pedantic     : ȥ case ʳƤηٹͭˤ
      -s            : ûٹåˤ (file ̾ϽϤʤ)
      -t            : ʷʷٹ⡼,  testsuite ͭ
      -todo         : $PROGRAM  todo list Ϥ
      -help | -U    :  usage åɽ
      -urlget       : URL 뤿˻Ȥ륳ޥɤꤹ
      -version | -v : version ɽ
      -warnings     : ݡȤٹ list 
      -x <extn>     : HTML ĥޤ
      -c <codetype> : ɷϤꤹ
      -explain      : -x, -c ץξܺ٤ɽ

  HTML file(s) åˤ, $PROGRAM ʲΤ褦˼¹Ԥ:
      $PROGRAM file1.html [... fileN.html]
  file  directory Ǥä, $PROGRAM ϺƵŪƤ file å.
EofUsage

#------------------------------------------------------------------------
# $todo - string displayed with the -todo switch
#------------------------------------------------------------------------
$todo=<<EofToDo;
Weblint  toDo list ϰʲǸ뤳ȤǤ:
	http://www.cre.canon.co.uk/~neilb/weblint/todo/
EofToDo

#------------------------------------------------------------------------
# $explain - explanation string displayed with the -explain command-line switch
#------------------------------------------------------------------------
$explain=<<EofExplain;
  -x <extn>     : HTML ĥޤ
    ݡȤĥ (alias)
      HTML3.2 (3.2, Wilbur)     HTML 3.2 (W3C Recommendation)
      i18n (HTML2.x, 2.x)       ݲĥ (RFC 2070)
      Cougar                    HTML 3.2 Ф¸Ūʳĥ
      Microsoft (MSIE)          Microsoft Internet Explorer 3.0 ĥ
      Netscape (Mozilla)        Netscape Navigator 3.0 ĥ

  HTML 3.2 ĥޤˤ: $PROGRAM -x HTML3.2 file.html

    : jweblint 97  DOCTYPE ˽äŬڤʳĥưŪꤹ,
        äŬڤ DOCTYPE 񤤤Ƥ, -x ץȤɬפϤʤ.

  -c <codetype> : ɷϤꤹ
    ݡȤ륳ɷ (ǥե: Japanese)
      literal        7bit ޤ 8bit , ASCII Ⱦ̸ߴΤ륳ɷ
                       i.e. US-ASCII, ISO-8859-*, VISCII [, ƹ EUC]
      ISO-2022-7bit  ISO-2022 7ñγĥˡ˽򤷤ɷ
                       i.e. ISO-2022-JP, ISO-2022-JP-2, ISO-2022-KR, ISO-2022-CN
      Unicode        UCS-2, a.k.a. Unicode; ̥ХȤǤʤФʤʤ
      Japanese       ܸ켫ưȽ (ISO-2022-JP, EUC-JP, Shift_JIS)

  ISO-2022-JP-2 򰷤ˤ: $PROGRAM -c ISO-2022-7bit file.html
EofExplain

*WARNING = *STDOUT;

# named entities
$entities = 'lt|gt|amp|quot|'.
# The following entities are included in "Proposed Entities"(RFC 1866),
# and should be supported. (not included in DTD.)
            'nbsp|iexcl|cent|pound|curren|yen|brvbar|sect|uml|copy|ordf|'.
            'laquo|not|shy|reg|macr|deg|plusmn|sup2|sup3|acute|micro|para|'.
            'middot|cedil|sup1|ordm|raquo|frac14|frac12|frac34|iquest|'.
# The following entities are "Added Latin 1" entities.
            'Agrave|Aacute|Acirc|Atilde|Auml|Aring|AElig|Ccedil|Egrave|'.
            'Eacute|Ecirc|Euml|Igrave|Iacute|Icirc|Iuml|ETH|Ntilde|Ograve|'.
            'Oacute|Ocirc|Otilde|Ouml|times|Oslash|Ugrave|Uacute|Ucirc|Uuml|'.
            'Yacute|THORN|szlig|agrave|aacute|acirc|atilde|auml|aring|aelig|'.
            'ccedil|egrave|eacute|ecirc|euml|igrave|iacute|icirc|iuml|eth|'.
            'ntilde|ograve|oacute|ocirc|otilde|ouml|divide|oslash|ugrave|'.
            'uacute|ucirc|uuml|yacute|thorn|yuml';

# obsolete tags
$obsoleteTags = 'PLAINTEXT|XMP|LISTING|COMMENT';

$maybePaired  = 'LI|DT|DD|P';

$pairElements = 'A|ADDRESS|HTML|HEAD|BLOCKQUOTE|BODY|H1|H2|H3|H4|H5|H6|'.
		'B|I|TT|STRONG|EM|CODE|KBD|VAR|DFN|CITE|SAMP|'.
		'UL|OL|DL|MENU|DIR|FORM|SELECT|'.
		'TEXTAREA|TITLE|CODE|PRE|'.
		$maybePaired.'|'.
		$obsoleteTags;

# container elements which shouldn't have leading or trailing whitespace
$cuddleContainers = 'A|H1|H2|H3|H4|H5|H6|TITLE|LI';

# expect to see these tags only once
%onceOnly = ('HTML', 1, 'HEAD', 1, 'BODY', 1, 'TITLE', 1);

%physicalFontElements =
 (
 'B',  'STRONG',
 'I',  'EM',
 'TT', 'CODE, SAMP, KBD, 뤤 VAR'
 );

# expect these tags to have attributes
# these are elements which have no required attributes, but we expect to
# see at least one of the attributes
$expectArgsRE = 'A';

# these tags can only appear in the head element
$headTagsRE = 'TITLE|NEXTID|LINK|BASE|META';

%requiredContext =
 (
 'DD',        'DL',
 'DT',        'DL',
 'INPUT',     'FORM',
 'LI',        'DIR|MENU|OL|UL',
 'OPTION',    'SELECT',
 'SELECT',    'FORM',
 'TEXTAREA',  'FORM',
 );

# these tags are allowed to appear in the head element
%okInHead = ('ISINDEX', 1, 'TITLE', 1, 'NEXTID', 1, 'LINK', 1,
	     'BASE', 1, 'META', 1, '!--', 1);

# expect to see these at least once.
# html-outer covers the HTML element
@expectedTags = ('HEAD', 'TITLE', 'BODY');

# elements which cannot be nested
$nonNest = 'A|FORM';

#
# This is a regular expression for all legal elements
# UPDATE: need to remove duplication in legalElements and pairElements
#
$legalElements =
   'A|ADDRESS|B|BASE|BLOCKQUOTE|BODY|BR|CITE|CODE|DD|DFN|DIR|DL|DT|'.
   'EM|FORM|H1|H2|H3|H4|H5|H6|HEAD|HR|HTML|I|IMG|INPUT|ISINDEX|KBD|'.
   'LI|LINK|MENU|META|OL|OPTION|P|PRE|SAMP|SELECT|STRONG|'.
   'TEXTAREA|TITLE|TT|UL|VAR|'.
   $obsoleteTags;

# This table holds the valid attributes for elements
# Where an element does not have an entry, this implies that the element
# does not take any attributes
%validAttributes =
   (
   'A',          'HREF|METHODS|NAME|REL|REV|TITLE|URN',
   'BASE',       'HREF',
   'DL',         'COMPACT',
   'FORM',       'ACTION|ENCTYPE|METHOD',
   'HTML',       'VERSION',
   'IMG',        'ALIGN|ALT|ISMAP|SRC',
   'INPUT',      'ALIGN|CHECKED|MAXLENGTH|NAME|SIZE|SRC|TYPE|VALUE',
   'LINK',       'HREF|METHODS|REL|REV|TITLE|URN',
   'META',       'CONTENT|HTTP-EQUIV|NAME',
   'NEXTID',     'N',
   'OL',         'COMPACT',
   'OPTION',     'SELECTED|VALUE',
   'PRE',        'WIDTH',
   'SELECT',     'NAME|MULTIPLE|SIZE',
   'TEXTAREA',   'COLS|NAME|ROWS',
   'UL',         'COMPACT',
   );

%requiredAttributes =
   (
   'BASE',     'HREF',
   'FORM',     'ACTION',
   'IMG',      'SRC',
   'LINK',     'HREF',
   'META',     'CONTENT',
   'NEXTID',   'N',
   'SELECT',   'NAME',
   'TEXTAREA', 'NAME|ROWS|COLS'
   );

%attributeFormat =
(
 'ALIGN',     'BOTTOM|MIDDLE|TOP',
 'COLS',      '\d+',
 'MAXLENGTH', '\d+',
 'METHOD',    'GET|POST',
 'ROWS',      '\d+',
 'SIZE',      '\d+',
 'TYPE',      'CHECKBOX|HIDDEN|IMAGE|PASSWORD|RADIO|RESET|SUBMIT|TEXT',
 'WIDTH',     '\d+',
);

%mustFollow =
(
 'HEAD',     'HTML',
 'BODY',     '/HEAD',
 '/HTML',    '/BODY',
 );

%badTextContext =
(
 'HEAD',  '餯 BODY, 뤤 TITLE',
 'UL',    'LI',
 'OL',    'LI',
 'DL',    'DT 뤤 DD',
);

%variable =
(
 'directory-index',		'index.html',
 'file-extensions',		'html, htm',
 'url-get',			'lynx -source',	# this is my preference :-)
 'message-style',		'lint',
 'WebRootDir',			'/usr/local/etc/httpd/htdocs',
 'url-watch-list',		''
);

@options = ('d=s', 'e=s', 'f=s', 'stderr', 'help', 'i', 'l', 's', 't',
	    'todo', 'U',
	    'noglobals', 'pedantic', 'urlget=s', 'v', 'version', 'warnings',
	    'x=s', 'c=s', 'explain');
@INC = ( @INC, '/usr/local/lib/perl');
$exit_status = 0;

require 'newgetopt.pl';
require 'find.pl';
require 'jcode.pl';
require 'html-ext.pl';	# load HTML extensions library
require 'htmli18n.pl';	# load internationalization library

die "$usage" unless @ARGV > 0;

# escape the `-' command-line switch (for stdin), so NGetOpt don't mess wi' it
grep(s/^-$/\tstdin\t/, @ARGV);

&NGetOpt(@options) || die "usage ɽˤ -U åȤäƤ.\n";

# put back the `-' command-line switch, if it was there
grep(s/^\tstdin\t$/-/, @ARGV);

die "$versionString\n"	if $opt_v || $opt_version;
die "$usage"		if $opt_u || $opt_help;
die "$explain"		if $opt_explain;

&ReadDefaults();

# Read configuration
if ($opt_f)
{
   &ReadConfigFile($opt_f);
}
elsif (-f $USER_RCFILE)
{
   &ReadConfigFile($USER_RCFILE);
}
elsif (! $opt_noglobals && -f $SITE_RCFILE)
{
   &ReadConfigFile($SITE_RCFILE);
}

# must do this after reading their config file to see a valid url-get
&PrintToDo()		if $opt_todo;

# pedantic command-line switch turns on all warnings except case checking
if ($opt_pedantic)
{
   foreach $warning (keys %enabled)
   {
      &enableWarning($warning, 1);
   }
   &enableWarning('lower-case', 0);
   &enableWarning('upper-case', 0);
   &enableWarning('bad-link', 0);
   &enableWarning('img-size', 0);
   &enableWarning('doctype-mismatch', 0);
   &enableWarning('trailing-slash', 0);
   &enableWarning('url-watch', 0);
   &enableWarning('non-ascii', 0);
}

&AddExtension("\L$opt_x");            # if $opt_x;
$variable{'message-style'} = 'short'  if $opt_s;
$variable{'message-style'} = 'terse'  if $opt_t;
$variable{'url-get'} = $opt_urlget    if $opt_urlget;
*WARNING = *STDERR                    if $opt_stderr;
&ListWarnings()                       if $opt_warnings;

($fileExtensions = $variable{'file-extensions'}) =~ s/,\s*/\|/g;

# WARNING file handle is default
select(WARNING);

$opt_l = 1                 if $ignore{'SYMLINKS'};

# -d to disable warnings
if ($opt_d)
{
   for (split(/,/,$opt_d))
   {
      &enableWarning($_, 0);
   }
}

# -e to enable warnings
if ($opt_e)
{
   for (split(/,/,$opt_e))
   {
      &enableWarning($_, 1) || next;
   }
}

# -i option to ignore case in element tags
if ($opt_i)
{
   $enabled{'lower-case'} = $enabled{'upper-case'} = 0;
}

# -c option to select code type
if ($opt_c)
{
   &CodeType("\L$opt_c");
}
elsif ($CodeType eq undef)
{
   $CodeType = "Japanese";
}

if (defined $variable{'directory-index'})
{
   @dirIndices = split(/\s*,\s*/, $variable{'directory-index'});
}

$argc = int(@ARGV);
while (@ARGV > 0)
{
   $arg = shift(@ARGV);

   &CheckURL($arg), next if $arg =~ m!^(http|gopher|ftp)://!;

   &find($arg), next if -d $arg;

   if ($opt_l && -l $arg && $argc == 1)
   {
      warn "$PROGRAM: $arg ϥܥå󥯤Ǥ, Ȥ⤢åƤߤޤ礦.\n";
   }

   &WebLint($arg), next if (-f $arg && -r $arg) || $arg eq '-';

   print "$PROGRAM: $arg ɤ߹ޤǤ: $!\n";
}

exit $exit_status;

#========================================================================
# Function:	WebLint
# Purpose:	This is the high-level interface to the checker.  It takes
#		a file and checks for fluff.
#========================================================================
sub WebLint
{
   local($filename,$relpath) = @_;
   local(@tags) = ();
   local($tagRE) = ('');
   local(@taglines) = ();
   local(@orphans) = ();
   local(@orphanlines) = ();
   local(%seenPage);
   local(%seenTag);
   local(%whined);
   local(*PAGE);
   local($line) = ('');
   local($id, $ID);
   local($tag, $tagNum);
   local($closing);
   local($tail);
   local(%args);
   local($arg);
   local($rest);
   local($lastNonTag);
   local(@notSeen);
   local($seenMailtoLink) = (0);
   local($matched);
   local($matchedLine);
   local($novalue);
   local($heading);
   local($headingLine);
   local($commentline);
   local($_);


   if ($filename eq '-')
   {
      *PAGE = *STDIN;
      $filename = 'stdin';
   }
   else
   {
      return if defined $seenPage{$filename};
      if (-d $filename)
      {
	 print "$PROGRAM: $filename  directory Ǥ.\n";
	 $exit_status = 0;
	 return;
      }
      $seenPage{$filename}++;
      open(PAGE,"<$filename") || do
      {
	 print "$PROGRAM: file $filename ɤ߹ޤǤ: $!\n";
	 $exit_status = 0;
	 return;
      };
      $filename = $relpath if defined $relpath;
   }

   undef $heading;
   $tagNum = 0;

 READLINE:
   while (<PAGE>)
   {
      if ($CodeType eq 'ISO-2022-7bit')
      {
         &ConvISO2022($_);
      }
      elsif ($CodeType eq 'Unicode')
      {
         $_ = &ConvUnicode($_);
      }
      elsif ($CodeType eq 'Japanese')
      {
         &jcode'convert(*_, 'euc');
      }
      else {}

      if ($enabled{'bad-entity'})
      {
         &CheckEntities($_);
      }

      $line .= $_;
      # $line =~ s/\n/ /g;

      # non-ascii character check
      if ($enabled{'non-ascii'})
      {
         @qqq = grep(/[\x80-\xff]/, split(//, $_));
         foreach(@qqq)
         {
            &whine($., 'non-ascii', $_);
         }
      }
      while ($line =~ /</o)
      {
	 $tail = $'; #'
	 undef $lastNonTag;
	 if ($` !~ /^\s*$/o)
	 {
	    $lastNonTag = $`;

	    # check for illegal text context
	    if (defined $badTextContext{$tags[$#tags]})
	    {
	       &whine($., 'bad-text-context',$tags[$#tags],
		      $badTextContext{$tags[$#tags]});
	    }

	    $lnt = $lastNonTag;
	    while ($lnt =~ />/o)
	    {
	       $nl = $lnt = $';
	       $nl =~ s/[^\n]//go;
	       if ('PRE' =~ /^($tagRE)$/)
	       {
		  &whine($. - length($nl), 'meta-in-pre', '&gt;', '>');
	       }
	       else
	       {
		  &whine($. - length($nl), 'literal-metacharacter', '>', '&gt;');
	       }
	    }
	 }

	 #--------------------------------------------------------
	 #== SGML comment: <!-- ... blah blah ... -->
	 #--------------------------------------------------------
	 if ($tail =~ /^!--/o)
	 {

	    $commentline = $. unless defined $commentline;

	    # push lastNonTag onto word list for spell checking

	    $ct = $';
	    next READLINE unless $ct =~ /--\s*>/o;

	    undef $commentline;

	    $comment = $`;
	    $line = $';

	    # markup embedded in comment can confuse some (most? :-) browsers
	    &whine($., 'markup-in-comment') if $comment =~ /<\s*[^>]+>/o;

	    $comments = ($comment =~ s/--//go);
	    &whine($., 'unterminated-comment') if $comments % 2 == 1;

	    next;
	 }
	 undef $commentline;

	 next READLINE unless $tail =~ /^(\s*)([^>]*)>/o;


	 &whine($., 'leading-whitespace', $2) if $1 ne '';

         $line = $';
         ($tag = $2) =~ s/\n/ /go;
	 $id = $tag;

         &whine($., 'unknown-element', $id),next if $id =~ /^\s*$/o;

	 # push lastNonTag onto word list for spell checking

         undef $tail;
         undef $closing;

         if ($id =~ /^!doctype/io)
         {
            &CheckDoctype($id, $opt_x);
            &SetExtension($id, $opt_x);
            $seenTag{'DOCTYPE'}=1;
            next;
         }
         elsif (!$whined{'require-doctype'} && !$seenTag{'DOCTYPE'})
	 {
            &whine($., 'require-doctype');
            $whined{'require-doctype'} = 1;
	 }

	 $closing = 0;
         if ($id =~ m@^/@o)
         {
            $id =~ s@^/@@o;
	    $ID = "\U$id";
            $closing = 1;
         }

	 &CheckAttributes();

	 $TAG = ($closing ? '/' : '').$ID;
	 if (defined $mustFollow{$TAG})
	 {
	    $ok = 0;
	    foreach $pre (split(/\|/, $mustFollow{$TAG}))
	    {
	       ($ok=1),last if $pre eq $lastTAG;
	    }
	    if (!$ok || $lastNonTag !~ /^\s*$/o)
	    {
	       &whine($., 'must-follow', $TAG, $mustFollow{$TAG});
	    }
	 }

	 #-- catch empty container elements
	 if ($closing && $ID eq $lastTAG && $lastNonTag =~ /^\s*$/o
	     && $tagNums[$#tagNums] == ($tagNum - 1)
	     && $ID ne 'TEXTAREA' && $ID ne 'TD')
	 {
	    &whine($., 'empty-container', $ID);
	 }

	 #-- special case for empty optional container elements
	 if (!$closing && $ID eq $tags[$#tags] && $lastTAG eq $ID
	     && $ID =~ /^($maybePaired)$/
	     && $tagNums[$#tagNums] == ($tagNum - 1)
	     && $lastNonTag =~ /^\s*$/o)
	 {
	    $t = pop @tags;
	    $tline = pop @taglines;
	    pop @tagNums;
	    &whine($tline, 'empty-container', $ID);
	    $tagRE = join('|',@tags);
	 }

         #-- whine about unrecognized element, and do no more checks ----
         if ($id !~ /^($legalElements)$/io)
	 {
	    if ($id =~ /^($extensionElements)$/io)
	    {
	       &whine($., 'extension-markup', ($closing ? "/$id" : "$id"));
	    }
	    else
	    {
	       &whine($., 'unknown-element', ($closing ? "/$id" : "$id"));
	    }
	    next;
	 }

         if ($closing == 0 && defined $requiredAttributes{$ID})
         {
	    @argkeys = keys %args;
	    foreach $attr (split(/\|/,$requiredAttributes{$ID}))
	    {
	       unless (defined $args{$attr})
	       {
		  &whine($., 'required-attribute', $attr, $id);
	       }
	    }
         }
         elsif ($closing == 0 && $id =~ /^($expectArgsRE)$/io)
         {
            &whine($., 'expected-attribute', $id) unless defined %args;
         }

         #--------------------------------------------------------
         #== check case of tags
         #--------------------------------------------------------
         &whine($., 'upper-case', $id) if $id ne $ID;
         &whine($., 'lower-case', $id) if $id ne "\L$id";


         #--------------------------------------------------------
         #== if tag id is /foo, then strip slash, and mark as a closer
         #--------------------------------------------------------
         if ($closing)
         {
	    if ($ID !~ /^($pairElements)$/o)
	    {
	       &whine($., 'illegal-closing', $id);
	    }

            if ($ID eq 'A' && $lastNonTag =~ /^\s*(here|||)\s*$/io)
            {
               &whine($., 'here-anchor');
            }

	    if ($ID eq 'TITLE' && length($lastNonTag) > 64)
	    {
	       &whine($., 'title-length');
	    }

	    #-- end of HEAD, did we see a TITLE in the HEAD element? ----
	    &whine($., 'require-head') if $ID eq 'HEAD' && !$seenTag{'TITLE'};

	    #-- was there a <LINK REV=MADE HREF="mailto:.."> element in HEAD?
	    &whine($., 'mailto-link') if $ID eq 'HEAD' && $seenMailtoLink == 0;
         }
         else
         {
            #--------------------------------------------------------
            # do context checks.  Should really be a state machine.
            #--------------------------------------------------------

	    if (defined $physicalFontElements{$ID})
	    {
	       &whine($., 'physical-font', $ID, $physicalFontElements{$ID});
	    }

	    if ($ID =~ /^H[1-6]$/o && 'A' =~ /^($tagRE)$/)
	    {
	       &whine($., 'heading-in-anchor', $ID);
	    }

            if ($args{'HREF'} =~ /\s/)
            {
               &whine($., 'url-whitespace', $args{'HREF'});
            }
            if ($args{'HREF'} =~ /\x5c/)
            {
               &whine($., 'url-backslash', $args{'HREF'});
            }
            if ($args{'HREF'} =~ /[\x00-\x08\x0b\x0e-\x1f\x7f-\xff]/)
            {
               &whine($., 'unsafe-url', $args{'HREF'});
            }

            if ($args{'HREF'} =~ /^file:/i)
            {
               &whine($., 'file-url', $args{'HREF'});
            }

            if ($enabled{'url-watch'} && $variable{'url-watch-list'} ne '')
            {
               if ($args{'HREF'} =~ /$variable{'url-watch-list'}/)
               {
                  &whine($., 'url-watch', $args{'HREF'});
               }
               if ($args{'SRC'} =~ /$variable{'url-watch-list'}/)
               {
                  &whine($., 'url-watch', $args{'SRC'});
               }
            }

            if ($ID eq 'A' && defined $args{'HREF'})
            {
	       $target = $args{'HREF'};
               if ($target =~ /([^:]+):\/\/([^\/]+)(.*)$/o
		   || $target =~ /^(news|mailto):/o)
               {
               }
               else
               {
		  # remove internal links to NAMEs and args to CGI scripts
		  $target =~ s/[?#][^\/]*$//o;
		  # unless target starts with / and you can add the webroot,
		  # it must be relative to our current directory -- add that
                  unless ($target =~ s#^/#$variable{'WebRootDir'}/#) {
		     # if you use Perl5, `my' is more preferable!
		     # my ($path) = ($filename =~ m#^(.*)/[^/]*$#);
		     local ($path) = ($filename =~ m#^(.*)/[^/]*$#);
		     $path = '.' unless ($path);
		     ($target =~ s#^([^/])#$path/$1#);
		  }
		  if (-d $target && $target !~ m#/$#)
		  {
		     ($Target = $args{'HREF'}) =~ s#$#/#;
		     &whine($., 'trailing-slash', $args{'HREF'}, 'Ǥ', $Target);
		  }
		  elsif (-f $target && $target =~ m#/$#)
		  {
		     ($Target = $args{'HREF'}) =~ s#/$##;
		     &whine($., 'trailing-slash', $args{'HREF'}, 'ǤϤޤ', $Target);
		  }
                  elsif ($target !~ /^\s*$/o && ! -f $target && ! -d $target)
		  {
		     &whine($., 'bad-link', $args{'HREF'});
		  }   
               }
            }

            if ($ID eq 'IMG' && defined $args{'SRC'})
            {
               $target = $args{'SRC'};
               if ($target =~ /([^:]+):\/\/([^\/]+)(.*)$/)
               {
               }
               else
               {
		  # unless target starts with / and you can add the webroot,
		  # it must be relative to our current directory -- add that
                  unless ($target =~ s#^/#$variable{'WebRootDir'}/#) {
		     # if you use Perl5, `my' is more preferable!
		     # my ($path) = ($filename =~ m#^(.*)/[^/]*$#);
		     local ($path) = ($filename =~ m#^(.*)/[^/]*$#);
		     $path = '.' unless ($path);
		     ($target =~ s#^([^/])#$path/$1#);
		  }
                  if ($target !~ /^\s*$/ && ! -f $target && ! -d $target)
                  {
                     &whine($., 'bad-link', $args{'SRC'});
                  }
               }
            }
 
           if ($ID =~ /^H(\d)$/o)
	    {
               if (defined $heading && $1 - $heading > 1)
               {
	          &whine($., 'heading-order', $ID, $heading, $headingLine);
               }
               $heading     = $1;
               $headingLine = $.;
	    }

	    #-- check for mailto: LINK ------------------------------
	    if ($ID eq 'LINK' && $args{'REV'} =~ /^made$/io
		&& $args{'HREF'} =~ /^mailto:/io)
	    {
	       $seenMailtoLink = 1;
	    }

	    if (defined $onceOnly{$ID})
	    {
	       &whine($., 'once-only', $ID, $seenTag{$ID}) if $seenTag{$ID};
	    }
            $seenTag{$ID} = $.;

            &whine($., 'body-no-head') if $ID eq 'BODY' && !$seenTag{'HEAD'};

            if ($ID ne 'HTML' && $ID ne '!DOCTYPE' && !$seenTag{'HTML'}
                && !$whined{'outer-html'})
            {
               &whine($., 'html-outer');
               $whined{'outer-html'} = 1;
            }

	    #-- check for illegally nested elements ---------------------
	    if ($ID =~ /^($nonNest)$/o && $ID =~ /^($tagRE)$/)
	    {
	       for ($i=$#tags; $tags[$i] ne $ID; --$i)
	       {
	       }
	       &whine($., 'nested-element', $ID, $taglines[$i]);
	    }

	    &whine($., 'unknown-element', $ID) unless $ID =~ /^($legalElements)$/o;

	    #--------------------------------------------------------
	    # check for tags which have a required context
	    #--------------------------------------------------------
	    if (defined ($reqCon = $requiredContext{$ID}))
	    {
	       $ok = 0;
	       foreach $context (split(/\|/, $requiredContext{$ID}))
	       {
		  ($ok=1),last if $context =~ /^($tagRE)$/;
	       }
	       unless ($ok)
	       {
                  &whine($., 'required-context', $ID, $requiredContext{$ID});
	       }
	    }

	    #--------------------------------------------------------
	    # check for tags which can only appear in the HEAD element
	    #--------------------------------------------------------
	    if ($ID =~ /^($headTagsRE)$/o && 'HEAD' !~ /^($tagRE)$/)
	    {
               &whine($., 'head-element', $ID);
	    }

	    if (! defined $okInHead{$ID} && 'HEAD' =~ /^($tagRE)$/)
	    {
               &whine($., 'non-head-element', $ID);
	    }

	    #--------------------------------------------------------
	    # check for tags which have been deprecated (now obsolete)
	    #--------------------------------------------------------
	    &whine($., 'obsolete', $ID) if $ID =~ /^($obsoleteTags)$/o;
         }

         #--------------------------------------------------------
         #== was tag of type <TAG> ... </TAG>?
         #== welcome to kludgeville, population seems to be on the increase!
         #--------------------------------------------------------
         if ($ID =~ /^($pairElements)$/o)
         {
	    if (!$closing && $ID eq $tags[$#tags] &&
		$ID =~ /^($maybePaired)$/o)
	    {
	       pop @tags;
	       pop @tagNums;
	       pop @taglines;
	       $tagRE = join('|',@tags);
	    }
	    if ($closing)
	    {
	       # trailing whitespace in content of container element
	       if ($lastNonTag =~ /\S\s+$/o && $ID =~ /^($cuddleContainers)$/o)
	       {
		  &whine($., 'container-whitespace', '', $ID);
	       }

	       #-- if we have a closing tag, and the tag(s) on top of the stack
	       #-- are optional closing tag elements, pop tag off the stack,
	       #-- unless it matches the current closing tag
	       if (@tags > 0 && $tags[$#tags] ne $ID
		   && $tags[$#tags] =~ /^($maybePaired)$/o
		   && $lastNonTag =~ /^\s*$/o
		   && $tagNums[$#tagNums] == ($tagNum - 1)
		   && $ID ne 'TD')
	       {
		  $tline = $taglines[$#taglines];
		  &whine($tline, 'empty-container', $tags[$#tags]);
	       }

	       while (@tags > 0 && $tags[$#tags] ne $ID
		      && $tags[$#tags] =~ /^($maybePaired)$/o)
	       {
		  pop @tags;
		  pop @tagNums;
		  pop @taglines;
	       }
	       $tagRE = join('|',@tags);
	    }
	    else
	    {
	       # leading whitespace in content of container element
	       if ($line =~ /^\s+/o && $ID =~ /^($cuddleContainers)$/o)
	       {
		  &whine($., 'container-whitespace', 'Ƭ', $ID);
	       }
	    }

            if ($closing && $tags[$#tags] eq $ID)
            {
               &PopEndTag();
            }
            elsif ($closing && $tags[$#tags] ne $ID)
            {
	       #-- closing tag does not match opening tag on top of stack
	       if ($ID =~ /^($tagRE)$/)
	       {
		  # If we saw </HTML>, </HEAD>, or </BODY>, then we try
		  # and resolve anything inbetween on the tag stack
		  if ($ID =~ /^(HTML|HEAD|BODY)$/o)
		  {
		     while ($tags[$#tags] ne $ID)
		     {
			$ttag = pop @tags;
			pop @tagNums;
			$ttagline = pop @taglines;
			if ($ttag !~ /^($maybePaired)$/o)
			{
			   &whine($., 'unclosed-element', $ttag, $ttagline);
			}

			#-- does top of stack match top of orphans stack? --
			while (@orphans > 0 && @tags > 0
			       && $orphans[$#orphans] eq $tags[$#tags])
			{
			   pop @orphans;
			   pop @orphanlines;
			   pop @tags;
			   pop @tagNums;
			   pop @taglines;
			}
		     }

		     #-- pop off the HTML, HEAD, or BODY tag ------------
		     pop @tags;
		     pop @tagNums;
		     pop @taglines;
		     $tagRE = join('|',@tags);
		  }
		  else
		  {
		     #-- matched opening tag lower down on stack
		     push(@orphans, $ID);
		     push(@orphanlines, $.);
		  }
	       }
	       else
	       {
                  if ($ID =~ /^H[1-6]$/o && $tags[$#tags] =~ /^H[1-6]$/o)
                  {
		     &whine($., 'heading-mismatch', $tags[$#tags], $ID);
                     &PopEndTag();
                  }
		  else
		  {
		     &whine($., 'mis-match', $ID);
                  }
	       }
            }
            else
            {
               push(@tags,$ID);
               $tagRE = join('|',@tags);
               push(@tagNums,$tagNum);
               push(@taglines,$.);
            }
         }

         #--------------------------------------------------------
         #== inline images (IMG) should have an ALT argument :-)
         #--------------------------------------------------------
         &whine($., 'img-alt') if ($ID eq 'IMG'
				   && !defined $args{'ALT'}
				   && !$closing);

         #--------------------------------------------------------
         #== WIDTH & HEIGHT on inline images (IMG) can help browsers
         #--------------------------------------------------------
         &whine($., 'img-size') if ($ID eq 'IMG'
				   && !defined $args{'WIDTH'}
				   && !defined $args{'HEIGHT'}
				   && !$closing);

      } continue {
	 $lastTagNum = $tagNum;
	 ++$tagNum;
         $lastTAG = $TAG;
      }
      $lastNonTag = $line;
   }
   close PAGE;

   if (defined $commentline)
   {
      &whine($commentline, 'unclosed-comment');
      return;
   }

   while (@tags > 0)
   {
      $tag = shift(@tags);
      shift(@tagNums);
      $line = shift(@taglines);
      if ($tag !~ /^($maybePaired)$/o)
      {
	 &whine($., 'unclosed-element', $tag, $line);
      }
   }

   for (@expectedTags)
   {
      # if we haven't seen TITLE but have seen HEAD
      # then we'll have already whined about the lack of a TITLE element
      next if $_ eq 'TITLE' && !$seenTag{$_} && $seenTag{'HEAD'};
      next if $_ eq 'BODY' && $seenTag{'FRAMESET'};
      push(@notSeen,$_) unless $seenTag{$_};
   }
   if (@notSeen > 0)
   {
      printf ("%sɬפʥĤޤ: @notSeen\n",
		      ($opt_s ? "" : "$filename(-): "));
      $exit_status = 1;
   }
}

#========================================================================
# Function:	CheckEntities
# Purpose:	If there are entity references, check them for validity.
#========================================================================
sub CheckEntities
{
   local($string) = @_;
   local(@words);
   local(@entities);
   local(@ENT);

   $string =~ s/&($entities|\#\d*|\W[^;]*)[;\s]//g;
   @words = split(/[\s<>"]/, $string);
   @entities = grep(/&/, @words);
   foreach $ENTITY (@entities)
   {
      if ($ENTITY =~ /&.*&/)
      {
         @ENT = split(/&/, $ENTITY);
         shift(@ENT);
         foreach $ent (@ENT)
         {
            $ent =~ s/^([^;]+;?).*$/&$1/;
            &whine($., 'bad-entity', $ent);
         }
      }
      else
      {
         $ENTITY =~ s/^[^&]*(&[^;]+;?).*$/$1/;
         &whine($., 'bad-entity', $ENTITY);
      }
   }
}

#========================================================================
# Function:	CheckAttributes
# Purpose:	If the tag has attributes, check them for validity.
#========================================================================
sub CheckAttributes
{
   undef %args;

   if ($closing == 0 && $tag =~ m|^(\S+)\s+(.*)|)
   {
      ($id,$tail) = ($1,$2);
      $ID = "\U$id";

      # don't worry, or warn, about attributes of unknown elements
      return unless $ID =~ /^($legalElements)$/io;

      $tail =~ s/\n/ /go;

      # check for odd number of quote characters
      ($quotes = $tail) =~ s/[^""]//go;
      &whine($., 'odd-quotes', $tag) if length($quotes) % 2 == 1;

      $novalue = 0;
      $valid = $validAttributes{$ID};
      while ($tail =~ /^\s*([^=\s]+)\s*=\s*(.*)$/o
	     # catch attributes like ISMAP for IMG, with no arg
	     || ($tail =~ /^\s*(\S+)(.*)/o && ($novalue = 1)))
      {
	 $arg = "\U$1";
	 $rest = $2;

	 &whine($., 'unexpected-open', $tag) if $arg =~ /</o;

	 if ($arg !~ /^($valid)$/i && $ID =~ /^($legalElements)$/o)
	 {
	    if ($enabled{'extension-attribute'} && $ExtensionAttributes != 1)
	    {
	       &ExtensionAttributes("\L$opt_x");
	    }

	    if ($arg =~ /^($extensionAttributes{$ID})$/i)
	    {
	       &whine($., 'extension-attribute', $arg, $id);
	    }
	    else
	    {
	       &whine($., 'unknown-attribute', $id, $arg);
	    }
	 }

	 #-- catch repeated attributes.  for example:
	 #--     <IMG SRC="foo.gif" SRC="bar.gif">
	 if (defined $args{$arg})
	 {
	    &whine($., 'repeated-attribute', $arg, $id);
	 }

	 if ($novalue)
	 {
	    $args{$arg} = '';
	    $tail = $rest;
	 }
	 elsif ($rest =~ /^'([^'']+)'(.*)$/o)
         {
	    &whine($., 'attribute-delimiter', $arg, $ID);
            $args{$arg} = $1;
            $tail = $2;
         }
	 elsif ($rest =~ /^"([^""]*)"(.*)$/o
		|| $rest =~ /^'([^'']*)'(.*)$/o)
         {
            $args{$arg} = $1;
            $tail = $2;
         }
	 elsif ($rest =~ /^(\S+)(.*)$/o)
         {
            $attrValue = $1;
            $args{$arg} = $attrValue;
            $tail = $2;
            if ($attrValue =~ /[^-.A-Za-z0-9]/o)
            {
               &whine($., 'quote-attribute-value', $arg, $attrValue, $ID);
            }
         }
         else
         {
	    $args{$arg} = $rest;
	    $tail = '';
         }
	 $novalue = 0;
      }
      foreach $attr (keys %args)
      {
         if (defined $attributeFormat{$attr} &&
             $args{$attr} !~ /^($attributeFormat{$attr})$/i)
         {
            &whine($., 'attribute-format', $attr, $id, $args{$attr});
         }
      }
      &whine($., 'unexpected-open', $tag) if $tail =~ /</o;
   }
   else
   {
      if ($closing && $id =~ m|^(\S+)\s+(.*)|)
      {
	 &whine($., 'closing-attribute', $tag);
	 $id = $1;
      }
      $ID = "\U$id";
   }
}

#========================================================================
# Function:	whine
# Purpose:	Give a standard format whine:
#			filename(line #): <message>
#               The associative array `enabled' is used as a gating
#               function, to suppress or enable each warning.  Every
#               warning has an associated identifier, which is used to
#               refer to the warning, and as the index into the hash.
#========================================================================
sub whine
{
   local($line, $id, @argv) = @_;
   local($mstyle)	    = $variable{'message-style'};


   return unless $enabled{$id};
   $exit_status = 1;
   (print "$filename:$line:$id\n"), return             if $mstyle eq 'terse';
   (eval "print \"\$filename($line): $message{$id}\n\""), return if $mstyle eq 'lint';
   (eval "print \"line $line: $message{$id}\n\""), return if $mstyle eq 'short';

   die "`$mstyle' ʥåǤ.\n";
}

#========================================================================
# Function:	ReadConfigFile
# Purpose:	Read the specified configuration file. This is used to
#		the user's .weblintrc file, or the global system config
#		file, if the user doesn't have one.
#========================================================================
sub ReadConfigFile
{
   local($filename) = @_;
   local(*CONFIG);
   local($arglist);
   local($keyword, $value);
   local($_);


   open(CONFIG,"< $filename") || do
   {
      print WARNING "config file `$filename' ɤ߹ޤ: $!\n";
      return;
   };

   while (<CONFIG>)
   {
      chop;
      s/#.*$//;
      next if /^\s*$/o;

      #-- match keyword: process one or more argument -------------------
      if (/^\s*(enable|disable|extension|ignore|codetype)\s+(.*)$/io)
      {
	 $keyword = "\U$1";
	 $arglist = $2;
	 while ($arglist =~ /^\s*(\S+)/o)
	 {
	    $value = "\L$1";

	    &enableWarning($1, 1) if $keyword eq 'ENABLE';

	    &enableWarning($1, 0) if $keyword eq 'DISABLE';

	    $ignore{"\U$1"} = 1 if $keyword eq 'IGNORE';

	    &AddExtension("\L$1") if $keyword eq 'EXTENSION';

	    &CodeType("\L$1") if $keyword eq 'CODETYPE';

	    $arglist = $';
	 }
      }
      elsif (/^\s*set\s+(\S+)\s*=\s*(.*)/o)
      {
         # setting a weblint variable
         if (defined $variable{$1})
         {
            $variable{$1} = $2;
         }
         else
         {
            print WARNING "configuration file ѿ `$1' ޤ.\n";
         }
      }
      elsif (/^\s*use\s*global\s*weblintrc/o)
      {
	 if (-f $SITE_RCFILE)
	 {
	    &ReadConfigFile($SITE_RCFILE);
	 }
	 else
	 {
	    print WARNING "$PROGRAM: global config file ɤ߹ޤ.\n";
	    next;
	 }
      }
      else
      {
	 print WARNING ("$PROGRAM: config file $filename ".
		 " sequence (\"$_\") ̵뤷ޤ.\n");
      }
   }

   close CONFIG;
}

#========================================================================
# Function:	enableWarning
# Purpose:	Takes a warning identifier and an integer (boolean)
#		flag which specifies whether the warning should be
#		enabled.
#========================================================================
sub enableWarning
{
   local($id, $enabled) = @_;


   if (! defined $enabled{$id})
   {
      print WARNING "$PROGRAM: \"$id\" ʷٹ̻ҤǤ.\n";
      return 0;
   }

   $enabled{$id} = $enabled;

   #
   # ensure consistency: if you just enabled upper-case,
   # then we should make sure that lower-case is disabled
   #
   $enabled{'lower-case'} = 0 if $_ eq 'upper-case';
   $enabled{'upper-case'} = 0 if $_ eq 'lower-case';
   $enabled{'upper-case'} = $enabled{'lower-case'} = 0 if $_ eq 'mixed-case';

   return 1;
}

#========================================================================
# Function:	ListWarnings()
# Purpose:	List all supported warnings, with identifier, and
#		whether the warning is enabled.
#========================================================================
sub ListWarnings
{
   local($id);
   local($message);


   foreach $id (sort keys %enabled)
   {
      ($message = $message{$id}) =~ s/\$argv\[\d+\]/.../g;
      $message =~ s/\\"/"/g;
      print WARNING "$id (", ($enabled{$id} ? "enabled" : "disabled"), ")\n";
      print WARNING "    $message\n\n";
   }
}

sub CheckURL
{
   local($url)		= @_;
   local($workfile)	= "$TMPDIR/$PROGRAM.$$";
   local($urlget)	= $variable{'url-get'};


   die "$PROGRAM: url-get ѿƤޤ -- ".
       "$url ˡ狼ޤ.\n" unless defined $urlget;

   system("$urlget $url > $workfile");
   &WebLint($workfile, $url);
   unlink $workfile;
}

sub PrintToDo
{
   print STDERR "$todo";
   exit 0;
}

#========================================================================
# Function:	wanted
# Purpose:	This is called by &find() to determine whether a file
#               is wanted.  We're looking for files, with the filename
#               extension .html or .htm.
#========================================================================
sub wanted
{
   local($foundIndex);

   if (-d $_)
   {
      $foundIndex = 0;
      foreach $legalIndex (@dirIndices)
      {
         $foundIndex=1,last if -f "$_/$legalIndex";
      }
      if (! $foundIndex)
      {
         &whine("$arg/$_", 'directory-index', "@dirIndices");
      }
   }

   /\.($fileExtensions)$/ &&   	# valid filename extensions: .html .htm
      -f $_ &&			# only looking for files
      (!$opt_l || !-l $_) &&	# ignore symlinks if -l given
      &WebLint($_,$name);	# check the file
}

sub PopEndTag
{
   $matched     = pop @tags;
   pop @tagNums;
   $matchedLine = pop @taglines;

   #-- does top of stack match top of orphans stack? --------
   while (@orphans > 0 && @tags > 0
	  && $orphans[$#orphans] eq $tags[$#tags])
   {
      &whine($., 'element-overlap', $orphans[$#orphans],
	     $orphanlines[$#orphanlines], $matched, $matchedLine);
      pop @orphans;
      pop @orphanlines;
      pop @tags;
      pop @tagNums;
      pop @taglines;
   }
   $tagRE = join('|',@tags);
}

#========================================================================
# Function:	PickTmpdir
# Purpose:	Pick a temporary working directory. If TMPDIR environment
#		variable is set, then we try that first.
#========================================================================
sub PickTmpdir
{
   local(@options) = @_;
   local($tmpdir);

   @options = ($ENV{'TMPDIR'}, @options) if defined $ENV{'TMPDIR'};
   foreach $tmpdir (@options)
   {
      return $tmpdir if -d $tmpdir && -w $tmpdir;
   }
   die "$PROGRAM: temporary directory 򸫤Ĥ뤳ȤǤޤ.\n",
       ' ' x (length($PROGRAM)+2), ": ",join(' ',@options),"\n";
}

#========================================================================
# Function:	ReadDefaults
# Purpose:	Read the built-in defaults.  These are stored at the end
#               of the script, after the __END__, and read from the
#               DATA filehandle.
#========================================================================
sub ReadDefaults
{
   local(@elements);


   while (<DATA>)
   {
      chop;
      s/^\s*//;
      next if /^$/;

      push(@elements, $_);

      next unless @elements == 3;

      ($id, $default, $message) = @elements;
      $enabled{$id} = ($default eq 'ENABLE');
      ($message{$id} = $message) =~ s/"/\\"/g;
      undef @elements;
   }
}

__END__
upper-case
	DISABLE
	 <$argv[0]> ʸǤϤޤ.
lower-case
	DISABLE
	 <$argv[0]> ʸǤϤޤ.
mixed-case
	ENABLE
	 case (ʸʸ) ̵뤵Ƥޤ.
here-anchor
	ENABLE
	󥫡Ȥ `here', `', `', `' ȤΤɤʤǤ!
require-head
	ENABLE
	HEAD  <TITLE> ޤ.
once-only
	ENABLE
	 <$argv[0]> 1ĤȤ٤Ǥ.  $argv[1] ܤ1ĸޤ!
body-no-head
	ENABLE
	<BODY> ϤäƤ <HEAD> ޤ.
html-outer
	ENABLE
	ֳ¦Υ <HTML> .. </HTML> Ǥ٤Ǥ.
head-element
	ENABLE
	<$argv[0]>  HEAD ǤΤ߻ȤȤǤޤ.
non-head-element
	ENABLE
	<$argv[0]>  HEAD ǻȤȤϤǤޤ.
obsolete
	ENABLE
	<$argv[0]> ϰ̤ˤϤ⤦ѤƤޤ.
mis-match
	ENABLE
	</$argv[0]> Ĥ礤ޤ (ޥå <$argv[0]> Ĥޤ).
img-alt
	ENABLE
	IMG  ALT ƥȤƤޤ.
nested-element
	ENABLE
	<$argv[0]> ϥͥȤ뤳ȤϤǤޤ -- $argv[1] ܤ <$argv[0]> б </$argv[0]> Ĥޤ.
mailto-link
	DISABLE
	HEAD  <LINK REV=MADE HREF="mailto..."> ĤޤǤ.
element-overlap
	ENABLE
	$argv[1] ܤ </$argv[0]> , $argv[3] ܤ˳Ϥ줿 <$argv[2]> ȽŤʤäƤ褦Ǥ.
unclosed-element
	ENABLE
	$argv[1] ܤ <$argv[0]> б뽪λ </$argv[0]> Ĥޤ.
markup-in-comment
	ENABLE
	ޤ줿ޡåפϴĤΥ֥饦𤵤뤳Ȥޤ.
unknown-attribute
	ENABLE
	<$argv[0]> Ȥ "$argv[1]" °Ǥ.
leading-whitespace
	ENABLE
	"<"  "$argv[0]>" δ֤˶ʸƤϤޤ.
required-attribute
	ENABLE
	<$argv[1]> Ȥˤ $argv[0] °ɬפǤ.
unknown-element
	ENABLE
	<$argv[0]> ʥȤǤ.
odd-quotes
	ENABLE
	<$argv[0]> ФˤʤäƤʤ䤬ޤ.
heading-order
	ENABLE
	ɤʤǤ - إǥ <$argv[0]>  $argv[2] ܤ <H$argv[1]> ³Ƥޤ.
bad-link
	DISABLE
	󥫡 "$argv[0]" ΥåȤĤޤǤ.
expected-attribute
	ENABLE
	<$argv[0]> ˤ°λ꤬ɬפǤ.
unexpected-open
	ENABLE
	ͽ <  <$argv[0]> ˤޤ -- ĤƤʤȤβǽޤ.
required-context
	ENABLE
	<$argv[0]> ˤȤäʸ̮Ǥ - <$argv[1]> ǻȤʤФʤޤ.
unclosed-comment
	ENABLE
	ȤĤƤޤ (Ȥ: <!-- ... -->).
illegal-closing
	ENABLE
	 <$argv[0]> ȤˤʤäƻȤޤ -- </$argv[0]> Ǥ.
extension-markup
	ENABLE
	<$argv[0]>  ĥޡåפǤ (ˤ "-x <extension>" ȤäƤ).
extension-attribute
	ENABLE
	<$argv[1]>  `$argv[0]' °ϳĥޡåפǤ (ˤ "-x <extension>" ȤäƤ).
physical-font
	DISABLE
	<$argv[0]> ʪŪեȥޡåפǤ -- ŪޡåפȤäƤ (㤨 $argv[1]).
repeated-attribute
	ENABLE
	<$argv[1]>  $argv[0] °֤Ƥޤ.
must-follow
	ENABLE
	<$argv[0]>  <$argv[1]> ľ³ʤФʤޤ.
empty-container
	ENABLE
	ȹ碌 <$argv[0]> Ǥ.
directory-index
	ENABLE
	directory  index file ޤ ($argv[0]).
closing-attribute
	ENABLE
	λ <$argv[0]> ˤϲ°ꤷƤϤʤޤ.
attribute-delimiter
	DISABLE
	°ͤΥǥߥå ' ȤΤƤΥ֥饦ǥݡȤƤϤޤ ($argv[1]  $argv[0] °).
img-size
	DISABLE
	IMG  WIDTH ڤ HEIGHT °ꤹ뤳Ȥ, ĤΥ֥饦Ǥθɤ夵뤳ȤǤޤ.
container-whitespace
	DISABLE
	ȹ碌 $argv[1] $argv[0]ʸޤ.
require-doctype
	ENABLE
	ǽΥȤ DOCTYPE εҤǤϤޤǤ.
literal-metacharacter
	ENABLE
	᥿饯 '$argv[0]'  '$argv[1]' ɽ٤Ǥ.
heading-mismatch
	ENABLE
	ʥإǥ󥰤Ǥ - ϥ <$argv[0]> Ǥ, λ </$argv[1]> Ǥ.
bad-text-context
	ENABLE
	<$argv[0]> ϥƥȤˤȤäʸ̮Ǥ; $argv[1] ˤ٤Ǥ.
attribute-format
	ENABLE
	$argv[1]  $argv[0] °ͤǤ ($argv[2]).
quote-attribute-value
	ENABLE
	$argv[2] Ȥ $argv[0] ° ($argv[1]) ϰǰϤޤ٤Ǥ. (i.e. $argv[0]="$argv[1]")
meta-in-pre
	ENABLE
	Ȥ PRE ȤǤäƤ, '$argv[1]'  '$argv[0]' Ȥ٤Ǥ.
heading-in-anchor
	ENABLE
	<$argv[0]>  <A> ΤǤϤʤ, <A>  <$argv[0]> ٤Ǥ.
title-length
	ENABLE
	HTML λͽ TITLE  64 ʸ˼뤳Ȥ侩Ƥޤ.
doctype-mismatch
	DISABLE
	򤵤줿ĥ `$argv[0]'  DOCTYPE Ȱפޤ.
url-whitespace
	ENABLE
	URL ˶ʸޤޤƤޤ ($argv[0]).
url-backslash
	ENABLE
	URL  `\x5c' ޤޤƤޤ -- path ζڤ `/' ǤʤФʤޤ ($argv[0]).
unsafe-url
	ENABLE
	URL ˰ǤʤʸޤޤƤޤ ($argv[0]).
trailing-slash
	DISABLE
	󥫡 "$argv[0]" ΥåȤ directory $argv[1] -- "$argv[2]" Ȥ٤Ǥ.
bad-entity
	ENABLE
	"$argv[0]" ʼλȤǤ.
unterminated-comment
	DISABLE
	 `--' бǤ.
file-url
	ENABLE
	`file' URL ϥۥȸ scheme Ǥ ($argv[0]).
url-watch
	DISABLE
	watch list ˤ URL "$argv[0]" Ĥޤ.
non-ascii
	DISABLE
	 ASCII ʸ "$argv[0]" ޤ.
