#!/bin/sh

# This script handles all aspects of installing and configuring the
# HCF drivers under Linux.  It aspires to be distribution-neutral
#
# Written by Marc Boucher <marc@mbsi.ca> for Conexant Systems Inc.

#
# Copyright (c) 2001-2002 Conexant Systems, Inc.
# 
# 1.   Permitted use. Redistribution and use in source and binary forms,
# with or without modification, are permitted under the terms set forth
# herein.
# 
# 2.   Disclaimer of Warranties. CONEXANT AND OTHER CONTRIBUTORS MAKE NO
# REPRESENTATION ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE.
# IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTIES OF ANY KIND.
# CONEXANT AND OTHER CONTRIBUTORS DISCLAIMS ALL WARRANTIES WITH REGARD TO
# THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE, GOOD TITLE AND AGAINST INFRINGEMENT.
# 
# This software has not been formally tested, and there is no guarantee that
# it is free of errors including, but not limited to, bugs, defects,
# interrupted operation, or unexpected results. Any use of this software is
# at user's own risk.
# 
# 3.   No Liability.
# 
# (a) Conexant or contributors shall not be responsible for any loss or
# damage to Company, its customers, or any third parties for any reason
# whatsoever, and CONEXANT OR CONTRIBUTORS SHALL NOT BE LIABLE FOR ANY
# ACTUAL, DIRECT, INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL, OR CONSEQUENTIAL
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED, WHETHER IN CONTRACT, STRICT OR OTHER LEGAL THEORY OF
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
# WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
# OF SUCH DAMAGE.
# 
# (b) User agrees to hold Conexant and contributors harmless from any
# liability, loss, cost, damage or expense, including attorney's fees,
# as a result of any claims which may be made by any person, including
# but not limited to User, its agents and employees, its customers, or
# any third parties that arise out of or result from the manufacture,
# delivery, actual or alleged ownership, performance, use, operation
# or possession of the software furnished hereunder, whether such claims
# are based on negligence, breach of contract, absolute liability or any
# other legal theory.
# 
# 4.   Notices. User hereby agrees not to remove, alter or destroy any
# copyright, trademark, credits, other proprietary notices or confidential
# legends placed upon, contained within or associated with the Software,
# and shall include all such unaltered copyright, trademark, credits,
# other proprietary notices or confidential legends on or in every copy of
# the Software.
#

ask_yesno()
{
	$automode && return $2

	while true; do
		echo -n "$1"
		read answer
		case "${answer}" in
		[yY] | [yY][eE][sS])
			return 0
			;;
		[nN] | [nN][oO])
			return 1
			;;
		"")
			return $2
			;;
		*)
			echo "Enter 'yes' or 'no'"
			;;
		esac
	done
}

# This function provides machine-readable pci device information
# it is a replacement for lspci -m which has a bug (pciutils <= 2.1.9) causing incorrect subsystem IDs to be printed
lspci_machine()
{
	lspci -nv $* | ${AWK} '
		(/^[0-9a-f][0-9a-f]:/ && $2 == "Class") {
			split($0,pcidev,/[ 	:\.]*/);
			getline;
			printf "%s %s %s %s %s %s", toupper(pcidev[1]), toupper(pcidev[2]), toupper(pcidev[3]), toupper(pcidev[5]),toupper(pcidev[6]),toupper(pcidev[7]);
			if (/^[ 	]*Subsystem:/) {
				split($0,pcidevsub,/[ 	:]*/);
				printf " %s %s", toupper(pcidevsub[3]),toupper(pcidevsub[4]);
			}
			printf "\n";
		}
	'
}

lspci_text()
{
	if [ -r /proc/pci ]; then
		# First, try to get information directly from /proc/pci, as the kernel
		# usually has more recent pci.ids than the vendor's pciutils
		${AWK}  '
			/Bus.*device.*function.*:/ {
				split($0,pcidev,/[ 	,:]*/);
				printf "%02x:%02x.%x ", pcidev[3],pcidev[5],pcidev[7];
				getline;
				gsub(/^[ 	]*/, "");
				print
			}' </proc/pci
	else
		lspci
	fi
}

choose_pcicomctrl()
{
	if ! ask_yesno "Is the modem presently installed in your system? [yes] " 0; then
		return 1
	fi

	lspci_text | egrep ' ((Communication|Serial) controller|Class ffff):' > /tmp/hcfconfig.com.$$
	if [ ! -s /tmp/hcfconfig.com.$$ ]; then
		echo ""
		echo "Sorry, no communication controllers were found."
		rm -f /tmp/hcfconfig.com.$$
		return 1
	fi

	while true; do
		echo ""
		${AWK} '{
			sub(/^[0-9a-fA-F:\.][0-9a-fA-F:\.]*.Communication controller: /, "");
			sub(/^[0-9a-fA-F:\.][0-9a-fA-F:\.]*.Serial controller: /, "");
			sub(/^[0-9a-fA-F:\.][0-9a-fA-F:\.]*.Class ffff: /, "");
			print NR") "$0;
		}' < /tmp/hcfconfig.com.$$
		notfound="`${AWK} 'END {print NR+1}' < /tmp/hcfconfig.com.$$`"
		echo "${notfound}) None of the above"

		echo ""
		echo -n "Which ${hcfbus} communication controller is your HCF modem? (1-${notfound}) "
		read answer
		case "${answer}" in
		[0-9] | [0-9][0-9] )
			;;
		*)
			echo "ERROR: Numeric answer between 1 and ${notfound} expected."
			continue
			;;
		esac

		if ! [ "${answer}" -ge 1 -a "${answer}" -le "${notfound}" ]; then
			echo "ERROR: Numeric answer between 1 and ${notfound} expected."
			continue
		fi

		if [ "${answer}" -eq "${notfound}" ]; then
			rm -f /tmp/hcfconfig.com.$$
			return 1
		fi

		pcislot="`sed -n \"${answer} {
			s/ .*//;p
		}\" < /tmp/hcfconfig.com.$$`"

		lspci_machine -s "${pcislot}" >/tmp/hcfconfig.com.$$

		bus=""
		slot=""
		function=""
		class=""
		vendorid=""
		deviceid=""
		subvendorid=""
		subdeviceid=""
		read bus slot function class vendorid deviceid subvendorid subdeviceid </tmp/hcfconfig.com.$$

		rm -f /tmp/hcfconfig.com.$$

		if [ -z "${vendorid}" -o -z "${deviceid}" ]; then
			return 1
		else
			return 0
		fi
	done
}

choose_inf()
{
	ls ${hcfinfdir}/*.inf > /tmp/hcfconfig.infs.$$

	if [ ! -s /tmp/hcfconfig.infs.$$ ]; then
		echo ""
		echo "Sorry, no INF files were found under ${hcfinfdir}."
		rm -f /tmp/hcfconfig.infs.$$
		return 1
	fi

	while true; do
		echo ""
		${AWK} '{print NR") "$0}' < /tmp/hcfconfig.infs.$$
		notfound="`${AWK} 'END {print NR+1}' < /tmp/hcfconfig.infs.$$`"
		echo "${notfound}) None of the above"

		echo ""
		echo -n "Which INF file would you like to use with your HCF modem? (1-${notfound}) "
		read answer

		case "${answer}" in
		[0-9] | [0-9][0-9] )
			;;
		*)
			echo "ERROR: Numeric answer between 1 and ${notfound} expected."
			continue
			;;
		esac

		if ! [ "${answer}" -ge 1 -a "${answer}" -le "${notfound}" ]; then
			echo "ERROR: Numeric answer between 1 and ${notfound} expected."
			continue
		fi

		if [ "${answer}" -eq "${notfound}" ]; then
			rm -f /tmp/hcfconfig.infs.$$
			return 1
		fi

		found_inf="`sed -n \"${answer} {
			s/ .*//;p
		}\" < /tmp/hcfconfig.infs.$$`"

		rm -f /tmp/hcfconfig.infs.$$

		if [ -z "${found_inf}" ]; then
			return 1
		else
			return 0
		fi
	done
}

