##############################################################
# This file contains the logic to process the netcard inf
#
# Aug 13, 1997  Created	t-obhatt
#
##############################################################
##############################################################
#
# Package with version constants
#
##############################################################
{ package NETSECVersion;

  $BuildVersion = "1.00.0019";
}

%NewModelKeys = (
	'CHARACTERISTICS',	"",
	'BUSTYPE',		"",
	'LOGCONFIG',		"CheckLogConfig",
	   );


%NetCardErrorTable = (
	3042 => ["Keyword Addreg not defined in install section"],
	3043 => ["Keyword Characteristics not defined in install section"],
	3044 => ["Keyword BusType not defined in install section"],
	3047 => ["Mandatory field &ltKey&gt is missing."],
	3048 => ["Field &ltFormat&gt should be 0 or 1."],
	3049 => ["Mandatory field &ltValue&gt is missing."],
	4001 => ["BusType does not match the NetCard model."],
	4002 => ["Characteristics can have only one of NCF_VIRTUAL,NCF_PHYSICAL & NCF_WRAPPER."],
	4003 => ["NCF_NO_SERVICE cannot be used with NCF_VIRTUAL or NCF_PHYSICAL."],
	4005 => ["Invalid interface used in Addreg section"], 
	4006 => ["Lower interface cannot have an install section"], 
	4007 => ["Reserved Word used as Parameter Key"], 
	4008 => ["Invalid Value-Name for Parameter Key"], 
	4009 => ["NCF_HAS_UI should be one of the characteristics for advanced parameters"],
	4010 => ["Addreg should be defined in the EventLog Section"],
	4011 => ["The primary service value not matching the first parameter of AddService"],
	4012 => ["The service name in DelService does not match the name in AddService"],
	4013 => ["AddService not defined in Services section"],
	4014 => ["Copyfile Value of the DestinationDirs section should be equal to 12"],
	4015 => ["Rank0 and Rank1 cant be the same."],
	4016 => ["%s, duplicate rank 0 Id in file(s) \"%s\"."],
	4017 => ["No ISAPNP netcard using this section."],
	4030 => ["Section [%s] is empty. It should contain \"*PNP8132\"."],
	4031 => ["String \"*PNP8132\" is missing for section [%s]."],
	4032 => ["Wrong strings in section [%s]. Only \"*PNP8132\" is allowed."],
	4100 => ["Invalid Priority Value for ConfigPriority"],
	4101 => ["Invalid Config Type for ConfigPriority"],
	4102 => ["Invalid IRQ value for IRQconfig"],
	4103 => ["The hexadecimal integer value exceeds range"],
	4104 => ["IRQConfig not assigned according to DDK specification"],
	4105 => ["IOConfig not assigned according to DDK specification"],
	4106 => ["Invalid DMA value for DMAconfig"],
	4107 => ["DMAConfig not assigned according to DDK specification"],
	4108 => ["MEMConfig not assigned according to DDK specification"],
	4200 => ["Invalid value for ServiceType in Services Install Section"],
	4201 => ["Invalid value for StartType in Services Install Section"],
	4202 => ["Invalid value for ErrorControl in Services Install Section"],
	4203 => ["ServiceBinary value not defined in Services Install Section"],
	4204 => ["Invalid value for LoadOrderGroup in Services Install Section"],
	4205 => ["Invalid value for StartName in Services Install Section"],
	);

%NetCardWarningTable = (
	5001 => ["PnP id \"%s\" requires ExcludeFromSelect. Vendors or INF floppy install users may ignore this message."],
	4014 => ["First field should be one of the HKR, HKU, HKLM, HKCR or HKCU keywords."],
	);

%ValidInterfaces = ('ETHERNET' => 1, 'ATM' => 1, 'TOKENRING' => 1, 'SERIAL' => 1, 'FDDI' => 1,
					'BASEBAND' => 1, 'BROADBAND' => 1,'ARCNET' => 1, 'ISDN' => 1, 'NOLOWER' => 1,
					'NDIS5' => 1,'NDISATM' => 1,'NETBIOS' => 1, 'IPX' => 1, 'TDI' => 1,
					'STREAMS' => 1, 'WINSOCK' => 1, 'WINNET5' => 1, 'NOUPPER' =>1, 'NDISWAN' => 1);

%LoadOrderGroup = (
	'SYSTEM BUS EXTENDER' => 1, 'SCSI MINIPORT' => 1,'PORT' => 1, 'PRIMARY DISK' => 1, 
	'SCSI CLASS' => 1, 'SCSI CDROM CLASS' => 1, 'FILTER' => 1, 'BOOT FILE SYSTEM' => 1, 
	'BASE' => 1, 'POINTER PORT' => 1, 'KEYBOARD PORT' => 1, 'POINTER CLASS' => 1, 
	'KEYBOARD CLASS' => 1, 'VIDEO INIT' => 1, 'VIDEO' => 1, 'VIDEO SAVE' => 1, 
	'FILE SYSTEM' => 1, 'EVENT LOG' => 1, 'STREAMS DRIVERS' => 1, 'PNP_TDI' => 1, 
	'NDIS' => 1, 'TDI' => 1, 'NETBIOSGROUP' => 1, 'SPOOLERGROUP' => 1, 'NETDDEGROUP' => 1, 
	'PARALLEL ARBITRATOR' => 1, 'EXTENDED BASE' => 1, 'REMOTEVALIDATION' => 1, 
	'PCI CONFIGURATION' => 1, );
