########################################################################
{ package INFPARSEVersion;

  $BuildVersion = "1.00.0027";
}


#--------------------------------------------------------------------
sub
InitGlobalsAll
{
########################################################################
#
# Globals
#
########################################################################
#--------------------------------------------------------------------
#
#	These are globals/constants used for all files without reset.
#	Include:
#		 Counts for files.
#		 Summary array.
#		 Predefined sections, keys.
#
#--------------------------------------------------------------------
$ProvKey		= "PROVIDER";	  #place holder for Provider key.
$InvalSec		= "NONE";		  #place holder for invalid section names.
$VerSec 		= 'VERSION';		   # expected section name.
$StrSec 		= 'STRINGS';		   # expected section name.
$ManuSec		= 'MANUFACTURER';	   # expected section name.
$CFlagSec		= 'CONTROLFLAGS';	   # expected section name.
$DestDirsSec	= 'DESTINATIONDIRS';   # expected section name.

%VerVal=('SIGNATURE',	'$CHICAGO$', 
		 'CLASS',		'ClassPlaceHolder', 
		 'CLASSGUID',	'ClassGUIPlaceHolder',
		 'PROVIDER',	'ProviderPlaceHolder',
		 'LAYOUTFILE',	'Layout.inf',
         'CATALOGFILE', '');

%CFlagKeys=('EXCLUDEFROMSELECT', 'ExSel', 
			'EXCLUDEFROMSELECT.NT', 'ExSel',
			'EXCLUDEFROMSELECT.NTX86', 'ExSel',
			'EXCLUDEFROMSELECT.NTMIPS', 'ExSel',
			'EXCLUDEFROMSELECT.NTALPHA', 'ExSel',
			'EXCLUDEFROMSELECT.NTPPC', 'ExSel',
			'EXCLUDEFROMSELECT.WIN', 'ExSel');

%ModelKeys=(
	'ADDREG',		"CheckSectionExist",
	'COPYFILES',	"CheckFileSectionString",
	'COPYFILES.NT',	"CheckFileSectionString",
	'DRIVERVER',	"",
	'UPDATEINIS',	"CheckSectionExist",
	'UNINSTALL',	"CheckSectionExist",
	'LOGCONFIG',	"CheckSectionExist",
	'RENFILES',		"CheckFileSectionString",
	'DELFILES',		"CheckFileSectionString",
	'UPDATEINIFIELDS',	"CheckSectionExist",
	'DELREG',		"CheckSectionExist",
	'INI2REG',		"CheckSectionExist",
	'UPDATECFGSYS', "CheckSectionExist",
	'UPDATEAUTOBAT',	"CheckSectionExist",
	'INCLUDE',		"CheckIncludeItem",
	'REBOOT',		"",
	'RESTART',		""
	   );

%LDIDValues = (	00 => 1, 01 => 1, 02 => 1, 03 => 1, 04 => 1, 10 => 1, 
				11 => 1, 12 => 1, 13 => 1, 14 => 1, 15 => 1, 16 => 1, 
				17 => 1, 18 => 1, 19 => 1, 20 => 1, 21 => 1, 22 => 1, 
				23 => 1, 25 => 1, 26 => 1, 27 => 1, 28 => 1, 30 => 1, 
				31 => 1, 33 => 1, 34 => 1 );

%InstallExtensions = (	"WIN"		=> "WIN", 
						"NT"		=> "NT",
						"NTX86"		=> "NTX86",
						"NTMIPS"	=> "NTMIPS",
						"NTALPHA"	=> "NTALPHA",
						"NTPPC"		=> "NTPPC" );

#--------------------------------------------------------------------
$NumFiles=0;		# Count for input files
$NumErrFiles=0; 	# Count for input files with errors

%Summary=();		# This is the summary assoc array with one entry per file. 
					# It has the key as the file name and the related info that 
					# should go to the summary page: ie #errs, #wrns

}

#--------------------------------------------------------------------
sub
InitGlobalsCurrent
{
  #--------------------------------------------------------------------
  # initialize variables for this input file
  #--------------------------------------------------------------------
  $MaxIDLen  = 50;
  $MaxStrLen  = 80;
  $MaxLineLen = 128;
  $InfDirectory = "";

  @INFlines=(); 		# this is the array of preprocessed input lines.
						# it is indexedby line numbers.
  $TotalLines=0;

  %IncludeFiles=(); 	# this is the array of included inf files.
						# it is indexed by FileName and the value is the
						# current line number in INFlines where the file starts.
  @IncludeLines=(); 	# this is the array of included inf files.
						# [startline, filename].

  %SecStart=(); 		# this is the array of start line num of sections
						# indexed by the section name.

  %SecEnd=();			# this is the array of end line num of sections
						# indexed by the section name.

  @SecRef=($StrSec);	# List of sections referenced.
			# Initialised to stand alone sections, you should add at @SecRef.

	$QuoteNum=0;		# Count of quoated strings (used to build the ids)
	%AllQuotes=();		# string values (without quotes)

			# Compile list of errrors and warnings and print them together.
	%StrErrs=();		# Assoc array of errors indexed by line number.
	$NumErrs=0;			# count for errors
	%StrWrns=();		# Assoc array of warnings indexed by line number.
	$NumWrns=0;			# count for warnings

	@RefStr=();		# referenced strings
	%FStr=(); 			# Assoc array of defined strings (in [STRINGS] section).

	%ManuList=(); 		# this is array of (ManuSection, %ManuName%) as 
						# read from the section MANUFACTURER.

	%ModelList=();		# This array has (ModelSec, ModelInfo) tuples.

	%AllIDList = ();		# ID => array of id definitons 
							# [ModelName, LineNumber, Rank, $OriginalName]

	%InstList=();		# list of Install sections

	%ExSelList=();		# This array has (ExcludeID => (ExcludeType => [LineNo, bIsDefined]))
						# tuples.
						# where ExcludeType is the keyword used for the exclude line

	%SourceDisksNames = ();	#	array with disk names containing
							#	DiskNamesSection => %DiskNames
							#		%DiskNames : DiskName => [Value, LineNo]
	%SourceDisksFiles = (); #	array with disk files containing
							#	DiskFilesSection => %FileNames
							#		%FileNames : FileName => [Value, LineNo]
	undef(%DestinationDirs);
							#	array with entryes found in DestinationDirs section
							#	File_Section => entry value (LDID, sub_dir)
	$DestinationDirsChecked = 0;
	$LayoutFilePresent	= 0;

}

#--------------------------------------------------------------------
sub
BeginTimer
{
local($ProcName) = @_;
local(@time);

   if (defined($TimeMarks{$ProcName}))
	  { print "Procedure $ProcName called recursively.\n"; }

   $TimeMarks{$ProcName} = time();
   @time = localtime($TimeMarks{$ProcName});
   printf "%0$IndentValue s$ProcName starts at %02d:%02d:%02d\n", "", $time[2], $time[1], $time[0];

   $IndentValue++;
}

#--------------------------------------------------------------------
sub
EndTimer
{
local($ProcName) = @_;
local($EndTime, @time);

   if (! defined($TimeMarks{$ProcName}))
	  { print "BeginTimer for procedure $ProcName not called.\n"; 
	return;
	  }

   $EndTime = time();
   @time = localtime($EndTime);
   $IndentValue--;
   printf "%0$IndentValue s$ProcName ends at %02d:%02d:%02d. ", "", $time[2], $time[1], $time[0];
   $Elapsed = $EndTime-$TimeMarks{$ProcName};
   printf "Elapsed time : %02dh %02dm %02ds\n", int($Elapsed / 3600), int($Elapsed / 60) % 60, $Elapsed % 60;
   undef ($TimeMarks{$ProcName});
}

#############################################################
#
#	Error printing related subs.
#
#############################################################
#------------------------------------------------------------
# This sub adds to thelist of errors.
#
# Usage: 
#		add_ref_err(error_number, line_num, @arg_err)
#------------------------------------------------------------
sub add_ref_err
{
local($this_err, $Lnum, @arg_err) = @_;
local($str, $this_str, $info_err, $format_err);

	$info_err = $ErrorTable{$this_err};
	if ( !defined($info_err) )
	{	$this_str = "Unknown error $this_err."; }
	else
	{	$format_err = $$info_err[0];
		$this_str = sprintf($format_err, @arg_err);
	}

	$NumErrs++;
	$str = $StrErrs{$Lnum};
	$str = join("; ", $str, "(E$this_err) ".$this_str);
	$StrErrs{$Lnum} = $str;
}

#------------------------------------------------------------
# This sub adds to the list of warnings.
#
# Usage: 
#		add_ref_wrn(warning_number, line_num, @arg_wrn)
#------------------------------------------------------------
sub add_ref_wrn
{
local($this_wrn, $Lnum,  @arg_wrn) = @_;
local($str, $this_str, $info_wrn, $format_wrn);

	$info_wrn = $WarningTable{$this_wrn};
	if ( !defined($info_wrn) )
	{	$this_str = "Unknown warning $this_wrn."; }
	else
	{	$format_wrn = $$info_wrn[0];
		$this_str = sprintf($format_wrn, @arg_wrn);
	}

	$NumWrns++;
	$str = $StrWrns{$Lnum};
	$str = join("; ", $str, "(W$this_wrn) ".$this_str);
	$StrWrns{$Lnum} = $str;
}

#-------------------------------------------------------------
#
# This sub ensures that the given line does not already have an 
# error flagged before adding an error. - Avoids multiple errors
# from installing the same section for multiple modems.
#
# Globals changed:
#
# Usage:
#	inst_err(error_str, line_num)
#-------------------------------------------------------------