ask_pci_id()
{
	while true; do
		echo ""
		echo -n "$1"
		read answer
		case "${answer}" in
		[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F] )
			;;
		*)
			echo "ERROR: 4-digit hexadecimal number expected."
			continue
			;;
		esac

		pci_id="${answer}"
		return 0
	done
}

preprocess_inf()
{
	HCFLINUXVERSION="${hcfversion}" HCFTARGET="@HCFTARGET@" ${AWK} < $1 '{
		while (1) {
			gsub(/\r/, ""); # strip any CRs
			gsub(/;.*$/, ""); # strip comments
			gsub(/^[ 	]*/, ""); # strip leading spaces
			gsub(/[ 	]*$/, ""); # strip trailing spaces
			gsub(/[ 	]*,[ 	]*/, ","); # strip spaces around commas
			gsub(/[ 	]*\\$/, "\\"); # strip spaces before cont. backslashes
			gsub(/[@]HCFLINUXVERSION[@]/, ENVIRON["HCFLINUXVERSION"]);
			gsub(/[@]HCFTARGET[@]/, ENVIRON["HCFTARGET"]);

			if ($0 !~ /\\$/)
				break;

			gsub(/\\$/, ""); # strip continuation backslashes
			prevline=$0;
			getline;
			sub(/^/, prevline); # prepend previous line
		}
		if ($0 != "") # ignore empty lines
			print $0;
	}'
}

get_inf_section()
{
	_inf=$1
	_section=$2

	( preprocess_inf ${_inf} | ${AWK} "
		/\[${_section}\]/ { p=1; next; }
		p == 1 {
			if (/^\[/) nextfile;
			print \$0;
		}
	" ) 2>/dev/null
}


find_inf()
{
	_vendor=$1
	_device=$2
	_subvendor=$3
	_subdevice=$4
	_infcache=$5

	if [ -n "${_subvendor}${_subdevice}" ]; then
		found_inf="`grep -i \"^.*:%HCFModem%[    ]*=[    ]*Modem,${hcfbus}\\\\\\\\${hcfbusvenkey}_${_vendor}&${hcfbusdevkey}_${_device}&SUBSYS_${_subvendor}${_subdevice}$\" ${_infcache} | awk -F: '{print $1}'`"
		if [ -n "${found_inf}" ]; then
			return 0
		fi
	fi

	found_inf="`grep -i \"^.*:%HCFModem%[    ]*=[    ]*Modem,${hcfbus}\\\\\\\\${hcfbusvenkey}_${_vendor}&${hcfbusdevkey}_${_device}$\" ${_infcache} | awk -F: '{print $1}'`"
	if [ -n "${found_inf}" ]; then
		return 0
	fi

	return 1
}

check_special_devs()
{
	case "${hcfbus}:$1:$2" in
	PCI:14[fF]1:2??? | PCI:127[aA]:4311 | PCI:14[fF]1:4311)
		# HSF
		echo ""
		echo "DEVICE $1:$2 SEEMS TO BE AN HSF MODEM!"
		echo ""
		echo "You might need the HSF driver instead?"
		echo "Please see http://www.mbsi.ca/cnxtlindrv/modemindent.html"
		return 0
		;;
	PCI:127[aA]:1025 | PCI:14[fF]1:1025 | PCI:127[aA]:2005)
		echo ""
		echo "DEVICE $1:$2 MIGHT BE AN HSF MODEM! (both types exist for the same ID)"
		echo "So if it doesn't work with this driver, try the HSF one"
		echo "available from http://www.mbsi.ca/cnxtlindrv"
		return 0
		;;
	PCI:127[aA]:4321 | PCI:14[fF]1:4321)
		# RipTide HCF
		echo ""
		echo "DEVICE $1:$2 IS A RIPTIDE HCF MODEM"
		if [ ! -d ${hcflibdir}/../riptide ]; then
			echo ""
			echo "NOTE: You also need to install the RIPTIDE driver in addition to this one"
			echo "Please see http://www.mbsi.ca/cnxtlindrv/riptide"
		fi
		return 0
		;;
	*)
		return 0
		;;
	esac
}