%ReservedWords = (
	'CHARACTERISTICS' => 1, 'COMPONENTID' => 1, 'DESCRIPTION' => 1, 'DRIVERDESC' => 1,
	'INFPATH' => 1, 'INFSECTION' => 1, 'INFSECTIONEXT' => 1, 'MANUFACTURER' => 1, 
	'NETCFGINSTANCEID' => 1, 'PROVIDER' => 1, 'PROVIDERNAME' => 1,);

%ParamKeys = ( 'PARAMDESC' => 1, 'DEFAULT' =>1, 'TYPE' => 1, 'MIN' => 1, 'MAX' => 1, 
				'STEP' => 1, 'BASE' => 1,);

#Assuming unsigned 64-bit value and unsigned 32-bit value

$Max64bitValue=0xffffffffffffffff; #64-bit value cannot be represented in Perl
$Max32bitValue=0xffffffff;


#-------------------------------------------------------------
#
#	Adds Netcard specific things to Global Variables
#
# Usage:
#	InitNetVars()
#
#-------------------------------------------------------------
sub
InitNetVars
{

%GlobalR0List=();
$NumR0Errs = 0;

%ModelKeys = (%ModelKeys, %NewModelKeys);
%ErrorTable = (%ErrorTable, %NetCardErrorTable);
%WarningTable = (%WarningTable, %NetCardWarningTable);

%AddRegList = ();

#might not need this..
#%ParamsList = ();

$Characteristics = 0;
%AddRegCollected = ();
%AddregInstList =();
}

	

#-------------------------------------------------------------
#
#	Checks if the given characteristics are correct
#
# Usage:
#	CheckCharacteristics(expected_section_name,line_num)
#
#-------------------------------------------------------------

sub 
CheckCharacteristics
{
local($sec, $Line_num) = @_;
local($RHS);
local($x,$y,$z);

	$RHS = $sec;
	$RHS= hex($RHS);
	$Characteristics = $RHS;

	$x=$RHS&0x1;
	$y=($RHS&0x2)>>1;
	$z=($RHS&0x4)>>2;
	
	if ($x+$y+$z!=1)
	{
		add_ref_err(4002,$Line_num);
	}
	if (($RHS&0x10)&&(($RHS&0x1)||($RHS&0x4)))
	{
		add_ref_err(4003,$Line_num);
	}
	
}

#-------------------------------------------------------------
#
#	Checks if the install section has valid bustype
#
# Usage:
#	CheckBusType(expected_section_name,bus_type,line_num)
#
#-------------------------------------------------------------

sub 
CheckBusType
{
local($sec,$RHS, $Line_num) = @_;

	$sec = uc $sec;

	while (($ModelName, $ModelRef) = each %ModelList)
	{
		if ($sec eq $$ModelRef[1])
		{	
			if (${$$ModelRef[2]}[0] =~ /ISAPNP/)
			{
				if ($RHS !~ /^14$/)
				{
					add_ref_err(4001,$Line_num);
				}
			}
#			else			Not sure whether other types can have any bustype..
#			{
#				if ($RHS !~ /^1$/)
#				{
#					add_ref_err(4001,$Line_num);
#				}
#			}
		last;
		}
	}
			
}
#-------------------------------------------------------------
#
#	Checks if the CopyFiles field of DestinationDirs section
#		is equal to 12
#
# Usage:
#	CheckCopyFile()
#
#-------------------------------------------------------------

sub 
CheckCopyFile
{
local ($sec,$CLnum, $LLnum, $line);
local($RHS,$LHS,@tmp);

	while (($sec, $CLnum) = each %SecStart)
	{
		if ($sec =~ /^DestinationDirs$/i)
		{
			die if (!defined($LLnum = $SecEnd{uc $sec}) );

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

				$LHS = &getLHS($line, $CLnum);
				$RHS = &getRHS($line, $CLnum);
				if ($LHS =~ /CopyFiles/i)
				{
					@tmp = split(",", $RHS);
					if ($tmp[0]!~ /12/ )
					{
						add_ref_err(4014, $CLnum);
					}
				}
			}
			last;
		}
	}
			
}