sub 
inst_err
{
local($this_err, $Lnum, @arg_err) = @_;

	add_ref_err($this_err, $Lnum, @arg_err) 
			if (!defined($StrErrs{$Lnum}) || 
				$StrErrs{$Lnum} !~ /\(E$this_err\)/);
}

#-------------------------------------------------------------
#
# This sub ensures that the given line does not already have a 
# warning flagged before adding an warning. - Avoids multiple warnings
# from installing the same section for multiple modems.
#
# Globals changed:
#
# Usage:
#	ins_wrn(error_str, line_num)
#
#-------------------------------------------------------------
sub 
inst_wrn
{
local($this_wrn, $Lnum, @arg_wrn) = @_;

	add_ref_wrn($this_wrn, $Lnum, @arg_wrn) 
		if (!defined($StrWrns{$Lnum}) || 
			$StrWrns{$Lnum} !~ /\(W$this_wrn\)/);
}

#------------------------------------------------------------
#	This sub does the following:
#	- flags warnings for length > MaxLineLen.
#	- joins lines to next line if ending in '\'
#	- replace quoted strings by __QUOTED_STR_XXXX__ and put the
#		unqouted string in an array. (use XXXX for indexing it.)
#	- flags warnings for tabs in quoted strings.
#	- gets rid of comments, preceeding and ending whitespaces,
#		end of line.
#	- adds non null strings to the array %INFline - indexing
#		with the line number. (NOTE: joined lines will be
#		indexed by the line joined line's line number.)
#
# Globals changed:
#		INFLines
#		TotalLines
#
# Usage: 
#		preprocess(infile_name)
#------------------------------------------------------------
sub 
preprocess
{
#local ($infile) = @_;
local($infile) = $_[0];
local($prev_line, $line, $CLnum, $poscomma, $posquote1, $posquote2, $bRemoveComment, $comment);


	$prev_line = "";
	open (INFILE, "$infile")	|| die "Can't open $infile for input\n";

	while(<INFILE>)
	{
	$CLnum = $.;
	chop if /\n$/;			 # removes \n
#	add_ref_wrn("Line has more than $MaxLineLen characters.", $.) 
	add_ref_wrn(2001, $., $MaxLineLen) 
		if (length($_) > $MaxLineLen); 
	#******** Warn for high ASCII and tabs.

	$line = join("", $prev_line, $_);
	$prev_line = "";

	#process for comments.

	$posquote1	= index($line, '"');
	$posquote2	= ($posquote1 >= 0) ? index($line, '"', $posquote1+1) : -1;
	$poscomma	= index($line, ';');
	$bRemoveComment = 0;

	while ($poscomma >= 0)
	{
		if ($poscomma < $posquote1 || $posquote1 < 0)	# ';' before '"' => is comment
		{	
			$bRemoveComment = 1;
			last;
		}

		if ($posquote2 >= 0 && $posquote1 >= 0)
		{
			if ($poscomma < $posquote2)			# ';' between '"' => sting first
			{
				$poscomma = index($line, ';', $posquote2 + 1);
			}
						# find next string
			$posquote1	= index($line, '"', $posquote2 + 1);	
			$posquote2	= ($posquote1 >= 0) ? index($line, '"', $posquote1+1) : -1;
			next;
		}
		last;		# ';' after '"' => sting not terminated yet, string first
	}

	if ($bRemoveComment == 1)
	{
		$comment = substr($line, $poscomma+1);
		add_ref_err(1033, $CLnum) if ($comment =~ /;/);
		$line = substr($line, 0, $poscomma);
	}

	$line =~ s/^[\s\t]*//; #strip leading white spaces.
	$line =~ s/[\s\t]*$//; #strip ending white spaces and eol.
	next if ($line =~ /^$/);

	if ($line =~ /(.)*\\$/) 
	{
		# need to join this line to the next.
		$line =~ s/\\$//;
		$prev_line = $line;
		next; # process joined line together for quotes.
	} 

	#process for quotes.
	$line = &proc_quotes($line, $CLnum);

	next if ($line =~ /^$/); # $line is empty.

	#put the preprocessed line in assoc array indexed by the linenum.
	$INFlines[$CLnum] = $line;
	}

	$TotalLines = $.;

	$FileSize = -s INFILE;
	add_ref_err(1034, $TotalLines) if ($FileSize > 62*1024);

	close (INFILE);
}


#------------------------------------------------------------
#	This sub looks at the list of preprocessed lines and 
#	assumes that all occurances of /[(.)*]/ not inside
#	quotes represent section headers.
#	It creates the assoc arrays related to sections and
#	also flags redefinition of sections.
#
# Globals changed:
#		SecStart
#		secEnd
#
# Usage: 
#		process_sections()
#------------------------------------------------------------
sub 
process_sections
{
local($CLnum, $prev_sec, $line, $sec, $x, $PrevLastLine);

	# go linewise in the assoc array INFline.
	$CLnum = 0; # holds current line num.
	$prev_sec = $InvalSec;
	$PrevSectLastLine = -1;

	while(++$CLnum <= $TotalLines)
	{

	$line = $INFlines[$CLnum];
	if (! defined($line))
	{
		next;
	}

	if ($line =~ /\[(.*)\]/) # section header
	{
		$sec = uc $1;  # section name in upper case
		if ($line !~ /^\[(.*)\]$/) # something else on line
		{
#		add_ref_err("Incorrect section definition.", $CLnum);
		add_ref_err(1001, $CLnum);
		}
		
		$SecEnd{$prev_sec} = $PrevSectLastLine + 1 if ($prev_sec !~ /^($InvalSec)$/);

		if (defined ($x = $SecStart{$sec})) # redefinition of sec
		{
#			add_ref_err("Section redefined. [$sec] already defined at Line $x. Not processing this section.", $CLnum);
			add_ref_err(1002, $CLnum, $sec, $x);
			$prev_sec = $InvalSec;
			$PrevSectLastLine = -1;
			next;
		}

		# add to the assoc arrays.
		$SecStart{$sec} = $CLnum;
		#$SecList{$CLnum} = $sec;
		$prev_sec = $sec;
	}

	$PrevSectLastLine = $CLnum;
	}

	$SecEnd{$prev_sec} = $PrevSectLastLine + 1 if ($prev_sec !~ /^($InvalSec)$/);

}

#################################################################
#
# Section processing subs.
#
#################################################################
#-------------------------------------------------------------
#
# This sub looks for all strings defined in the strings section
# and adds them to the FStr list after getting the original
# quoted string from AllQuotes.
#
# Globals changed:
#
# Usage:
#	StrProc(expected_section_name)
#
#-------------------------------------------------------------
sub 
StrProc
{
local($sec) = @_;			# expected section name.
local($line, $LHS, $RHS, $CLnum, $LLnum);

	if (! defined ($CLnum = $SecStart{$sec}))
	{
#		add_ref_err("Section [$sec] not defined.", $TotalLines);
		add_ref_err(1003, $TotalLines, $sec);
		return;
	}
	@SecRef = (@SecRef, $sec);
	die "EndLine of section [$sec] not defined.\n" 
	unless (defined ($LLnum = $SecEnd{$sec}));
	while(++$CLnum < $LLnum)
	{
		$line = $INFlines[$CLnum];
		next if (! defined($line));

		$LHS = &getLHS($line, $CLnum);
		# if LHS should be of the form %xx%
		if ($LHS !~ /%([a-zA-Z0-9_]*)%/) 
		{
			$LHS =~ s/^%//;
			$LHS =~ s/%$//;
			$LHS =~ s/(.*)/%$1%/;
		}
		# add LHS to found strings.

		$RHS = &getRHS($line, $CLnum);
		# here RHS should be in quotes
		$LHS = uc $LHS;
		if (defined($FStr{$LHS}))
		{
			add_ref_err(1048, $CLnum);
			next;
		}
		#	right side should be in quotes or a numeric value
		if($RHS !~ /^__QUOTED_STR_\(([0-9]*)\)__$/) 
		{
			if ($RHS !~ /^(0x)??[0-9a-f]*$/i)	#neither numeric
			{
	#			add_ref_err("Entry value should be in quotes.", $CLnum);
				add_ref_err(1004, $CLnum);
				$FStr{$LHS} = $RHS;
			}
		} else { 
			#$quote_num = $1;
			$FStr{$LHS} = $AllQuotes{$1};
	#		add_ref_err("String has more than $MaxStrLen characters.", $CLnum);
			add_ref_err(1005, $CLnum, $MaxStrLen)
				if (length($FStr{$LHS}) > $MaxStrLen);
		}
	}
}

