##############################################################
#
# Sept 17, 1996  Created	ReenaA
# Jan  06, 1997  Modified	CostelR
#
##############################################################
########################################################################
#
# Package with version constants
#
########################################################################
{ package ADDREGVersion;

  $BuildVersion = "1.00.0027";
}


#################################################################
#
# Main subs - called from other subs outside this file.
#
#################################################################

#-------------------------------------------------------------
#
# 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
#
# Usage:
#		AddRegProc(section_name)
#
#-------------------------------------------------------------

sub 
AddRegProc
{
local($sec) = @_;
local($line, $LHS, $RHS, $CLnum, $LLnum, $all_lines);
local($key, $value, $UpperKey, %local_HKR, @local_Responses, $RefRespStruct);

	$sec = uc $sec;
	return if (!defined ($CLnum = $SecStart{$sec}));
	return if (defined $AddRegList{$sec});		# already collected

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

	%local_HKR = ();
	@local_Responses = ();

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

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

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

		if ($key =~ /^Responses,/i)
		{
			$RefRespStruct	= parse_response($key, $value);
			if (defined($RefRespStruct))
			{ push(@local_Responses, $RefRespStruct);	}
		}
		else
		{
			$UpperKey = uc $key;
			if ( defined($local_HKR{$UpperKey}) )
			{	
	#			add_ref_err("\"HKR,$key\" already defined in this section. Keep last definition.", $CLnum);
				add_ref_err(3046, $CLnum, $key);
				next;
			}
			$local_HKR{$UpperKey} = $value;
		}

	}

	$AddRegList{$sec} = [\%local_HKR, \@local_Responses]; 
}

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_err(3084, $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.
}


#-------------------------------------------------------------
# This sub processes the $HKR assuming it to be a whole list
# of HKRs of all sections put together. 
#
# Globals changed:
#
# Usage:
#		ProcInstall(line_num, section, Install_HKR)
#		Install_HKR has all keys and line_num points to the AddRegKey.
#-------------------------------------------------------------
sub
ProcInstall
{
local($Lnum, $model, %Inst_HKR) = @_;
local(@ResponseStructs);

	# check for the AT-Command syntax

	# Process the different types of HKR strings
	
	# this print is just to tell the user I am processing ....
	print "."; 

	# first get rid of the keys that we do lesser processing with.
	# this will remove the most of HKR keys
	@ResponseStructs = @InstallResponses;

	&proc_skip($Lnum, $model);
	&proc_SVDKeys($Lnum, $model);
	&proc_voice($Lnum, $model);
	&proc_vv($Lnum, $model);

	&proc_set($Lnum, $model);
	&proc_propset($Lnum, $model);
	&proc_DeviceType($Lnum, $model);	# to be called before proc_DCB
	&proc_DCB($Lnum, $model);

	&proc_init($Lnum, $model);
	&proc_ans($Lnum, $model);
	&proc_hup($Lnum, $model);
	&proc_mon($Lnum, $model);
	&proc_reset($Lnum, $model);
	
	&proc_fax($Lnum, $model);

	&proc_resp($Lnum, $model, @ResponseStructs);	# to be called last

    if (defined($CallerIDCheck{uc $model}))
    {
        if ($CallerIDCheck{uc $model} != 0x000001ff)
        {
            local($index,$checkmissing);
            $index = 0;
            $checkmissing = $CallerIDCheck{uc $model};
            while ($index < 9)
            {
                if (($checkmissing & 0x1) == 0)
                {
                    add_ref_err(3144,$Lnum,$sec,$MissingCallerIDKeys[$index]);
                }
                $index++;
                $checkmissing = $checkmissing >> 1;
            }
        }
    }

	while(($key, $val) = each %Inst_HKR)
	{	
		@X =  split(/:/,$val);
#		inst_err("Old key - cant be used any more.", $X[0]) 
		inst_err(3074, $X[0]) if ($key =~ /CELLULAR_O/);
	}
}

#################################################################
#
# Utility subs.
#
#################################################################
#-------------------------------------------------------------
# Checks the Answer key
#
# Globals changed:
#
# Usage:
#
#-------------------------------------------------------------


sub
proc_ans
{
local($Lnum, $model) = @_;

	@list = getPathKey("Answer,");
	if (($count = @list) < 1)
	{
#		add_ref_err("$model does not have any Answer.", $Lnum);
		add_ref_err(3050, $Lnum, $model);
		return;
	}
	# check for the AT-Command syntax
	CheckATCommands($model, "Answer,", @list);

	$this_val = &join_strval(@list);
#	inst_wrn("$model: Answer suspicious. Expect \"ATA&ltcr&gt\".", $L)
	inst_wrn(4016, $L, $model)
		if ($this_val !~ /^ATA<CR>$/);
}
	


#-------------------------------------------------------------
#
# Checks the Hangup key
#
# Globals changed:
#
# Usage:
#
#-------------------------------------------------------------

sub
proc_hup
{
local($Lnum, $model) = @_;

	@list = getPathKey("Hangup,");
	if (($count = @list) < 1)
	{
#		add_ref_err("$model does not have any Hangup.", $Lnum);
		add_ref_err(3051, $Lnum, $model);
		return;
	}

	# check for the AT-Command syntax
	CheckATCommands($model, "Hangup,", @list);

	$this_val = &join_strval(@list);
#	inst_wrn("$model: Hangup suspicious. Expect \"ATH&ltcr&gt\".", $L)
	inst_wrn(4017, $L, $model)
		if ($this_val !~ /^ATH.*<CR>$/);

}
	


#-------------------------------------------------------------
# Checks the Init key
#
# Globals changed:
#
# Usage:
#
#-------------------------------------------------------------
sub
proc_init
{
local($Lnum, $model) = @_;

	@list = getPathKey("Init,");
	if (($count = @list) < 1)
	{
#		add_ref_err("$model does not have any Init.", $Lnum);
		add_ref_err(3052, $Lnum, $model);
		return;
	}

	# check for the AT-Command syntax
	CheckATCommands($model, "Init,", @list);

	# now the list has no duplicates.  Just multiple levels.
	# join all levels and see if we have a good enough init.
	# make sure that all levels are in strings.

	$this_val = &join_strval(@list);

	#ensure that this_val is a strong and robust init.
	local($str);
	$str = "";

	$str = join("", $str, "AT") if ($this_val !~ /^AT/);
	$str = join("", $str, "&F") if ($this_val !~ /&F/);
	$str = join("", $str, "E0") if ($this_val !~ /E(\d*)/ || $1 !~ /^0?$/);
	$str = join("", $str, "&D2") if ($this_val !~ /&D2/);
	$str = join("", $str, "&C1") if ($this_val !~ /&C1/);
	$str = join("", $str, "S0=0") if ($this_val !~ /S0=0/);
	if ($str !~ /^$/) # found errs
	{
#		inst_wrn("$model: Init not robust. Expect \"$str\" also.", $L);
		inst_wrn(4018, $L, $model, $str);
	}

	if ($this_val !~ /V(\d*)/ || $1 !~ /^(0|1)?$/)
	{
		inst_err(3089, $L);
	}

	if (!defined($InstallProps{uc $model}))
	{
		$InstallProps{uc $model} = [];
	}

	${$InstallProps{uc $model}}[2] = [($this_val =~ /V(\d*)/ && $1 =~ /^0?$/) ? 0 : 1 ];	# verbose mode
}



#-------------------------------------------------------------
# Checks the Reset key
#
# Globals changed:
#
# Usage:
#
#-------------------------------------------------------------
sub
proc_reset
{
local($Lnum, $model) = @_;

	@list = getPathKey(",Reset");
	if (($count = @list) < 1)
	{
#		add_ref_err("$model does not have any Reset.", $Lnum);
		add_ref_err(3053, $Lnum, $model);
		return;
	} elsif (($count = @list) > 1)
	{
#		add_ref_err("More than one Reset in model $model.", $Lnum);
		add_ref_err(3054, $Lnum, $model);
		return;
	}

	$this_val = &join_strval(@list);

#	inst_wrn("$model: Reset suspicious. Expect \"ATZ&ltcr&gt\" or \"AT&F..&ltcr&gt\".", $L)
	inst_wrn(4019, $L, $model)
		unless (($this_val =~ /^ATZ.*<CR>$/) || ($this_val =~ /^AT&F.*<CR>$/));
}



#-------------------------------------------------------------
# Checks the Monitor key
#
# Globals changed:
#
# Usage:
#
#-------------------------------------------------------------
sub
proc_mon
{
local($Lnum, $model) = @_;

	@list = getPathKey("Monitor,");
	if (($count = @list) < 1)
	{
#		add_ref_err("$model does not have any Monitor.", $Lnum);
		add_ref_err(3055, $Lnum, $model);
		return;
	} 

	# check for the AT-Command syntax
	CheckATCommands($model, "Monitor,", @list);

	$this_val = &join_strval(@list);
#	inst_err("$model: Monitor suspicious. Expect 1: \"ATS0=0&ltcr&gt\" and 2: \"None\".", $L)
	inst_err(3100, $L, $model)
		unless ($this_val =~ /^ATS0=0<CR>NONE$/);
}
	
# process mandatory settings fields here.

#-------------------------------------------------------------
# Checks the madatory Settings, * keys.
#
# Globals changed:
#
# Usage:
#
#-------------------------------------------------------------
sub
proc_set
{
local($Lnum, $model) = @_;

	@list = getPathKey("Settings,Prefix",1);
	if (($count = @list) < 1)
	{
#		add_ref_err("$model does not have any Settings,Prefix.", $Lnum);
		add_ref_err(3056, $Lnum, $model);
	} 
	else
	{
	$this_val = &join_strval(@list);
#	inst_wrn("$model: Settings, Prefix suspicious. Expect \"AT\".", $L)
	inst_wrn(4021, $L, $model)
		unless ($this_val =~ /AT/);
	}

	@list = getPathKey("Settings,DialPrefix",1);
	if (($count = @list) < 1)
	{
#		add_ref_err("$model does not have any Settings,DialPrefix.", $Lnum);
		add_ref_err(3057, $Lnum, $model);
	}
	else
	{ 
	$this_val = &join_strval(@list);
#	inst_wrn("$model: Settings, DialPrefix suspicious. Expect \"D\".", $L)
	inst_wrn(4022, $L, $model)
		unless ($this_val =~ /D/);
	}

	@list = getPathKey("Settings,DialSuffix",1);
	if (($count = @list) < 1)
	{
#		add_ref_err("$model does not have any Settings,DialSuffix.", $Lnum);
		add_ref_err(3058, $Lnum, $model);
	}
	else
	{ 
	$this_val = &join_strval(@list);
#	inst_wrn("$model: Settings, DialSuffix suspicious. Expect \"\" or \";\".", $L)
	inst_wrn(4023, $L, $model)
		unless (($this_val =~ /^$/) || ($this_val =~ /;/));
	}

	@list = getPathKey("Settings,Terminator",1);
	if (($count = @list) < 1)
	{
#		add_ref_err("$model does not have any Settings,Terminator.", $Lnum);
		add_ref_err(3059, $Lnum, $model);
	}
	else
	{ 
	$this_val = &join_strval(@list);
#	inst_wrn("$model: Settings, Terminator suspicious. Expect \"&ltcr&gt\".", $L)
	inst_wrn(4024, $L, $model)
		unless ($this_val =~ /<CR>/);
	}
}
	
	
#-------------------------------------------------------------
# Get the valid keys out - dont know how to process them!!
#
# Globals changed:
#
# Usage:
#
#-------------------------------------------------------------
sub
proc_skip
{
local($Lnum, $model) = @_;

	@list = getPathKey(",FriendlyDriver");
	@list = getPathKey(",DevLoader");
	@list = getPathKey(",ConfigDialog");
	@list = getPathKey(",EnumPropPages");
	@list = getPathKey(",PortSubClass");
	@list = getPathKey(",PortDriver");
	@list = getPathKey(",Contention");
	@list = getPathKey("Override,");
}
	
