#!/bin/sh 
#
# @(#)get_nsap.sh 1.25 91/01/11 Copyr 1986 Sun Micro
#

# NSAPs have an NSEL of 0 when CALLED_BY_SCRIPT is set.
# 

#
# Constants
#
	CLNS_FAMILIES="
	nbs		NBS format for MAP2.1/TOP1.0
	osinet		OSINET address format
	us-gosip-v1	US GOSIP Version 1 address format
	us-gosip-v2 	US GOSIP Version 2 address format
	user-defined	Any legal NSAP
	hex-pub 	Hexadecimal reference publication format"

	CONS_FAMILIES="
	cons-1980	X.121 address plus pid for CONS(1980).
	rfc-1006	OSI over TCP/IP according to Internet RFC-1006.
	inactive	The Inactive subset of CLNP."

	CLNS_ROUTING_FAMILIES="nbs osinet"
#
	BAD_NUM_HEX="Bad number of octets in"
	BAD_NUM_DEC="Bad number of digits in"
	BAD_DEC_SYNTAX="Bad syntax for decimal number"
	BAD_HEX_SYNTAX="Bad syntax for hex number"
#
	INSTALL_HELP="/usr/sunlink/osi/etc/install.help"

debug_echo()
{
	if [ "$DEBUG" = 1 ]
	then
		echo ">>$1"
	fi
}


### Input library

stdin_log() {
	if [ -z "$input_file" ]
	then
		read $1
		if [ "$?" = 1 ]
		then
			echo -n "[TAKING INPUT FROM TERMINAL NOW] "
			read $1 < /dev/tty
			input_file=stdin
		fi
	else
		read $1 < /dev/tty
	fi
	eval echo "\$$1" >> $LOGFILE
}

# Ask for input and handle '?' for help
# $1 is the asking string
# $2... is the help info
answer()
{
	answer_tmp="?"
	question=$1
	shift
	while [ "$answer_tmp" = "?" ]
	do
		echo -n "$question: "
		stdin_log answer_tmp
		if [ "$answer_tmp" = "?" ]; then
			$INSTALL_HELP $*
		fi
	done
}

# Ask for input and handle '?' for help
# $1 is the asking string
# $2 default answer (used when carriage return is entered)
# $3... is the help info
answer_default()
{
	answer_tmp="?"
	question=$1
	default=$2
	shift; shift
	while [ "$answer_tmp" = "?" ]
	do
		echo -n "$question [$default]: "
		stdin_log answer_tmp
		if [ "$answer_tmp" = "?" ]; then
			$INSTALL_HELP $*
		fi
		if [ "$answer_tmp" = "" ]; then
			answer_tmp=$default
		fi
	done
}

# $1 is the question
# $2... the help info
answer_yes_no()
{
	answer_tmp=""
	question=$1
	shift
	while [ "$answer_tmp" != "y" -a "$answer_tmp" != "n" ]
	do
		echo -n "$question (y/n): "
		stdin_log answer_tmp
		if [ "$answer_tmp" = "?" ]; then
			$INSTALL_HELP $*
		fi
	done
}

# $1 question
# $2 default
# $3... help info
answer_yes_no_default()
{
	answer_tmp=""
	question=$1
	default=$2
	shift; shift
	while [ "$answer_tmp" != "y" -a "$answer_tmp" != "n" ]
	do
		echo -n "$question (y/n)[$default]: "
		stdin_log answer_tmp
		if [ "$answer_tmp" = "?" ]; then
			$INSTALL_HELP $*
		fi
		if [ "$answer_tmp" = "" ]; then
			answer_tmp=$default
		fi
	done
}

### Input library END

syntax2metric()
{
	case $1 in
		hex)
			metric="octets";;
		dec)
			metric="digits";;
		char|natchar)
			metric="characters";;
	esac
}

# $1 string
answer2raw_len()
{
	raw_len=`echo "$1" | wc -c`
	raw_len=`expr $raw_len - 1`
}

# $1 string
# $2 metric
# Returns len and odd
answer2len()
{
	odd=0
	answer2raw_len "$1"
	len=$raw_len
	case "$2" in
		hex)
			odd=`expr $len / 2`
			odd=`expr $odd \* 2`
			odd=`expr $len - $odd`
			# Round up and divide by 2 
			len=`expr $len + 1`
			len=`expr $len / 2`
			;;
		dec|char|natchar)
			;;
	esac
}

# $1 string
# $2 starting position (1...)
# $3 length
substr()
{
	debug_echo "substr of: $1, $2, $3"
	start=`expr $2 + 1`
	substr_tmp=`echo "" | awk "{ print substr(\"f$1\", $start, $3)}"`
	debug_echo "substr: $substr_tmp"
}

strip_leading_pad()
{
	strip_tmp=$1
	strip_char=$2
	debug_echo "stripping: $strip_tmp"
	while true
	do
		old_tmp=$strip_tmp
		strip_tmp=`echo $strip_tmp | sed s/'^'$strip_char//`
		if [ "$old_tmp" = "$strip_tmp" ] 
		then
			break
		fi
		debug_echo "stripping: $strip_tmp"
	done
	if [ "$strip_tmp" = "" ]
	then
		strip_tmp=0
	fi
	debug_echo "strip: $strip_tmp"
}