#-------------------------------------------------------------
#
# This sub looks for all mandatory and optional Version section
# keys and validates them using the %VerVal array.
#
# Globals changed:
#
# Usage:
#	VerProc(expected_section_name)
#
#-------------------------------------------------------------
sub 
VerProc
{
local($sec) = @_;			# expected section name.
local($line, $LHS, $RHS, $CLnum, $LLnum);
local(%valid_keys, $key);

	if (! defined ($CLnum = $SecStart{$sec}))
	{
#	add_ref_err("Section [$sec] not defined.", $TotalLines);
	add_ref_err(1003, $TotalLines, $sec);
	return;
	}
	@SecRef = (@SecRef, $sec);
	die "EndLine of section $sec not defined\n" 
	unless (defined ($LLnum = $SecEnd{$sec}));

	# make a copy of VerVal so that we can compare and delete
	# keys already defined for this model.
	%valid_keys = %VerVal;

	while(++$CLnum < $LLnum)
	{
	$line = $INFlines[$CLnum];
	if (! defined($line))
	{
		next;
	}
	# print "DEBUG: $line\n";
	$LHS = uc &getLHS($line, $CLnum); #Change LHS to uppercase. 
	$RHS = &getRHS($line, $CLnum);
	$RHS = $AllQuotes{$1} if($RHS =~ /^__QUOTED_STR_\(([0-9]*)\)__$/);

	if (!defined ($expect_RHS = $valid_keys{$LHS}))
	{
#		add_ref_err("Don't know to process $LHS keyword or redefined key.", $CLnum);
		add_ref_err(1006, $CLnum, $LHS);
		delete $valid_keys{$LHS};
		next;
	}

	# delete this key from the assoc to know that we are done with it.
	delete $valid_keys{$LHS};

	#check if Provider key.
	if ($LHS =~ /^($ProvKey)$/)
	{
		if ($RHS !~ /^%(.*)%$/) 
		{
#		add_ref_wrn("Provider is expected to be of the form %name%.", $CLnum);
		add_ref_wrn(2002, $CLnum);
		}else {
#		add_ref_err("Provider $RHS not found in [STRINGS] section.", $CLnum)
		add_ref_err(1007, $CLnum, $RHS)
			if (!defined $FStr{uc $RHS}); 
		@RefStr = (@RefStr, uc $RHS);
		}
		next;
	}

	#check if SIGNATURE key.
	if ($LHS =~ /^SIGNATURE$/)
	{
		if (uc $RHS ne uc $expect_RHS) 
		{
#		add_ref_err("Signature is expected to be \"\$CHICAGO\$\"", $CLnum);
		add_ref_err(1008, $CLnum);
		}
		next;
	}

	#check if LAYOUTFILE key.
	if ($LHS =~ /^LAYOUTFILE$/)
	{
		$LayoutFilePresent = 1;
		if (uc $RHS ne uc $expect_RHS) 
		{
#		add_ref_wrn("LayoutFile is optional. If present should be $expect_RHS.", $CLnum);
		add_ref_wrn(2003, $CLnum, $expect_RHS);
		}

		add_ref_wrn(2013, $CLnum);
		next;
	}

	#check if CLASS key.
	if ($LHS =~ /^CLASS$/)
	{
		if ((uc $RHS ne uc $expect_RHS) &&
			(length($expect_RHS) != 0)) 
		{
#		add_ref_err("Class is expected to be $expect_RHS.", $CLnum);
		add_ref_err(1009, $CLnum, $expect_RHS);
		}
		next;
	}

	#check if CLASSGUID key.
	if ($LHS =~ /^CLASSGUID$/)
	{
		if ( ($RHS ne $expect_RHS) &&
			 (length($expect_RHS) != 0)) 
		{
#		add_ref_err("ClassGUID is expected to be $expect_RHS.", $CLnum);
		add_ref_err(1010, $CLnum, $expect_RHS);
		}
		next;
	}
	if ($LHS =~ /^CATALOGFILE$/)
	{
        local($catfile);
        $catfile = $infile;
        $catfile =~ s/^(([a-zA-Z]:)?)//;    # Strip off preceeding drive letter.
        $catfile =~ s/(^.*\\)//;            # Strip off directories
        $catfile =~ s/\.\w*$//;             # Strip off extension
        $catfile = "$catfile.cat";

        if (uc $RHS ne uc $catfile)
        {
#    		add_ref_wrn(2017, $CLnum, $catfile);
#           print"\nCatalogFile is optional. If present should be $catfile.\n";
        }
    }
	} 

	foreach $key (keys(%valid_keys))
	{
	#	add_ref_err("Key $key not defined.", $LLnum-1)
		if ($key =~ /LAYOUTFILE/)
		{	add_ref_wrn(2014, $LLnum-1);	}
		else
        {
            if ($key !~ /CATALOGFILE/)
		    {	add_ref_err(1011, $LLnum-1, $key); }
        }
	}

}

#-------------------------------------------------------------
#
# This section looks for the Manufacurer section
#
# Globals changed:
#	ManuList
#
# Usage:
#	ManuProc(expected_section_name)
#
#-------------------------------------------------------------
sub 
ManuProc
{
local($sec) = @_;			# expected section name.
local($line, $LHS, $RHS, $CLnum, $LLnum);
local($manu, $count);

	if (! defined ($CLnum = $SecStart{$sec}))
	{
#	add_ref_err("Section [$sec] not defined.", $TotalLines);
	add_ref_err(1003, $TotalLines, $sec);
	return;
	}
	@SecRef = (@SecRef, $sec);
	die "EndLine of section $sec not defined\n" 
	unless (defined ($LLnum = $SecEnd{$sec}));
	while(++$CLnum < $LLnum)
	{
		$line = $INFlines[$CLnum];
		if (! defined($line))
		{
			next;
		}
		$LHS = &getLHS($line, $CLnum);
		# if LHS is of the form %xx%
		if ($LHS =~ /^%(.*)%$/) {
#			add_ref_err("Manufacturer $LHS not found in [STRINGS] section.", $CLnum)
			add_ref_err(1012, $CLnum, $LHS )
				if (!defined $FStr{uc $LHS} );
			@RefStr = (@RefStr, uc $LHS);
		}

		$RHS = &getRHS($line, $CLnum);

		# here RHS should be just one word.
		if($RHS =~ /[\s\t]+/) {
#			add_ref_err("Not more than one word on the right of \"=\".", $CLnum);
			add_ref_err(1013, $CLnum);
		} 

		# add RHS to manu sections.
		if (! defined ($SecStart{uc $RHS}))
		{
#			add_ref_err("Section [$RHS] not defined.", $CLnum);
			add_ref_err(1003, $CLnum, $RHS);
		}
		else
		{
			$ManuList{uc $RHS} = [$LHS];
		}
	}

	if (($count = %ManuList) == 0)
	{
		add_ref_err(1039, $SecStart{$sec}, "Manufacturer");
	}
}

#-------------------------------------------------------------
#
# This sub processes the ControlFalgs section and adds the 
# IDs defined here to the respective lists of Gen/NT by using
# ExSel.
#
# Globals changed:
#
# Usage:
#	CFlagProc(expected_section_name)
#
#-------------------------------------------------------------
sub 
CFlagProc
{
local($sec) = @_;			# expected section name.
local($line, $LHS, $RHS, $CLnum, $LLnum);
local($proc);

	if (! defined ($CLnum = $SecStart{$sec}))
	{
		return;
	}

	@SecRef = (@SecRef, $sec);
	die "EndLine of section $sec not defined\n" 
	unless (defined ($LLnum = $SecEnd{$sec}));
	while(++$CLnum < $LLnum)
	{
	$line = $INFlines[$CLnum];
	if (! defined($line))
	{
		next;
	}
	$LHS = uc &getLHS($line, $CLnum);
	# if LHS should be one of the CFlagKeys.
	if (!defined ($proc = $CFlagKeys{$LHS}))
	{ 
#		add_ref_err("Illegal entry name.", $CLnum);
		add_ref_err(1014, $CLnum);
		next;
	}
	$RHS = &getRHS($line, $CLnum);
	&$proc($LHS, $RHS, $CLnum);
	}
}

#-------------------------------------------------------------
#
# This sub processes the DestinationDirs section and retains 
# the list of file sections defined
#
# Globals changed:
#	%DestinationDirs
#
# Usage:
#	DestDirs(expected_section_name)
#
#-------------------------------------------------------------
sub 
DestDirs
{
local($sec) = @_;			# expected section name.
local($line, $LHS, $RHS, $CLnum, $LLnum);
local(@FileSectValue);

	if (! defined ($CLnum = $SecStart{$sec}))
	{
		return;
	}

	@SecRef = (@SecRef, $sec);
	die "EndLine of section $sec not defined\n" 
	unless (defined ($LLnum = $SecEnd{$sec}));

	%DestinationDirs = ();
	while(++$CLnum < $LLnum)
	{
		$line = $INFlines[$CLnum];
		next	if (! defined($line));

		$LHS = &getLHS($line, $CLnum);
		if (defined($DestinationDirs{uc $LHS}))
		{
			add_ref_err(1042, $CLnum);
			next;
		}
		$RHS = &getRHS($line, $CLnum);

		$DestinationDirs{uc $LHS} = $RHS;

		if ($LHS !~ /^DefaultDestDir$/i)
		{
			CheckSectionExist($LHS, $CLnum);
		}

		@FileSectValue = split(",", $RHS);
		if (!defined($FileSectValue[0]))
		{
			add_ref_err(1043, $CLnum);
		}
		elsif (!defined($LDIDValues{$FileSectValue[0]}))
		{
			add_ref_err(1044, $CLnum);
		}
	}
}