auto_find_inf()
{
	if [ "@HCFTARGET@" = hcfusb ]; then
		found_inf=${hcfinfdir}/hcfusbc2_linux.inf
		vendorid=""
		deviceid=""
		return 0
	fi

	lspci_machine > /tmp/hcfconfig.pci.$$

	for f in ${hcfinfdir}/*.inf; do

		if [ ! -e "${f}" ]; then
			continue
		fi

		get_inf_section "${f}" HCF_MODEM | sed "s@^@${f}:@"
	done > /tmp/hcfconfig.infs.$$

	while read bus slot function class vendorid deviceid subvendorid subdeviceid; do
		if find_inf "${vendorid}" "${deviceid}" "${subvendorid}" "${subdeviceid}" /tmp/hcfconfig.infs.$$; then
			rm -f /tmp/hcfconfig.pci.$$ /tmp/hcfconfig.infs.$$
			return 0
		fi
		check_special_devs "${vendorid}" "${deviceid}"
	done < /tmp/hcfconfig.pci.$$

	rm -f /tmp/hcfconfig.pci.$$ /tmp/hcfconfig.infs.$$
	return 1
}

adapt_inf()
{
	if grep -q big_endian /usr/include/asm/byteorder.h 2>/dev/null; then
		# does our awk support strtonum() and bit operations ? [only GNU seems to]
		if ! ${AWK} 'BEGIN {print strtonum("1234") lshift(1,1)};' </dev/null >/dev/null 2>&1; then
			echo 1>&2 ""
			echo 1>&2 "ERROR: GNU Awk version 3.1.0 or newer must be installed"
			exit 1
		fi

		${AWK} '

BEGIN {
	inverse = 0;
	dontswap = 0;
}


{
	if ($0 ~ /,FLAGS/) {
		inverse=1;
	}
	if ($0 ~ /,SREG/) {
		dontswap=1;
	}

	if(!dontswap) {
		lm = 1;
		while(1) {
			m = match(substr($0, lm), "[0-9A-F][0-9A-F],[0-9A-F][0-9A-F][ 	,]");
			if(m == 0) {
				m = match(substr($0, lm), "[0-9A-F][0-9A-F],[0-9A-F][0-9A-F]$");
			}
			if(m == 0) {
				break;
			}
			sok=0; eok=0;
			if (((lm + m) == 1) || (substr($0, lm + m - 1 - 1, 1) != ",") || (substr($0, lm + m - 1 - 3, 1) == ",")) {
				sok=1;
			}
			if((length(substr($0, lm + m - 1)) == 5) || match(substr($0, lm + m - 1 + 5 + 1, 1), "[0-9A-F]") != 1) {
				eok=1;
			}

			if(sok && eok) {
				new = substr($0, lm + m - 1, 5);
				new = gensub("([0-9A-F][0-9A-F]\),\([0-9A-F][0-9A-F]\)", "\\2,\\1", "g", new);
				$0 = substr($0, 0, lm + m - 2) new substr($0, lm + m + 5 - 1);
			}

			lm += m - 1 + 5;
		}

		lm = 1;
		while(1) {
			m = match(substr($0, lm), "[0-9A-F][0-9A-F],[0-9A-F][0-9A-F],[0-9A-F][0-9A-F],[0-9A-F][0-9A-F][ 	,]");
			if(m == 0) {
				m = match(substr($0, lm), "[0-9A-F][0-9A-F],[0-9A-F][0-9A-F],[0-9A-F][0-9A-F],[0-9A-F][0-9A-F]$");
			}
			if(m == 0) {
				break;
			}
			sok=0; eok=0;
			if (((lm + m) == 1) || (substr($0, lm + m - 1 - 1, 1) != ",") || (substr($0, lm + m - 1 - 3, 1) == ",")) {
				sok=1;
			}
			if((length(substr($0, lm + m - 1)) == 11) || match(substr($0, lm + m - 1 + 11 + 1, 1), "[0-9A-F]") != 1) {
				eok=1;
			}

			if(sok && eok) {
				new = substr($0, lm + m - 1, 11);
				new = gensub("([0-9A-F][0-9A-F]\),\([0-9A-F][0-9A-F]\),([0-9A-F][0-9A-F]\),\([0-9A-F][0-9A-F]\)", "\\4,\\3,\\2,\\1", "g", new);
				$0 = substr($0, 0, lm + m - 2) new substr($0, lm + m + 11 - 1);
			}

			lm += m - 1 + 11;
		}
	}

	if(inverse) {
		m = match($0, "[0-9A-F][0-9A-F],[0-9A-F][0-9A-F],[0-9A-F][0-9A-F],[0-9A-F][0-9A-F]");
	
		val=strtonum("0x"gensub(",", "", "g", substr($0, m, 11)));

		inv = 0;
		for (i = 0; i < 32; i++) {
			if(and(val, lshift(1, i))) {
				inv = or (inv, lshift(1, 31 - i));
			}
		}

		new = sprintf("%08X", inv);
		new = gensub("\([0-9A-F][0-9A-F]\)", "\\1,", "g", new);
		new = gensub(",$", "", "g", new);
		$0 = substr($0, 0, m-1) new substr($0, m + 11);
	}

	print $0;

	if ($0 !~ /\\$/) {
		inverse=0;
		dontswap=0;
	}
}
		'
	else
		cat
	fi
}

configure_inf()
{
	found_inf=""

	$automode || echo ""
	if ask_yesno "Should we attempt to automatically configure your hardware? [yes] " 0; then
		if ! auto_find_inf; then
			echo ""
			echo "No supported modem device found."
			if ${automode}; then
				echo "Re-run \"@HCFTARGET@config\" manually!"
				return 1
			fi
		fi
	fi

	if [ -z "${found_inf}" ]; then

		if ! choose_pcicomctrl; then
			echo ""
			ask_pci_id "Please enter your modem's ${hcfbus} Vendor ID [${vendorid}]: "
			vendorid="${pci_id}"
			ask_pci_id "Please enter your modem's ${hcfbus} Device ID [${deviceid}]: "
			deviceid="${pci_id}"
			subvendorid=""
			subdeviceid=""
		fi

		check_special_devs "${vendorid}" "${deviceid}" || return 1

		choose_inf

		if [ -z "${found_inf}" ]; then
			return 1
		fi
	else
		check_special_devs "${vendorid}" "${deviceid}" || return 1
	fi

	hcfconf_infFile="${found_inf}"
	hcfconf_vendorId="${vendorid}"
	hcfconf_deviceId="${deviceid}"
	hcfconf_subvendorId="${subvendorid}"
	hcfconf_subdeviceId="${subdeviceid}"

	echo ""
	echo "Selected ${hcfbus} VendorID=${vendorid} DeviceID=${deviceid}"
	echo "and INF file ${found_inf}"

	[ -e "${hcfinffil}" ] && mv -f "${hcfinffil}" "${hcfinffil}.oldcfg"
	[ -e "${hcfctyfil}" ] && mv -f "${hcfctyfil}" "${hcfctyfil}.oldcfg"

	sed -e "s/[@]HCFLINUXVERSION[@]/${hcfversion}/g" -e "s/[@]HCFTARGET[@]/@HCFTARGET@/g" < "${hcfconf_infFile}" | adapt_inf > "${hcfinffil}" || return 1

	ctyFile="`echo \"${hcfconf_infFile}\" | sed 's/\.inf$//'`.cty"
	if [ ! -e "${ctyFile}" ]; then
		ctyFile="${hcfinfdir}/hcf_linux.cty"
	fi
	if [ -e "${ctyFile}" ]; then
		adapt_inf < "${ctyFile}" > "${hcfctyfil}" || return 1
	fi
}

get_country_list()
{
	preprocess_inf $1 | \
		${AWK} -F, '
			{ if ($0 !~ /^HKR,Country\\[0-9a-fA-F]*,(NAME|T35CODE)/)
				next;
			}
			{
				sub(/HKR,Country\\/, "");
				gsub(/ /, "_"); # convert spaces to underscores
				gsub(/\"/, ""); # remove double-quotes
			}
			$2 == "T35CODE" {t35code[$1]=$4","$5}
			$2 == "NAME" {name[$1]=$4}
			END { for (x in name)
				if(t35code[x] != "")
					print name[x]" "t35code[x]
			}' | \
		sort
}

ask_country()
{
	get_country_list ${hcfctyfil} > /tmp/hcfconf.sup.$$

	while true; do
		if [ -n "${setcountry}" ]; then
			answer="${setcountry}"
			setcountry=""
		elif ${automode}; then
			answer="${country}"
		else
			echo ""
			echo "Supported countries: "
			echo ""

			${AWK} '{
					if (NR != 1) printf ", "
					if (NR % 5 == 0) print ""
					printf "%s",$1
				}
				END { print ""}' </tmp/hcfconf.sup.$$

			echo ""
			echo -n "Please enter your country name [$country]: "
			read answer
			case "${answer}" in
			"" | *"[ 	]"*)
				answer="${country}"
				;;
			*)
				answer="`echo \"${answer}\" | tr '[a-z ]' '[A-Z_]'`"
				;;
			esac
		fi

		if grep -i "^${answer} " /tmp/hcfconf.sup.$$ > /tmp/hcfconf.ncty.$$
		then
			read country countrycode < /tmp/hcfconf.ncty.$$
			rm -f /tmp/hcfconf.sup.$$ /tmp/hcfconf.ncty.$$
			return 0
		else
			echo ""
			echo "ERROR: Country \"${answer}\" is not supported."

			if ${automode}; then
				rm -f /tmp/hcfconf.sup.$$ /tmp/hcfconf.ncty.$$
				return 1
			fi
		fi
	done
}

autodetect_country()
{
	# Try to guess what country we're in, using the timezone settings

	localtime_size="`/bin/ls -lL /etc/localtime 2>/dev/null | ${AWK} '{print $5}'`"

	if ! [ ${localtime_size} -gt 0 ]; then
		return 1
	fi

	zoneinfo_dir=/usr/share/zoneinfo

	if [ ! -d ${zoneinfo_dir} -o ! -f ${zoneinfo_dir}/zone.tab ]; then
		return 1
	fi

	# The following maps ISO-3166 country codes to names used by HCF 
	iso_AR='ARGENTINA'
	iso_AU='AUSTRALIA'
	iso_AT='AUSTRIA'
	iso_BE='BELGIUM'
	iso_BG='BULGARIA'
	iso_BR='BRAZIL'
	iso_CA='CANADA'
	iso_CL='CHILE'
	iso_CN='CHINA'
	iso_HR='CROATIA'
	iso_CZ='CZECH_REPUBLIC'
	iso_DK='DENMARK'
	iso_FI='FINLAND'
	iso_FR='FRANCE'
	iso_DE='GERMANY'
	iso_GR='GREECE'
	iso_HK='HONG-KONG'
	iso_HU='HUNGARY'
	iso_IS='ICELAND'
	iso_IN='INDIA'
	iso_ID='INDONESIA'
	iso_IE='IRELAND'
	iso_IL='ISRAEL'
	iso_IT='ITALY'
	iso_JP='JAPAN'
	iso_KR='KOREA'
	iso_LU='LUXEMBOURG'
	iso_MY='MALAYSIA'
	iso_MX='MEXICO'
	iso_NL='NETHERLANDS'
	iso_NZ='NEW_ZEALAND'
	iso_NO='NORWAY'
	iso_PH='PHILIPPINES'
	iso_PL='POLAND'
	iso_PT='PORTUGAL'
	iso_RU='RUSSIA'
	iso_SG='SINGAPORE'
	iso_SK='SLOVAKIA'
	iso_SI='SLOVENIA'
	iso_ZA='SOUTH_AFRICA'
	iso_ES='SPAIN'
	iso_SE='SWEDEN'
	iso_CH='SWITZERLAND'
	iso_TW='TAIWAN'
	iso_TH='THAILAND'
	iso_TR='TURKEY'
	iso_GB='UK'
	iso_AE='UNITED_ARAB_EMIRATES'
	iso_US='USA'

	(
		cd ${zoneinfo_dir} 2>/dev/null || return 1
		find . -type f -size "${localtime_size}"c -print | sed 's@^\./@@' | \
			while read file; do
				cmp -s /etc/localtime $file || continue
# in the egrep and sed regular expressions below, it is very important to
# have tabs, not spaces
				egrep "	$file(	.*|\$)" ${zoneinfo_dir}/zone.tab
			done | sed -n '/^[^#]/s/	.*//p' | sort | uniq | \
				while read code; do
					eval "echo \${iso_${code}}"
				done | sort | uniq
		return 0
	)
}