#-------------------------------------------------------------
# 
# Checks [XXX.POSDUP] section.
#
# Usage:
#	checkPOSDUP(PosdupSection)
#
#	To be called when processing the netcard specific 
#	dangling sections and strings
#-------------------------------------------------------------
sub 
checkPOSDUP
{
local ($section) = @_;
local ($CLnum, $LLnum, $line);
local (@PosDupLines, $count, $present);

	return if (!defined ($CLnum = $SecStart{uc $section}));
	die if (!defined($LLnum = $SecEnd{uc $section}) );

		# the section should contain exacly one non empty line
		# equal to "*PNP8132"

	$count = 0;
	$present = 0;
	while(++$CLnum < $LLnum)
	{
		$line = $INFlines[$CLnum];
		if (! defined($line))
		{ next; }
		$count++;
		if ($line =~ /^*PNP8132$/i)
		{	$present++; }
	}

	if ($count == 0)
	{
		add_ref_err(4030, $LLnum-1, $section);
		return;
	}

	if ($present == 0)
	{
		add_ref_err(4031, $LLnum-1, $section);
		return;
	}

	if ($count > 1)
	{
		add_ref_err(4032, $LLnum-1, $section);
	}
}

#-------------------------------------------------------------
#
#	Checks the LogConfig Section for any errors
#
# Usage:
#	CheckLogConfig(expected_section_name,line_num)
#
#Note: The error values for LogConfig have to be defined
#	The numbers are starting from 4100.
#-------------------------------------------------------------