#-------------------------------------------------------------
#
# This proc goes thru the manufacturer's section and lists out
# the models under this mfg and the expected install sections,
# the rank IDs etc.....
#
# Globals changed:
#	ModelList		= hash table with $ModelName => ModelInfoReference
#						ModelInfoReference = [	$ModelDescription,
#												$InstallSection,
#												RefArrayWithRank_0_IDs,
#												RefArrayWithRank_1_IDs,
#												RefArrayWithRank_2_IDs,
#												RefArrayWithLineNo,
#												LastLineOfSection]
#						RefArrayWithRank_n_IDs = [array of rank n IDs found for the 
#													same model description], n =0,1,2
#					
#	ManuList
#
# Usage:
#	ModelFind(expected_section_name)
#
#-------------------------------------------------------------
sub 
ModelFind
{
local($sec) = @_;
local($line, $LHS, $RHS, $CLnum, $LLnum);
local(%NewModelList, @id, $sz, $model, $ranks, $Type, $id_item, $ExcludeID, $ExcludeRef);
local(%ModelErrorList, @ArrayRanks, $r, $ManuRef, $count, $ExcludeKey, $ExcludeVal);

	%NewModelList = ();
	%ModelErrorList = ();

	if (! defined ($CLnum = $SecStart{$sec}))
	{
	return;
	}

	@SecRef = (@SecRef, $sec);
	die "EndLine of section $sec not defined\n" 
	unless (defined ($LLnum = $SecEnd{$sec}));

	while(++$CLnum < $LLnum)
	{
	$line = $INFlines[$CLnum];
	if (! defined($line))
	{
		next;
	}


#	>>> $LHS = &getDescriptionString($line, $CLnum);

	# if LHS should be of the form %xx%
	$LHS = &getLHS($line, $CLnum);
	if ($LHS !~ /^%(.*)%$/) 
	{
#		add_ref_wrn("Entry keyword should have the form %Device_description_name%", $CLnum);
		add_ref_wrn(2004, $CLnum);
	}
	elsif ($1 =~ /[%\[\]\s\t]/) 
	{
#		add_ref_wrn("Illegal character in string key.", $CLnum);
		add_ref_wrn(2005, $CLnum);
	}
	else
	{
#		add_ref_err("Model $LHS not found in [STRINGS] section.",	$CLnum)
		add_ref_err(1015, $CLnum, $LHS)
			if (!defined $FStr{uc $LHS} );
		# add LHS to refered strings.
		@RefStr = (@RefStr, uc $LHS);
	}

	$RHS = &getRHS($line, $CLnum);

	@id = ();
	@id = get_list($RHS, $CLnum);
	$sz = @id;

		#  install section
	$inst = uc shift(@id);
	if (($sz < 1) || ($inst =~ /^$/))
	{
#		add_ref_err("Missing Install section identifier. Not processing this model.", $CLnum);
		add_ref_err(1016, $CLnum);
		next;
	}

	# check for the install section
	# possible to use extensions
	$InstList{$inst} = 1; # placeholder....

	{
		local($SectionsDefined, $Extension);
		$SectionsDefined = 0;

		if (defined ($SecStart{uc $inst}))
		{	$SectionsDefined++;	}

		foreach $Extension (keys %InstallExtensions)
		{
			if (defined ($SecStart{uc ($inst.".$Extension")}))
			{
				$SectionsDefined++;	
				$InstList{uc ($inst.".$Extension")} = 1;
			}
		}
		if ($SectionsDefined == 0)
		{
	#		add_ref_err("cannot find install section (or extensions).", $CLnum);
			add_ref_err(1052, $CLnum, $inst);
		}
	}


	$sz = @id;
		# at most 3 ids
#	add_ref_err("Too many rank ids (three commas maximum).", $CLnum) if ($sz > 3);
	add_ref_err(1017, $CLnum) if ($sz > 3);

	for($i=0; $i < $sz; $i++)
	{
		if($id[$i] =~ /^__QUOTED_STR_\(([0-9]*)\)__$/)
		{
			$id[$i] = $AllQuotes{$1};
			add_ref_err(1051, $CLnum);
		}
	}

		# add reference for exclude from select list
	$model = $LHS;
	$count = 0;
	foreach $ExcludeID (@id)
	{
		my $UCExcludeID = uc $ExcludeID;

		add_ref_err(1049, $CLnum, $MaxIDLen)
			if (length($ExcludeID) > $MaxIDLen);
		if ($ExcludeID !~ /^$/)
		{
			#	add to the list of all ids
			if (!defined($AllIDList{$UCExcludeID}))
			{
				$AllIDList{$UCExcludeID} = [];
			}
			#	model, line, rank, original value
			push(@{$AllIDList{$UCExcludeID}}, [$model, $CLnum, $count, $ExcludeID]);

			if (defined($ExcludeRef = $ExSelList{$UCExcludeID}))
			{
				while (($ExcludeKey, $ExcludeVal) = each %$ExcludeRef)
				{
					$$ExcludeVal[1] = 1;		# Yes, the ID is defined
				}
			}
		}
		$count++;	# this gives the rank of the ID
	}

		# Process Rank0 id
#	add_ref_err("Missing Rank0 ID.", $CLnum)  if (($sz < 1) || ($id[0] =~ /^$/));
	add_ref_err(1018, $CLnum)  if (($sz < 1) || ($id[0] =~ /^$/));

	if (defined $ModelList{uc $model})  # same modem under diff mfg!!
	{
#		add_ref_wrn("Same model \"$model\", already defined under a different manufacturer.",  $CLnum);
		add_ref_wrn(2006,  $CLnum, $model);
	}

	if (defined $NewModelList{uc $model})	# same modem with diff id.
	{
		local($ROldModel);
		#add the new rank0 & rank1id and proceed.

		$ROldModel = $NewModelList{uc $model}; # pointer to the model array 

			# add new ranks
		for($r = 0; $r < 3; $r++)
		{
		   push( @{$$ROldModel[2+$r]} , (defined($id[$r])) ? $id[$r] : "" );
		}

			#  Compare if same modem -> same Inst section.
		if ( ($inst ne $$ROldModel[1]) &&
		 ! defined($ModelErrorList{$$ROldModel[0]}) )
		{ $ModelErrorList{$$ROldModel[0]} = 1019;
#			"$$ROldModel[0] : install sections are inconsistent.";
		}

			#  Add line number for ID definition.
		push(@{$$ROldModel[5]},  $CLnum);

		next;
	}
	# add this model name info to ManuList and ModelList.
	$ManuRef = $ManuList{$sec};				# add the new model to the list of models defined 
	if ( !defined($$ManuRef[1]) )			# in this device section
	   { $$ManuRef[1] = [$model]; }
	else
	   { push(@{$$ManuRef[1]}, $model); }

		#		Encountered first time, add the Install section name.
	$NewModelList{uc $model} = [$LHS, $inst] ;

	for($r = 0; $r < 3; $r++)
	   { push(@{$NewModelList{uc $model}}, [ (defined($id[$r])) ? $id[$r] : "" ]);
	   }

		#		Add line number for ID definition.
	push(@{$NewModelList{uc $model}}, [$CLnum]);
		#		Add line number for Section.
	push(@{$NewModelList{uc $model}}, $LLnum-1);
	}

	# report error for $sec
	foreach $model (keys %ModelErrorList)
	{
	   add_ref_err($ModelErrorList{$model}, $LLnum-1, $model);
	}

	if ( ($count = %NewModelList) == 0)
	{
		add_ref_err(1040, $SecStart{$sec}, $sec);
	}

	foreach $model (keys %NewModelList)
	{
		# append the new model list to global model list.
		$ModelList{$model} =  $NewModelList{$model};
	}
}