# $1 syntax (hex, dec, ..)
# $2 string
check_syntax()
{
	string=$2
	case $1 in
		hex)
			return `echo "$string" | \
					grep '^[0-9a-fA-F]*$' >/dev/null`
			;;
		dec)
			return `echo "$string" | \
					grep '^[0-9]*$' >/dev/null`
			;;
		*)
			echo "Unsupported syntax: $1"
			exit 5
			;;
	esac
}

### General input routing

# $1 field (internal use e.g. in help info) e.g. "subnet_nbs"
# $2 field_name (prompted to user) e.g. "(nbs) subnetwork number"
# $3 syntax (hex, dec) hex syntax has to be padded to an integral number of
#	octets. (one leading 0)
# $4 maxlen (0..40) For hex syntax this is in octets, for dec syntax in digits.
# $5 fixlen (true/false) true means that the user has to enter exactly $maxlen
#	digits/octets.
# $6 default value
# Returns $field_tmp
get_field()
{
	field=$1
	field_name=$2
	syntax=$3
	maxlen=$4
	fixlen=$5
	field_default=$6

	syntax2metric $syntax
	# Returned metric

	if $fixlen
	then
		str="Exactly"
	else
		str="Maximum"
	fi
	while true
	do
		if [ "$field_default" = "" ]
		then
			answer "Enter a $field_name ($str $maxlen $metric)" \
				install_routes nsap $field
		else
			answer_default "Enter a $field_name ($str $maxlen $metric)"\
				"$field_default" install_routes nsap $field
		fi
		field_tmp="$answer_tmp"
		check_syntax $syntax "$field_tmp"
		if [ $? != 0 ]
		then 
			case $syntax in
			hex)
				echo "$BAD_HEX_SYNTAX: $field_tmp";;
			dec)
				echo "$BAD_DEC_SYNTAX: $field_tmp";;
			esac
			continue
		fi
		answer2len "$field_tmp" $syntax
		# Returned len and odd
		if [ $odd = 1 ]
		then
			answer2raw_len $field_tmp
			# Returned raw_len
               		cat << EOF
	The $field_name is $raw_len digits and will be padded 
	with a leading 0 (to "0$field_tmp") to form a result 
	having an integral number of $metric.
EOF
			answer_yes_no_default "Is this what you intended?" \
				"y" install_routes nsap pad2octet
			if [ "$answer_tmp" = "n" ]; then
                                continue
			fi
			field_tmp="0$field_tmp"
                fi

		if [ $fixlen = true ]
		then
			if [ $len != $maxlen ]
			then
				cat << EOF
	Bad number of $metric in $field_name "$field_tmp".
	The required length is $maxlen $metric and you entered $len $metric.
EOF
				continue
			fi
		else
			if [ $len -gt $maxlen ]
			then
				cat << EOF
	Bad number of $metric in $field_name "$field_tmp".
	The maximum length is $maxlen $metric and you entered $len $metric.
EOF
				continue
			fi
		fi
		break
	done
}



# $1 field (internal use e.g. in help info) e.g. "subnet_nbs"
# $2 field_name (prompted to user) e.g. "(nbs) subnetwork number"
# $3 syntax (hex, dec). No padding of hex syntax (to an integral number of
#	octets)
# $4 maxlen (0..40) For hex syntax this is in octets, for dec syntax in digits.
# $5 fixlen (true/false) true means that the user has to enter exactly $maxlen
#	digits/octets.
# $6 default value
# Returns $field_tmp
get_field_prefix()
{
	field=$1
	field_name=$2
	syntax=$3
	maxlen=$4
	fixlen=$5
	field_default=$6

	syntax2metric $syntax
	# Returned metric

	if $fixlen
	then
		str="Exactly"
	else
		str="Maximum"
	fi
	while true
	do
		if [ "$field_default" = "" ]
		then
			answer "Enter a $field_name ($str $maxlen $metric)" \
				install_routes nsap $field
		else
			answer_default "Enter a $field_name ($str $maxlen $metric)"\
				"$field_default" install_routes nsap $field
		fi
		field_tmp="$answer_tmp"
		check_syntax $syntax "$field_tmp"
		if [ $? != 0 ]
		then 
			case $syntax in
			hex)
				echo "$BAD_HEX_SYNTAX: $field_tmp";;
			dec)
				echo "$BAD_DEC_SYNTAX: $field_tmp";;
			esac
			continue
		fi
		answer2len "$field_tmp" $syntax
		# Returned len and odd
		if [ $fixlen = true ]
		then
			if [ $len != $maxlen ]
			then
				cat << EOF
	Bad number of $metric in $field_name "$field_tmp".
	The required length is $maxlen $metric and you entered $len $metric.
EOF
				continue
			fi
		else
			if [ $len -gt $maxlen ]
			then
				cat << EOF
	Bad number of $metric in $field_name "$field_tmp".
	The maximum length is $maxlen $metric and you entered $len $metric.
EOF
				continue
			fi
		fi
		break
	done
}