sub 
CheckLogConfig
{
local($sec,$Lnum) = @_;			# 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);
	#	$RHS = $AllQuotes{$1} if($RHS =~ /^__QUOTED_STR_\(([0-9]*)\)__$/);

	
		#check if ConfigPriority key.
		if ($LHS =~ /^CONFIGPRIORITY$/)
		{
			if ($RHS =~  /^([A-Z]*)[,]?([A-Z]*)$/)
			{
				local(%Priority_Value,%ConfigType);
				local($expect_RHS);

				%Priority_Value=('HARDWIRED' => 1,'DESIRED' => 1,'NORMAL' => 1,
							'SUBOPTIMAL' => 1,'DISABLED' => 1,'RESTART' => 1,
							'REBOOT' => 1,'POWEROFF' => 1,'HARDRECONFIG' => 1);
				%ConfigType=('FORCED' => 1,'BASIC' => 1);
				if (!defined($expect_RHS=$Priority_Value{$1}))
				{
					add_ref_err(4100,$CLnum);

				}
				if ($2 !~ /^$/)
				{
					add_ref_err(4101,$CLnum) 
					if (!defined($expect_RHS=$ConfigType{$2}));
				}
			}
			next;
		}

		#check if IRQCONFIG key.
		if ($LHS =~ /^IRQCONFIG$/)
		{
			if ($RHS =~  /^[SL]?[:]?([0-9,]*)$/)
			{
				local(@IRQList,$cnt,$i);
				@IRQList=get_list($1,$CLnum,1);
				$cnt=@IRQList;
				for($i = 0; $i < $cnt; $i++)
				{
					add_ref_err(4102,$CLnum) 
						if (($IRQList[$i]>15)||($IRQList[$i]<0));
				}
			}
			else
			{
				add_ref_err(4104,$CLnum);
			}
			next;
		}

		#check if IOCONFIG key.
		if ($LHS =~ /^IOCONFIG$/)
		{
			local(@IOList,$cnt,$i);
			@IOList=get_list($RHS,$CLnum,1);
			$cnt=@IOList;
			for($i = 0; $i < $cnt; $i++)
			{

				#start-end[([decode-mask]:[alias-offset]:[attr])]
				if ($IOList[$i] =~  /^([0-9a-fA-F]*)-([0-9a-fA-F]*)[\(]?([0-9a-fA-F]*)[:]?([0-9a-fA-F]*)[:]?[M]?[\)]?$/)
				{
					#Type 1 I/O Range..
					Check64bitvalue($1,$CLnum); #start value
					Check64bitvalue($2,$CLnum);	#end value
					Check64bitvalue($3,$CLnum);	#decode-mask value
					Check32bitvalue($4,$CLnum);	#alias-offset value
				}

				#size@min-max[%align-mask][([decode-mask]:[alias-offset]:[attr])]
				#RHS has to be of the form: $RHS='10@200-39f%fff0(3ff::)'

				elsif ($IOList[$i] =~ /^([0-9a-fA-F]*)@([0-9a-fA-F]*)-([0-9a-fA-F]*)[%]?([0-9a-fA-F]*)[\(]?([0-9a-fA-F]*)[:]?([0-9a-fA-F]*)[:]?[M]?[\)]?$/)
				{
					Check32bitvalue($1,$CLnum);	#size value
					Check64bitvalue($2,$CLnum);	#min value
					Check64bitvalue($3,$CLnum);	#max value
					Check64bitvalue($4,$CLnum);	#align-mask value
					Check64bitvalue($5,$CLnum);	#decode-mask value
					Check32bitvalue($6,$CLnum);	#alias-offset value
				}
				else
				{
					add_ref_err(4105,$CLnum);
				}
			}	#for ends..
			next;
		}

		#check if DMACONFIG key.
		if ($LHS =~ /^DMACONFIG$/)
		{
			if ($RHS =~  /^[DW]?[:]?([0-9,]*)$/)
			{
				local(@DMAList,$cnt,$i);
				@DMAList=get_list($1,$CLnum,1);
				$cnt=@DMAList;
				for($i = 0; $i < $cnt; $i++)
				{
					add_ref_err(4106,$CLnum) 
						if (($DMAList[$i]>7)||($DMAList[$i]<0));
				}
			}
			else
			{
				add_ref_err(4107,$CLnum);
			}
				
			next;
		}
		#check if MEMCONFIG key.
		if ($LHS =~ /^MEMCONFIG$/)
		{
			local(@MEMList,$cnt,$i);
			@MEMList=get_list($RHS,$CLnum,1);
			$cnt=@MEMList;
			for($i = 0; $i < $cnt; $i++)
			{

				#start-end[(attr)]
				if ($MEMList[$i] =~  /^([0-9a-fA-F]*)-([0-9a-fA-F]*)[\(][CDFRW]*[\)]?$/)
				{
					#Type 1 I/O Range..
					Check64bitvalue($1,$CLnum);	#start value
					Check64bitvalue($2,$CLnum);	#end value
				}

				#size@min-max[%align-mask][(attr)]

				#RHS has to be of the form: $RHS='10@200-39f%fff0(3ff::)'

				elsif ($MEMList[$i] =~ /^([0-9a-fA-F]*)@([0-9a-fA-F]*)-([0-9a-fA-F]*)[%]?([0-9a-fA-F]*)[\(]?[CDFRW]*[\)]?$/)
				{
					Check32bitvalue($1,$CLnum);	#size value
					Check64bitvalue($2,$CLnum);	#min value
					Check64bitvalue($3,$CLnum);	#max value
					Check64bitvalue($4,$CLnum);	#align-mask value
				}
				else
				{
					add_ref_err(4108,$CLnum);
				}
			}	#for ends..
			next;
		}
	} #while ends..

}
#-------------------------------------------------------------
#
#	Checks for 64-bit value
#
#-------------------------------------------------------------
sub
Check64bitvalue
{
local($num,$Lnum)=@_;
local($len);
	$num =~ s/^[0]*//;	#Remove Starting 0s
	print "\nHERE:$num\n";
	$len = length $num;
	add_ref_err(4103,$Lnum) if ($len > 16);
}
#-------------------------------------------------------------
#
#	Checks for 32-bit value
#
#-------------------------------------------------------------
sub
Check32bitvalue
{
local($num,$Lnum)=@_;
local($len);
	$num =~ s/^[0]*//;	#Remove Starting 0s
	$len = length $num;
	add_ref_err(4103,$Lnum) if ($len > 8);
}
#-------------------------------------------------------------
#
#	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);
}
#-------------------------------------------------------------
#
#	Checks the Services  Section for any errors
#
# 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);
		add_ref_err(4011,$CLnum) if ($tmp[0] != $AddRegList{'SERVICE'});
		$tmp[2] =~ s/^[\s\t]*//;
		$tmp[3] =~ s/^[\s\t]*//;
		$serinstsec=$tmp[2];
		$eventinstsec=$tmp[3];
		&CheckServiceInst($serinstsec);
		&CheckEventLogInst($eventinstsec);
		$present = 1;
	}
	#check if DelService key.
	if (($LHS =~ /^DELSERVICE$/i)&&($present==1))
	{
		add_ref_err(4012,$CLnum) if ($tmp[0] != $RHS);
	}

	}
	add_ref_err(4013,$LLnum-1) 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 !~ /^%(.*)%$/) 
		{
		add_ref_wrn(5200, $CLnum);
		}else {
		add_ref_err(1007, $CLnum, $RHS)
			if (!defined $FStr{uc $RHS}); 
		@RefStr = (@RefStr, uc $RHS);
		}
		next;
	}
	#check if ServiceType key
	if ($LHS =~ /^SERVICETYPE$/)
	{
		local($num);
		$num=hex $RHS;
		add_ref_err(4200,$CLnum) if (!(($num==1)||($num==2))||($num==4));
		#Error flagged if service type other than
		#SERVICE_KERNEL_DRIVER, SERVICE_FILE_SYSTEM_DRIVER, SERVICE_ADAPTER
		next;
	}

	#check if StartType key
	if ($LHS =~ /^STARTTYPE$/)
	{
		local($num);
		$num=hex $RHS;
		add_ref_err(4201,$CLnum) if ($num<0)&&($num>4);
		next;
	}
	#check if ErrorControl key
	if ($LHS =~ /^ERRORCONTROL$/)
	{
		local($num);
		$num=hex $RHS;
		add_ref_err(4202,$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(4203,$CLnum) if ($RHS=~ /^$/);
		next;
		#Just checking if something is specified for the binary filename.
	}
	#check if LoadOrderGroup key
	if ($LHS =~ /^LOADORDERGROUP$/)
	{
		add_ref_err(4204,$CLnum) if (!defined($LoadOrderGroup{uc $RHS}));
		next;
	}
	#check if Dependencies key
	if ($LHS =~ /^DEPENDENCIES$/)
	{
		local(@tmp);
		@tmp = split ( ',', $RHS);
		if ($tmp[0] =~ /^+(.*)/)
		{
			add_ref_err(4204,$CLnum) if (!defined($LoadOrderGroup{uc $1}));
		}
		next;
	}
	#check if StartName key
	if ($LHS =~ /^STARTNAME$/)
	{
		add_ref_err(4205,$CLnum) if ($RHS=/^$/);
		next;
		#Just checking if something is specified for the driver object name.
	}
	} #while ends
}