#-------------------------------------------------------------
#
# This proc processes the given Install section using the
# ModelKeys and the subs defined there.
#
# Globals changed:
#	$InstList	= hash table with $InstallSection => RefInstallInfo
#							RefInstallInfo	= reference to a hash table with
#													$InstallKeyword	=> RefKeywordInfo
#							RefKeywordInfo = [RefArrayWithKeywordValues, KeywordLine]
#							RefArrayWithKeywordValues = List of values for the given keyword 
#														as found in INF
#
# Usage:
#	InstProc(expected_section_name, IsHWInstall)
#
#-------------------------------------------------------------
sub 
InstProc
{
local($sec, $IsHWInstall) = @_;
local($line, $LHS, $RHS, $CLnum, $LLnum);
local(%valid_keys, $key, $sec_name, @sec_list, $count, $proc, $OriginalLHS);
local($model, $InstKeywordsHash, $KeywordValuesArray);

	$model = $sec;	# used by older procedures
	if (!defined($IsHWInstall))
	{	$IsHWInstall = 0; }			# used to mark and additional 
									# default section used to add hardware registry keys 
									# we validate this section but nothing more

	if (! defined ($CLnum = $SecStart{$sec}))
	{
		return;
	}

	@SecRef = (@SecRef, $sec);
	die "EndLine of section $sec not defined\n" 
	unless (defined ($LLnum = $SecEnd{$sec}));

	# make a copy of ModelKeys so that we can compare and delete
	# keys already defined for this model.
	%valid_keys = %ModelKeys;

	$InstKeywordsHash = {}; # reference to a new hash

	while(++$CLnum < $LLnum)
	{
	$line = $INFlines[$CLnum];
	if (! defined($line))
	{
		next;
	}

	if (uc($line) !~ /^REBOOT$/ && uc($line) !~ /^RESTART$/)
	{  $OriginalLHS = &getLHS($line, $CLnum);
	}
	else
	{  $OriginalLHS = $line;
	}
	$LHS = uc $OriginalLHS;


	# if LHS should be one of the valid_keys.
	if (!defined ($ModelKeys{$LHS}) )
	{ 
#		add_ref_err("Entry name $OriginalLHS illegal.", $CLnum);
		add_ref_err(1020, $CLnum, $OriginalLHS);
		next;
	}

	# if LHS cannot be duplicated.
	if (!defined ($proc = $valid_keys{$LHS}))
	{ 
#		add_ref_err("Duplicated key $OriginalLHS.", $CLnum);
		add_ref_err(1021, $CLnum, $OriginalLHS);
		next;
	}
	delete $valid_keys{$LHS};


	$KeywordValuesArray = ();
	${$InstKeywordsHash}{$LHS} = [$KeywordValuesArray,	$CLnum];

	if ($LHS =~ /^REBOOT$/ || $LHS =~ /^RESTART$/)
	{	if ( $line =~ /=$/ )
		{ 
#		add_ref_wrn("$OriginalLHS doesn't require = .", $CLnum);
		add_ref_wrn(2007, $CLnum, $OriginalLHS);
		}
		elsif ( $line =~ /=/ )
		{ 
#		add_ref_err("$OriginalLHS should not have a value.", $CLnum);
		add_ref_err(1022, $CLnum, $OriginalLHS);
		}

		if ( $LHS =~ /^REBOOT$/ && !defined $valid_keys{"RESTART"} )
		{
#		add_ref_err("Only one of the keys REBOOT and RESTART is allowed.", $CLnum);
		add_ref_err(1023, $CLnum);
		}

		if ( $LHS =~ /^RESTART$/ && !defined $valid_keys{"REBOOT"} )
		{
#		add_ref_err("Only one of the keys REBOOT and RESTART is allowed.", $CLnum);
		add_ref_err(1023, $CLnum);
		}

		&$proc($LHS, $CLnum) if ($proc !~ /^$/);

		next;
	}

	if ( ($LHS =~ /^COPYFILES$/i || $LHS =~ /^COPYFILES.NT$/i) )
	{
		if ($LayoutFilePresent == 0)
			{	add_ref_wrn(2016, $CLnum);	}
	}


	$RHS = &getRHS($line, $CLnum);
	@sec_list = get_list($RHS, $CLnum, 1);
	$count = @sec_list;

	@{$KeywordValuesArray} = @sec_list;
	${$InstKeywordsHash}{$LHS} = [$KeywordValuesArray,	$CLnum];

	if ( ($LHS =~ /^UPDATECFGSYS$/ || $LHS =~ /^UPDATEAUTOBAT$/)
		 && $count > 1)
	{
#		add_ref_err("Only one section allowed for $OriginalLHS.", $CLnum);
		add_ref_err(1024, $CLnum, $OriginalLHS);
		next;
	}

    if  ($LHS =~ /^DRIVERVER$/i )
    {
        &CheckDriverVersion($RHS,$CLnum);
    }

	if ($count < 1)
	{
#		add_ref_err("No value for $OriginalLHS.", $CLnum);
		add_ref_err(1025, $CLnum, $OriginalLHS);
		next;
	}

	foreach $sec_name (@sec_list)
	{
		if ($sec_name =~ /^$/) # check for blank names.
		{
#		add_ref_err("Empty section name for $OriginalLHS.", $CLnum);
		add_ref_err(1026, $CLnum, $OriginalLHS);
		next;
		}

		# process each entry.
 		&$proc($sec_name, $CLnum) if ($proc !~ /^$/);		 # process the section.

	} 

	}

# DriverVer entry should be present for any install section..if absent,
# it means still there in valid_keys hash array..

    if ( ($IsHWInstall == 0) && (defined($valid_keys{"DRIVERVER"})))
    {
        add_ref_err(1053,$SecStart{$sec},$sec);
    }


	if ($IsHWInstall == 0)	#	 is not a HW section
	{	$InstList{$sec} = $InstKeywordsHash;	}
}


#-------------------------------------------------------------
# 
# Check sections [SourceDisksName] and [SourceDisksFiles]
#
# Globals changed:
#		%SourceDisksNames
#		%SourceDisksFiles
#
# Usage:
#	SourceDisks()
#
#-------------------------------------------------------------

sub 
SourceDisks
{
	%SourceDisksNames = ();
	%SourceDisksFiles = ();

	&GetSourceDisks("SourceDisksNames");
	&GetSourceDisks("SourceDisksNames.alpha");
	&GetSourceDisks("SourceDisksNames.mips");
	&GetSourceDisks("SourceDisksNames.ppc");
	&GetSourceDisks("SourceDisksNames.x86");

	&GetSourceFiles("SourceDisksFiles", "SourceDisksNames");
	&GetSourceFiles("SourceDisksFiles.alpha", "SourceDisksNames.alpha");
	&GetSourceFiles("SourceDisksFiles.mips", "SourceDisksNames.mips");
	&GetSourceFiles("SourceDisksFiles.ppc", "SourceDisksNames.ppc");
	&GetSourceFiles("SourceDisksFiles.x86", "SourceDisksNames.x86");
}

sub 
GetSourceDisks
{
local($section)=@_;
local($UCSection, $CLnum, $LLnum, $line);
local(%DiskNames, @list);

	$UCSection = uc $section;

	return if (!defined($CLnum = $SecStart{$UCSection}));
	@SecRef = (@SecRef, $UCSection);

	die "EndLine of section $section not defined\n" 
		unless (defined ($LLnum = $SecEnd{$UCSection}));
	
	add_ref_wrn(2015, $CLnum);

	%DiskNames = ();
	while(++$CLnum < $LLnum)
	{
		$line = $INFlines[$CLnum];
		next if (! defined($line));

		$LHS = uc &getLHS($line, $CLnum);
		if ($LHS !~ /^$/)
		{	
			$RHS = &getRHS($line, $CLnum);
			$DiskNames{$LHS} = [$RHS, $CLnum];

			@list = get_list($RHS, $CLnum, 1);
			IsStringKey($CLnum, @list);
		}
	}

	$SourceDisksNames{$UCSection} = \%DiskNames;
}

sub 
GetSourceFiles
{
local($section, $DiskSection)=@_;
local($UCSection, $CLnum, $LLnum, $line, $UCDiskSection);
local(%DiskFiles, @list);
local($RefDiskNames);

	$UCSection = uc $section;
	$UCDiskSection = uc $DiskSection;

	return if (!defined($CLnum = $SecStart{$UCSection}));
	@SecRef = (@SecRef, $UCSection);

	die "EndLine of section $section not defined\n" 
		unless (defined ($LLnum = $SecEnd{$UCSection}));

	add_ref_wrn(2015, $CLnum);

	%DiskFiles = ();
	$RefDiskNames = $SourceDisksNames{$UCDiskSection};

	if (!defined ($RefDiskNames))
	{
		add_ref_err(1037, $CLnum, $DiskSection);
	}

	while(++$CLnum < $LLnum)
	{
		$line = $INFlines[$CLnum];
		next if (! defined($line));

		$LHS = uc &getLHS($line, $CLnum);
		if ($LHS !~ /^$/)
		{
			$RHS = &getRHS($line, $CLnum);
			$DiskFiles{$LHS} = [$RHS, $CLnum];

			@list = get_list($RHS, $CLnum, 1);
			IsStringKey($CLnum, @list);
#			if (defined ($RefDiskNames) &&
#				!defined( $$RefDiskNames{uc $list[0]} ) )
#			{
#				add_ref_err(1038, $CLnum, $list[0], $DiskSection);
#			}
		}
	}

	$SourceDisksFiles{$UCSection} = \%DiskFiles;
}

#-------------------------------------------------------------
# 
# Check if the file_section string is OK: should be @<File_Name> or <Section_Name>.
#
# Globals changed:
#
# Usage:
#	CheckFileSectionString(expected_section_name)
#
#-------------------------------------------------------------
sub 
CheckFileSectionString
{
local($sec, $Line_num) = @_;

	return if ($sec =~ /^@/); # file name - do not process.

	# check if section defined
	CheckSectionExist($sec, $Line_num);

	# check if section defined in DestinationDir
	if (!defined(%DestinationDirs))
	{
		add_ref_err(1046, $Line_num) 
					if ($DestinationDirsChecked == 0);
		$DestinationDirsChecked = 1;
	}
	elsif (!defined($DestinationDirs{uc $sec}))
	{
		add_ref_err(1045, $Line_num, $sec);
	}
    elsif ($DestinationDirs{uc $sec} != 12)
	{
		add_ref_err(3152, $Line_num, $sec);
	}
    
	$DestinationDirsChecked = 2;
    CheckForParameterString($sec, $Line_num);
}
#-------------------------------------------------------------
#
# Check if the Driver Version Entry in Install Section is valid.
#
# Globals changed:
#
# Usage:
#   CheckDriverVersion(RHS,line_num)
#
#-------------------------------------------------------------
sub
CheckDriverVersion
{
local($RHS, $Line_num) = @_;
local(@tmp,$date,$version);

    @tmp = split(',',$RHS,2);
    $date = $tmp[0];
    $version = $tmp[1];

    if ( ($date =~ /^$/) && ($version =~ /^$/))
    {
        add_ref_wrn(2018,$Line_num);
    }

    if  ( ($date =~ /^([0-9]*)\/([0-9]*)\/([0-9]*)$/ ) && ($date !~ /^$/ ) )
    {
        local($day,$month,$year,$presentday,$presentmonth,$presentyear);
        local($IsNotValid);

        $month = $1; $day = $2; $year =$3;

        $IsNotValid = 0;

        if (($day>31) || ($day < 1))
        {
            add_ref_err(1055,$Line_num);
            $IsNotValid = 1;
        }

        if (($month>12) || ($month < 1))
        {
            add_ref_err(1055,$Line_num);
            $IsNotValid = 1;
        }

        if (($year>2010) || ($year < 1980))
        {
            add_ref_err(1055,$Line_num);
            $IsNotValid = 1;
        }

        $presentday = (localtime)[3];
        $presentmonth = (localtime)[4]+1;
        $presentyear = 1900 + (localtime)[5];
#       print "Debug: Today is $presentmonth-$presentday-$presentyear..\n";

        if ($IsNotValid == 0)
        {
            if ($year > $presentyear)
            {
                add_ref_err(1054,$Line_num);
            }
            if ( ($month > $presentmonth) && ($year == $presentyear) )
            {
                add_ref_err(1054,$Line_num);
            }
            if ( ($day > $presentday) && ($month == $presentmonth) && ($year == $presentyear) )
            {
                add_ref_err(1054,$Line_num);
            }
        }
    }
    else
    {
        add_ref_err(1055,$Line_num);
    }

    if  ($version =~ /^([0-9]*)\.([0-9]*)\.([0-9]*)\.([0-9]*)$/ ) 
    {
        print "\nFirst is $1, Second is $2,Third is $3 and Fourth is $4\n";
    }
    else
    {
         if ($version !~ /^$/ ) {add_ref_err(1056,$Line_num);}
    }

    print "\nDate is $date and Driver Version is $version\n";
}