configure_country()
{
	# To configure the country type we need an inf file
	if [ -z "${hcfconf_infFile}" ]; then
		echo ""
		echo 1>&2 "$0: configure_country error: no inffile; run @HCFTARGET@config -hwtype first."
		return 1
	fi

	country="${setcountry}"
	if [ -z "${country}" -a -n "${hcfconf_countryName}" ]; then
		country="${hcfconf_countryName}"
	fi

	if [ -z "${country}" -o "${country}" = "AUTO" ]; then
		country="`autodetect_country | head -1`"
		echo ""
		if [ -z "${country}" ]; then
			echo "Unable to determine country, defaulting to \"USA\""
			country=USA
		else
			echo "Automatically guessed country (using timezone): \"${country}\""
		fi
		setcountry="${country}"
	fi

	ask_country || return 1

	hcfconf_countryName="${country}"
	hcfconf_countryT35c="${countrycode}"

	echo ""
	echo "Configuring modem for country: \"${hcfconf_countryName}\""
	echo "You can change this setting later with the command \"@HCFTARGET@config --country\""
	return 0
}

use_prebuilt_modules()
{
	rm -f "/lib/modules/`uname -r`/misc/@HCFTARGET@"*.o
	if [ ! -d "/lib/modules/`uname -r`/misc" ]; then
		mkdir -p "/lib/modules/`uname -r`/misc"
	fi
	ln -s "${hcflibdir}/modules/binaries/$1/@HCFTARGET@"*.o "/lib/modules/`uname -r`/misc/"
}

files_present()
{
	for f; do
		if [ ! -f "${f}" ]; then
			missing_file="${f}"
			return 1
		fi
	done
}

remove_temp_kernel_tree()
{
	if [ -n "${KERNELORG}" ]; then
		rm -rf "${KERNELSRC}"
		KERNELSRC="${KERNELORG}"
		KERNELORG=""
		return 0
	fi

	return 1
}

create_temp_kernel_tree()
{
	KERNELORG="${KERNELSRC}"
	# get absolute pathname with cd/pwd to ensure that our temp dir
	# is on same filesystem due to use of hardlinks by cp -alL
	KERNELSRC="`(cd \"${KERNELORG}\" && /bin/pwd)`-hcftmp$$"
	rm -rf "${KERNELSRC}"
	if ! cp -alL "${KERNELORG}" "${KERNELSRC}"; then
		remove_temp_kernel_tree
		return 1
	fi

	return 0
}

kernelrebuild_instructions()
{
		echo ""
		echo "First, ensure that the proper kernel source and compiler packages"
		echo "from your distribution vendor and/or the community are installed."
		echo ""
		echo "The Linux kernel can then be reconfigured by running \"make menuconfig\""
		echo "under the kernel source directory (usually /usr/src/linux)."
		echo ""
		echo "Verify that the proper options for your system are selected,"
		echo "and that CONFIG_SMP (\"Symmetric multi-processing support\" under"
		echo "\"Processor type and features\") is disabled, as this driver is"
		echo "presently designed to work on single-processor machines."
		echo ""
		echo "Then compile and install your new kernel (for more information about"
		echo "this procedure, see the README file under the kernel source directory),"
		echo "reboot the system using the new kernel, and re-run \"@HCFTARGET@config\"."
}

check_kernel()
{
	if [ -n "$OSKERNSMP" ]; then
		echo 1>&2 ""
		echo 1>&2 "HCF drivers have not been fully tested on SMP kernels."
		echo 1>&2 "Please report results to <hcflinux-bugs@mbsi.ca>"
	fi

	return 0
}

right_version_file()
{
	_versionf="$1"

	[ -n "${KERNELVER}" ] || KERNELVER="`uname -r`"

	FILEKERNELVER="`echo UTS_RELEASE | gcc -E -I"${KERNELSRC}/include" -include "${_versionf}" - | grep '^"' | tr -d '"	 '`"

	[ "${KERNELVER}" = "${FILEKERNELVER}" ]
}

recompile_modules()
{
	KERNELVER="`uname -r`"
	KERNELSRC=/usr/src/linux
	KERNELORG=""

	if [ -h /lib/modules/${KERNELVER}/build -a ! -d /lib/modules/${KERNELVER}/build/. ]; then
		echo ""
		echo "ERROR: /lib/modules/${KERNELVER}/build points to a missing directory"
		echo ""
		ls -l /lib/modules/${KERNELVER}/build
		echo ""
		if rpm -qf /lib/modules/${KERNELVER} >/dev/null 2>&1; then
			echo "Please ensure that the kernel-source RPM package is properly installed."
		else
			echo "Please ensure that the kernel-source is properly installed."
		fi
		return 1
	fi

	if [ -d /lib/modules/${KERNELVER}/build/include ]; then
		KERNELSRC=/lib/modules/${KERNELVER}/build
	elif [ ! -d "${KERNELSRC}" -a -d "/usr/local/src/linux" ]; then
		KERNELSRC=/usr/local/src/linux
	fi
	if ! $automode; then
		echo ""
		echo "Where is the linux source build directory that matches your running kernel?"
		echo -n "[${KERNELSRC}] "
		read answer
		case "${answer}" in
		"")
			;;
		*)
			KERNELSRC="${answer}"
			;;
		esac
	fi

	if ! files_present \
		"${KERNELSRC}/include/linux/autoconf.h" \
		"${KERNELSRC}/include/linux/version.h" \
		"${KERNELSRC}/include/linux/modversions.h"; then
		echo ""
		echo "WARNING: missing file ${missing_file}"
		suspect_tree=true
	elif ! right_version_file "${KERNELSRC}/include/linux/version.h"; then
		echo ""
		echo "WARNING: the kernel version (${FILEKERNELVER}) defined in"
		echo "${KERNELSRC}/include/linux/version.h"
		echo "does not match the currently running kernel (${KERNELVER})"
		echo "The cause of this problem is an incorrect kernel source path."
		echo "Please check that ${KERNELSRC} points to the right tree."
		suspect_tree=true
	else
		suspect_tree=false
	fi

	if ${suspect_tree}; then
		echo "The cause of this problem is usually a missing or misconfigured"
		echo "kernel source tree (and sometimes an incorrect directory or symbolic link)."
		# SuSE has copies of autoconf.h and version.h available under /boot
		if [ -d "${KERNELSRC}/include/linux" -a -f /boot/vmlinuz.autoconf.h -a -f /boot/vmlinuz.version.h ] && right_version_file /boot/vmlinuz.version.h && [ -f "${KERNELSRC}/include/linux/modversions.h" ]; then
			echo ""
			echo "However, proper /boot/vmlinuz.{autoconf.h,version.h} were found."
			if ask_yesno "Would you like to try using them (in a temporary kernel tree)? [yes] " 0; then
				if ! create_temp_kernel_tree; then
					echo ""
					echo "Unable to create temporary kernel tree"
					kernelrebuild_instructions
					return 1
				fi
				rm -f "${KERNELSRC}/include/linux/.config" \
						"${KERNELSRC}/include/linux/autoconf.h" \
						"${KERNELSRC}/include/linux/version.h"
				if ! cp -p /boot/vmlinuz.autoconf.h "${KERNELSRC}/include/linux/autoconf.h" || \
					! cp -p /boot/vmlinuz.version.h "${KERNELSRC}/include/linux/version.h"; then
					echo ""
					echo "Unable to copy files to temporary kernel tree"
					remove_temp_kernel_tree
					kernelrebuild_instructions
					return 1
				fi

				# we don't really need .config but it can't hurt if available
				if [ -f /boot/vmlinuz.config ]; then
					cp -p /boot/vmlinuz.config "${KERNELSRC}/include/linux/.config"
				fi
			fi
		fi
	fi

	# are we ok now?
	if [ ! -f "${KERNELSRC}/include/linux/version.h" \
			-o ! -f "${KERNELSRC}/include/linux/autoconf.h" \
			-o ! -f "${KERNELSRC}/include/linux/modversions.h" ]; then
		remove_temp_kernel_tree
		kernelrebuild_instructions
		return 1
	fi

	recomplogf=/tmp/@HCFTARGET@config-buildlog.txt

	# set FILEKERNELVER if it isn't already
	[ -n "${FILEKERNELVER}" ] || right_version_file "${KERNELSRC}/include/linux/version.h"

	echo ""
	echo "Re-compiling HCF modules for kernel ${FILEKERNELVER}, using source directory"
	echo "${KERNELSRC}. Please wait.."

	(cd ${hcflibdir}/modules && make "KERNELSRC=${KERNELSRC}" clean minstall) \
		> ${recomplogf} 2>&1

	res=$?

	(cd ${hcflibdir}/modules && make "KERNELSRC=${KERNELSRC}" clean) >/dev/null 2>&1

	remove_temp_kernel_tree

	if [ $res -eq 0 ]; then
		rm -f ${recomplogf}
		echo "Re-compilation and installation of HCF modules succeeded."
		return 0
	else
		echo ""
		echo "ERROR: Module re-compilation and installation failed!"
		echo "Please examine the log file \"${recomplogf}\" to determine why."
		return 1
	fi
}