#-------------------------------------------------------------
# Flagg errors for invalid SVD keys
#
#-------------------------------------------------------------
sub
proc_SVDKeys
{
local($Lnum, $model) = @_;
local(@list, $line, $count, @tmp, @tmp2);
local(@IllegalKeys, $Key);

	@IllegalKeys = ("SVDmode,", "MediaLinkCntrl,", "MediaLinkModulation,");

	foreach $Key  (@IllegalKeys)
	{
		@list = getPathKey($Key);

		if (($count = @list) != 0)
		{
			@tmp = ();
			$L = 0;
			foreach $line (@list) 
			{
				@tmp = split(',', $line, 4);
				@tmp2 = split(':', $tmp[2], 2);
				$L =  $tmp2[0];
				$T =  $tmp2[1];
			}
			inst_wrn(4042, $L);
		}
	}
}

#-------------------------------------------------------------
# Get the Fax keys out - dont know how to process them!!
#
# Globals changed:
#
# Usage:
#
#-------------------------------------------------------------
sub
proc_fax
{
local($Lnum, $model) = @_;

	if(($FaxCheck == 1) and defined(&proc_faxreg))
	{
		proc_faxreg($Lnum, $model);
	}

	@list = getPathKey(",Default",1);
	foreach $line (@list) 
	{
		@tmp = split(',', $line, 4);
		@tmp2 = split(':', $tmp[2], 2);
		$L =  $tmp2[0];
		inst_wrn(4053, $L);
	}

	@list = getPathKey(",FClass",1);
	foreach $line (@list) 
	{
		@tmp = split(',', $line, 4);
		@tmp2 = split(':', $tmp[2], 2);
		$L =  $tmp2[0];
		inst_wrn(4053, $L);
	}

	@list = getPathKey("Fax,");
	foreach $line (@list) 
	{
		@tmp = split(',', $line, 4);
		@tmp2 = split(':', $tmp[2], 2);
		$L =  $tmp2[0];
		inst_wrn(4053, $L);
	}
}
	
#-------------------------------------------------------------
#
# Get the Voice keys
#
# Globals changed:
#
# Usage:
#
#-------------------------------------------------------------
sub
proc_voice
{
local($Lnum, $model) = @_;
my ($VoiceProfile, $count);

	voice_minimal_keys($Lnum, $model);
	$VoiceProfile	= $InstallProps{uc $model}->[3];

#	caller ID
	@list = getPathKey("EnableCallerID,");
	# check for the AT-Command syntax
	CheckATCommands($model, "EnableCallerID,", @list);
	if (($count = @list) > 0)
	{	inst_wrn(4065, $Lnum, "EnableCallerID") 
			if (($VoiceProfile & 0x00000080) ||
				!defined($VoiceProfile));
        $CallerIDCheck{uc $model} = 1;
	}
	elsif (defined($VoiceProfile))
	{	add_ref_err(3130, $Lnum) if (($VoiceProfile & 0x00000080) == 0);	}

	@list = getPathKey(",CallerIDPrivate");
	if (($count = @list) > 0)
	{	inst_wrn(4065, $Lnum, "CallerIDPrivate") 
			if (($VoiceProfile & 0x00000080) ||
				!defined($VoiceProfile));

        local(@tmp);
        @tmp = ();
        foreach $field (@list)
        {
            @tmp = split(",",$field,4);
            if ($tmp[3] =~ /P/)
            {
                $CallerIDCheck{uc $model} =  $CallerIDCheck{uc $model} | 0x000000002;  
            }
        }
	}

	@list = getPathKey(",CallerIDOutSide");
	if (($count = @list) > 0)
	{	inst_wrn(4065, $Lnum, "CallerIDOutSide") 
			if (($VoiceProfile & 0x00000080) ||
				!defined($VoiceProfile));

        local(@tmp);
        @tmp = ();
        foreach $field (@list)
        {
            @tmp = split(",",$field,4);
            if ($tmp[3] =~ /O/)
            {
                $CallerIDCheck{uc $model} =  $CallerIDCheck{uc $model} | 0x000000004;  
            }
        }
	}

#	distinctive rings
	@list = getPathKey("EnableDistinctiveRing,");
	# check for the AT-Command syntax
	CheckATCommands($model, "EnableDistinctiveRing,", @list);
	if (($count = @list) > 0)
	{	inst_wrn(4066, $Lnum, "EnableDistinctiveRing") 
			if (($VoiceProfile & 0x00001000) ||
				!defined($VoiceProfile));
	}
	elsif (defined($VoiceProfile))
	{	add_ref_err(3131, $Lnum) if (($VoiceProfile & 0x00001000) == 0);	}

	#	VariableTerminator used by either callerID or distinctive ring
	@list = getPathKey(",VariableTerminator");
	if (($count = @list) == 0 and defined($VoiceProfile))
	{	add_ref_err(3132, $Lnum) if (($VoiceProfile & 0x00000080) == 0 ||
								     ($VoiceProfile & 0x00001000) == 0 );
	}
    if (($count = @list) > 0)
    {
        local(@tmp);
        @tmp = ();
        foreach $field (@list)
        {
            @tmp = split(",",$field,4);

            if ($tmp[3] =~ /<cr><lf>/)
            {
                $CallerIDCheck{uc $model} =  $CallerIDCheck{uc $model} | 0x000000008;  
            }
        }
    }

# generate digits
	@list = getPathKey("GenerateDigit,");
	# check for the AT-Command syntax
	CheckATCommands($model, "GenerateDigit,", @list);
	if (($count = @list) > 0)
	{	inst_wrn(4067, $Lnum, "GenerateDigit") 
			if (($VoiceProfile & 0x00020000) ||
				!defined($VoiceProfile));
	}
	elsif (defined($VoiceProfile))
	{	add_ref_err(3133, $Lnum) if (($VoiceProfile & 0x00020000) == 0);	}

# handset
	@list = getPathKey("OpenHandset,");
	# check for the AT-Command syntax
	CheckATCommands($model, "OpenHandset,", @list);
	if (($count = @list) > 0)
	{	inst_wrn(4068, $Lnum, "OpenHandset") 
			if (($VoiceProfile & 0x00000002) == 0 || 
				!defined($VoiceProfile));
	}
	elsif (defined($VoiceProfile))
	{	add_ref_err(3134, $Lnum, "OpenHandset") 
				if ($VoiceProfile & 0x00000002);
	}

	@list = getPathKey("CloseHandset,");
	# check for the AT-Command syntax
	CheckATCommands($model, "CloseHandset,", @list);
	if (($count = @list) > 0)
	{	inst_wrn(4068, $Lnum, "CloseHandset") 
			if (($VoiceProfile & 0x00000002) == 0 || 
				!defined($VoiceProfile));
	}
	elsif (defined($VoiceProfile))
	{	add_ref_err(3134, $Lnum, "CloseHandset") 
				if ($VoiceProfile & 0x00000002);
	}

	@list = getPathKey("HandsetSetRecordFormat,");
	# check for the AT-Command syntax
	CheckATCommands($model, "HandsetSetRecordFormat,", @list);
	if (($count = @list) > 0)
	{	inst_wrn(4068, $Lnum, "HandsetSetRecordFormat") 
			if (($VoiceProfile & 0x00000002) == 0  ||
				($VoiceProfile & 0x00000020) == 0 ||
				!defined($VoiceProfile));
	}
	elsif (defined($VoiceProfile))
	{	add_ref_err(3134, $Lnum, "HandsetSetRecordFormat") 
				if (($VoiceProfile & 0x00000002) &&
					($VoiceProfile & 0x00000020));
	}

	@list = getPathKey("HandsetSetPlayFormat,");
	# check for the AT-Command syntax
	CheckATCommands($model, "HandsetSetPlayFormat,", @list);
	if (($count = @list) > 0)
	{	inst_wrn(4068, $Lnum, "HandsetSetPlayFormat") 
			if (($VoiceProfile & 0x00000002) == 0 ||
				($VoiceProfile & 0x00000020) == 0 ||
				!defined($VoiceProfile));
	}
	elsif (defined($VoiceProfile))
	{	add_ref_err(3134, $Lnum, "HandsetSetPlayFormat") 
				if (($VoiceProfile & 0x00000002) &&
					($VoiceProfile & 0x00000020));
	}

# SpeakerPhone
	@list = getPathKey("SpeakerPhoneEnable,");
	# check for the AT-Command syntax
	CheckATCommands($model, "SpeakerPhoneEnable,", @list);
	if (($count = @list) > 0)
	{	inst_wrn(4069, $Lnum, "SpeakerPhoneEnable") 
			if (($VoiceProfile & 0x00000004) == 0 ||
				!defined($VoiceProfile));
	}
	elsif (defined($VoiceProfile))
	{	add_ref_err(3136, $Lnum, "SpeakerPhoneEnable") 
				if ($VoiceProfile & 0x00000004);
	}

	@list = getPathKey("SpeakerPhoneDisable,");
	# check for the AT-Command syntax
	CheckATCommands($model, "SpeakerPhoneDisable,", @list);
	if (($count = @list) > 0)
	{	inst_wrn(4069, $Lnum, "SpeakerPhoneDisable") 
			if (($VoiceProfile & 0x00000004) == 0 ||
				!defined($VoiceProfile));
	}
	elsif (defined($VoiceProfile))
	{	add_ref_err(3136, $Lnum, "SpeakerPhoneDisable") 
				if ($VoiceProfile & 0x00000004);
	}

	@list = getPathKey("SpeakerPhoneSetVolumeGain,");
	# check for the AT-Command syntax
	CheckATCommands($model, "SpeakerPhoneSetVolumeGain,", @list);
	if (($count = @list) > 0)
	{	inst_wrn(4069, $Lnum, "SpeakerPhoneSetVolumeGain") 
			if (($VoiceProfile & 0x00000004) == 0 ||
				!defined($VoiceProfile));
	}
	elsif (defined($VoiceProfile))
	{	add_ref_err(3136, $Lnum, "SpeakerPhoneSetVolumeGain") 
				if ($VoiceProfile & 0x00000004);
	}

	if (proc_speaker_specs() == 0)
	{
		add_ref_err(3136, $Lnum, "SpeakerPhoneSpecs") 
				if ($VoiceProfile & 0x00000004);
	}

	@list = getPathKey("SpeakerPhoneMute,");
	# check for the AT-Command syntax
	CheckATCommands($model, "SpeakerPhoneMute,", @list);
	if (($count = @list) > 0)
	{	inst_wrn(4070, $Lnum, "SpeakerPhoneMute") 
			if (($VoiceProfile & 0x00000004) &&
				($VoiceProfile & 0x00400000) ||
				!defined($VoiceProfile));
	}
	elsif (defined($VoiceProfile))
	{	add_ref_err(3139, $Lnum, "SpeakerPhoneMute") 
				if (($VoiceProfile & 0x00000004) &&
					($VoiceProfile & 0x00400000) == 0) ;
	}

	@list = getPathKey("SpeakerPhoneUnMute,");
	# check for the AT-Command syntax
	CheckATCommands($model, "SpeakerPhoneUnMute,", @list);
	if (($count = @list) > 0)
	{	inst_wrn(4070, $Lnum, "SpeakerPhoneUnMute") 
			if (($VoiceProfile & 0x00000004) &&
				($VoiceProfile & 0x00400000) ||
				!defined($VoiceProfile));
	}
	elsif (defined($VoiceProfile))
	{	add_ref_err(3139, $Lnum, "SpeakerPhoneUnMute") 
				if (($VoiceProfile & 0x00000004) &&
					($VoiceProfile & 0x00400000) == 0) ;
	}

# Line record
	@list = getPathKey("LineSetRecordFormat,");
	# check for the AT-Command syntax
	CheckATCommands($model, "LineSetRecordFormat,", @list);
	if (($count = @list) > 0)
	{	inst_wrn(4071, $Lnum, "LineSetRecordFormat") 
			if (($VoiceProfile & 0x00000020) == 0 ||
				!defined($VoiceProfile));
	}
	elsif (defined($VoiceProfile))
	{	add_ref_err(3140, $Lnum, "LineSetRecordFormat") 
				if (($VoiceProfile & 0x00000020));
	}

	@list = getPathKey("LineSetPlayFormat,");
	# check for the AT-Command syntax
	CheckATCommands($model, "LineSetPlayFormat,", @list);
	if (($count = @list) > 0)
	{	inst_wrn(4071, $Lnum, "LineSetPlayFormat") 
			if (($VoiceProfile & 0x00000020) == 0 ||
				!defined($VoiceProfile));
	}
	elsif (defined($VoiceProfile))
	{	add_ref_err(3140, $Lnum, "LineSetPlayFormat") 
				if (($VoiceProfile & 0x00000020));
	}

	@list = getPathKey("StartPlay,");
	# check for the AT-Command syntax
	CheckATCommands($model, "StartPlay,", @list);
	if (($count = @list) > 0)
	{	inst_wrn(4071, $Lnum, "StartPlay") 
			if (($VoiceProfile & 0x00000020) == 0 ||
				!defined($VoiceProfile));
	}
	elsif (defined($VoiceProfile))
	{	add_ref_err(3140, $Lnum, "StartPlay") 
				if (($VoiceProfile & 0x00000020));
	}

	@list = getPathKey("StartRecord,");
	# check for the AT-Command syntax
	CheckATCommands($model, "StartRecord,", @list);
	if (($count = @list) > 0)
	{	inst_wrn(4071, $Lnum, "StartRecord") 
			if (($VoiceProfile & 0x00000020) == 0 ||
				!defined($VoiceProfile));
	}
	elsif (defined($VoiceProfile))
	{	add_ref_err(3140, $Lnum, "StartRecord") 
				if (($VoiceProfile & 0x00000020));
	}

	@list = getPathKey("StopPlay,");
	# check for the AT-Command syntax
	CheckATCommands($model, "StopPlay,", @list);
	if (($count = @list) > 0)
	{	inst_wrn(4071, $Lnum, "StopPlay") 
			if (($VoiceProfile & 0x00000020) == 0 ||
				!defined($VoiceProfile));
	}
	elsif (defined($VoiceProfile))
	{	add_ref_err(3140, $Lnum, "StopPlay") 
				if (($VoiceProfile & 0x00000020));
	}

	@list = getPathKey("StopRecord,");
	# check for the AT-Command syntax
	CheckATCommands($model, "StopRecord,", @list);
	if (($count = @list) > 0)
	{	inst_wrn(4071, $Lnum, "StopRecord") 
			if (($VoiceProfile & 0x00000020) == 0 ||
				!defined($VoiceProfile));
	}
	elsif (defined($VoiceProfile))
	{	add_ref_err(3140, $Lnum, "StopRecord") 
				if (($VoiceProfile & 0x00000020));
	}

	@list = getPathKey(",AbortPlay");
	&proc_voice_range2(@list, "AbortPlay");
	if (($count = @list) > 0)
	{	inst_wrn(4071, $Lnum, "AbortPlay") 
			if (($VoiceProfile & 0x00000020) == 0 ||
				!defined($VoiceProfile));
	}
	elsif (defined($VoiceProfile))
	{	add_ref_err(3140, $Lnum, "AbortPlay") 
				if (($VoiceProfile & 0x00000020));
	}

	@list = getPathKey(",TerminatePlay");
	&proc_voice_range2(@list, "TerminatePlay");
	if (($count = @list) > 0)
	{	inst_wrn(4071, $Lnum, "TerminatePlay") 
			if (($VoiceProfile & 0x00000020) == 0 ||
				!defined($VoiceProfile));
	}
	elsif (defined($VoiceProfile))
	{	add_ref_err(3140, $Lnum, "TerminatePlay") 
				if (($VoiceProfile & 0x00000020));
	}

	@list = getPathKey(",TerminateRecord");
	&proc_voice_range2(@list, "TerminateRecord");
	if (($count = @list) > 0)
	{	inst_wrn(4071, $Lnum, "TerminateRecord") 
			if (($VoiceProfile & 0x00000020) == 0 ||
				!defined($VoiceProfile));
	}
	elsif (defined($VoiceProfile))
	{	add_ref_err(3140, $Lnum, "TerminateRecord") 
				if (($VoiceProfile & 0x00000020));
	}

# wavedriver
	if ($VoiceProfile & 0x02000000)
	{
		@list = getPathKey("WaveDriver,BaudRate");
		if (($count = @list) > 0)
		{	inst_wrn(4071, $Lnum, "WaveDriver,BaudRate") 
				if (($VoiceProfile & 0x00000020) == 0 ||
					!defined($VoiceProfile));
		}
		elsif (defined($VoiceProfile))
		{	add_ref_err(3140, $Lnum, "WaveDriver, BaudRate") 
					if (($VoiceProfile & 0x00000020));
		}

		@list = getPathKey("WaveDriver,WaveHardwareID");
		if (($count = @list) > 0)
		{	inst_wrn(4071, $Lnum, "WaveDriver,WaveHardwareID") 
				if (($VoiceProfile & 0x00000020) == 0 ||
					!defined($VoiceProfile));
		}
		elsif (defined($VoiceProfile))
		{	add_ref_err(3140, $Lnum, "WaveDriver, WaveHardwareID") 
					if (($VoiceProfile & 0x00000020));
		}

		@list = getPathKey("WaveDriver,XformID");
		if (($count = @list) > 0)
		{	inst_wrn(4071, $Lnum, "WaveDriver,XformID") 
				if (($VoiceProfile & 0x00000020) == 0 ||
					!defined($VoiceProfile));
		}
		elsif (defined($VoiceProfile))
		{	add_ref_err(3140, $Lnum, "WaveDriver, XformID") 
					if (($VoiceProfile & 0x00000020));
		}

		@list = getPathKey("WaveDriver,XformModule");
		if (($count = @list) > 0)
		{	inst_wrn(4071, $Lnum, "WaveDriver,XformModule") 
				if (($VoiceProfile & 0x00000020) == 0 ||
					!defined($VoiceProfile));
		}
		elsif (defined($VoiceProfile))
		{	add_ref_err(3140, $Lnum, "WaveDriver, XformModule") 
					if (($VoiceProfile & 0x00000020));
		}
# baudrate
		@list = getPathKey(",VoiceBaudRate");
		if (($count = @list) == 0 && defined($VoiceProfile))
		{	add_ref_err(3142, $Lnum) 
					if (($VoiceProfile & 0x00080000));
		}
	}

# voice commands
	@list = getPathKey("VoiceAnswer,");
	# check for the AT-Command syntax
	CheckATCommands($model, "VoiceAnswer,", @list);
	if (($count = @list) > 0)
	{	inst_wrn(4072, $Lnum, "VoiceAnswer") 
			if (($VoiceProfile & 0x00000001) == 0 ||
				!defined($VoiceProfile));
	}
	elsif (defined($VoiceProfile))
	{	add_ref_err(3140, $Lnum, "VoiceAnswer") 
				if (($VoiceProfile & 0x00000001));
	}

	@list = getPathKey("VoiceToDataAnswer,");
	# check for the AT-Command syntax
	CheckATCommands($model, "VoiceToDataAnswer,", @list);
	if (($count = @list) > 0)
	{	inst_wrn(4072, $Lnum, "VoiceToDataAnswer") 
			if (($VoiceProfile & 0x00000001) == 0 ||
				!defined($VoiceProfile));
	}
	elsif (defined($VoiceProfile))
	{	add_ref_err(3140, $Lnum, "VoiceToDataAnswer") 
				if (($VoiceProfile & 0x00000001));
	}

	@list = getPathKey("VoiceDialNumberSetup,");
	# check for the AT-Command syntax
	CheckATCommands($model, "VoiceDialNumberSetup,", @list);
	if (($count = @list) > 0)
	{	inst_wrn(4072, $Lnum, "VoiceDialNumberSetup") 
			if (($VoiceProfile & 0x00000001) == 0 ||
				!defined($VoiceProfile));
	}
	elsif (defined($VoiceProfile))
	{	add_ref_err(3140, $Lnum, "VoiceDialNumberSetup") 
				if (($VoiceProfile & 0x00000001));
	}

# others ignore
	@list = getPathKey(",ForwardDelay");
	@list = getPathKey(",HandsetCloseDelay");
	@list = getPathKey("Voice");
	@list = getPathKey(",Voice");

}