#-------------------------------------------------------------
#
#	Checks if a given section is defined
#
# Usage:
#	CheckSectionExist(expected_section_name)
#
#-------------------------------------------------------------

sub 
CheckSectionExist
{
local($sec, $Line_num) = @_;
local($ucsec);

	$ucsec = uc $sec;
	if (! defined ($SecStart{$ucsec}))
	{
	#	add_ref_err("Section [$sec] not defined.", $Line_num);
		add_ref_err(1003, $Line_num, $sec);
		return;
	}
	@SecRef = (@SecRef, $ucsec);
}


#------------------------------------------------------------
# Adds the ID passed to the list of excluded IDs.
#
# Usage:
#		ExSel(ExcludeType, ID, line_num)
#------------------------------------------------------------

sub ExSel
{
local($LHS, $RHS, $CLnum) = @_;
local(@list, $id, $OtherExSelLine, $ExcludeRef, $ExcludeLine);
local($Index, $count);

	@list = get_list($RHS, $CLnum, 1);
	$count = @list;
	for($Index = 0; $Index < $count ; $Index++)
	{
		$id = $list[$Index];
		if($id =~ /^__QUOTED_STR_\(([0-9]*)\)__$/)
		{
			$id = $AllQuotes{$1};
			inst_err(1051, $CLnum);
		}
		if ($id =~ /^$/)
		{
#			add_ref_err("Blank identifier.", $CLnum);
			add_ref_err(1027, $CLnum);
			next;
		}
		if (defined($ExcludeRef = $ExSelList{uc $id}) &&
			defined($$ExcludeRef{uc $LHS}))
		{
			add_ref_wrn(2012, $CLnum, $id, $LHS);
		}
		else
		{
			if (defined($ExcludeRef))
			{
				$$ExcludeRef{uc $LHS}	= [$CLnum, 0];
			}
			else
			{
				$ExSelList{uc $id}	= {uc $LHS => [$CLnum, 0]};
			}
		}
	}
}

#------------------------------------------------------------
# this sub extracts quoted strings from the line and puts them
# (unquoted) in assoc_array AllQuotes index by a number and
# replaces the quoted string by __QUOTED_STR_XXX__ where XXX
# is the index number.
#
# Usage: 
#		proc_quotes(line, line_num)
#------------------------------------------------------------
sub proc_quotes
{
local($line, $Lnum) = @_;
local($cnt, $quote);

	$_ = $line;

	# Count the number of ", and tack on extra " if we have an odd number;
	# otherwise perl faults!
	$cnt = tr/"/"/;

	if ($cnt % 2)
	{
	# check if this odd quote is after a semicolon.
	if ($_ =~ /(.(".*")*)*;.*"(.*".*")*/) {
#		add_ref_wrn("Line has an odd number of \"s. Correct this to avoid erroneous processing.", $Lnum);
		add_ref_wrn(2008, $Lnum);
		} else{
#		add_ref_err("Line has and odd number of \"s. Adding one \" at the end of line to continue processing.", $Lnum);
		add_ref_err(1028, $Lnum);
	}
		$_ = "$_\"";
	}

	# Process compound quoted strings one-at-a-time, saving the quoted string
	# when we find it and doing some further processing to replace "" by ", and
	# to remove the enclosing quotes.
	while(/"/)
	{
	$QuoteNum++;
	s/"([^"]*("")*)*"/__QUOTED_STR_($QuoteNum)__/;
	$quote = $&;
	$quote =~ s/^"(.*)"$/\1/;
	$quote =~ s/""/"/g;
	# NOTE: you can test for TABs inside quoted strings here, for chkinf.
#	add_ref_wrn("TAB inside quoted string.", Lnum) if ($quote =~ /\t/);
	add_ref_wrn(2009, Lnum) if ($quote =~ /\t/);
	$AllQuotes{$QuoteNum}=$quote;
	}
	$_;

}

#################################################################
#
# This sub checks if a string is a key string and if it is defined.
#
#################################################################
sub 
IsStringKey
{
local($CLnum, @StringList)=@_;
local($String);
	
	foreach $String (@StringList)
	{
		if ($String =~ /^%(.*)%$/)
		{
			add_ref_err(1036, $CLnum, $String)
				if (!defined $FStr{uc $String}); 
			@RefStr = (@RefStr, uc $String);
		}
	}
}


#################################################################
#
# This sub checks for dangling (unreferenced) sections.
#
#################################################################
sub 
Section_usage
{
	local($sec, $model, $count);

	foreach $sec (sort keys %SecStart)
	{
#	add_ref_wrn("Section [$sec] not referenced.", $TotalLines)
	add_ref_wrn(2010, $TotalLines, $sec)
		if(($count = grep(($sec eq $_), @SecRef)) < 1);
	}

	if (defined(%DestinationDirs) && $DestinationDirsChecked == 0)
	{
		add_ref_err(1047, $SecStart{$DestDirsSec});
	}
}

#################################################################
#
# This sub checks for dangling (unreferenced) strings.
#
#################################################################
sub 
String_usage
{
	local($str, $count);
	local($Lnum);

	if (defined($Lnum = $SecEnd{$StrSec}))
	{
		foreach $str (sort keys %FStr)
		{
#			add_ref_err("String $str not referenced.", $Lnum-1)
			add_ref_err(1029, $Lnum-1, $str)
				if(($count = grep(($str eq $_), @RefStr)) < 1);
		}
	}
}

#################################################################
#
# This sub checks if the ExcludeFromSelect IDs were defined.
#
#################################################################
sub
ExcludeSelect_usage
{
	local($ID, $ExcludeRef, $ExcludeLine, $ExcludeKey);

	while (($ID, $ExcludeRef) = each %ExSelList)
	{
		while (($ExcludeKey, $ExcludeLine) = each %$ExcludeRef)
		{
			add_ref_err(1035, $$ExcludeLine[0], $ID) 
				if ($$ExcludeLine[1] == 0);
		}
	}
}

#################################################################
#
# Utility subs....
#
#################################################################

#------------------------------------------------------------
# this sub extracts a list of elements seperated by ',' from
# the given line. It strips leading and ending whitespaces.
# It flags error for whitespaces in elements.
#
# Usage: 
#		get_list(line, line_num, check_empty)
#------------------------------------------------------------
sub get_list
{
local($line, $Lnum, $check_empty) = @_;
local(@list, $element);

	if (!defined($check_empty))
	{
		$check_empty	= 0;
	}
	if ($check_empty)
	{
		add_ref_err(1041, $Lnum) if ($line =~ /,$/);
	}
	@list = split(',', $line);
	foreach $element (@list) 
	{
		$element =~ s/^[\s\t]*//; # remove blanks at ends.
		$element =~ s/[\s\t]*$//; # remove blanks at ends.
	
#		add_ref_wrn("White spaces in identifier \"$element\".", $Lnum)
		add_ref_wrn(2011, $Lnum, $element)
			if ($element =~ /[\s\t]+/);
	}
	@list;	#return value.
}

#-------------------------------------------------------------
#
# To sort in a numeric fashion!!!!!
#
# Globals changed:
#
# Usage:
#
#-------------------------------------------------------------
sub 
numeric 
{ $a <=> $b }

#------------------------------------------------------------
# this sub extracts the LHS from a line. 
# It ensures that the line has a '=' and that the left of '='
# is just one word. Else it generates an error.
# Replaces text if it is a quoted string.
#
# Usage: 
#		getLHS(line, line_num)
#------------------------------------------------------------
sub getLHS
{
local($LHS, $this_num) = @_;

	$LHS =~ s/=.*$/=/;
	if($LHS !~ /=/) {
#	add_ref_err("Line is expected to be of the form \"Entry_Name = Entry_Value\".", $this_num);
	add_ref_err(1030, $this_num);
	} 
	$LHS =~ s/[\s\t]*=//;
	if($LHS =~ /[\s\t]+/) {
#	add_ref_err("Not more than one word on the left of \"=\".", $this_num);
	add_ref_err(1031, $this_num);
	} 
	$LHS = $AllQuotes{$1} if($LHS =~ /^__QUOTED_STR_\(([0-9]*)\)__$/);
	#print "DEBUG: $LHS";
	$LHS; #return value
}