#-------------------------------------------------------------
#
# This sub processes the given section assuming it is an 
# addreg section unless it is in the list AddREgList.
# Flags duplicate keys in the same section and finally 
# adds the list of keys to AddRegList.
#
# Globals changed:
#		AddRegList,  ParamsList
#
# Usage:
#		AddRegProc(section_name)
#
#-------------------------------------------------------------

sub 
AddRegProc
{
local($sec) = @_;
local($line, $LHS, $RHS, $CLnum, $LLnum);
local($key, $value, %interface_Install);

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

	return if (defined $AddRegCollected{$sec});		 #already collected

	@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));

		($key, $value) = &get_addreg_list($line, $CLnum);

		next if (!defined($key) || $key =~ /^$/);

		if ($key =~ /^Ndi/i)
		{

			if ($key =~ /Service$/i)
			{
				local(@tmp);
				@tmp= split(',',$value);
				if ($tmp[1] =~ /__QUOTED_STR_\(([0-9]*)\)__$/)
				{
					$AddRegList{'SERVICE'} = $AllQuotes{$1};
				}
				else
				{
					add_ref_err(1004, $CLnum);
				}

				next;
			}
			if ($key =~ /Install/i)				#Remove sections??
			{
				local(@tmp,@tmp1,$inst);
				@tmp= split(',',$key);		
				@tmp2= split(',',$value);
				if ($tmp[1] =~ /^$/)			#Whether to call &InstProc??
				{	
					if ($tmp2[1] =~ /__QUOTED_STR_\(([0-9]*)\)__$/)
					{
						local($Extension,$SectionsDefined,$inst,$k);
						$AddRegList{'INSTALL'} = $AllQuotes{$1};
						$inst=$AllQuotes{$1};
						# possible to use extensions
						foreach $Extension (keys %InstallExtensions)
						{		
							if (defined ($SecStart{uc ($inst.".$Extension")}))
							{
								$SectionsDefined++;	
								$AddregInstList{uc ($inst.".$Extension")} = 1;
							}
						}
						if ($SectionsDefined == 0)
						{
							add_ref_err(1052, $CLnum, $inst);
						}
					}
					else
					{
						add_ref_err(1004, $CLnum);
					}

					#Default install section..
				}
				else
				{
					#Stores the interface name => install section
					#This part is incomplete: need to look at an example interface
					#install section to verify if InstProc can be used or not.

					if ($tmp2[1] =~ /__QUOTED_STR_\(([0-9]*)\)__$/)
					{
						$interface_Install{uc $tmp[1]} = $AllQuotes{$1};
					}
					else
					{
						add_ref_err(1004, $CLnum);
					}

					#Interface install section..
				}
				next;
			}


			if ($key =~ /Interfaces/i)
			{
				local(@tmp,@tmp2,@tmp3,$cnt,$i,$str);	#set of interfaces can be defined..
				@tmp= split(',',$value);
				@tmp2= split(',',$key);
				if ($tmp[1] =~ /__QUOTED_STR_\(([0-9]*)\)__$/)
				{
					$str = $AllQuotes{$1};
					@tmp3= split(',',$str);
					$cnt = @tmp3;
				}
				else
				{
					add_ref_err(1004, $CLnum);
				}

				for ($i=0; $i < $cnt; $i++)
				{
						add_ref_err(4005,$CLnum) if (!defined($ValidInterfaces{uc $tmp3[$i]}));
				}
				#checks that no lower interface has install section..
				if (($tmp2[1] =~ /DefLower/i)||($tmp2[1] =~ /LowerRange/i))
				{
				for ($i=0; $i < $cnt; $i++)
				{
						add_ref_err(4006,$CLnum) if (defined($interface_Install{uc $tmp3[$i]}));
				}
				}
				next;
			}
			if ($key =~ /Params/i)
			{
				local(@tmp,@tmp2,@tmp3);
				@tmp = split(',',$key);
				@tmp2 = split(',',$value);
				@tmp3 = split(/\\/,$tmp[0]);

				add_ref_err(4009,$CLnum) if ($Characteristics&0x80==0);
				add_ref_err(4007,$CLnum) if (defined($ReservedWords{uc $tmp3[2]}));
				if ($key !~ /enum/i)
				{
					add_ref_err(4008,$CLnum) if (!defined($ParamKeys{uc $tmp[1]}));
					add_ref_err(1004, $CLnum) if ($tmp2[1] !~ /^__QUOTED_STR_\(([0-9]*)\)__$/);
				}
				elsif ($key =~ /enum/i)
				{
					add_ref_err(1004, $CLnum) if ($tmp2[1] !~ /^__QUOTED_STR_\(([0-9]*)\)__$/);
				}
				next;
			}
		}	#ndi-if ends.
		else
		{
			local(@tmp,@tmp2);
			@tmp = split(',',$key);
			@tmp2 = split(',',$value);

			if ($tmp[1] =~ /^$/ )		#Defaults section...
			{

				#Also flag an error if the value-name doesnt satisfy "some" criterion..
				# This criteria is unknown for DynamicCRTMax, DynamicCRTIncrement,
				# DynamicCRTInterval, EventMessageFile, TypesSupported and TextModeFlags.

				add_ref_err(1004, $CLnum) 
					if (($tmp2[1] !~ /__QUOTED_STR_\(([0-9]*)\)__$/)||($tmp2[1] !~ /[0x]?[0-9a-fA-F]*/));
			}
		}



	} #while ends..

}