modules_exist()
{
	files_present "/lib/modules/`uname -r`/misc/@HCFTARGET@"*.o
}

remove_kernel_modules()
{
	if modules_exist; then
		echo ""
		echo "Removing HCF drivers from /lib/modules/`uname -r`/misc/"
		rm -f "/lib/modules/`uname -r`/misc/@HCFTARGET@"*.o
	fi
	return 0
}

binary_modules_avail()
{
	[ -d "${hcflibdir}/modules/binaries/$1" ]
}

choose_kernel()
{
	if modules_exist; then
		echo ""
		echo "Warning: existing HCF modules found under /lib/modules/`uname -r`/misc/"
		if ask_yesno "Would you like to keep using them? [no] " 1; then
			return 0
		fi
	fi

	chosen_binary_modules=""
	if binary_modules_avail "${OSKERNTYPE}/${OSDISTNAME}/${OSKERNNAME}-${OSKERNVERS}${OSKERNSMP}"; then
		chosen_binary_modules="${OSKERNTYPE}/${OSDISTNAME}/${OSKERNNAME}-${OSKERNVERS}${OSKERNSMP}"
	fi

	if [ -n "${chosen_binary_modules}" ]; then
		echo ""
		echo "Pre-built HCF modules that seem compatible with your system were found under"
		echo "${hcflibdir}/modules/binaries/${chosen_binary_modules}."

		if ask_yesno "Would you like to use them? [yes] " 0; then
			if use_prebuilt_modules "${chosen_binary_modules}"; then
				return 0
			fi
		fi
	else
		echo ""
		echo "This package does not contain pre-built modules suitable for your system:"
		echo "  Distribution: ${OSDISTNAME}-${OSDISTVERS}"
		echo "  Kernel arch: ${OSKERNTYPE}${OSKERNSMP}"
		echo "  Kernel version: ${OSKERNNAME}-${OSKERNVERS}"
	fi

	if [ -f ${hcflibdir}/modules/makefile ]; then
		echo ""
		echo "Trying to automatically re-compile the modules.."
		echo "(this requires a C compiler and proper kernel sources to be installed)"

		if recompile_modules; then
				return 0
		fi
	else
		echo ""
		echo "Please obtain the appropriate variant of this package for your system"
		echo "or try the generic RPM or tar version."
	fi

	return 1
}

choose_major()
{
	device=$1
	shift

	first=240
	max=250

	major=${first}

	while [ ${major} -ne ${max} ]; do
		excluded=false
		for exclude in $*; do
			if [ ${major} -eq ${exclude} ]; then
				excluded=true
				break
			fi
		done

		if ! ${excluded} && \
			! grep -q "^ *${major} " /proc/devices && \
			! sed -e 's/#.*//g' -e '/@HCFTARGET@serial/d' ${modulesconf} | grep -q "^alias[ 	][ 	]*char-major-${major}"
			then
				break
		fi

		major="`expr ${major} + 1`"
	done

	if [ ${major} -ne ${max} ]; then
		echo ${major}
		return 0
	else
		echo 1>&2 "$0: Cannot find major device number for ${device} in range ${first}-${max}"
		return 1
	fi
}

devfsd_running()
{
	ps -e | grep -q 'devfsd$'
}

unconfigure_serial()
{
	if [ -f /etc/modutils/@HCFTARGET@ ]; then
		rm -f /etc/modutils/@HCFTARGET@
		update-modules
	elif [ -f /etc/modules.d/@HCFTARGET@ ]; then
		rm -f /etc/modules.d/@HCFTARGET@
		update-modules
	else
		if sed -e '/^alias.*@HCFTARGET@serial/d' -e '/^options.*@HCFTARGET@serial/d' \
				-e '/^below[ 	]@HCFTARGET@serial/d' \
		< ${modulesconf} > ${modulesconf}.$$; then
			cp ${modulesconf}.$$ ${modulesconf}
		fi

		rm -f ${modulesconf}.$$ ${outmodulesconf}
	fi

	if [ -f /etc/devfs/conf.d/@HCFTARGET@.conf ]; then
		rm -f /etc/devfs/conf.d/@HCFTARGET@.conf
		[ -x /usr/sbin/update-devfsd ] && update-devfsd
		killall -HUP devfsd 2>/dev/null
	else
		if [ -f /etc/devfsd.conf ]; then
			if sed -e '/^REGISTER.*ttyS*@HCFSERDEV@[0-9]/d' -e '/^LOOKUP.*ttyS@HCFSERDEV@[0-9]/d' \
					< /etc/devfsd.conf > /etc/devfsd.conf.$$; then
				cp /etc/devfsd.conf.$$ /etc/devfsd.conf
			fi
			rm -f /etc/devfsd.conf.$$
			killall -HUP devfsd 2>/dev/null
		fi
	fi

	ttydevicename=/dev/ttyS@HCFSERDEV@0
	cuadevicename=/dev/cua@HCFSERDEV@0

	if ! devfsd_running; then
		rm -f ${ttydevicename} ${cuadevicename}
	fi

	if [ -h /dev/modem -a "`/bin/ls -l /dev/modem 2>/dev/null | ${AWK} '{print $11}'`" = ${ttydevicename} ]; then
		rm -f /dev/modem
	fi
}