#-------------------------------------------------------------
#
# Check if Enumerator and VoiceProfile present
#	for voice modem ("Voice" in the friendly name ?)
#
#-------------------------------------------------------------
sub
voice_minimal_keys
{
local($Lnum, $model) = @_;
local(@list, $line, $count, @tmp, @tmp2, $IsValid, @KeyValues);

	$IsValid = 1;

	@list = getPathKey(",Enumerator");
	$IsValid = 0 if (($count = @list) == 0);

	@tmp = ();
	foreach $line (@list) 
	{
		@tmp = split(',', $line, 4);
		@tmp2 = split(':', $tmp[2], 2);
		$L =  $tmp2[0];
		$T =  $tmp2[1];
	}

	@KeyValues = split(',', $tmp[3]);
	if (defined($KeyValues[0]) && $KeyValues[0] !~ /^serwave.vxd$/i)
	{
		inst_err(3107, $L);
	}

	if (proc_voice_profile($model) == 0)
	{	$IsValid = 0;	}

	if ($IsValid == 1)
	{
		$VoiceInstallList{uc $model} = 1;	#placeholder
	}
}


#-------------------------------------------------------------
#
# Check for the VoiceProfile settings for Rockwell chips
#
# Globals changed:
#
# Usage:
#
#-------------------------------------------------------------
sub
proc_voice_profile
{
local($model) = @_;
local(@list, $line, $count, @tmp, @tmp2, $VoiceProfile);

	#	instalation properties: VoiceProfile
	undef($InstallProps{uc $model}->[3]);

	@list = getPathKey(",VoiceProfile", 1);
	return 0 if (($count = @list) == 0);

	foreach $line (@list) 
	{
		@tmp = split(',', $line, 4);
		@tmp2 = split(':', $tmp[2], 2);
		$L =  $tmp2[0];
		$T =  $tmp2[1];
		return 1 if ($T !~ /^1$/);
	}

	@bytes = split(',', $tmp[3]);
	if (($count = @bytes) != 4)
	{
#		inst_err("VoiceProfile should have one DWORD.", $L);
		inst_err(3099, $L);
		return 1;
	}

	local(@dwords) = ();
	@dwords = get_dwords(4, @bytes);

	if (defined($dwords[0]))
	{
		$VoiceProfile	= hex($dwords[0]);

		inst_wrn(4073, $L)
			if (($VoiceProfile & 0x02000000) == 0);

		inst_wrn(4074, $L) 
			if (($VoiceProfile & 0x00000001) == 0);

		if ( !($VoiceProfile & 0x00080000) ||		# SetBaudBeforeWave
			 !($VoiceProfile & 0x00000200)			# ForceBlindDial
			)
		{	inst_wrn(4041, $L);	}
		if ($VoiceProfile & 0x00000100)			# Modem speaker volume can be changed with multimedia mixer.
		{	inst_wrn(4052, $L);	}

		$InstallProps{uc $model}->[3] = $VoiceProfile;
	}

	return 1;
}