get_ip_addr()
{
	q_tmp="$1"
	d_tmp="$2"
	shift
	shift
	h_tmp="$*"

	# We check the IP address and the netmask to make sure they have
	# the form "a.b.c.d", with a through d being decimal numbers. 
	# We *do not* check that each number is less then 256.
	# hostnames are allowed as long as they in /etc/hosts or in the NIS 
	# hosts.byname map.

	while true
	do
		# Ask for the IP address
		if [ "$d_tmp" = "" ]; then
			answer "$q_tmp" $h_tmp
		else
			answer_default "$q_tmp" $d_tmp $h_tmp
		fi
		if [ "$answer_tmp" = "" ]; then
			continue
		fi
		tmp_ip=$answer_tmp
		echo $tmp_ip | grep -s '^[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*$' 
		if [ $? = 0 ]; then
			break;
		fi
		grep -s $tmp_ip'$' /etc/hosts
		if [ $? = 0 ]; then
			tmp_ip=`grep $tmp_ip'$' /etc/hosts  | awk '{print $1}'`
			break
		fi
		ypmatch $tmp_ip hosts >/dev/null 2>&1
		if [ $? = 0 ]; then
			tmp_ip=`ypmatch $tmp_ip hosts | awk '{print $1}'`
			break
		fi
		echo "Bad syntax or unknown IP address: $tmp_ip"
	done
	# XXX Convert 1.2.3.4 to hex
}



### NSAP handling routines

get_family()
{
	loop=true
	while [ $loop = true ]
	do
		answer "Please select one of the families: $CLNS_FAMILIES 
"\
			install_routes nsap family

		case "$answer_tmp" in
			nbs|"(nbs)")
				family_tmp=nbs
				loop=false;;
			osinet|"(osinet)")
				family_tmp=osinet
				loop=false;;
			user-defined|"(user-defined)")
				family_tmp=user-defined
				loop=false;;
			hex-pub|"(hex-pub)")
				family_tmp=hex-pub
				loop=false;;
			us-gosip-v1|"(us-gosip-v1)")
				family_tmp=us-gosip-v1
				loop=false;;
			us-gosip-v2|"(us-gosip-v2)")
				family_tmp=us-gosip-v2
				loop=false;;
			"")
				;;
			*)
				echo "Bad family: $answer_tmp";;
		esac
	done
}

get_family_or_cons()
{
	loop=true
	while [ $loop = true ]
	do
		answer "Please select one of the families: $CLNS_FAMILIES$CONS_FAMILIES
"\
			install_routes nsap family_cons

		case "$answer_tmp" in
			nbs|"(nbs)")
				family_tmp=nbs
				loop=false;;
			osinet|"(osinet)")
				family_tmp=osinet
				loop=false;;
			user-defined|"(user-defined)")
				family_tmp=user-defined
				loop=false;;
			hex-pub|"(hex-pub)")
				family_tmp=hex-pub
				loop=false;;
			us-gosip-v1|"(us-gosip-v1)")
				family_tmp=us-gosip-v1
				loop=false;;
			us-gosip-v2|"(us-gosip-v2)")
				family_tmp=us-gosip-v2
				loop=false;;
			cons-1980)
				family_tmp=cons-1980
				loop=false;;
			rfc-1006)
				family_tmp=rfc-1006
				loop=false;;
			inactive)
				family_tmp=inactive
				loop=false;;
			"")
				;;
			*)
				echo "Bad family: $answer_tmp";;
		esac
	done
}

get_routing_family()
{
	loop=true
	while [ $loop = true ]
	do
		answer "Please select one of the families ($CLNS_ROUTING_FAMILIES)"  \
			install_routes nsap routing_family
		case "$answer_tmp" in
			nbs|"(nbs)")
				family_tmp=nbs
				loop=false;;
			osinet|"(osinet)")
				family_tmp=osinet
				loop=false;;
			"")
				;;
			*)
				echo "Bad family: $answer_tmp";;
		esac
	done
}

nsel_len()
{
	case $1 in
		hex)
			nsel_len_tmp=1;;
		dec)
			nsel_len_tmp=2;;
		char|natchar)
			nsel_len_tmp=2;;	# Really?
	esac
}

# $1 address family
# $2 syntax 
get_nsel()
{
	family="$1"
	syntax=$2
	nsel_len $syntax
	# Returned nsel_len_tmp
	if [ $CALLED_BY_SCRIPT = 1 ]
	then
		# Always use an NSEL of 0.
		nsel_tmp="00"
	else
		while true
		do
			get_field nsel "$family NSEL" $syntax $nsel_len_tmp\
				 true 00
			# *** Warn/prevent non-zero NSELs?
			nsel_tmp="$field_tmp"
			break
		done
	fi
}


### One input routine per family

get_nbs_subnet()
{
	loop=true
	while $loop
	do
		get_field subnet_nbs "(nbs) subnetwork number" hex 5 false 01
		# Check that the subnetwork number is in 00-ff or
		# 0100000000-ffffffffff
		answer2len "$field_tmp" hex
		case $len in
			1)
				field_tmp="00$field_tmp"
				loop=false
				;;
			5)
				echo "$field_tmp" | grep '^00' >/dev/null
				if [ $? != 0 ]
				then
					loop=false
				fi	
				;;
			2)
				echo "$field_tmp" | grep '^00' >/dev/null
				if [ $? = 0 ]
				then
					loop=false
				fi
				;;
			3|4)
				;;
		esac
		if $loop
		then
			cat << EOF
	Bad number of octets in (nbs) subnetwork number "$field_tmp".
	The subnetwork number must be either a 1 octet number (00-ff)
	or a five octet number with a non-zero leading octet (in 
	the range 0100000000 through ffffffffff).
EOF
			continue
		fi
	done
	subnet_tmp=$field_tmp
}