sub
CheckHKey
{
local($Lnum, @list) = @_;

	# warning if not HKR, HKU, HKLM, HKCR, HKCU

	if ($list[0] !~ /^HKR$/i && 
		$list[0] !~ /^HKU$/i &&
		$list[0] !~ /^HKLM$/i &&
		$list[0] !~ /^HKCR$/i &&
		$list[0] !~ /^HKCU$/i )
	{
		add_ref_wrn(4014, $Lnum);
	}

	IsStringKey($Lnum, @list);
}

#-------------------------------------------------------------
# this sub takes the given line and breaks it into a list of five
# fields. It checks the fields for existance and correct range.
#
# Returns: array of four elemnts - excluding the HKR key.
#
# Usage:
#		get_addreg_fields(line, line_num)
#
#-------------------------------------------------------------
sub
get_addreg_list
{
local($line, $Lnum) = @_;

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

	&CheckHKey($Lnum, @list);

	if ($list[0] !~ /^HKR$/i)
	{	return () ;	}

	# Field 2 Optional - check not required.
	# Field 3 Mandatory - check for existance.
#	add_ref_err("Mandatory field &ltKey&gt is missing.", $Lnum)
	add_ref_err(3047, $Lnum)
		if ($list[2] =~ /^$/);

	# Field 4 If not present - make it 0; if present ensure 0 or 1.
	$list[3] = '0' 
		if ($list[3] =~ /^$/);

#	add_ref_err("Field &ltFormat&gt should be 0 or 1.", $Lnum)
	add_ref_err(3048, $Lnum)
		unless ($list[3] =~ /^[01]$/);

	# Field 5 Mandatory - check for existance. 
	if ($list[4] =~ /^$/)
	{
#		add_ref_err("Mandatory field &ltValue&gt is missing.", $Lnum);
		add_ref_err(3049, $Lnum);
	}

	$list[3] = join(":", $Lnum, $list[3]);

	(join(",", $list[1], $list[2]) , join(",", $list[3], $list[4]) ); # return value.
}
#-------------------------------------------------------------
# 
# Process the sections defined by ADDREG keyword for each install section. 
#
# Usage:
#	CheckInstSections()
#
#-------------------------------------------------------------
sub 
CheckInstSections
{
local ($sec_name, @sec_list, $RefListKeywords, $RefSectList, $CLnum);
local ($sec, $LLnum);
local($key, $value, $RefHashAddReg);

	while (($sec, $RefListKeywords)	= each %InstList)
	{
		next if (!defined($LLnum = $SecEnd{$sec}));

		if (!defined($RefSectList = ${$RefListKeywords}{'CHARACTERISTICS'} ) )
		{ 
			add_ref_err(3043, $LLnum-1);
			next;
		}
		else
		{
			@sec_list = @{$$RefSectList[0]};
			$CLnum = $$RefSectList[1];
			&CheckCharacteristics($sec_list[0],$CLnum);
		}
		if (!defined($RefSectList = ${$RefListKeywords}{'BUSTYPE'} ) )
		{ 
			add_ref_err(3044, $LLnum-1);
			next;
		}
		else
		{
			@sec_list = @{$$RefSectList[0]};
			$CLnum = $$RefSectList[1];
			&CheckBusType($sec,$sec_list[0],$CLnum);
		}

		if (!defined($RefSectList = ${$RefListKeywords}{'ADDREG'} ) )
		{ 
			add_ref_err(3042, $LLnum-1);
			next;
		}
		else
		{
			@sec_list = @{$$RefSectList[0]};
			$CLnum = $$RefSectList[1];

			foreach $sec_name (@sec_list)
			{
				next if ($sec_name =~ /^$/); # check for blank names.

				$sec_name = uc $sec_name;
				AddRegProc($sec_name);		 # process the AddReg sections.

				$AddRegCollected{$sec_name}=1;
			}
		}
	}
}
#-------------------------------------------------------------
# 
# Process the install sections defined by ADDREG keyword. 
#
# Usage:
#	CheckAddRegInstSections()
#
#-------------------------------------------------------------
sub 
CheckAddRegInstSections
{
	local($k);
	foreach $k (keys %AddregInstList)
	{
		&InstProc($k);
	}
}
#-------------------------------------------------------------
# 
# Process the Service sections defined in ADDREG section. 
#
# Usage:
#	CheckServiceSections()
#
#-------------------------------------------------------------
sub 
CheckServiceSections
{
	local($k);
	foreach $k (keys %InstList)
	{
		local($servicesec);
		$servicesec = $k.".Services";
		&ServiceProc($servicesec) if (defined($SecStart{uc $servicesec}));

	}
}