sub
proc_speaker_specs
{
local($model) = @_;
local(@list, $line, $count, @tmp, @tmp2, $VoiceProfile);

	@list = getPathKey(",SpeakerPhoneSpecs", 1);
	return 0 if (($count = @list) == 0);

	foreach $line (@list) 
	{
		@tmp = split(',', $line, 4);
		@tmp2 = split(':', $tmp[2], 2);
		$L =  $tmp2[0];
		$T =  $tmp2[1];
		return 1 if ($T !~ /^1$/);
	}

	@bytes = split(',', $tmp[3]);
	if (($count = @bytes) != 16)
	{
#		inst_err("SpeakerPhoneSpecs should have 16 bytes.", $L);
		inst_err(3137, $L);
	}

	return 1;
}



#-------------------------------------------------------------
#
# Get the Voice special keys AbortPlay, TerminatePlay, TerminateRecord
#
# Globals changed:
#
# Usage:
#
#-------------------------------------------------------------
sub
proc_voice_range2
{
local(@list, $command) = @_;
local($this_val);

	$this_val = &join_strval(@list);

	inst_wrn(4040, $L, $command) if ($this_val =~ /AT<CR>$/i);
}
	
#-------------------------------------------------------------
#
# Get the VoiceView keys 
#
# Globals changed:
#
# Usage:
#
#-------------------------------------------------------------

sub
proc_vv
{
local($Lnum, $model) = @_;

	@list = getPathKey("MonitorVoiceViewOn,");
	# check for the AT-Command syntax
	CheckATCommands($model, "MonitorVoiceViewOn,", @list);

	@list = getPathKey("MonitorVoiceViewOff,");
	# check for the AT-Command syntax
	CheckATCommands($model, "MonitorVoiceViewOff,", @list);

	@list = getPathKey("MonitorVoice");
	@list = getPathKey(",RunOnce");
	@list = getPathKey(",VoiceView");
	if (($count = @list) > 0)
	{
#		add_ref_wrn("$model: VoiceView may not be supported in future.", $Lnum);
		add_ref_wrn(4015, $Lnum, $model);
		return;
	} 
}

#-------------------------------------------------------------
#
# Get the Responses keys out - do minimal processing.
#
# Globals changed:
#
# Usage:
#
#-------------------------------------------------------------
sub
proc_resp
{
local($Lnum, $model, @CurrentResponses) = @_;
local(@Properties, $MaxDTE, $MaxDCE, $MaxSpeed);
local($RefResponse, @ResponseBinary, $ResponseString, $ResponseLine, $IsNumericResponse);
local($DCESpeedErrors,$DTESpeedErrors);
local(%MinimalNumericResponse, $NumericValue, $NumericRespErrors, %MinimalVerboseResponse);
local($CountMinimalNumeric, $CountMinimalVerbose);
local($CountNumericResponse, $CountVerboseResponse, @InitSettings);
local(%SectionPatterns, $ResponsePattern, %PatternSpeeds);
local(%GlobalResponseStrings, $StrippedResponses);
local(%HashDCESpeeds, %HashDTESpeeds);
local(@ExceptStrippedResponses);

	%MinimalNumericResponse = (	0 => 0, 2 => 0, 3 => 0, 4 => 0,
								6 => 0, 7 => 0, 8 => 0);
	%MinimalVerboseResponse = (	"OK" => "OK", "CONNECT" => "CONNECT", "RING" => "RING", 
								"NOCARRIER" => "NO CARRIER", "ERROR" => "ERROR",
								"NODIALTONE" => "NO DIALTONE", "BUSY" => "BUSY", 
								"NOANSWER" => "NO ANSWER");
	@ExceptStrippedResponses	= ( 
							'^(?i)NAME *= *$',      #required for CID
							'^(?i)TIME *= *$',      #required for CID
							'^(?i)CALR *= *$',
							'^(?i)NMBR *= *$',      #required for CID
							'^(?i)MESG *= *$',      #required for CID
							'^(?i)DATE *= *$',      #required for CID
							'^(?i)VCON *$',
							'^(?i)RING *1$',
							'^(?i)RING *2$',
							'^(?i)RING *3$',
							'^(?i)RING *A$',
							'^(?i)RING *B$',
							'^(?i)RING *C$',
							'^(?i)<cr>$',
							'^(?i)<lf>$',
							'^(?i)<cr><lf>$',
							);

	$CountMinimalNumeric = keys %MinimalNumericResponse;
	$CountMinimalVerbose = keys %MinimalVerboseResponse;

	%GlobalResponseStrings = ();

	if (($count = @CurrentResponses) < 1)
	{
#		add_ref_err("$model does not have any Responses.", $Lnum);
		add_ref_err(3060, $Lnum, $model);
	}

	@Properties = @{${$InstallProps{uc $model}}[0]};
	@InitSettings = @{${$InstallProps{uc $model}}[2]};
	$MaxDTE	= $Properties[6];
	$MaxDCE	= $Properties[7];
	$MaxSpeed = ($MaxDCE < $MaxDTE) ? $MaxDTE : $MaxDCE;
	$DCESpeedErrors = 0;
	$DTESpeedErrors = 0;
	%HashDCESpeeds = ();
	%HashDTESpeeds = ();
	$CountNumericResponse = 0;
	$CountVerboseResponse = 0;
	$StrippedResponses = 0;

	foreach $RefResponse (@CurrentResponses)
	{
		local($SearchString, $DuplicateLine, $DupRefStructResp);

		$ResponseString	= $AllQuotes{$$RefResponse[0]};
		@ResponseBinary = @{$$RefResponse[2]};
		$ResponseLine	= $$RefResponse[1];
		$IsNumericResponse = ${$$RefResponse[3]}[0];
		$NumericValue = ${$$RefResponse[3]}[1];
		$ResponsePattern = ${$$RefResponse[4]}[0];

		$SearchString = $ResponseString;
		$SearchString =~ s/\s$//;
		if(defined($GlobalResponseStrings{uc $SearchString}))
		{
			($DuplicateLine, $DupRefStructResp) = 
				@{$GlobalResponseStrings{uc $SearchString}};
			if ($DupRefStructResp->[0] == $ResponseBinary[0] and 
				$DupRefStructResp->[1] == $ResponseBinary[1] and 
				$DupRefStructResp->[2] == $ResponseBinary[2] and 
				$DupRefStructResp->[3] == $ResponseBinary[3])
			{
				inst_wrn(4064, $ResponseLine, $SearchString, $DuplicateLine);
			}
		}
		$GlobalResponseStrings{uc $SearchString} = 
						[$ResponseLine, [@ResponseBinary]];

				# assure minimal numeric responses
		if ($IsNumericResponse != 0)
		{
			$CountNumericResponse++;
			delete($MinimalNumericResponse{$NumericValue});
		}
		else
		{

			$CountVerboseResponse++;
			$SearchString = $ResponseString;

            if (($ResponseBinary[0] == 0x96)||($ResponseBinary[0] == 0x97)||
                ($ResponseBinary[0] == 0x95)||($ResponseBinary[0] == 0x94)||
                ($ResponseBinary[0] == 0x93))
            {
                local($checkbit,$shiftamount);
                $shiftamount = ($ResponseBinary[0] - 0x93) + 4;
                $checkbit = 1;
                $checkbit = $checkbit << $shiftamount;

                $CallerIDCheck{uc $model} =  $CallerIDCheck{uc $model} | $checkbit;
            }
            

			# check if stripped responses are used (only for verbose)
			if ($StrippedResponses == 0)
			{
				my ($Exception, $Excepted);
				
				$Excepted = 0;
				foreach $Exception (@ExceptStrippedResponses)
				{
					if ($SearchString =~ /$Exception/ )
					{
						$Excepted = 1;
						last;
					}
				}
					 
				if ($Excepted == 0 and 
					$SearchString !~ /^<cr><lf>/i and 
					$SearchString !~ /<cr><lf>$/i)
				{
					$StrippedResponses = 1;
				}
			}

			$SearchString =~ s/<cr>//gi;
			$SearchString =~ s/<lf>//gi;
			$SearchString =~ s/\s+//gi;
			
			delete($MinimalVerboseResponse{uc $SearchString});
		}

				# check speeds against properties
		if ($ResponseBinary[2] > $MaxSpeed)		# first dword could be either DTE or DCE
		{
			# log error if in advisory mode
			# add_ref_wrnX(4020, $ResponseLine, "Model $model", $MaxSpeed);
			$DCESpeedErrors++;
			$HashDCESpeeds{$ResponseLine} = 1;
		}
		if ($ResponseBinary[3] > $MaxDTE)
		{
			# log error if in advisory mode
			# add_ref_wrnX(4051, $ResponseLine, "Model $model", $MaxDTE);
			$DTESpeedErrors++;
			$HashDTESpeeds{$ResponseLine} = 1;
		}
	}

	# if ($AdvisoryMode == 0)	
	# log only one error for speed if NOT in advisory mode
	{
		local($RefSpeedError);

		if ($DCESpeedErrors > 0)
		{
			if (!defined($ErrSpeedReport{"DCE".$MaxSpeed}))
			{
				$ErrSpeedReport{"DCE".$MaxSpeed} = [\%HashDCESpeeds, 1, $MaxSpeed, $Lnum];
			}
			else
			{
				$RefSpeedError = $ErrSpeedReport{"DCE".$MaxSpeed};
				foreach $k (keys %HashDCESpeeds)
				{
					${$$RefSpeedError[0]}{$k} = 1;
				}
				$$RefSpeedError[1]++;
			}
		}

		if ($DTESpeedErrors > 0)
		{
			if (!defined($ErrSpeedReport{"DTE".$MaxDTE}))
			{
				$ErrSpeedReport{"DTE".$MaxDTE} = [\%HashDTESpeeds, 1, $MaxDTE, $Lnum];
			}
			else
			{
				$RefSpeedError = $ErrSpeedReport{"DTE".$MaxDTE};
				foreach $k (keys %HashDTESpeeds)
				{
					${$$RefSpeedError[0]}{$k} = 1;
				}
				$$RefSpeedError[1]++;
			}
		}
	}

		# check for minimal numeric responses
	$NumericRespErrors = (keys %MinimalNumericResponse);
	$Message = join(', ', sort keys %MinimalNumericResponse);
	add_ref_err(3123, $Lnum, $NumericRespErrors, $Message) 
			if ($NumericRespErrors > 0);

		# check for minimal verbose responses
	$VerboseRespErrors = (keys %MinimalVerboseResponse);
	$Message = join(', ', sort values %MinimalVerboseResponse);
	add_ref_err(3127, $Lnum, $VerboseRespErrors, $Message) 
			if ($VerboseRespErrors > 0);

		# check if responses (numeric/verbose) agree with Init V0/V1
	if (defined($InitSettings[0]))
	{
		if ($InitSettings[0] == 1)	# verbose response
		{ 
			add_ref_err(3124, $Lnum) if ($CountVerboseResponse <= $CountMinimalVerbose);
			add_ref_wrn(4047, $Lnum) if ($CountVerboseResponse < $CountNumericResponse);
		}
		else
		{ 
			add_ref_err(3125, $Lnum) if ($CountNumericResponse <= $CountMinimalNumeric); 
			add_ref_wrn(4048, $Lnum) if ($CountVerboseResponse > $CountNumericResponse);
		}
	}

		# padded responses prefered
	if ($StrippedResponses == 1)
	{
		add_ref_err(3104, $Lnum);
	}

		# because responses preferred, <cr>, <lf> must not appear
	if (defined($GlobalResponseStrings{"<CR>"}))
	{
		inst_wrn(4050, $GlobalResponseStrings{"<CR>"}->[0]);
	}
	elsif ($StrippedResponses == 1)
	{
		add_ref_err(3129, $Lnum, "&ltcr&gt");
	}

	if (defined($GlobalResponseStrings{"<LF>"}))
	{
		inst_wrn(4050, $GlobalResponseStrings{"<LF>"}->[0]);
	}
	elsif ($StrippedResponses == 1)
	{
		add_ref_err(3129, $Lnum, "&ltlf&gt");
	}
}