# $1 "nsap" or "net"
# $2 default mac address (excluding LSEL)
# Return is $afi $idi and $dsp
get_nbs()
{
	type=$1
	default_mac=$2

	afi=49
	lookup_afi $afi
	idi=""

	get_nbs_subnet
	subnet="$subnet_tmp"

	while true
	do
		if [ "$default_mac" = "" ]; then
			default_station=""
		else
			default_station="$default_mac"fe
		fi
		get_field station_nbs "(nbs) station id (ending in \"fe\")" \
				hex 7 true $default_station

		echo "$field_tmp" | grep 'fe$' >/dev/null
                if [ $? != 0 ]
                then
 			cat << EOF
	The LSEL (last octet) in the station id "$field_tmp" is 
	not "fe" which is the standard LSEL.
EOF
			answer_yes_no_default "Use this non-standard LSEL?" \
					"y" install_routes nsap lsel
			if [ "$answer_tmp" = "n" ]; then
	                               continue
			fi
                fi
		station="$field_tmp"

		break
	done

	if [ $type = nsap ]; then
		get_nsel '(nbs)' hex
		nsel="$nsel_tmp"
	else
		nsel=""
	fi
	dsp="$subnet$station$nsel"
}

get_osinet_subnet()
{
	get_field org_osinet "(osinet) organization number" hex 2 \
				false 21
	org="$field_tmp"

	get_field subnet_osinet "(osinet) subnetwork number" hex 2 \
				false 01
	subnet="$field_tmp"
	subnet_tmp="$org, $subnet"
}

# $1 "nsap" or "net"
# $2 default mac address (excluding LSEL)
# Return is $afi $idi and $dsp
get_osinet()
{
	type=$1
	default_mac=$2

	afi=47
	lookup_afi $afi
	idi=4

	get_field org_osinet "(osinet) organization number" hex 2 \
				true 0021
	org="$field_tmp"

	get_field subnet_osinet "(osinet) subnetwork number" hex 2 \
				true 0001
	subnet="$field_tmp"

	get_field station_osinet "(osinet) station id" hex 6 true \
				$default_mac
	station="$field_tmp"

	if [ $type = nsap ]; then
		get_nsel '(osinet)' hex
		nsel="$nsel_tmp"
	else
		nsel=""
	fi

	dsp="$org$subnet$station$nsel"
}

# $1 "nsap" or "net"
# $2 default mac address (excluding LSEL)
# Return is $afi $idi and $dsp
get_user_defined()
{
	type=$1
	default_dsp="$2"

	while true 
	do
		get_field afi "AFI" dec 2 true ""
		if [ $field_tmp -lt 36 -o $field_tmp -gt 59 ]
		then
			echo "	Illegal AFI value: \"$answer_tmp\"."
			continue
		fi
		afi=$field_tmp
		if lookup_afi $afi
		then
			echo "	Unsupported AFI value: \"$answer_tmp\"."
			continue
		fi
		break
	done
		
	# Returned idi_maxlen, idi_fixlen, idi_pad, dsp_syntax, dsp_maxlen

	if [ $idi_maxlen = 0 ]
	then
		idi=""
	else
		while true 
		do
			get_field idi "IDI" dec $idi_maxlen false ""
			idi=$field_tmp
			if [ false = false ]
			then
				echo "$idi" | grep '^'"$idi_pad" >/dev/null
				if [ $? = 0 ]
				then
					cat << EOF
	With AFI $afi the IDI cannot have any leading $idi_pad's (since
	$idi_pad's are used when padding the IDI to its maximum length). 
	Please enter "$idi" without any	leading $idi_pad's or use an
	AFI that allows leading $idi_pad's in the address.
EOF
					continue
				fi
			fi
			break
		done
 	fi

	# Remove the NSEL from the length
	nsel_len $dsp_syntax
	dsp_maxlen=`expr $dsp_maxlen - $nsel_len_tmp`	
	get_field dsp "DSP (excluding the NSEL)" $dsp_syntax $dsp_maxlen \
			false "$default_dsp"
	dsp=$field_tmp

	if [ $type = nsap ]; then
		get_nsel '(user-defined)' $dsp_syntax
		nsel="$nsel_tmp"
	else
		nsel=""
	fi


	dsp="$dsp$nsel"
}

# $1 "nsap" or "net"
# $2 default station id
# Return is $afi $idi and $dsp
get_hex_pub()
{
	type=$1
	default_station="$2"
	FAM="HRPF (excluding the leading '/')"
	while true
	do
		get_field hex_pub "$FAM" hex 20 false ""
		nsap=$field_tmp

		decode_nsap_binary $nsap
		case $? in
			0)
				;;
			1)
				cat << EOF
	Bad address format "$nsap". The length must be at least 1 octet.
EOF
				continue;;
			2)
				echo "	Illegal AFI value: \"$afi\" in NSAP \"$nsap\"."
				continue;;
			20)
				echo "	Unsupported AFI value: \"$afi\" in NSAP \"$nsap\"."
				continue;;
			3)

					cat << EOF
	Too short NSAP "$nsap". The encoded IDI should be $idi_encode_len
	digits (including a trailing 'f') but there are only $len 
	digits after the AFI.
EOF
					continue;;
			4)
				cat << EOF
	The IDI binary encoding "$idi" does not have a trailing 'f'.
	It is required to have that since its length is an odd
	number of digits and the syntax of the DSP is not decimal. 
	The AFI $afi indicates that the IDI should be $idi_maxlen.
EOF
				continue;;
			5)
				cat << EOF
	Too short NSAP "$nsap". The encoded IDI should be $idi_encode_len
	digits but there are only $len digits after the AFI.