#------------------------------------------------------------
# this sub extracts the LHS as a combination of chars and string keys. 
# It ensures that the line has a '='. 
# Replaces text if it is a quoted string or contains string keys.
#
# Usage: 
#		getLHS(line, line_num)
#------------------------------------------------------------
sub getDescriptionString
{
local($LHS, $this_num) = @_;

	$LHS =~ s/=.*$/=/;
	if($LHS !~ /=/)
	{
#		add_ref_err("Line is expected to be of the form \"Entry_Name = Entry_Value\".", $this_num);
		add_ref_err(1030, $this_num);
	} 
	$LHS =~ s/[\s\t]*=//;

	if($LHS =~ /^__QUOTED_STR_\(([0-9]*)\)__$/)
	{
		$LHS = $AllQuotes{$1};
		return $LHS;
	}

	# replace string keys
	while (TRUE)
	{
		$LHS =~ s/%%/\x01/g;
		last if ($LHS !~ /(.*)(%(?:.+)%)(.*)/);

		local($TempString, $KeyString);

		$KeyString = $2;
		$KeyString =~ s/\x01/%%/g;

#		print "Before: $LHS\n";
#		print "Desc: $1;$KeyString;$3\n";

		$TempString = $FStr{uc $KeyString};
		if (!defined($TempString) )
		{
			add_ref_err(1036, $CLnum, $KeyString);
		}
		else
		{
			@RefStr = (@RefStr, uc $KeyString);
		}
		$LHS = $1.$TempString.$3;
#		print "After: $LHS\n";
	}

	$LHS =~ s/\x01/%%/g;
	print "Final: $LHS\n";

	$LHS; #return value
}

#------------------------------------------------------------
# this sub extracts the RHS from a line. 
# It ensures that the line has a '=' 
#
# Usage: 
#		getRHS(line, line_num)
#------------------------------------------------------------
sub getRHS
{
local($RHS, $this_num) = @_;

	$RHS =~ s/^.*=/=/;

	if($RHS !~ /=/) {
	# probably already flagged during LHS check.
	# add_ref_err("Line is expected to be of the form \"Entry_Name = Entry_Value\".", $this_num);
	} 
	$RHS =~ s/=[\s\t]*//; 
	
#	add_ref_err("Entry value is empty.", $this_num) if ($RHS =~ /^$/);
	add_ref_err(1032, $this_num) if ($RHS =~ /^$/);

	#  print "DEBUG: $RHS\n";
	$RHS; #return value

} 

#------------------------------------------------------------
#------------------------------------------------------------
sub 
GetEntryName
{
local($LHS) = @_;

	$LHS =~ s/=.*$/=/;			# left part
	$LHS =~ s/[\s\t]*=//;		# extract leading spaces
	$LHS = $AllQuotes{$1}		# replace with stored string
	if($LHS =~ /^__QUOTED_STR_\(([0-9]*)\)__$/);
	$LHS; #return value
}

#------------------------------------------------------------
#------------------------------------------------------------
sub 
GetEntryValue
{
local($RHS) = @_;

	$RHS =~ s/^.*=/=/;			# right part
	$RHS =~ s/=[\s\t]*//;		# extract leading spaces
	$RHS; #return value
} 


#------------------------------------------------------------
#	private version of glob function that doesn't work on WIN95
#	parameter: a file path specifier including wildcards
#	returns: the list of file that matches the file specifier
#------------------------------------------------------------
sub 
myglob
{
	my ($files) = @_;
	my ($dir, $filepattern, @DirectoryFiles, @MatchedFiles, $file);

	@MatchedFiles = ();

	if (!defined($files) or $files =~ /^$/)
	{	$files = ".";	}	#	current directory

	#	is it a directory name ?
	if ( opendir(LOCALDIR,  $files) )
	{
		$dir = $files;
		$filepattern = "*.*";
	}
	else	#	extract the directory path
	{
		if ($files !~ /(.*)\\(.*)/)		# find the last \
		{
			$filepattern = $files;
			$dir = ".";
		}
		else
		{	
			$filepattern = $2;
			$dir = (($1 =~ /^$/) ? "." : $1) ;
		}

		return @MatchedFiles if (!opendir(LOCALDIR,  $dir));
	}

	$filepattern = quotemeta($filepattern);
	$filepattern =~ s/\\\*/.*/g;
	$filepattern =~ s/\\\?/./g;

	@DirectoryFiles = readdir LOCALDIR;
	foreach $file (sort @DirectoryFiles)
	{	
		# remove . and .. entries
		next if (($file =~ /^\.$/) or ($file =~ /^\.\.$/));

		#	add a dor at the end if no one present
		$file .= "." if ($file !~ /\./);

		push(@MatchedFiles, (($dir =~ /^\.$/) ? "" : $dir."\\") .$file) 
			if ($file =~ /^$filepattern$/i);
	}

	closedir LOCALDIR;

	@MatchedFiles;	# return array
}

#------------------------------------------------------------
#	finds max length of the given array of strings
#------------------------------------------------------------
sub 
maxlen
{
	my (@StrArray) = @_;
	my ($maxvalue, $len, $val);

	$maxvalue	= length($StrArray[0]);
	foreach $val (@StrArray)
	{
		$len	= length($val);
		if ($len > $maxvalue)
		{	$maxvalue = $len;	}
	}
	$maxvalue;
}

#------------------------------------------------------------
#	finds min length of the given array of strings
#------------------------------------------------------------
sub 
minlen
{
	my (@StrArray) = @_;
	my ($minvalue, $len, $val);

	$minvalue	= length($StrArray[0]);
	foreach $val (@StrArray)
	{
		$len	= length($val);
		if ($len < $minvalue)
		{	$minvalue = $len;	}
	}
	$minvalue;
}

#------------------------------------------------------------
#	finds max value of the given array
#------------------------------------------------------------
sub 
max
{
	my (@ValArray) = @_;
	my ($maxvalue, $val);

	$maxvalue	= $ValArray[0];
	foreach $val (@ValArray)
	{
		if ($val > $maxvalue)
		{	$maxvalue = $val;	}
	}
	$maxvalue;
}


#------------------------------------------------------------
#	finds max value of the given array
#------------------------------------------------------------
sub 
min
{
	my (@ValArray) = @_;
	my ($minvalue, $val);

	$minvalue	= $ValArray[0];
	foreach $val (@ValArray)
	{
		if ($val < $minvalue)
		{	$minvalue = $val;	}
	}
	$minvalue;
}


#------------------------------------------------------------
#	This sub does the following:
#	- flags warnings for length > MaxLineLen.
#	- joins lines to next line if ending in '\'
#	- replace quoted strings by __QUOTED_STR_XXXX__ and put the
#		unqouted string in an array. (use XXXX for indexing it.)
#	- flags warnings for tabs in quoted strings.
#	- gets rid of comments, preceeding and ending whitespaces,
#		end of line.
#	- adds non null strings to the array %INFline - indexing
#		with the line number. (NOTE: joined lines will be
#		indexed by the line joined line's line number.)
#
# Globals changed:
#		INFLines
#		TotalLines
#
# Usage: 
#		process_include_file(include_file)
#------------------------------------------------------------
sub 
process_include_file
{
local($includefile) = @_;
local($prev_line, $line, $CLnum, $poscomma, $posquote1, $posquote2, $bRemoveComment, $comment);


	$prev_line = "";
	if (!open(INCLUDEFILE, "$includefile"))
	{
		print "Can't open $includefile for input\n";
		return;
	}

	while(<INCLUDEFILE>)
	{
	$CLnum = $. + $TotalLines;
	chop if /\n$/;			 # removes \n
#	add_ref_wrn("Line has more than $MaxLineLen characters.", $.) 
	add_ref_wrn(2001, $CLnum, $MaxLineLen) 
		if (length($_) > $MaxLineLen); 
	#******** Warn for high ASCII and tabs.

	$line = join("", $prev_line, $_);
	$prev_line = "";

	#process for comments.

	$posquote1	= index($line, '"');
	$posquote2	= ($posquote1 >= 0) ? index($line, '"', $posquote1+1) : -1;
	$poscomma	= index($line, ';');
	$bRemoveComment = 0;

	while ($poscomma >= 0)
	{
		if ($poscomma < $posquote1 || $posquote1 < 0)	# ';' before '"' => is comment
		{	
			$bRemoveComment = 1;
			last;
		}

		if ($posquote2 >= 0 && $posquote1 >= 0)
		{
			if ($poscomma < $posquote2)			# ';' between '"' => sting first
			{
				$poscomma = index($line, ';', $posquote2 + 1);
			}
						# find next string
			$posquote1	= index($line, '"', $posquote2 + 1);	
			$posquote2	= ($posquote1 >= 0) ? index($line, '"', $posquote1+1) : -1;
			next;
		}
		last;		# ';' after '"' => sting not terminated yet, string first
	}

	if ($bRemoveComment == 1)
	{
		$comment = substr($line, $poscomma+1);
		add_ref_err(1033, $CLnum) if ($comment =~ /;/);
		$line = substr($line, 0, $poscomma);
	}

	$line =~ s/^[\s\t]*//; #strip leading white spaces.
	$line =~ s/[\s\t]*$//; #strip ending white spaces and eol.
	next if ($line =~ /^$/);

	if ($line =~ /(.)*\\$/) 
	{
		# need to join this line to the next.
		$line =~ s/\\$//;
		$prev_line = $line;
		next; # process joined line together for quotes.
	} 

	#process for quotes.
	$line = &proc_quotes($line, $CLnum);

	next if ($line =~ /^$/); # $line is empty.

	#put the preprocessed line in assoc array indexed by the linenum.
	$INFlines[$CLnum] = $line;
	}

	$TotalLines = $TotalLines + $.;

	close (INCLUDEFILE);
}