#############################################################################
#
#	pattern analysis for modem responses
#
#############################################################################

sub
CheckResponsePatterns
{
	local($sec_name, $RefHKR);
	local(%SectionPatterns, %PatternSpeeds);
	local($RefResponse, @ResponseBinary, $ResponseString, $ResponseLine, $IsNumericResponse, 
			$ResponsePattern);
	local(@AddRegResponses);
	local($RefErrResponse, $PatternKey);
	local($Lnum);

	return if ($AdvisoryMode == 0);

	while (($sec_name, $RefHKR) = each %AddRegList)
	{
		$Lnum = $SecStart{$sec_name};
		@AddRegResponses = @{$$RefHKR[1]};

		%PatternSpeeds = ();
		%SectionPatterns = ();

			# get patterns
		foreach $RefResponse (@AddRegResponses)
		{
			$ResponseString	= $AllQuotes{$$RefResponse[0]};
			@ResponseBinary = @{$$RefResponse[2]};
			$ResponseLine	= $$RefResponse[1];
			$ResponsePattern = ${$$RefResponse[4]}[0];

			# build the section patterns
			if (defined($ResponsePattern))
			{
				if ($ResponseString =~ /$ResponsePattern/i)		# it should be
				{	
					$PatternSpeeds{uc $1} = 0;
					if (!defined($SectionPatterns{$ResponsePattern}))
					{	$SectionPatterns{$ResponsePattern} = [${$$RefResponse[4]}[1], ${$$RefResponse[4]}[2], uc $1];	}
					else
					{	push(@{$SectionPatterns{$ResponsePattern}}, uc $1);	}
				}
			}
		}

		# analyze response patterns and see if any speeds are missing
		foreach $ResponsePattern (keys %SectionPatterns)
		{	
			local(@ReferencedSpeeds, %CopyPatternSpeeds, $MissingSpeeds);

			@ReferencedSpeeds = splice(@{$SectionPatterns{$ResponsePattern}},2);
			%CopyPatternSpeeds = %PatternSpeeds;

			foreach $ASpeed (@ReferencedSpeeds)
			{
				delete($CopyPatternSpeeds{$ASpeed});
			}
					# for VFC responses the speeds can be only 14.4 - 28.8 (connect)
					# and for carrier messages 14.4 to 115200

			if ( ${$SectionPatterns{$ResponsePattern}}[0] =~ /\bVFC\b/i )
			{
				local($LowerLimit, $UpperLimit, $IntSpeed);
				$LowerLimit = 14400;
				$UpperLimit = (${$SectionPatterns{$ResponsePattern}}[1] == 0x01) ? 115200 : 28800;
				foreach $ASpeed (keys %PatternSpeeds)
				{
					$IntSpeed = 0;
					if ($ASpeed =~ /(\d+)K/i)
					{	$IntSpeed = int($1) * 1000;	}
					elsif ($ASpeed =~ /(\d+),(\d\d\d)/)
					{	$IntSpeed = int($1.$2);	}
					elsif ($ASpeed =~ /(\d+)([TR]X|)\/(\d+)([TR]X|)/i)
					{	$IntSpeed = (int($1) > int($3)) ? int($1) : int($3);	}
					elsif ($ASpeed =~ /(\d+)/)
					{	$IntSpeed = int($1);	}
					if ($IntSpeed < $LowerLimit || $IntSpeed > $UpperLimit)
					{	delete($CopyPatternSpeeds{$ASpeed});	}
				}
			}

			if ($count = (keys %CopyPatternSpeeds))
			{
				local($MissingSpeeds);
				$MissingSpeeds = join(', ', sort NumericSpeed keys %CopyPatternSpeeds);

				${$SectionPatterns{$ResponsePattern}}[0] =~ s/</&lt/g;
				${$SectionPatterns{$ResponsePattern}}[0] =~ s/>/&gt/g;
				add_ref_wrnX(4049, $Lnum, ${$SectionPatterns{$ResponsePattern}}[0], $MissingSpeeds);
			}
		}
	}
}

#############################################################################
sub
NumericSpeed
{  local ($ASpeed, $BSpeed);

  $ASpeed = $a;
  $BSpeed = $b;

	$AIntSpeed = 0;
	if ($ASpeed =~ /(\d+)K/i)
	{	$AIntSpeed = int($1) * 1000;	}
	elsif ($ASpeed =~ /(\d+),(\d\d\d)/)
	{	$AIntSpeed = int($1.$2);	}
	elsif ($ASpeed =~ /(\d+)([TR]X|)\/(\d+)([TR]X|)/i)
	{	$AIntSpeed = (int($1) > int($3)) ? int($1) : int($3);	}
	elsif ($ASpeed =~ /(\d+)/)
	{	$AIntSpeed = int($1);	}

	$BIntSpeed = 0;
	if ($BSpeed =~ /(\d+)K/i)
	{	$BIntSpeed = int($1) * 1000;	}
	elsif ($BSpeed =~ /(\d+),(\d\d\d)/)
	{	$BIntSpeed = int($1.$2);	}
	elsif ($BSpeed =~ /(\d+)([TR]X|)\/(\d+)([TR]X|)/i)
	{	$BIntSpeed = (int($1) > int($3)) ? int($1) : int($3);	}
	elsif ($BSpeed =~ /(\d+)/)
	{	$BIntSpeed = int($1);	}

	$AIntSpeed <=> $BIntSpeed;  #compare as numbers
}