EOF
				continue;;
			6)
				cat << EOF
	The IDI binary encoding "$idi" has a trailing 'f' which it shouldn't 
	have. A trailing 'f' should only be used for IDIs with an odd length
	when the syntax of the DSP is not decimal.
	The AFI $afi indicates that the IDI length is $idi_maxlen.
EOF
				continue;;
			7)
				echo "$BAD_DEC_SYNTAX: $idi"
				continue;;
			8)
				case $dsp_syntax in
					dec)
						echo "$BAD_DEC_SYNTAX: $dsp";;
					hex)
						echo "$BAD_HEX_SYNTAX: $dsp";;
				esac					
				continue;;
			9)
				cat << EOF
	Internal error: the hex syntax DSP "$dsp" is not an integral
	number of octets even though the NSAP is.
EOF
				continue;;
			10)
				syntax2metric $dsp_syntax
				cat << EOF
	Too long DSP: "$dsp". The maximum DSP length for AFI $afi 
	is $dsp_maxlen $metric and you entered $len $metric.
EOF
				continue;;
		esac
		break
	done

}
		

# $1 "nsap" or "net"
# $2 default station id
# Return is $afi $idi and $dsp
get_us_gosip_v1()
{
	type=$1
	default_station="$2"
	FAM="GOSIP V1"
	afi=47
 	lookup_afi $afi
	idi=5

	get_field org_gosip "$FAM organization ID" hex 2 true ""
	org=$field_tmp

	get_field subnet_gosip "$FAM subnet ID" hex 2 true 0001
	subnet=$field_tmp

	while true
	do
		get_field station_gosip "$FAM end system ID" hex 8 false \
				"$default_station" 
		station=$field_tmp
		answer2len "$station" hex
		# Returned len
		if [ $len -lt 4 ]
		then
			cat << EOF
	Bad number of octets in $FAMend system ID "$field_tmp".
	The end system ID must be between 4 and 8 octets and you entered $len
	octets.
EOF
			continue
		fi
		break
	done
	if [ $type = nsap ]; then
		get_nsel "$FAM" hex
		nsel="$nsel_tmp"
	else
		nsel=""
	fi

	dsp="$org$subnet$station$nsel"
}



# $1 "nsap" or "net"
# $2 default station id
# Return is $afi $idi and $dsp
get_us_gosip_v2()
{
	type=$1
	default_station="$2"
	FAM="GOSIP V2"
	afi=47
	lookup_afi $afi
	idi=5

	get_field version_gosip "$FAM data format identifier" hex 1 true "80"
	version=$field_tmp

	get_field admin_gosip "$FAM admin authority" hex 3 true ""
	admin=$field_tmp

	reserved="0000"

	get_field rtdomain_gosip "$FAM routing domain" hex 2 true ""
	rtdomain=$field_tmp

	get_field area_gosip "$FAM area" hex 2 true 0001
	area=$field_tmp

	get_field es_gosip "$FAM end system" hex 6 true \
				"$default_station" 
	es=$field_tmp

	if [ $type = nsap ]; then
		get_nsel "$FAM" hex
		nsel="$nsel_tmp"
	else
		nsel=""
	fi

	dsp="$version$admin$reserved$rtdomain$area$es$nsel"
}

# Return is $x121 and $pid
get_cons_1980()
{
	get_field x121 "X.121 address" dec 14 false ""
	x121="$field_tmp"

	answer_yes_no_default "Does the address have a PID?" y \
		install_routes nsap has_pid
	if [ $answer_tmp = n ]; then
		pid=""
	else
		get_field pid "X.25 protocol id" hex 4 true "03010100"
		pid="$field_tmp"
	fi
}

# Return is $dsp
get_inactive()
{
	get_field interface "LLC interface number" hex 1 true "00"
	if="$field_tmp"

	get_field mac_addr "MAC address" hex 6 true ""
	mac="$field_tmp"

	nsel=00

	dsp=$if$mac$nsel
}

# Return is $prefix_tmp
get_prefix()
{
	while true; do
		get_field_prefix prefix "NSAP prefix (all digits)" \
			hex 19 false ""
		prefix_tmp="$field_tmp"
		answer2len "$prefix_tmp" dec
		# Returned len (and odd)
		# Verify the AFI
		case $len in
		0) ;;
		1) 
			if [ $prefix_tmp -lt 3 -o $prefix_tmp -gt 5 ]; then
				echo "Bad AFI in NSAP prefix: $prefix_tmp"
				continue
			fi
			;;
		*)
			substr $prefix_tmp 1 2
			afi=$substr_tmp

			if [ $afi -lt 36 -o $afi -gt 59 ]
			then
				echo "Bad AFI in NSAP prefix: $prefix_tmp"
				continue
			fi
			;;
		esac
		break
	done
}

# Return is $prefix_tmp
get_idi_prefix()
{
	while true; do
		get_field_prefix idi_prefix "IDI NSAP prefix (all digits)" \
			hex 19 false ""
		prefix_tmp="$field_tmp"
		plen=`expr length "$prefix_tmp"`
		if [ $plen -lt 2 ]; then
			echo "Too short IDI prefix. Must be at least 2 digits."
			continue
		fi
		# Verify the AFI
		substr $prefix_tmp 1 2
		afi=$substr_tmp

		if [ $afi -lt 36 -o $afi -gt 59 ]
		then
			echo "Bad AFI in NSAP prefix: $prefix_tmp"
			continue
		fi
		# Verify the length and that there are no leading pad digits
		idi_len=`expr $plen - 2`
		substr $prefix_tmp 3 $idi_len
		idi="$substr_tmp"
		
		if lookup_afi $afi
		then
			echo "Unsupported AFI in NSAP prefix: $prefix_tmp"
			continue
		fi
		if [ $idi_len -gt $idi_maxlen ]; then
			echo "Too long IDI in prefix: $prefix_tmp"
			continue
		fi
		echo "$idi" | grep -s '^'$idi_pad
		if [ $? = 0 ]; then
			echo "IDI begins with a pad digit: $prefix_tmp"
			continue
		fi
		break
	done
}