configure_serial()
{
	hcftty_major="`choose_major @HCFTARGET@serial`"

	if [ -z ${hcftty_major} ]; then
		exit 1
	fi

	hcfcua_major="`choose_major @HCFTARGET@serial_callout ${hcftty_major}`"
	if [ -z ${hcfcua_major} ]; then
		exit 1
	fi

	if [ -d /etc/modutils -a -x /sbin/update-modules ]; then
		# Debian
		runupdatemodules=true
		outmodulesconf=/etc/modutils/@HCFTARGET@
	elif [ -d /etc/modules.d -a -x /usr/sbin/update-modules ]; then
		# Gentoo
		runupdatemodules=true
		outmodulesconf=/etc/modules.d/@HCFTARGET@
	else
		runupdatemodules=false
		outmodulesconf=/tmp/hcfmodconf.$$
	fi

	(
		echo "alias /dev/ttyS@HCFSERDEV@[0-9]* @HCFTARGET@serial"
		echo "alias char-major-${hcftty_major} @HCFTARGET@serial"

		echo "alias /dev/cua@HCFSERDEV@[0-9]* @HCFTARGET@serial"
		echo "alias char-major-${hcfcua_major} @HCFTARGET@serial"
		echo "alias /dev/modem @HCFTARGET@serial"
		if [ "${hcfbus}" = PCI -a -n "${hcfconf_vendorId}" -a -n "${hcfconf_deviceId}" ]; then
			echo "options @HCFTARGET@serial serialmajor=${hcftty_major} calloutmajor=${hcfcua_major} pcivendorid=0x${hcfconf_vendorId} pcideviceid=0x${hcfconf_deviceId}"
		else
			echo "options @HCFTARGET@serial serialmajor=${hcftty_major} calloutmajor=${hcfcua_major}"
		fi

		if [ "${hcfbus}" = "PCI" -a "${hcfconf_deviceId}" = "4321" ]; then
			# RipTide
			echo "below @HCFTARGET@serial riptidelow"
		fi
	) > ${outmodulesconf}

	if ${runupdatemodules}; then
		update-modules
	else
		if (
			sed -e '/^alias.*@HCFTARGET@serial/d' -e '/^options.*@HCFTARGET@serial/d' \
				-e '/^below[ 	]@HCFTARGET@serial/d'

			cat ${outmodulesconf}

		) < ${modulesconf} > ${modulesconf}.$$; then
			if ! cp ${modulesconf}.$$ ${modulesconf}; then
				rm -f ${modulesconf}.$$ ${outmodulesconf}
				exit 1
			fi
		fi

		rm -f ${modulesconf}.$$ ${outmodulesconf}
	fi

	depmod -e -a

	ttydevicename=/dev/ttyS@HCFSERDEV@0
	cuadevicename=/dev/cua@HCFSERDEV@0

	if [ -d /etc/devfs/conf.d ]; then
		# we use nice to bypass the stupid check in devfsd (Hi Richard!)
		# that prevents direct execution of modprobe (in favor of MODLOAD,
		# which unfortunately doesn't provide a way to specify a module
		# name other than the device itself and under Mandrake at least,
		# the ttyS* alias in /etc/modules.devfs overrides our own alias
		# in modules.conf)
		echo 'LOOKUP	^(ttyS@HCFSERDEV@[0-9]|modem$) EXECUTE nice /sbin/modprobe @HCFTARGET@serial
REGISTER	^ttyS@HCFSERDEV@0$ CFUNCTION GLOBAL symlink $devname modem
UNREGISTER	^ttyS@HCFSERDEV@0$ CFUNCTION GLOBAL unlink modem' \
			> /etc/devfs/conf.d/@HCFTARGET@.conf
		rm -f /dev/modem
		[ -x /usr/sbin/update-devfsd ] && update-devfsd
		killall -HUP devfsd 2>/dev/null
	else
		if [ -f /etc/devfsd.conf ]; then
			if (
				sed -e '/REGISTER.*ttyS*@HCFSERDEV@[0-9]/d' -e '/^LOOKUP.*ttyS@HCFSERDEV@[0-9]/d'
				echo 'LOOKUP	^(ttyS@HCFSERDEV@[0-9]|modem$) EXECUTE nice /sbin/modprobe @HCFTARGET@serial
REGISTER	^ttyS@HCFSERDEV@0$ CFUNCTION GLOBAL symlink $devname modem
UNREGISTER	^ttyS@HCFSERDEV@0$ CFUNCTION GLOBAL unlink modem'
			) < /etc/devfsd.conf > /etc/devfsd.conf.$$; then
				cp /etc/devfsd.conf.$$ /etc/devfsd.conf
			fi
			rm -f /etc/devfsd.conf.$$
			rm -f /dev/modem
			killall -HUP devfsd 2>/dev/null
		fi
	fi

	if ! devfsd_running; then
		hcf_minor=@HCFSERIALMINOR@

		rm -f ${ttydevicename} ${cuadevicename}

		mknod -m 666 ${ttydevicename} c ${hcftty_major} ${hcf_minor} || exit 1
		mknod -m 666 ${cuadevicename} c ${hcfcua_major} ${hcf_minor} || exit 1

		if [ -h /dev/modem -a "`/bin/ls -l /dev/modem 2>/dev/null | ${AWK} '{print $11}'`" = ${ttydevicename} ]; then
			rm -f /dev/modem
		fi

		if [ -e /dev/modem ]; then
			if ! fuser /dev/modem >/dev/null 2>&1; then
				rm -f /dev/modem.old
				mv /dev/modem /dev/modem.old

				echo "WARNING: renamed existing /dev/modem to /dev/modem.old:"
				echo ""
				/bin/ls -l /dev/modem.old
			fi
		fi

		if [ ! -e /dev/modem ]; then
			ln -s ${ttydevicename} /dev/modem
		fi
	fi

	echo ""
	echo "Your HCF modem should now be accessible as ${ttydevicename}"

	if [ -h /dev/modem -a "`/bin/ls -l /dev/modem 2>/dev/null | ${AWK} '{print $11}'`" = ${ttydevicename} ]; then
		echo "or through the symbolic link /dev/modem."
	fi

	echo ""

	oldnames="/dev/cnxt0 /dev/hcf0"

	for oldname in ${oldnames}; do
		if [ -e ${oldname} ]; then
			echo "WARNING: old device ${oldname} still exists; you should probably remove it"
		fi
	done
}

identify_system()
{
	OSDISTNAME=unknown
	OSDISTVERS=unknown

	if [ -f /etc/debian_version ]; then
		OSDISTNAME=Debian
		read OSDISTVERS < /etc/debian_version
	elif [ -f /etc/slackware-version ]; then
		OSDISTNAME=Slackware
		OSDISTVERS="`${AWK} '{print $1; exit}' /etc/slackware-version`"
	elif [ -f /etc/conectiva-release ]; then
		OSDISTNAME=Conectiva
	elif [ -f /etc/SuSE-release ]; then
		OSDISTNAME=SuSE
		OSDISTVERS="`sed -n '/^VERSION/s/^.*=[ 	]*//p' < /etc/SuSE-release`"
	elif [ -f /etc/mandrake-release ]; then
		OSDISTNAME=Mandrake
	#it would be better to use rpm instead of sed but it doesn't work when
	#recursively (due to database locking) when this script is called when the
	#package is being installed
	#	OSDISTVERS="`rpm -q --queryformat '%{VERSION}\n' mandrake-release | sort -r | head -1`"
		OSDISTVERS="`sed -n '/release /{s/^.*release  *\([^ ]*\)/\1/;s/ .*//;p;q;}' < /etc/mandrake-release`"
	#elif [ -f /etc/yellowdog-release ]; then
	#	OSDISTNAME=YellowDog
	elif [ -f /etc/redhat-release ]; then
		OSDISTNAME=RedHat
		OSDISTVERS="`sed -n '/release /{s/^.*release  *\([^ ]*\)/\1/;s/ .*//;p;q;}' < /etc/redhat-release`"
	elif [ -d /var/lib/LST ]; then
		OSDISTNAME=Caldera
	fi

	OSKERNNAME="`uname -s | tr '[A-Z]' '[a-z]'`"
	OSKERNARCH="`uname -m`"
	if [ -r /boot/kernel.h ]; then
		OSKERNTYPE="`sed -n '/^#define[ 	]__MODULE_KERNEL_/s/^#define[ 	]__MODULE_KERNEL_\([^ 	]*\)[ 	]*.*/\1/p' </boot/kernel.h`"
	fi
	if [ -z "${OSKERNTYPE}" ]; then
		OSKERNTYPE="${OSKERNARCH}"
	fi
	OSKERNVERS="`uname -r`"

	if uname -v | grep -q SMP; then
		OSKERNSMP="-SMP"
	else
		OSKERNSMP=""
	fi

	if [ -r /proc/ksyms -a `grep -c '_R[a-f0-9]*$' /proc/ksyms` -gt 100 ]; then
		OSKERNMODV="-MODVERS"
	else
		OSKERNMODV=""
	fi

	echo "$OSKERNNAME-$OSKERNARCH($OSKERNTYPE)-$OSKERNVERS$OSKERNSMP$OSKERNMODV $OSDISTNAME-$OSDISTVERS"
}