#############################################################################
sub
AnalyseResponse
{
local($RefResponse) = @_;
local($QuoteIndex, $RespLine, @ResponseStruct);
local($OptionBits, $ResponseString, $NumericResponse, $MatchingState, $SearchResp, $KeywordFound);
local($GoodState);

	$QuoteIndex = $$RefResponse[0];
	$ResponseString = $AllQuotes{$QuoteIndex};
	return if (!defined($ResponseString));

	$RespLine	= $$RefResponse[1];
	@ResponseStruct = @{$$RefResponse[2]};

	#	sanity checking
	# DTE speed reported, put up an warning
	inst_wrnX(4045, $RespLine) if ($ResponseStruct[3] != 0);

	# First to bytes are OK ?
	#	Response state : 0x00 to 0x08 (data/fax) 
	#					 0x09 to 0x10 (voice)
	#					 0x18 to 0x1b and 0x91 to 0x97
	#	Option Bits	 : 0x01 | 0x02 | 0x08

	$OptionBits = ~(0x01 | 0x02 | 0x08);

	$GoodState = 1;
	if ( !(  $ResponseStruct[0] <= 0x10 || 
			($ResponseStruct[0] >= 0x18 && $ResponseStruct[0] <= 0x1d) ||
			($ResponseStruct[0] >= 0x91 && $ResponseStruct[0] <= 0x97)
		  ))
	{
		inst_err(3114, $RespLine);
		$GoodState = 0;
	}

	inst_err(3115, $RespLine)	if ($ResponseStruct[1] & $OptionBits);

	if ($GoodState && $ResponseStruct[1] != 0 && $ResponseStruct[0] != 0x01 &&  $ResponseStruct[0] != 0x02)
	{
			# bad usage of negotiated options for the response state
		inst_wrn(4060, $RespLine, $ResponseStateComment{$ResponseStruct[0]});
	}


	if ( $GoodState && ($ResponseStruct[2] != 0 || $ResponseStruct[3] != 0) && 
		  $ResponseStruct[0] != 0x01 &&  $ResponseStruct[0] != 0x02)
	{
			# bad usage of speed for the response state
		inst_wrn(4061, $RespLine, $ResponseStateComment{$ResponseStruct[0]});
	}

	#	verify if numeric response
	$NumericResponse = 0;
	if ($ResponseString =~ /^(\d+)/)
	{
		$NumericResponse	= 1;
		#	check for usual values 
		if (defined($MatchingState = $NumRespMatching{int($1)}) &&
			$MatchingState != $ResponseStruct[0])
		{	inst_wrn(4056, $RespLine);	}
	}
	$$RefResponse[3] = [$NumericResponse, int($1)] ;
	return if ($NumericResponse == 1);

	# check verbose responses
		# remove spaces <cr>, <lf>, <ff>
	$SearchResp = $ResponseString;
	if ($SearchResp !~ /^<(cr|lf|ff)>$/i &&
		$SearchResp !~ /^<ff><cr>$/i )
	{
		$SearchResp =~ s/<cr>/ /gi;
		$SearchResp =~ s/<lf>/ /gi;
		$SearchResp =~ s/<ff>/ /gi;
	}
	$SearchResp =~ s/^\s+//;
	$SearchResp =~ s/\s+$//;

				# check if ReponseState is correct (try to find a keyword in response string)
	$KeywordFound = 0;
	if ($GoodState and $AdvisoryMode == 1)
	{
		foreach $RefArray (@{$ResponseStateLookup{$ResponseStruct[0]}})
		{
			if ($SearchResp =~ /$$RefArray[0]/i)
			{
				$KeywordFound++;
				last;
			}
		}
		if ($KeywordFound == 0)
		{	
			inst_wrnX(4057, $RespLine, $ResponseStruct[0], $ResponseStateComment{$ResponseStruct[0]});
#			printf "Wrong State: 0x%02x %s\n", $ResponseStruct[0], $SearchResp;
		}
	}

			# The reverse, interpret the response and suggest the true response state
	if ($AdvisoryMode == 1)	# check this only if in advisory mode
	{
		my	@PossibleStates = ();
		my	($IsInList, $PossCount);

		$IsInList = 0;
		while (($StateKey, $RefLookup) = each %ResponseStateLookup)
		{
			$KeywordFound = 0;
			foreach $RefArray (@{$RefLookup})
			{
				if ($SearchResp =~ /$$RefArray[0]/i)
				{
					$KeywordFound++;
					last;
				}
			}
			if ($KeywordFound != 0)
			{
				push(@PossibleStates, $StateKey);
				if ($StateKey == $ResponseStruct[0])
				{	$IsInList = 1;	}
			}
		}
		if ($IsInList == 0 && (($PossCount = @PossibleStates) != 0))
		{
			my $ListOfStates = "";
			my $StateFound = "";

			$ListOfStates	= '"'.$ResponseStateComment{$PossibleStates[0]}.'"';
			shift(@PossibleStates);
			foreach $StateFound (@PossibleStates)
			{
				$ListOfStates	= $ListOfStates.", ".
									'"'.$ResponseStateComment{$StateFound}.'"';
			}
			inst_wrnX(4054, $RespLine, $ListOfStates,
									$ResponseStateComment{$ResponseStruct[0]});
#			printf "Suggested state 0x%02x: 0x%02x %s\n", $StateKey, $ResponseStruct[0], $SearchResp;
		}
	}


			# Negotiated options
	if (($AdvisoryMode == 1) and
		($ResponseStruct[1] != 0 || $ResponseStruct[0] == 0x01 ||  $ResponseStruct[0] == 0x02))
	{
		foreach $NegotiatedKey (keys %NegotiatedOptionLookup)
		{
			local($CheckCompression);
			$KeywordFound = 0;
			$CheckCompression = 1;		# 0 if found at least one keyword not dependent
										# 1 if only dependent on compression
			foreach $RefArray (@{$NegotiatedOptionLookup{$NegotiatedKey}})
			{
				if ($SearchResp =~ /$$RefArray[0]/i)
				{	
					if ($$RefArray[1] & $ForceNotSet)
					{
						$KeywordFound = 0;
						last;
					}
							# see if state is dependent on compression settings
					if ( ($$RefArray[1] & $DependentOnCompression) == 0 )
					{	$CheckCompression = 0;	}

					$KeywordFound++;
				}
			}

				# check if Negotiated Options are correct (try to find a keyword in response string)
			if (($ResponseStruct[1] & $NegotiatedKey) && $KeywordFound == 0)
			{
				inst_wrnX(4058, $RespLine, $NegotiatedOptionComment{$NegotiatedKey}, $NegotiatedKey);
#				printf "Wrong Option (1): 0x%02x $SearchResp\n", $NegotiatedKey;
			}

			# cross check 
			if ($KeywordFound != 0)		# something found
			{
						# see if option set
				if ($CheckCompression != 0)
				{
					# option not set is OK
					if ($ResponseStruct[1] & $NegotiatedKey)
					{
						if ($ResponseStruct[1] & 0x01)
									# if option is set, then compression should be set
						{				# put an warning
							inst_wrnX(4046, $RespLine);
#							printf "Dependence: 0x%02x $SearchResp\n", $NegotiatedKey;
						}
						else			# like keyword not found
						{
							inst_wrnX(4058, $RespLine, $NegotiatedOptionComment{$NegotiatedKey}, $NegotiatedKey);
#							printf "Wrong Option (2): 0x%02x $SearchResp\n", $NegotiatedKey;
						}
					}
				}
				elsif (($ResponseStruct[1] & $NegotiatedKey) == 0)	# option not set, keyword present
				{	inst_wrnX(4059, $RespLine, $NegotiatedOptionComment{$NegotiatedKey});
#					printf "Missing Option: 0x%02x $SearchResp\n", $NegotiatedKey;
				}
			}
		}
	}

	# test if speed given in response equals that given in dwords
	if (($AdvisoryMode == 1) and
		($ResponseStruct[0] == 0x01 ||  $ResponseStruct[0] == 0x02))
	{
		foreach $i (2,3)
		{
			next if ($ResponseStruct[$i] == 0);

			# verify the speed given in dword equals that from response
			$SpeedString = sprintf "%d", $ResponseStruct[$i];
			local (@SpeedFormats);

			push(@SpeedFormats, $SpeedString);	# normal format %d
			$SpeedString =~ /(.*)(\d\d\d)$/;	# comma format %d,%d
			push(@SpeedFormats, "$1,$2");
			if ($ResponseStruct[$i] >= 64000)
			{
				$SpeedString = sprintf "%d", int($ResponseStruct[$i]/1000);
				push(@SpeedFormats, $SpeedString."K");	# K format %dK (like in 112K)
			}

			$KeywordFound = 0;
			foreach $SpeedString (@SpeedFormats)
			{
				if ($SearchResp =~ /$SpeedString/i)
				{	
					$KeywordFound = 1;
					last;
				}
			}

			if ($KeywordFound == 0)
			{
				inst_wrnX(4062, $RespLine, $ResponseStruct[$i]);
#				printf "Wrong Speed: %d in $SearchResp\n", $ResponseStruct[$i];
			}
		}

		# missing speed report (dwords are zero while some speed reported in response)
		if ($ResponseStruct[2] == 0 && $ResponseStruct[3] == 0)
		{
			local($SpeedValue);

			$SpeedValue = 0;
			# try to find the speed in 3 formats 
			# \d+ (normal), \d+,\d\d\d, \d+K
			if ($SearchResp =~ /(\d+)K/i)
			{	$SpeedValue = int($1) * 1000;	}
			elsif ($SearchResp =~ /(\d+),(\d\d\d)/)
			{	$SpeedValue = int($1.$2);	}
			elsif ($SearchResp =~ /(\d+)([TR]X|)\/(\d+)([TR]X|)/)
			{	$SpeedValue = (int($1) > int($3)) ? int($1) : int($3);	}
			elsif ($SearchResp =~ /(\d+)/)
			{	$SpeedValue = int($1);	}
			if ($SpeedValue != 0 && defined($ResponseSpeeds{$SpeedValue}))
			{
				inst_wrnX(4063, $RespLine, $SpeedValue);
#				printf "Missing Speed: %d in $SearchResp\n", $SpeedValue;
			}
		}
	}

	# find response pattern
	if ( ($AdvisoryMode == 1) and
		 ($ResponseStruct[0] == 0x01 ||  $ResponseStruct[0] == 0x02) && 
		 ($ResponseStruct[2] != 0 || $ResponseStruct[3] != 0)
		)
	{
		local($SpeedFormat);
		$SpeedFormat = '\d+(?:(?:,\d\d\d)|(?:K\b)|(?:TX\/\d+RX)|(?:\/\d+)|)';	
									# without ?: brackets are numbered (and usege below would be wrong)
		# find the speed and keep the rest
		if ( $ResponseString =~ /^(\D*)($SpeedFormat)(.*)$/i )
		{	local($beginPattern, $endPattern, $FinalPattern);
			$beginPattern = $1;
			$endPattern = $3;
			$FinalPattern = str2regex(uc $beginPattern)."($SpeedFormat)".str2regex(uc $endPattern);
			$$RefResponse[4] = [$FinalPattern, $beginPattern."<speed>".$endPattern, $ResponseStruct[0]];
		}
	}
}

# translate a string in a regular expression replacing special character and spaces
sub
str2regex
{
local($string)=@_;
local(@metachars);

	@metachars = ('\/', '\^', '\.', '\$', '\|', '\(', '\)', '\[', '\]', '\*', '\+', '\?', '\{', '\}', 
					'\-', '\,', '\<', '\>');

	$string =~ s/\\/\\/g;		# \ with \\
	foreach $specchar (@metachars)
	{	$string =~ s/$specchar/$specchar/g;
	}

	$string =~ s/\s+/ \*/g;		# \ with \\

	return $string;
}

sub
parse_response
{
local($StrResp, $BinResp) = @_;
local($QuoteStr, $QuoteIndex, $ReturnArray);

	@tmp = split(',', $StrResp, 2);
	$QuoteStr = $tmp[1];

	@tmp = split(',', $BinResp, 2);
	@tmp2 = split(':', $tmp[0], 2);
	$L =  $tmp2[0];
	$T =  $tmp2[1];
	if($QuoteStr !~ /^__QUOTED_STR_\(([0-9]*)\)__$/)
	{
#		inst_err("Expect &ltKey&gt to be a quoted string.", $L);
		inst_err(3075, $L);
		return undef;
	} else {
		# get the quoted string.
		$QuoteIndex = $1;
		if($AllQuotes{$QuoteIndex} =~ /^$/)
		{
#			inst_err("&ltKey&gt cant be empty string.", $L);
			inst_err(3076, $L);
			return undef;
		}
		$tmp[1] =~ s/[\s\t]//g; # get rid of all white spaces for efficient comparison later!
	}
	if ($T !~ /^1$/)
	{
#		inst_err("Expect response to be in binary.", $L);
		inst_err(3077, $L);
		return undef;
	}

	local(@dwords, @bytes, $count);
	@bytes = split(',', $tmp[1]);
	if ( ($count = @bytes) != 10)
	{
		inst_err(3109, $L);
		return undef;
	}

	@dwords = get_dwords(1, splice(@bytes, 0, 2));
	@dwords = (@dwords, get_dwords(4, @bytes) );
	local(@ResponseStruct, $dw);
	@ResponseStruct = ();
	foreach $dw (@dwords)
	{	push(@ResponseStruct, hex($dw));	}

	$ReturnArray = [$QuoteIndex, $L, \@ResponseStruct];

	AnalyseResponse($ReturnArray);		# adds flag if numeric response

	$ReturnArray;
}

#-------------------------------------------------------------
#
# This sub looks at the modem's properties and decides the	'
# relevent Settings keys it must find and validate.
#
# Globals changed:
#
# Usage:
#
#-------------------------------------------------------------
sub
proc_propset
{
local($Lnum, $model) = @_;
local(@tmp, @tmp2);

	@list = getPathKey(",Properties",1);
	if (($count = @list) < 1)
	{
#		add_ref_err("$model does not have any Properties.", $Lnum);
		add_ref_err(3061, $Lnum, $model);
		return;
	} elsif (($count = @list) > 1)
	{
#		add_ref_err("$model has more than one \"Properties\" entry.", $Lnum);
		add_ref_err(3062, $Lnum, $model);
		return;
	}

	foreach $line (@list) 
	{
		@tmp = split(',', $line, 4);
		@tmp2 = split(':', $tmp[2], 2);
		$L =  $tmp2[0];
		$T =  $tmp2[1];
		return if ($T !~ /^1$/);	# dont know what to do with binaries!!!!
	}

	@bytes = split(',', $tmp[3]);
	if (($count = @bytes) != 32)
	{
#		inst_err("Properties should have 8 DWORDS.", $L);
		inst_err(3078, $L);
		return;
	}
	local($PropLine, @dwords);
	@dwords = get_dwords(4, @bytes);

	if (!defined($InstallProps{uc $model}))
	{
		$InstallProps{uc $model} = [];
	}

	local(@PropStruct, $dw);
	@PropStruct = ();
	foreach $dw (@dwords)
	{	push(@PropStruct, hex($dw));	}
	${$InstallProps{uc $model}}[0] = \@PropStruct;

	$PropLine = $L;
	&proc_callsetup($dwords[1], $Lnum, $PropLine, $model);
	&proc_inact($dwords[2], $Lnum, $PropLine, $model);
	&proc_spvol($dwords[3], $Lnum, $model);
	&proc_spmode($dwords[4], $Lnum, $model);
	&proc_mdop($dwords[5], $Lnum, $model);
}

#-------------------------------------------------------------
#
# This sub looks at the modem's DCB structure	'
#
#-------------------------------------------------------------
sub
proc_DeviceType
{
local($Lnum, $model) = @_;
local(@tmp, @tmp2, @list);
local($DeviceType, $RefAddregList);

	$DeviceType = 0;
	@list = getPathKey(",DeviceType");
	if (($count = @list) > 1)
	{
		add_ref_err(3094, $Lnum, $model);
	}
	elsif ($count == 0)
	{
		add_ref_err(3095, $Lnum, $model);
	}
	else
	{
		foreach $line (@list) 
		{
			@tmp = split(',', $line, 4);
			@tmp2 = split(':', $tmp[2], 2);
			$L =  $tmp2[0];
			$T =  $tmp2[1];
		}

		@bytes = split(',', $tmp[3]);
		if (($count = @bytes) > 1)
		{
			inst_err(3096, $L);
		}
		if (defined($bytes[0]))
		{
			$bytes[0] =~ s/^[\s\t]//;
			$bytes[0] =~ s/[\s\t]$//;
			if ($bytes[0] !~ /^[a-fA-F0-9][a-fA-F0-9]$*/ )
			{
				inst_err(3080, $L);
			}
			$DeviceType	= hex($bytes[0]);
			if ($DeviceType > 5)
			{
				inst_err(3097, $L);
			}
		}
	}

	$RefAddregList		= ${$InstList{$model}}{'ADDREG'};
	if (defined($RefAddregList))
	{	push(@{$RefAddregList},$DeviceType);	}

}