# Return is $internet
get_rfc_1006()
{
	get_ip_addr "Enter an IP address" "" install_routes rfc_1006 
	internet=$tmp_ip
}

### AFI table lookup

# $1 afi
# Returns: 
#	  idi_maxlen: the maximum length of the IDI
#	  idi_fixlen: true/false
#	  idi_pad: The pad character used to pad the IDI to its max length.
#	  dsp_syntax: one of "dec", "hex", "char" and "natchar"
#	  dsp_maxlen: in octets or digits (char, natchar) depending on syntax.
lookup_afi()
{
	afi=$1
	idi_pad='0'
	case "$afi" in
		36)	idi_maxlen=14; dsp_syntax=dec; 
			idi_fixlen=false; idi_pad='0'; dsp_maxlen=24;;
		37)	idi_maxlen=14; dsp_syntax=hex;
			idi_fixlen=false; idi_pad='0'; dsp_maxlen=9;;
		38)	idi_maxlen=3; dsp_syntax=dec; 
			idi_fixlen=true; dsp_maxlen=35;;
		39)	idi_maxlen=3; dsp_syntax=hex; 
			idi_fixlen=true; dsp_maxlen=14;;
		40)	idi_maxlen=8; dsp_syntax=dec; 
			idi_fixlen=false; idi_pad='0'; dsp_maxlen=30;;
		41)	idi_maxlen=8; dsp_syntax=hex; 
			idi_fixlen=false; idi_pad='0'; dsp_maxlen=12;;
		42)	idi_maxlen=12; dsp_syntax=dec; 
			idi_fixlen=false; idi_pad='0'; dsp_maxlen=26;;
		43)	idi_maxlen=12; dsp_syntax=hex; 
			idi_fixlen=false; idi_pad='0'; dsp_maxlen=10;;
		44)	idi_maxlen=15; dsp_syntax=dec; 
			idi_fixlen=false; idi_pad='0'; dsp_maxlen=23;;
		45)	idi_maxlen=15; dsp_syntax=hex; 
			idi_fixlen=false; idi_pad='0'; dsp_maxlen=9;;
		46)	idi_maxlen=4; dsp_syntax=dec; 
			idi_fixlen=true; dsp_maxlen=34;;
		47)	idi_maxlen=4; dsp_syntax=hex; 
			idi_fixlen=true; dsp_maxlen=13
# XXX This is a gross hack since GOSIP and RFC 1069 addresses are too long
			dsp_maxlen=17;;
		48)	idi_maxlen=0; dsp_syntax=dec; 
			idi_fixlen=true; dsp_maxlen=38;;
		49)	idi_maxlen=0; dsp_syntax=hex; 
			idi_fixlen=true; dsp_maxlen=15;;
		50)	idi_maxlen=0; dsp_syntax=char
			idi_fixlen=true; dsp_maxlen=19
			echo "Character abstract syntax not supported"
			return 0
			;;
		51)	idi_maxlen=0; dsp_syntax=natchar
			idi_fixlen=true; dsp_maxlen=7
			echo "National Character abstract syntax not supported"
			return 0
			;;
		52)	idi_maxlen=14; dsp_syntax=dec; 
			idi_fixlen=false; idi_pad='1'; dsp_maxlen=24;;
		53)	idi_maxlen=14; dsp_syntax=hex;
			idi_fixlen=false; idi_pad='1'; dsp_maxlen=9;;
		54)	idi_maxlen=8; dsp_syntax=dec; 
			idi_fixlen=false; idi_pad='1'; dsp_maxlen=30;;
		55)	idi_maxlen=8; dsp_syntax=hex; 
			idi_fixlen=false; idi_pad='1'; dsp_maxlen=12;;
		56)	idi_maxlen=12; dsp_syntax=dec; 
			idi_fixlen=false; idi_pad='1'; dsp_maxlen=26;;
		57)	idi_maxlen=12; dsp_syntax=hex; 
			idi_fixlen=false; idi_pad='1'; dsp_maxlen=10;;
		58)	idi_maxlen=15; dsp_syntax=dec; 
			idi_fixlen=false; idi_pad='1'; dsp_maxlen=23;;
		59)	idi_maxlen=15; dsp_syntax=hex; 
			idi_fixlen=false; idi_pad='1'; dsp_maxlen=9;;
		*)
			echo "Illegal AFI value ($answer)"
			return 0
			;;
	esac
	return 1
}

### Encoding/decoding functions

