: # 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]" があります.