#-------------------------------------------------------------
#
# This sub looks at the modem's DCB structure	'
#
#-------------------------------------------------------------
sub
proc_DCB
{
local($Lnum, $model) = @_;
local(@tmp, @tmp2, @list);
local($RefAddregList);

	@list = getPathKey(",DCB",1);
	if (($count = @list) > 1)
	{
#		add_ref_err("$model has more than one \"DCB\" entry.", $Lnum);
		add_ref_err(3092, $Lnum, $model);
		return;
	}
	
	return if ($count == 0);

	foreach $line (@list) 
	{
		@tmp = split(',', $line, 4);
		@tmp2 = split(':', $tmp[2], 2);
		$L =  $tmp2[0];
		$T =  $tmp2[1];
		return if ($T !~ /^1$/);
	}

	@bytes = split(',', $tmp[3]);
	if (($count = @bytes) != 26)
	{
		inst_err(3093, $L);
	}

	local(@dwords) = ();
	@dwords = get_dwords(4, splice(@bytes, 0, 12));
	@dwords = (@dwords, get_dwords(2, splice(@bytes, 0, 6)) );
	@dwords = (@dwords, get_dwords(1, @bytes));

	if (!defined($InstallProps{uc $model}))
	{
		$InstallProps{uc $model} = [];
	}

	local(@DCBStruct, $dw);
	@DCBStruct = ();
	foreach $dw (@dwords)
	{	push(@DCBStruct, hex($dw));	}
	${$InstallProps{uc $model}}[1] = \@DCBStruct;

	$RefAddregList		= ${$InstList{$model}}{'ADDREG'};
	if (hex($dwords[1]) >= 230000 && 
		defined($$RefAddregList[2])	&& 
		$$RefAddregList[2] == 1)
	{
		add_ref_err(3098, $Lnum, $model);
	}


}

#-------------------------------------------------------------
#
# This sub validates the CallSetupFailTimer related keys.
#
# Globals changed:
#
# Usage:
#	proc_callsetup(hex_val)
#
#-------------------------------------------------------------
sub
proc_callsetup
{
local($val, $Lnum, $PropLine, $model) = @_;

	$val = hex($val);
	if ($val == 0)
	{
		return; # CallSetupFailTimer not supported.
	}

	inst_wrn(4044, $PropLine) if ($val != 255);

	get_settings("CallSetupFailTimer");
}

#-------------------------------------------------------------
#
# This sub validates the InactivityTimeout related keys.
#
# Globals changed:
#
# Usage:
#	proc_inact(hex_val)
#
#-------------------------------------------------------------
sub
proc_inact
{
local($val, $Lnum, $PropLine, $model) = @_;

	$val = hex($val);
	if ($val == 0)
	{
		return; # InactivityTimeout not supported.
	}

	inst_wrn(4043, $PropLine) if ($val != 255 && $val != 90);

	get_settings("InactivityTimeout");

	@list = getPathKey(",InactivityScale",1);
	if (($count = @list) < 1)
	{
#		add_ref_err("$model does not have any InactivityScale.", $Lnum);
		add_ref_err(3063, $Lnum, $model);
		return;
	}
	foreach $line (@list)
	{
		@tmp = split(',', $line, 4);
		@tmp2 = split(':', $tmp[2], 2);
		$L =  $tmp2[0];
		$T =  $tmp2[1];
		return if ($T !~ /^1$/);	# dont know what to do with binaries!!!!
	}
	
		#take the last one
	@bytes = split(',', $tmp[3]);
	if (($count = @bytes) != 4)
	{
#		inst_err("InactivityScale should have 1 DWORD.", $L);
		inst_err(3079, $L);
		return;
	}
	get_dwords(4, @bytes);
}

#-------------------------------------------------------------
#
# This sub validates the Speaker Volume related keys.
#
# Globals changed:
#
# Usage:
#	proc_spvol(hex_val)
#
#-------------------------------------------------------------
sub 
proc_spvol
{
local($val, $Lnum, $model) = @_;

	$val = hex($val);

	@SetList=();	#List of expected settings
	@SetList = (@SetList, "SpeakerVolume_Low") if ($val & 0x00000001);
	@SetList = (@SetList, "SpeakerVolume_Med") if ($val & 0x00000002);
	@SetList = (@SetList, "SpeakerVolume_High") if ($val & 0x00000004);


	if (($count = @SetList) > 0)
	{
		@S = get_settings(@SetList);

		#check if any two are same.
#		inst_wrn("$model: Same &ltValue&gt for at least two of SpeakerVolume_XXX Settings.", $L)
		inst_wrn(4025, $L, $model)
				if((($count = @S) >1) && &check_dup(@S));
	}
	 
}

#-------------------------------------------------------------
#
# This sub validates the speaker modes related keys.
#
# Globals changed:
#
# Usage:
#	proc_spmode(hex_val)
#
#-------------------------------------------------------------
sub 
proc_spmode
{
local($val, $Lnum, $model) = @_;

	$val = hex($val);
	@SetList=();	#List of expected settings
	@SetList = (@SetList, "SpeakerMode_Off") if ($val & 0x00000001);
	@SetList = (@SetList, "SpeakerMode_Dial") if ($val & 0x00000002);
	@SetList = (@SetList, "SpeakerMode_On") if ($val & 0x00000004);
	@SetList = (@SetList, "SpeakerMode_Setup") if ($val & 0x00000008);

	if (($count = @SetList) > 0)
	{
	@S = get_settings(@SetList);

	#check if any two are same.
#	inst_wrn("$model: Same &ltValue&gt for at least two of SpeakerMode_XXX Settings.", $L)
	inst_wrn(4026, $L, $model)
				if((($count = @S) >1) && &check_dup(@S));
	}
}

#-------------------------------------------------------------
#
# This sub validates the modem options related keys.
#
# Globals changed:
#
# Usage:
#	proc_mdop(hex_val)
#
#-------------------------------------------------------------
sub 
proc_mdop
{
local($val, $Lnum, $model) = @_;

	$val = hex($val);

	# compression settings.
	@SetList=();
	@SetList = ("Compression_On", "Compression_Off") if ($val & 0x00000001);
	if (($count = @SetList) > 0)
	{
		@S = get_settings(@SetList);
#		inst_wrn("$model: Same &ltValue&gt for Compression_On/Off Settings.", $L)
		inst_wrn(4027, $L, $model)
				if((($count = @S) >1) && &check_dup(@S));
	}

	# err ctrl settings.
	@SetList=();
	@SetList = ("ErrorControl_On", "ErrorControl_Off")	if ($val & 0x00000002);
	@SetList = ("ErrorControl_Off", "ErrorControl_Forced")	if ($val & 0x00000004);
	@SetList = ("ErrorControl_On", "ErrorControl_Off", "ErrorControl_Forced") 
											if (($val & 0x00000002)&&($val & 0x00000004));
	if (($count = @SetList) > 0)
	{
		@S = get_settings(@SetList);
#		inst_wrn("$model: Same &ltValue&gt for ErrorControl_On/Off/Forced Settings.", $L)
		inst_wrn(4028, $L, $model)
				if((($count = @S) >1) && &check_dup(@S));
	}

	# ****** Forced, Cell
	@SetList=();
	@SetList = ("ErrorControl_Cellular", "ErrorControl_Cellular_Forced")  if ($val & 0x00000008);
	if (($count = @SetList) > 0)
	{
		@S = get_settings(@SetList);
#		inst_wrn("$model: Same &ltValue&gt for ErrorControl_Cellular/Cellular_Forced Settings.", $L)
		inst_wrn(4030, $L, $model)
				if((($count = @S) >1) && &check_dup(@S));
	}

	# flow ctrl settings.
	@SetList=();
	@SetList = ("FlowControl_Off", "FlowControl_Hard") if ($val & 0x00000010);
	@SetList = ("FlowControl_Off", "FlowControl_Soft") if ($val & 0x00000020);
	@SetList = ("FlowControl_Off", "FlowControl_Soft", "FlowControl_Hard") 
											if (($val & 0x00000010)&&($val & 0x00000020));
	if (($count = @SetList) > 0)
	{
		@S = get_settings(@SetList);
#		inst_wrn("$model: Same &ltValue&gt for at least two of FlowControl_XXX Settings.", $L)
		inst_wrn(4031, $L, $model)
				if((($count = @S) >1) && &check_dup(@S));
	}

	@SetList=();
	@SetList = ("Modulation_CCITT", "Modulation_Bell") if ($val & 0x00000040);
	@SetList = ("Modulation_CCITT_V23") if ($val & 0x00000400);
	@SetList = ("Modulation_CCITT", "Modulation_Bell", "Modulation_CCITT_V23") 
											if (($val & 0x00000040)&&($val & 0x00000400));
	if (($count = @SetList) > 0)
	{
		@S = get_settings(@SetList);
#		inst_wrn("$model: Same &ltValue&gt for Modulation_XXX Settings.", $L)
		inst_wrn(4032, $L, $model)
				if((($count = @S) >1) && &check_dup(@S));
#		inst_wrn("$model: Possible inversion of &ltValue&gt between Settings Modulation_CCITT/Bell.", $L)
		inst_wrn(4033, $L, $model)
				if((($count = @S) >1) && (($S[0] eq 'B1') && ($S[1] eq 'B0')));
	}

	@SetList=();
	@SetList = ("SpeedNegotiation_On", "SpeedNegotiation_Off") if ($val & 0x00000080);
	if (($count = @SetList) > 0)
	{
		@S = get_settings(@SetList);
#		inst_wrn("$model: Same &ltValue&gt for SpeedNegotiation_On/Off Settings.", $L)
		inst_wrn(4034, $L, $model)
				if((($count = @S) >1) && &check_dup(@S));
#		inst_wrn("$model: Possible inversion of &ltValue&gt between Settings SpeedNegotiation_On/Off.", $L)
		inst_wrn(4035, $L, $model)
				if((($count = @S) >1) && (($S[0] eq 'N0') && ($S[1] eq 'N1')));
	}

	@SetList=();
	@SetList = ("Pulse", "Tone") if ($val & 0x00000100);
	if (($count = @SetList) > 0)
	{
		@S = get_settings(@SetList);
#		inst_wrn("$model: Same &ltValue&gt for Tone/Pulse Settings.", $L)
		inst_wrn(4036, $L, $model)
				if((($count = @S) >1) && &check_dup(@S));
#		inst_wrn("$model: Possible inversion of &ltValue&gt between Settings Tone/Pulse.", $L)
		inst_wrn(4037, $L, $model)
				if((($count = @S) >1) && (($S[0] eq 'T') && ($S[1] eq 'P')));
	}

	@SetList=();
	@SetList = ("Blind_On", "Blind_Off") if ($val & 0x00000200);
	if (($count = @SetList) > 0)
	{
		@S = get_settings(@SetList);
#		inst_wrn("$model: Same &ltValue&gt for Blind_On/Off Settings.", $L)
		inst_wrn(4038, $L, $model)
				if((($count = @S) >1) && &check_dup(@S));
#		inst_wrn("$model: Possible inversion of &ltValue&gt between Settings Blind_On/Off.", $L)
		inst_wrn(4039, $L, $model)
				if((($count = @S) >1) && (($S[0] eq 'X4') && ($S[1] eq 'X3')));
	}
}