# $1 afi
# $2 idi
# $3 idi_maxlen
# $4 idi_fixlen
# $5 idi_pad
# $6 dsp
# $7 dsp_syntax
# returns $encode_tmp
encode_nsap_binary()
{
	afi_tmp=$1
	idi_tmp="$2"
	idi_maxlen_tmp=$3
	idi_fixlen_tmp=$4
	idi_pad_tmp=$5
	dsp_tmp=$6
	dsp_syntax_tmp=$7

	if [ $idi_maxlen_tmp = 0 ]
	then
		idi_tmp=""
	else
		answer2len "$idi_tmp" dec
		# Returned len and odd

		# We can pad all IDI with leading 0's to their maxlen
		pad=`expr $idi_maxlen - $len`
		while [ $pad -gt 0 ]
		do
			idi_tmp=$idi_pad_tmp$idi_tmp
			pad=`expr $pad - 1`
		done
		# If the dsp is not decimal we have to pad to an integral
		# number of octets
		if [ $dsp_syntax != "dec" ]
		then
			answer2len "$idi_tmp" hex
			if [ $odd = 1 ]
			then
				idi_tmp=$idi_tmp"f"
			fi
		fi		
	fi
	case $dsp_syntax_tmp in
		hex)
			;;
		dec)
			# If the nsap will not have an integral number of
			# octets we pad the dsp with a trailing 'f'
			answer2len "$idi_tmp$dsp_tmp" hex
			# Returned len and odd
			if [ $odd = 1 ]
			then
				dsp_tmp=$dsp_tmp"f"
			fi
			;;
		char|natchar)
			echo "Char/natchar abstract syntaxes are not supported"
			exit 5
			;;
	esac
	encode_tmp="$afi_tmp$idi_tmp$dsp_tmp"
}

# $1 nsap encoding
# returns afi, idi, dsp
# Return value 0 if ok, else error code 1 through 10
decode_nsap_binary()
{
	nsap="$1"

	answer2len "$nsap" hex
	# Returned len (and odd)
	if [ $len = 0 ]
	then
		return 1
	fi
	nsap_len=$len
	substr $nsap 1 2
	afi=$substr_tmp

	if [ $afi -lt 36 -o $afi -gt 59 ]
	then
		return 2
	fi

	if lookup_afi $afi
	then
		return 20
	fi

	odd=`expr $idi_maxlen / 2`
	odd=`expr $odd \* 2`
	odd=`expr $idi_maxlen - $odd`
	if [ $odd = 1 -a $dsp_syntax != "dec" ]
	then
		idi_encode_len=`expr $idi_maxlen + 1`		
		substr $nsap 3 $idi_encode_len
		idi=$substr_tmp
		answer2len "$idi" dec
		if [ $len != $idi_encode_len ]
		then
			return 3
		fi
		echo $idi | grep 'f$' >/dev/null
		if [ $? != 0 ]
		then
			return 4
		fi
		idi=`echo $idi | sed 's/f$//'`
	else
		idi_encode_len=$idi_maxlen
		substr $nsap 3 $idi_encode_len
		idi=$substr_tmp
		answer2len "$idi" dec
		if [ $len != $idi_encode_len ]
		then
			return 5
		fi
		echo $idi | grep 'f$' >/dev/null
		if [ $? = 0 ]
		then
			return 6
		fi
	fi
	check_syntax dec $idi 
	if [ $? != 0 ]
	then 
		return 7
	fi
	if [ false = false ]
	then
		strip_leading_pad $idi $idi_pad
		idi=$strip_tmp
	fi
	# The IDI starts at 3 and is idi_encode_len long
	dsp_start=`expr 3 + $idi_encode_len`
	substr $nsap $dsp_start 40
	dsp=$substr_tmp
	if [ $dsp_syntax = "dec" ]
	then
		# Strip trailing 'f' if any
		dsp=`echo $dsp | sed 's/f$//'`
	fi
		
	check_syntax $dsp_syntax $dsp
	if [ $? != 0 ]
	then 
		return 8
	fi
	answer2len "$dsp" $dsp_syntax
	if [ $odd = 1 ]
	then
		return 9
	fi
	if [ $len -gt $dsp_maxlen ]
	then
		return 10
	fi
	debug_echo "AFI: $afi, IDI: $idi, DSP: $dsp"
	return 0
}



# $1 "net" or "nsap"
# $2 "net" or not there
# $3 default_mac in station id (used by local address and possibly host_routes)
get_nsap()
{
	type=$1	# nsap or net
	shift
	if [ $# != 0 -a "$1" = num ]; then
 		retnum=1
		shift
	else
		retnum=0
	fi
	default_mac=$1

	get_family
	case $family_tmp in
		nbs)
			get_nbs $type $default_mac;;
		osinet)
			get_osinet $type $default_mac;;
		user-defined)
			get_user_defined $type $default_mac;;
		hex-pub)
			get_hex_pub $type $default_mac;;
		us-gosip-v1)
			get_us_gosip_v1 $type $default_mac;;
		us-gosip-v2)
			get_us_gosip_v2 $type $default_mac;;
	esac		
	if [ $retnum = 0 ]; then
		case $family_tmp in
			nbs)
				# Use "Local" macro
				addr_tmp="Local="$dsp
				;;
			osinet)
				# Use "OSINET" macro
				addr_tmp="OSINET="$dsp
				;;
			us-gosip-v1|us-gosip-v2)
				# Use "GOSIP" macro
				addr_tmp="GOSIP="$dsp
				;;
			*)
				debug_echo "AFI:$afi,IDI:$idi,DSP:$dsp"
				encode_nsap_binary $afi "$idi" $idi_maxlen false\
						 "$idi_pad" "$dsp" $dsp_syntax
				# Returned encode_tmp
				addr_tmp="NSAP="$encode_tmp
				;;	
		esac
	else
		debug_echo "AFI:$afi,IDI:$idi,DSP:$dsp"
		encode_nsap_binary $afi "$idi" $idi_maxlen false "$idi_pad"\
				 "$dsp" $dsp_syntax
		# Returned encode_tmp
		addr_tmp=$encode_tmp
	fi
	nsap_tmp="$addr_tmp"
}