#------------------------------------------------------------
#
#This subroutine is a driver module for the lower level 
#checking modules for netcard inf checking
#
#------------------------------------------------------------
sub
NetcardCheck
{
	# look for the models defined under the manufacturers



	&ModelIDTypes();

	# Process ADDREG sections for each install section
	&CheckInstSections;
	&CheckAddRegInstSections;
	&CheckServiceSections;
	&CheckCopyFile;
	&netcard_section_usage;

}
#------------------------------------------------------------
# This sub also checks for proper posdup and noresdup sections.
#
# Globals changed:
#
# Usage: 
#		netcard_section_usage()
#------------------------------------------------------------

sub 
netcard_section_usage
{
	local($sec, $model, $count);
	foreach $sec (sort keys %SecStart)
	{
		if ($sec =~ /(.+)\.POSDUP/)
		{
			$model = $1;
			if(($count = grep(($sec eq $_), @SecRef)) < 1)
			{
				add_ref_err(4017, $SecStart{$sec}); 
			}
			&checkPOSDUP($sec);
			next;
		}
	}
}
######################################################
#
#	Main logic subs.
#
######################################################
#-------------------------------------------------------------
#
# prcesses the GlobalR0List to check for duplicates.
#
# Globals changed:
#
# Usage:
#
#-------------------------------------------------------------

sub
proc_AllR0
{

	foreach $ID (keys %GlobalR0List)
	{
	if ($GlobalR0List{$ID} =~ /,/)
	{
		add_ref_R0_err($ID, $GlobalR0List{$ID});
	}
	}
}

#------------------------------------------------------------
# This sub adds to the list of Rank0 errors. 
#
# Usage:
#		add_ref_R0_err(ID, files)
#------------------------------------------------------------
sub add_ref_R0_err
{
local($ID, $files) = @_;
local($str);

	@files = split(/, /, $files);
	$str = "<br><tr>\n<td><font color=#904040>$ID</font></td>\n";
	$str = join("", $str, "<td><pre>$files[0]</pre></td>\n</tr>\n");
	shift (@files); 
	foreach $file (@files) 
	{
		$str = join("", $str, "<tr>\n<td></td>\n<td><pre>$file</pre></td>\n</tr>\n");
	}

	$StrR0Errs[$NumR0Errs] = $str;
	$NumR0Errs++;

}

#-------------------------------------------------------------
#
# Analyse rank ids for all models defined
#
#
# Usage:
#	ModelIDTypes()
#
#-------------------------------------------------------------
sub 
ModelIDTypes
{
local($ModelKey, $ModelInfo, $inst, $sz, $r);
local($id_item, @rank0,@rank1,@lines, @rank, $Type);

	while (($ModelKey, $ModelInfo) = each (%ModelList) )
	{	$inst	= $$ModelInfo[1];

		@rank0	= @{$$ModelInfo[2]};
		@rank1	= @{$$ModelInfo[3]};
		@lines	= @{$$ModelInfo[5]};

		# Upper case
		$sz = @rank0;		# all arrays should have the same length
		for($id_item = 0; $id_item < $sz; $id_item++)
		{	$rank0[$id_item] = uc ($rank0[$id_item]) ;
			$rank1[$id_item] = uc ($rank1[$id_item]) ;
			@rank = ();
			push(@rank, $rank0[$id_item], $rank1[$id_item]); 
			&GetRankType($inst, $lines[$id_item], @rank);
		}
	}
}