#-------------------------------------------------------------
#
# This sub returns the values for each of the settings keys 
# passed in SetList. It also flags error if any of the keys
# is not defined.
#
# Globals changed:
#
# Usage:
#	get_settings(SetList)
#
#-------------------------------------------------------------
sub
get_settings
{
local(@SetList) = @_;
local (@S);

	@S = ();

	foreach $set (@SetList)
	{
		@list = getPathKey("Settings,$set", 1);
		if (($count = @list) > 0)
		{ 
			$this_val = &join_strval(@list);
			$this_val =~ s/0//; #0s are insignificant here!!
			@S = (@S, $this_val);
		}
		else
		{
#			add_ref_err("$model\'s properties expect a key \"Settings, $set\" which is not defined.", $Lnum);
			add_ref_err(3064, $Lnum, $model, $set);
		}
	}
	@S; # return value.
}

#-------------------------------------------------------------
#
# This sub checks for duplicates within the list passed.
#
# Globals changed:
#
# Usage:
#	check_dup(List)
#
#-------------------------------------------------------------
sub
check_dup
{
local(@list) = @_;

	$count = @list;
	$i=0;
	while ($i < $count)
	{
		$j = $i+1;
		while ($j < $count)
		{
			return(1) if ($list[$i] eq $list[$j]);
			$j++;
		}
		$i++;

	}
	return(0);
}

#-------------------------------------------------------------
#
# This sub makes dwords out of every 4 hex bytes in the list
# of bytes and returns the list of dwords in hex.
#
# Globals changed:
#
# Usage:
#	 hex_dwords_list = get_dwords(length_in_bytes, list of bytes)
#
#-------------------------------------------------------------
sub
get_dwords
{
local($multiple, @list) = @_;

	foreach (@list) # make them sane.
	{
	  #strip all of their whitspaces and add 0s if needed.	 
		s/^[\s\t]*//;
		s/[\s\t]*$//;
		$_ = join("", 0, $_) if (length($_) < 2); 
		$_ = join("", 0, $_) if (length($_) < 2); 
		if ($_ !~ /^[a-fA-F0-9][a-fA-F0-9]$/)
		{
#			inst_err("&ltValue&gt is expected to have only hex digits.", $L);
			inst_err(3080, $L);
			$_ = '00';
		}
	}

	local(@dw) = ();
	$cnt = 0;
	$i = 0; 
	foreach $byte (@list)
	{
		$dw[$i] = join("", $byte, $dw[$i]);
		$cnt++;
		$i++ if (!($cnt % $multiple));
	}
	@dw;
}


#-------------------------------------------------------------
#
# This sub returns the list of HKRs that match the given Path
# and removes the entries found from the list of current HKR's
# 
#
# Globals changed:
#	Inst_HKR in CheckInstSections
#
# Usage:
#	getPathKey(Path, MatchWholeWord)
#
#-------------------------------------------------------------
sub
getPathKey
{
local ($match, $bWholeWord) = @_;
local ($bKeyNum, $key, @SortedKeys, %list, @tmp);

	if (!defined($bWholeWord))
	{	$bWholeWord = 0; }

	%list = ();
	$match = uc $match;

	$bKeyNum = 1;
	foreach $key (keys %Inst_HKR)
	{
		if ( ($bWholeWord) ? ($key =~ /^$match$/) : ($key =~ /^$match/) )
		{
			@tmp = split(',', $key, 2);

			#if at least one command key is alpha, use string compare
			if ( $bKeyNum && ($tmp[1] !~ /^[0-9]*$/) )
			{	$bKeyNum = 0; }

			$list{$key} = $Inst_HKR{$key};

			delete $Inst_HKR{$key};
		}

	}
	@ret = ();
	if ($bKeyNum)
	{	@SortedKeys = sort StrIntCompare (keys %list); }
	else
	{	@SortedKeys = sort(keys %list); }

	foreach $key (@SortedKeys)
	{
		push(@ret, join(",", $key, $list{$key}));
	}

	@ret;
}

#-------------------------------------------------------------
#
# Procedure used to compare the keys in getPathKey
# 
#-------------------------------------------------------------
sub
StrIntCompare
{  local (@AParts, @BParts, $StrPart);

  @AParts = split(",",$a);
  @BParts = split(",",$b);

  $StrPart = uc($AParts[0]) cmp uc($BParts[0]); #compare first parts as strings
  if ($StrPart == 0)
	 { $AParts[1] <=> $BParts[1]; } #compare second parts as numbers
  else
	 {$StrPart;}
}

#-------------------------------------------------------------
#
# This sub returns the last line of the list of HKRs
# 
#-------------------------------------------------------------
sub
getKeyLastLine
{
	my (@HKRLines) = @_;
	my ($line, $L, @tmp);

	foreach $line (@HKRLines) 
	{
		@tmp = split(',', $line, 4);
		@tmp = split(':', $tmp[2], 2);
		$L =  $tmp[0];
	}

	$L;
}
#-------------------------------------------------------------
#
# This sub retrives the <Value> as a string from the list and
# joins if multiple lines are passed. This can be used for, 
# say, multilevel Inits where you pass the list of Inits (as 
# recieved from a call to getPathKey("Init,") and this sub will
# return a single variable with the join of all the Inits.
# It also checks for empty strings and the %s in the string.
#
# Globals changed:
#
# Usage:
#	join_strval(list)
#
#-------------------------------------------------------------
sub 
join_strval
{
local(@list) = @_;

	$L = $TotalLines;	# this variable can be used later by the calling routine

	$this_val ="";
	foreach $line (@list)
	{
		@tmp = split(',', $line, 4);
		@tmp2 = split(':', $tmp[2], 2);
		$L =  $tmp2[0];
		$T =  $tmp2[1];
		return if ($T !~ /^0$/);	# dont know what to do with binaries!!!!
		if($tmp[3] !~ /^__QUOTED_STR_\(([0-9]*)\)__$/)
		{
#			inst_err("Expect &ltValue&gt to be a quoted string.", $L);
			inst_err(3081, $L);
			return;
		} else {

			# get the quoted string.
			$tmp[3] = $AllQuotes{$1};
			if($tmp[3] =~ /^$/)
			{
#				inst_err("&ltValue&gt cant be empty string.", $L) unless ($tmp[1] =~ /DIALSUFFIX/);
				inst_err(3082, $L) unless ($tmp[1] =~ /DIALSUFFIX/);
				return;
			}

			# check for %s.
			$_ = $tmp[3];
			$cnt = tr/%/%/;
			$cnt2 = s/%%/%%/g;

			if (($cnt % 2) || ($cnt != 2*$cnt2))
			{
#				inst_err("&ltValue&gt should have doubled %s.", $L);
				inst_err(3083, $L);
			}
			
			$tmp[3] =~ s/[\s\t]//g; # get rid of all white spaces for efficient comparison later!
		}

		$this_val = join("", $this_val, $tmp[3]);
	}
	$this_val = uc $this_val;

	$this_val;
}


#-------------------------------------------------------------
#	Checks if the command lines are in sequence. The input lines 
#	are in the form <command>, <sequence_number>, <lineNo>:[0/1] ...
#
# Usage:
#		CheckSeqCommand(Inst_Section, array_command_lines)
#
#-------------------------------------------------------------

sub 
CheckSeqCommand
{
local($InstSect, $command, @CommandLines) = @_;
local(@ArgLine,$size,@ErrReturn, $ErrCount, @aux, $NoExpect, $CommandIndex, $first, $last);

	$size = @CommandLines;
	$CommandIndex = 0;
	$NoExpect = 1;

	@ArgLine = split(",", $command);
	$command = $ArgLine[0];

	while ($CommandIndex < $size)
	{	@ArgLine = split(",", $CommandLines[$CommandIndex]);
		@ErrReturn = ();
		while ($ArgLine[1] > $NoExpect )
		{	push(@ErrReturn, $NoExpect);
			$NoExpect++;
		}
		if (($ErrCount = @ErrReturn) > 0)
		{	@aux = split(":", $ArgLine[2]);
			$first = $ErrReturn[0];
			$last = $ErrReturn[$ErrCount-1];
			if ($ErrCount == 1)
			   {  
#			   add_ref_err("$InstSect: Command $command, level #$first not found.", $aux[0]);
			   add_ref_err(3065, $aux[0], $InstSect, $command, $first);
			   }
			else
			   { 
#			   add_ref_err("$InstSect: Command $command, levels #$first-#$last not found.", $aux[0]);
			   add_ref_err(3066, $aux[0], $InstSect, $command, $first, $last);
				}
		}
		$NoExpect++;
		$CommandIndex++;
	}
}


#-------------------------------------------------------------
#	Checks if AT commands end with <cr>
#
# Globals changed:
#	%ATSyntaxErr
#
# Usage:
#		CheckATString(HKR_lines)
#
#-------------------------------------------------------------
sub 
CheckATString
{
local(@HKRLines)=@_;
local($command, @HKRArg, @aux, $lineNo, $CmdString, $NoErrs);

	foreach $command (@HKRLines)
	{	@HKRArg = split(",", $command);
		@aux = split(":", $HKRArg[2]);
		$lineNo = $aux[0];

		# skip if line already processed
		next
			if ( defined($ATSyntaxErr{$lineNo}) );

		$NoErrs = 0;
		if($HKRArg[3] =~ /^__QUOTED_STR_\(([0-9]*)\)__$/)
		{ 
			$CmdString = uc $AllQuotes{$1};

			if ($CmdString =~ /^[\s\t]+/)
			{
#				add_ref_err("AT Command should not start with spaces", $lineNo);
				add_ref_err(3067, $lineNo);
			}

			$CmdString =~ s/^[\s\t]*//;
			$CmdString =~ s/[\s\t]*$//;

			goto LineSyntax
				if ( ($CmdString =~ /^NONE$/) || ($CmdString =~ /^NORESPONSE$/) );

			if ( ($CmdString =~ /NONE/))
			{	if ($CmdString =~ /^NONE<CR>$/)
				{	
#				add_ref_err("AT-Command \"None\" should not end with \"&ltCR&gt\".", $lineNo);
				add_ref_err(3068, $lineNo);
				}
				else
				{
#					add_ref_err("AT-Command \"None\" suspicious.", $lineNo);
					add_ref_err(3069, $lineNo);
				}
				$NoErrs++;
				goto LineSyntax;
			}

			if ( ($CmdString =~ /NORESPONSE/))
			{	if ($CmdString =~ /^NORESPONSE<CR>$/)
				{	
#					add_ref_err("AT-Command \"NoResponse\" should not end with \"&ltCR&gt\".", $lineNo);
					add_ref_err(3070, $lineNo);
				}
				else
				{
#					add_ref_err("AT-Command \"NoResponse\" suspicious.", $lineNo);
					add_ref_err(3071, $lineNo);
				}
				$NoErrs++;
				goto LineSyntax;
			}

			if ($CmdString !~ /^(AT|<H10>|<DLE>)/)
			{	
#				add_ref_err("AT-Command string should start with \"AT\".", $lineNo);
				add_ref_err(3072, $lineNo);
				$NoErrs++;
			}
			if ($CmdString !~ /<CR>$/ && $CmdString =~ /^AT/)
			{	
#				add_ref_err("AT-Command string should end with \"&ltCR&gt\".", $lineNo);
				add_ref_err(3073, $lineNo);
				$NoErrs++;
			}
		}
		LineSyntax: {$ATSyntaxErr{$lineNo} = $NoErrs;}
	}
}

#-------------------------------------------------------------
#
# Checks the ATCommands : 
#	- correctness of the sequences for multiple commands 
#	- AT..<cr> syntax
#
# Usage:
#	CheckATCommands(Install_sect, HKRLinesArray)
#
#-------------------------------------------------------------
sub
CheckATCommands
{	local($InstSect, $command, @CommandList) = @_;

	&CheckSeqCommand($InstSect, $command , @CommandList);
	&CheckATString(@CommandList);
}

1;	# To tell require this file is okay!