# $1 default nsap?
get_nsap_or_cons()
{
	default_nsap=$1
	default_mac=""

	get_family_or_cons
	case $family_tmp in
		nbs)
			get_nbs nsap $default_mac;;
		osinet)
			get_osinet nsap $default_mac;;
		user-defined)
			get_user_defined nsap $default_mac;;
		hex-pub)
			get_hex_pub nsap $default_mac;;
		us-gosip-v1)
			get_us_gosip_v1 nsap $default_mac;;
		us-gosip-v2)
			get_us_gosip_v2 nsap $default_mac;;
		cons-1980)
			get_cons_1980;;
		rfc-1006)
			get_rfc_1006;;
		inactive)
			get_inactive;;
	esac		
	case $family_tmp in
		nbs)
			# Use "Local" macro
			addr_tmp="Local="$dsp
			;;
		osinet)
			# Use "OSINET" macro
			addr_tmp="OSINET="$dsp
			;;
		us-gosip-v1|us-gosip-v2)
			# Use "GOSIP" macro
			addr_tmp="GOSIP="$dsp
			;;
		cons-1980)
			# Use Int-X25 macro
			addr_tmp="Int-X25=$x121"
			if [ "$pid" != "" ]; then
				addr_tmp="$addr_tmp+PID+$pid"
			fi
			;;
		rfc-1006)
			# Use Internet macro
			addr_tmp="Internet=$internet"
			;;
		inactive)
			# Use INACTIVE macro
			addr_tmp="INACTIVE=$dsp"
			;;
		*)
			debug_echo "AFI:$afi,IDI:$idi,DSP:$dsp"
			encode_nsap_binary $afi "$idi" $idi_maxlen false "$idi_pad"\
					 "$dsp" $dsp_syntax
			# Returned encode_tmp
			addr_tmp="NSAP="$encode_tmp
			;;	
	esac
	nsap_tmp="$addr_tmp"
}

# $1 family (nbs/osinet)
# returns $subnet_tmp
get_subnet()
{
	case $1 in
		nbs)
			get_nbs_subnet
			;;
		osinet)
			get_osinet_subnet
			;;
		*)
			echo "	BAD family: $1"
			exit 5
			;;
	esac
}
	
decode_nsap()
{
	answer2len "$1" dec
	if [ $len -lt 2 -o $len -gt 40 -o $odd = 1 ]
	then
		return 99
	fi
	decode_nsap_binary $1
	return $?
}	

main()
{
	if [ "$LOGFILE" = "" ] 
	then
		LOGFILE="/dev/null"
		export LOGFILE
	fi
	if [ "$#" = 0 ]
	then
		CALLED_BY_SCRIPT=0
		echo ""
		echo "Enter an NSAP:"
		get_nsap nsap ""
		echo "NSAP: $nsap_tmp"
		return 0
	fi
	if [ "$1" = "-C" ]
	then
		CALLED_BY_SCRIPT=1
		shift
	else
		CALLED_BY_SCRIPT=0
	fi
	if [ "$1" = "-f" ]
	then
		file=$2
		shift;shift
	else
		file=/dev/tty
	fi
	function=$1
	shift
	case $function in
		get_nsap)
			get_nsap nsap $*
			result=$nsap_tmp;;
		get_nsap_num)
			get_nsap nsap num $*
			result=$nsap_tmp;;
		get_nsap_or_cons)
			get_nsap_or_cons $*
			result=$nsap_tmp;;
		get_net)
			get_nsap net $*
			result=$nsap_tmp;;
		get_routing_family)
			get_routing_family $*
			result=$family_tmp;;
		get_subnet)
			get_subnet $*
			result=$subnet_tmp;;
		get_prefix)
			get_prefix $*
			result=$prefix_tmp;;
		get_idi_prefix)
			get_idi_prefix $*
			result=$prefix_tmp;;
		get_ip_addr)
			get_ip_addr "Enter an IP address or a host name" "" \
				install_routes nsap ip
			result=$tmp_ip;;
		decode_nsap)
			# Return "afi idi dsp" or "afi dsp" if the idi is null
			# or "ERROR <number>" on error.
			decode_nsap $*
			res=$?
			if [ $res = 0 ]
			then
				result="$afi $idi $dsp"
			else
				result="ERROR $res"
			fi
			;;
#		decode_address)
			# Return a string describing the address?
			# decode_nsap + decode the dsp based on afi,idi 
			# return (family) field1, field2, ....
#			decode_address $*			
#			;;
		convert)
			# Convert (nbs) and (osinet) to (user-defined)
			case $1 in
				"(user-defined)") 
					decode_nsap $2
					res=$?
					if [ $res = 0 ]
					then
						result="$afi $idi $dsp"
					else
						result="ERROR $res"
					fi;;
	
				"(osinet)")
					# Strip out org, subnet, station, nsel
					;;
				"(nbs)")
					# Strip out subnet + fix its length
					;;
			esac
			;;
		*)
			echo "Bad function: $function"
			exit 5;;
	esac
	echo $result > $file
}

main $*