#------------------------------------------------------------
#	This sub looks at the list of preprocessed lines and 
#	assumes that all occurances of /[(.)*]/ not inside
#	quotes represent section headers.
#	It creates the assoc arrays related to sections and
#	also flags redefinition of sections.
#
# Globals changed:
#		SecStart
#		secEnd
#
# Usage: 
#		process_include_sections()
#------------------------------------------------------------
sub 
process_include_sections
{
local($firstline) = @_;
local($CLnum, $prev_sec, $line, $sec, $x, $PrevLastLine);

	# go linewise in the assoc array INFline.
	$CLnum = $firstline; # holds current line num.
	$prev_sec = $InvalSec;
	$PrevSectLastLine = -1;

	while(++$CLnum <= $TotalLines)
	{

	$line = $INFlines[$CLnum];
	if (! defined($line))
	{
		next;
	}

	if ($line =~ /\[(.*)\]/) # section header
	{
		$sec = uc $1;  # section name in upper case
		if ($line !~ /^\[(.*)\]$/) # something else on line
		{
#		add_ref_err("Incorrect section definition.", $CLnum);
		add_ref_err(1001, $CLnum);
		}
		
		$SecEnd{$prev_sec} = $PrevSectLastLine + 1 if ($prev_sec !~ /^($InvalSec)$/);

		if (defined ($x = $SecStart{$sec})) # redefinition of sec
		{
#			add_ref_err("Section redefined. [$sec] already defined at Line $x. Not processing this section.", $CLnum);
			add_ref_err(1002, $CLnum, $sec, $x)
				if ($sec !~ /^$VerSec$/i && $sec !~ /^$StrSec$/i);
			$prev_sec = $InvalSec;
			$PrevSectLastLine = -1;
			next;
		}

		# add to the assoc arrays.
		$SecStart{$sec} = $CLnum;
		$prev_sec = $sec;
	}

	$PrevSectLastLine = $CLnum;
	}

	$SecEnd{$prev_sec} = $PrevSectLastLine + 1 if ($prev_sec !~ /^($InvalSec)$/);

}


#-------------------------------------------------------------
#
#	Checks the line given in include
#
# Usage:
#	CheckIncludeItem(expected_section_name)
#
#-------------------------------------------------------------

sub 
CheckIncludeItem
{
	my ($includefile) = @_;
	my ($startline, $fullpath);

	$fullpath = $InfDirectory.$includefile;

	return if (defined($IncludeFiles{uc $fullpath}));

	$startline	= $TotalLines;
	$IncludeFiles{uc $fullpath}	= $startline;
	push(@IncludeLines, [$startline, $includefile, $fullpath]);

	process_include_file($fullpath);
	process_include_sections($startline);
}

sub 
GetFileByLine
{
	my ($lineInFile) = @_;
	my ($key);

	foreach $key (@IncludeLines)
	{
		return $key
			if $key->[0] <= $lineInFile;
	}

	return undef;
}

sub
CheckForParameterString
{
local($sec,$Lnum) = @_ ;
local($line, $CLnum, $LLnum);

    $sec = uc $sec;
    return if (!defined ($CLnum = $SecStart{$sec}));

    die "EndLine of section $sec not defined\n"
        unless (defined ($LLnum = $SecEnd{$sec}));

    while(++$CLnum < $LLnum)
    {
        $line = $INFlines[$CLnum];
        next if (! defined($line));

        @list = ();
        @tmp = split(',', $line, 4);
        # remove whitespaces from all.
        foreach $field (@tmp)
        {
            $field =~ s/^[\s\t]*//;
            $field =~ s/[\s\t]*$//;
            push(@list, $field);
        }

        foreach $field (@list)
        {
            if ($field =~ /^%(.*)%/ )
                { add_ref_err(3143,$Lnum,$field,$sec);return;}
        }
    }
}

#-------------------------------------------------------------
#
#   Checks the Services  Section for any errors
#
#   Mainly used by Controllerless modems which add a
#   service
#
# Usage:
#   ServiceProc(expected_section_name)
#
#Note: The error values for Services have to be defined
#-------------------------------------------------------------

sub 
ServiceProc
{
local($sec) = @_;           # expected section name.
local($line, $LHS, $RHS, $CLnum, $LLnum,$present,@tmp,$serinstsec,$eventinstsec);


    $sec=uc $sec;
    if (! defined ($CLnum = $SecStart{$sec}))
    {
    add_ref_err(1003, $TotalLines, $sec);
    return;
    }

    @SecRef = (@SecRef, $sec);
    die "EndLine of section $sec not defined\n" 
    unless (defined ($LLnum = $SecEnd{$sec}));

    $present = 0;
    while(++$CLnum < $LLnum)
    {
    $line = $INFlines[$CLnum];
    if (! defined($line))
    {
        next;
    }

        $LHS = uc &getLHS($line, $CLnum); #Change LHS to uppercase. 
        $RHS = &getRHS($line, $CLnum);
    
        #check if AddService key.
        if ($LHS =~ /^ADDSERVICE$/i)
        {
            @tmp= split (',',$RHS);
            $tmp[2] =~ s/^[\s\t]*//;
            $tmp[3] =~ s/^[\s\t]*//;
            $serinstsec=$tmp[2];
            $eventinstsec=$tmp[3];
            
            if ($serinstsec !~ /^$/ )
            {
                &CheckServiceInst($serinstsec);
            }
            else
            {
                add_ref_err(3151,$CLnum);
            }
            if ($eventinstsec !~ /^$/ ) { &CheckEventLogInst($eventinstsec);}
            $present = 1;
        }
    }

    add_ref_err(3145,$LLnum-1,$sec) if ($present !=1);
}


#-------------------------------------------------------------
#
#   Checks the Service Install Section for any errors
#
# Usage:
#   CheckServiceInst(expected_section_name)
#
#Note: The error values for Service Install have to be defined
#   The numbers are starting from 4200.
#-------------------------------------------------------------

sub 
CheckServiceInst
{
local($sec) = @_;           # expected section name.
local($line, $LHS, $RHS, $CLnum, $LLnum);

    $sec=uc $sec;
    if (! defined ($CLnum = $SecStart{$sec}))
    {
    add_ref_err(1003, $TotalLines, $sec);
    return;
    }

    @SecRef = (@SecRef, $sec);
    die "EndLine of section $sec not defined\n" 
    unless (defined ($LLnum = $SecEnd{$sec}));

    while(++$CLnum < $LLnum)
    {
    $line = $INFlines[$CLnum];
    if (! defined($line))
    {
        next;
    }

    $LHS = uc &getLHS($line, $CLnum); #Change LHS to uppercase. 
    $RHS = &getRHS($line, $CLnum);

    #check if DisplayName key.
    if ($LHS =~ /^DISPLAYNAME$/)
    {
        if($RHS !~ /^__QUOTED_STR_\(([0-9]*)\)__$/)
        {
            add_ref_err(1004, $CLnum);
        }
        next;
    }
    #check if ServiceType key
    if ($LHS =~ /^SERVICETYPE$/)
    {
        local($num);
        $num=hex $RHS;
        add_ref_err(3146,$CLnum) if ($num!=1);
        #Error flagged if service type other than
        #SERVICE_KERNEL_DRIVER
        next;
    }

    #check if StartType key
    if ($LHS =~ /^STARTTYPE$/)
    {
        local($num);
        $num=hex $RHS;
        add_ref_err(3147,$CLnum) if ($num<0)&&($num>4);
        next;
    }
    #check if ErrorControl key
    if ($LHS =~ /^ERRORCONTROL$/)
    {
        local($num);
        $num=hex $RHS;
        add_ref_err(3148,$CLnum) if ($num<0)&&($num>3);
        next;
    }
    #check if Addreg key
    if ($LHS =~ /^ADDREG$/)
    {
        &AddRegProc($RHS);
        $AddRegCollected{uc $RHS}=1;
        next;
    }
    #check if ServiceBinary key
    if ($LHS =~ /^SERVICEBINARY$/)
    {
        add_ref_err(3149,$CLnum) if ($RHS=~ /^$/);
        next;
        #Just checking if something is specified for the binary filename.
    }
    #check if LoadOrderGroup key
    if ($LHS =~ /^LOADORDERGROUP$/)
    {
        add_ref_err(3150,$CLnum) if (uc $RHS != /BASE/);
        next;
    }
    } #while ends
}
#-------------------------------------------------------------
#
#   Checks the EventLog Install Section for any errors
#
# Usage:
#   CheckEventLogInst(expected_section_name)
#
#Note: The error values for EventLog Install have to be defined
#-------------------------------------------------------------

sub 
CheckEventLogInst
{
local($sec) = @_;           # expected section name.
local($line, $LHS, $RHS, $CLnum, $LLnum,$present);

    $sec = uc $sec;
    if (! defined ($CLnum = $SecStart{$sec}))
    {
    add_ref_err(1003, $TotalLines, $sec);
    return;
    }

    @SecRef = (@SecRef, $sec);
    die "EndLine of section $sec not defined\n" 
    unless (defined ($LLnum = $SecEnd{$sec}));

    $present = 0;
    while(++$CLnum < $LLnum)
    {
    $line = $INFlines[$CLnum];
    if (! defined($line))
    {
        next;
    }
    $LHS = uc &getLHS($line, $CLnum); #Change LHS to uppercase. 
    $RHS = &getRHS($line, $CLnum);

    
    #check if Addreg key.
    if ($LHS =~ /^ADDREG$/i)
    {
        &AddRegProc($RHS);
        $AddRegCollected{uc $RHS}=1;
        $present = 1;
    }
    #check if Delreg key.
    }
    add_ref_err(4010,$LLnum-1) if ($present !=1);
}


1;