dump_cmd()
{
	echo ""
	echo "${dumpprefix} $*"
	$@
}

dump_file()
{
	dump_cmd cat $@
}

dump_diagnostics()
{
	dumpprefix="+"

	echo ""
	echo "Dumping system diagnostic information..."

    case "${hsfdiagfile}" in
	*longdiag*)
		longdiag=true
		;;
	*)
		longdiag=false
		;;
	esac

	(
		dump_cmd date
		dump_cmd @HCFTARGET@config --info
		identify_system
		dump_cmd uname -a

		dump_file /proc/version
		dump_file /proc/cpuinfo

		dump_cmd lspci -n
		dump_cmd lspci -n -vvv

		dump_file /proc/pci

		for p in cmdline uptime \
			devices misc filesystems \
			interrupts iomem ioports dma \
			mtrr
		do
			dump_file /proc/${p}
		done

		${longdiag} && for p in partitions mounts swaps \
			meminfo slabinfo stat \
			apm
		do
			dump_file /proc/${p}
		done

		dump_file /proc/modules
		dump_file "${modulesconf}"

		dump_cmd dmesg
		dump_cmd depmod -e -a

		if dmesg | grep -qi 'panic'; then
			dump_cmd dmesg | ksymoops
		fi

		dump_file /proc/ksyms

		dump_file /proc/tty/drivers
		for f in /proc/tty/driver/*; do
			if [ -r "${f}" ]; then
				dump_cmd cat -v "${f}"
			fi
		done
		dump_file /proc/tty/ldiscs

		dump_cmd ls -l '/dev/modem*'
		dump_cmd ls -l '/dev/*@HCFSERDEV@[0-9]*'
		dump_cmd ls -l '/dev/*cnxt*'
		dump_cmd ls -l '/dev/*hcf*'
		dump_cmd ls -l '/dev/ttyS*'
		dump_cmd ls -l '/dev/tts*'

		dump_cmd ls -lR /etc/.
		dump_cmd ls -lR "${hcflibdir}/."
		dump_cmd ls -lR /lib/modules/.
		dump_cmd ls -lR /boot/.

		dump_cmd ls -l /usr/include/. /usr/src/.
		
		if ${longdiag}; then
			dump_cmd ls -l /usr/src/linux/include/.
			dump_cmd ls -l /usr/src/linux/include/linux/.
			dump_cmd ls -l /usr/src/linux/include/asm-i386/.

			dump_file /lib/modules/`uname -r`/build/.config
			dump_file /lib/modules/`uname -r`/build/include/linux/version.h
			dump_file /lib/modules/`uname -r`/build/include/linux/autoconf.h
			dump_file /lib/modules/`uname -r`/build/include/linux/autoconf-up.h
			dump_file /lib/modules/`uname -r`/build/include/linux/rhconfig.h
			dump_file /lib/modules/`uname -r`/build/include/linux/modules/ksyms.ver

			dump_file /boot/vmlinuz.version.h
			dump_file /boot/vmlinuz.autoconf.h
			dump_file /boot/vmlinuz.config

			dump_file /boot/kernel.h

			dump_cmd insmod -V
		fi

		dump_cmd env

		for f in /etc/*_version /etc/*-version /etc/*-release; do
			if [ -r "${f}" ]; then
				dump_cmd cat -v "${f}"
			fi
		done

		dump_cmd ps -efw

		dump_cmd sh --version
		dump_cmd sed --version
		dump_cmd ${AWK} --version
		dump_cmd printf --version

		dump_cmd tar --version

		dump_cmd rpm --version
		dump_cmd rpm --showrc
		dump_cmd rpm --query --all

	) > "${hcfdiagfile}" 2>&1

	if [ -f "${hcfdiagfile}" ]; then
		echo ""
		echo "System diagnostic information has been saved to the file:"
		echo ""
		ls -l ${hcfdiagfile}
		echo ""
		echo "As it could contain private information about this system, we recommend that"
		echo "you verify its contents before sending it to third parties."
	fi

}

countryT35c_to_name()
{
	get_country_list "$1" | ${AWK} "\$2 == \"$2\" {print \$1}"
}

get_current_country_from_inf()
{
		# does our awk support strtonum() ? [only GNU seems to]
		if ${AWK} 'BEGIN {print strtonum("1234")};' </dev/null >/dev/null 2>&1; then
			get_inf_section "${hcfinffil}" Country | ${AWK} -F, '
				/^HKR,Country,Current,1,/ {printf "%02X,%02X\n", strtonum("0x"$5), strtonum("0x"$6);}
			'
		else
			# bash's printf builtin does not support the 0x argument prefix
			get_inf_section "${hcfinffil}" Country | ${AWK} -F, '
				BEGIN {print "enable -n printf >/dev/null 2>&1" }
				/^HKR,Country,Current,1,/ {print "printf \"%02X,%02X\\n\" 0x"$5" 0x"$6};
			' | sh
		fi
}

load_config()
{
	hcfconf_countryName="AUTO"
	hcfconf_countryT35c=""
	hcfconf_vendorId=""
	hcfconf_deviceId=""
	hcfconf_subvendorId=""
	hcfconf_subdeviceId=""
	hcfconf_infFile=""
	if [ -f ${hcfetcdir}/config ]; then
		. ${hcfetcdir}/config || return $?
	fi

	if [ -f "${hcfinffil}" -a -f "${hcfctyfil}" ]; then
		hcfconf_countryT35c="`get_current_country_from_inf \"${hcfinffil}\"`"
		hcfconf_countryName="`countryT35c_to_name \"${hcfctyfil}\" \"${hcfconf_countryT35c}\"`"
	fi

	return 0
}

save_config()
{
	rm -f ${hcfetcdir}/config
	(
		echo "# WARNING: DO NOT EDIT THIS AUTOMATICALLY GENERATED FILE DIRECTLY !"
		echo "# TO PROPERLY CHANGE THE HCF MODEM CONFIGURATION, RUN \"@HCFTARGET@config\""
		echo "#"
		echo "hcfconf_countryName=\"${hcfconf_countryName}\""
		echo "hcfconf_countryT35c=\"${hcfconf_countryT35c}\""
		echo "hcfconf_vendorId=\"${hcfconf_vendorId}\""
		echo "hcfconf_deviceId=\"${hcfconf_deviceId}\""
		echo "hcfconf_subvendorId=\"${hcfconf_subvendorId}\""
		echo "hcfconf_subdeviceId=\"${hcfconf_subdeviceId}\""
		echo "hcfconf_infFile=\"${hcfconf_infFile}\""
	) > ${hcfetcdir}/config || return $?

	if [ -f "${hcfinffil}" ]; then
		infCountryT35c="`get_current_country_from_inf \"${hcfinffil}\"`"
		if [ "${infCountryT35c}" != "${hcfconf_countryT35c}" ]; then
			if ${AWK} "
				p == 1 && ctycur != 1 && /^HKR,Country,Current/ { 
					while(/\\\\/) getline;
					print \"HKR,Country,Current,1,${hcfconf_countryT35c} ;This key was modified by @HCFTARGET@config\";
					print \"HKR,Country,Previous,1,${infCountryT35c} ;This key was modified by @HCFTARGET@config\";
					ctycur == 1;
					next;
				}
				p == 1 && ctyprv != 1 && /^HKR,Country,Previous/ { 
					while(/\\\\/) getline;
					ctyprv == 1;
					next;
				}
				p != 1 && /^[Country]/ { p=1; }
				{ print \$0; }
			" < "${hcfinffil}" > "${hcfinffil}".new; then
				mv "${hcfinffil}" "${hcfinffil}".old
				mv "${hcfinffil}".new "${hcfinffil}"
			else
				rm -f "${hcfinffil}".new
				return 1
			fi
		fi
	fi

	return 0
}

show_config()
{
	echo ""
	echo "Current HCF configuration:"
	echo ""
	if [ -n "${hcfconf_countryT35c}" ]; then
		echo "	Country name: \"${hcfconf_countryName}\", T.35 code: `echo \"${hcfconf_countryT35c}\" | ${AWK} -F, '{print $2$1}'`"
	else
		echo "	Country name: \"${hcfconf_countryName}\""
	fi
	echo "	${hcfbus} Modem vendor-id: ${hcfconf_vendorId}, device-id: ${hcfconf_deviceId}"
	if [ -n "${hcfconf_subvendorId}${hcfconf_subdeviceId}" ]; then
		echo "	${hcfbus} Modem subvendor-id: ${hcfconf_subvendorId}, subdevice-id: ${hcfconf_subdeviceId}"
	fi
	echo "	Driver inf filename: ${hcfconf_infFile}"
}

show_intro()
{
	echo "Linux HCF controllerless modem drivers, version ${hcfversion}"

	case "${hcfversion}" in
	*beta*)
		(
		echo ""
		echo "WARNING: this is an EXPERIMENTAL BETA VERSION of the HCF drivers for Linux."
		echo "USE AT YOUR OWN RISK! See the file ${hcflibdir}/LICENSE for details."
		) 1>&2
		;;
	esac

	case "${hcfversion}" in
	*mbsi*)
		(
		echo ""
		echo "Conexant Systems neither distributes nor maintains this package."
		echo "PLEASE DO NOT CONTACT CONEXANT REGARDING THIS SOFTWARE!!!"
		echo "If you require assistance or need more information, go to:"
		echo "              http://www.mbsi.ca/cnxtlindrv"
		) 1>&2
		;;
	esac
}


show_usage_and_exit()
{
	show_intro

	cat 1>&2 << EOT

Usage: `basename $0` [options]

  -t, --hwtype                 Configure hardware type and parameters
  -c, --country[=<name>]       Configure country, to <name> if specified
                               (Use country name "auto" to infer from timezone)
  -k, --kernel                 Configure and install HCF kernel modules
  -s, --serial                 Install HCF serial port devices

  -i, --info                   Show current configuration parameters

  -d, --dumpdiag[=<file>]      Dump system diagnostic information to <file>
                               (default: ${hcfdiagfile})

  -a, --auto                   Run in automatic mode, without prompting user
  -r, --remove                 Remove HCF devices and kernel modules

  -h, --help                   This small usage guide
  -V, --version                Show HCF version

Default options if none selected: --kernel --hwtype --country --serial
EOT
	exit 1
}

# end of functions; execution starts here

PATH=/usr/sbin:/sbin:/usr/bin:/bin:/usr/local/sbin:/usr/local/bin
export PATH

umask 022

hcflibdir="@HCFLIBDIR@"
hcfetcdir="@HCFETCDIR@"
hcfinfdir="@HCFINFDIR@"
hcfinffil="@HCFINF_FILE@"
hcfctyfil="@HCFCTY_FILE@"
hcfversion="@HCFLINUXVERSION@"

hcfdiagfile="/tmp/hcfdiag.txt"

if [ "@HCFTARGET@" = hcfusb ]; then
	hcfbus=USB
	hcfbusvenkey=VID
	hcfbusdevkey=PID
else
	hcfbus=PCI
	hcfbusvenkey=VEN
	hcfbusdevkey=DEV
fi

if [ -f /etc/modules.conf ]; then
	modulesconf=/etc/modules.conf
elif [ -f /etc/conf.modules ]; then
	modulesconf=/etc/conf.modules
fi

TEMP=`getopt -a -o tc::ksid::arhV --long hwtype,country::,kernel,serial,info,dumpdiag::,auto,remove,help,version -n "\`basename $0\`" -- "$@"`
if [ $? != 0 ]; then
	show_usage_and_exit
fi

eval set -- "$TEMP"

automode=false
do_cfghwtype=false
do_cfgsave=false
do_cfgkernel=false
do_cfgserial=false
do_infocfg=false
do_dumpdiag=false
do_cfgremove=false
do_cfgcountry=false
setcountry=""
setdiagfile=""

if which gawk >/dev/null 2>&1; then
	AWK=gawk
else
	AWK=awk
fi

explicitopt=false

while true ; do
	case "$1" in
		-a|--auto) automode=true; shift ;;
		-c|--country) 
			explicitopt=true
			do_cfgcountry=true
			if [ -n "$2" ]; then
				setcountry="`echo \"$2\" | tr '[a-z ]' '[A-Z_]'`"
			fi
			shift 2
			;;
		-t|--hwtype) explicitopt=true; do_cfghwtype=true; shift ;;
		-k|--kernel) explicitopt=true; do_cfgkernel=true; shift ;;
		-s|--serial) explicitopt=true; do_cfgserial=true; shift ;;
		-i|--info)   explicitopt=true; do_infocfg=true; shift ;;
		-d|--dumpdiag)
			explicitopt=true
			do_dumpdiag=true
			setdiagfile="$2"
			shift 2
			;;
		-r|--remove) explicitopt=true; do_cfgremove=true; shift ;;
		-h|--help) show_usage_and_exit; shift ;;
		-V|--version) echo "${hcfversion}"; exit 0;;
		--) shift ; break ;;
		*) echo "Internal error!" ; exit 1 ;;
	esac
done

# Accomodate --country <name> as equivalent to --country=<name>
if ${do_cfgcountry} && [ $# -ge 1 -a -z "${setcountry}" ]; then
	setcountry="`echo \"$1\" | tr '[a-z ]' '[A-Z_]'`"
	shift
fi

# Accomodate --dumpdiag <file> as equivalent to --dumpdiag=<file>
if ${do_dumpdiag} && [ $# -ge 1 -a -z "${setdiagfile}" ]; then
	setdiagfile="$1"
	shift
fi

if [ $# -ne 0 ]; then
	echo 1>&2 "$0: extraneous arguments: $*"
	show_usage_and_exit
fi

# If nothing explicitely specified, configure everything
if ! ${explicitopt}; then
	do_cfghwtype=true
	do_cfgcountry=true
	do_cfgkernel=true
	do_cfgserial=true
fi

if ! ${do_cfgremove}; then
	show_intro
fi

# before doing anything, try to unload any hcf drivers possibly running
@HCFTARGET@stop >/dev/null 2>&1

identify_system >/dev/null

load_config || exit $?

if ${do_cfgremove}; then
	remove_kernel_modules || exit $?
	unconfigure_serial || exit $?
fi

if ${do_cfghwtype}; then
	configure_inf || exit $?
	do_cfgsave=true
	do_cfgserial=true
fi

if ${do_cfgcountry}; then
	configure_country || exit $?
	do_cfgsave=true
fi

if ${do_cfgsave}; then
	save_config || exit $?
fi

if ${do_cfgkernel}; then
	check_kernel || exit $?
	choose_kernel || exit $?
fi

if ${do_cfgserial}; then
	configure_serial || exit $?
fi

if ${do_infocfg}; then
	show_config || exit $?
fi

if ${do_dumpdiag}; then
	if [ -n "${setdiagfile}" ]; then
		hcfdiagfile="${setdiagfile}"
	fi
	dump_diagnostics || exit $?
fi