#-------------------------------------------------------------
# 
# Globals changed:
#
# Usage:
#	GetRankType($inst_section, $Cur_line_num, id_array);
#-------------------------------------------------------------
sub
GetRankType
{
local ($inst, $CLnum, @id ) = @_;
local ($rank0, $rankNext, $sz, $Type, $TypeCur, $Count, $bError, $ExcludeRef);

		# Process Rank0 id
	$rank0 = $id[0];

	$Type = $IdType'UNKNOWN_ID;
	if ($rank0 !~ /^$/)
	{
		$Type = checkR0($rank0, $inst, $CLnum);
		if ($Type cmp $IdType'ISAPNP)
		{
			if (!defined($ExcludeRef = $ExSelList{uc $rank0}) || 
				!defined($$ExcludeRef{'EXCLUDEFROMSELECT'}) )
			{
				add_ref_wrn(5001, $CLnum, $rank0);
			}
		}

		AddGlobR0($rank0, $CLnum) if ($Type !~ /$IdType'UNKNOWN_ID/);
		add_ref_err(4015, $CLnum) 
					if ($id[0] eq $id[1]);
	}
}
#------------------------------------------------------------
# Checks validity of the R0 id.
#	package $IdType
#		- ISAPNP_ID		(not used anymore)
#		- LEGACY_ID
#
# Usage:
#		checkR0(ID, model, line_num)
#------------------------------------------------------------

sub checkR0
{
local($ID, $model, $Lnum) = @_;
local($Type);

	$Type = $IdType'UNKNOWN_ID;

	if ($ID =~ /^ISAPNP\\/)
	{
		$Type = $IdType'ISAPNP_ID;
	}
	else
	{
		$Type = $IdType'LEGACY_ID;
	}

	$Type; # return value

}


#------------------------------------------------------------
# Puts the given R0 id in the list of global R0 ids.
#
# Clobals changed:
#		GlobalR0List
# Usage:
#		AddGlobR0(ID)
#------------------------------------------------------------


sub
AddGlobR0
{
local($ID, $CLnum) = @_;

	# print "$infile: $ID\n";
	if (defined ($val = $GlobalR0List{uc $ID}))
	{
		add_ref_err(4016, $CLnum, $ID, $val);
		$GlobalR0List{uc $ID} = join(", ", $val, $infile." (line $CLnum)");
	} else {
		$GlobalR0List{uc $ID} = $infile." (line $CLnum)";
	}
}
#-------------------------------------------------------------
#
# This sub prints the summary for this files' mfgs and models.
#
# Globals changed:
#
# Usage:
#
#-------------------------------------------------------------
sub 
ModelSummary
{

local($name, @list, @id0, @id1, $id1, $id0, @list2, $model, @id2, $id2, $ExType, $ExcludeRef);
local($ExcludeKey, $ExcludeLine);

	#print summary for this file

	print "<PRE>\n";

	#print manu name (substituted from strings section.), 
	foreach $key (sort keys %ManuList) 
	{
		$ManuInfo = $ManuList{$key};
		$name = $$ManuInfo[0];	   #manu name.
		print "<FONT SIZE=+2><FONT COLOR=GREEN><B>Mfg:</FONT> ";
		print "$name</B></FONT>\n"; 	#manu name.

		# model names and model info: rank0, rank1 etc.
		@list = @{$$ManuInfo[1]};
		while ($model = shift (@list))
		{
			print "\n";
			$ModelInfo = $ModelList{uc $model};

			print "   <FONT SIZE=+1><FONT COLOR=GREEN><B>Model:</FONT> "; 
			print "$$ModelInfo[0]</B></FONT>\n";	  #model name.

			@id0 = @{$$ModelInfo[2]};
			@id1 = @{$$ModelInfo[3]};
			@id2 = @{$$ModelInfo[4]};
			foreach $id0 (@id0) 
			{
				print "   <FONT SIZE=+0><FONT COLOR=GREEN>Rank0:</FONT> "; 
				print "$id0</FONT>\n";

				$id1 = shift(@id1);
		undef($id1) if ($id1 =~ /^$/);
		$id2 = shift(@id2);
		undef($id2) if ($id2 =~ /^$/);
		if (defined($id1) || defined($id2))
		   {  print "   <FONT SIZE=+0><FONT COLOR=GREEN>Rank1:</FONT> "; 
			  print "$id1</FONT>\n";
		   }
		if (defined ($id2))
		  { print "   <FONT SIZE=+0><FONT COLOR=GREEN>Rank2:</FONT> "; 
					print "$id2</FONT>\n";
		  }


			  if (defined ($ExcludeRef = $ExSelList{uc $id0}))
			  {
				while (($ExcludeKey, $ExcludeLine) = each %$ExcludeRef)
				{
					print "    Excluded from Select.\n" if ($ExcludeKey =~ /^EXCLUDEFROMSELECT$/i);
					print "    Excluded from Select in NT.\n" if ($ExcludeKey =~ /^EXCLUDEFROMSELECT.NT$/i);
				}
			  }
			  print "\n";
			}
		}
		print "\n";
	}
	
	print "</PRE>\n";
}	 