:
#	@(#) tz.sh 1.23 89/07/05 
#
#

#	Copyright (C) The Santa Cruz Operation 1988, 1989.
#	This Module contains Proprietary Information of
#	The Santa Cruz Operation and should be treated as Confidential.
#		      All Rights Reserved
#

#
# Begin SCO_BASE 
# This script is run at installation to set the proper
# time zone variable (TZ) in /etc/TIMEZONE
#

# destination directory
dest=$ROOT

PATH=/bin:/usr/bin:/etc

tmp=$dest/tmp/tz$$
trap 'echo "$0: interrupted; time zone not changed.\n" >&2
	rm -f $tmp.*
	exit 1' 	1 2 3 15

# Define return values
: ${OK=0} ${FAIL=1} ${STOP=10} ${HALT=11}

# Initialize some variables
sdate=	edate=	hours=	smhours=
hh=	sthh=	mm=	stmm=
ss=	stss=	dst=
offset=2
quit=quit

########################
# Function definitions #
########################

# Prompt for yes or no answer - returns non-zero for no
getyn() {
	while	echo "\n$* (y/n) \c" >&2
	do	read yn rest
		case $yn in
		[yY])	return 0 				;;
		[nN])	return 1				;;
		*)	echo "\nPlease answer y or n" >&2	;;
		esac
	done
}

# Print an error message
error() {
	echo "\nError: $*" >&2
	return 1
}

# Prompt with mesg, return non-zero on q
prompt() {
	mesg=$1
	while	echo "\n${mesg}or enter q to $quit: \c" >&2
	do	read cmd
		case $cmd in
		+x|-x)	set $cmd					;;
		Q|q)	return 1					;;
		!*)	eval `expr "$cmd" : "!\(.*\)"`			;;
		"")	# If there is an second argument use it
			# as the default else loop until 'cmd' is set
			[ "$2" ] && { 
				cmd=$2
				return 0
			}
			: continue
			;;
		*)	return 0					;;
		esac
	done
}

# Get time zone name and check validity. Return non-zero if invalid
setzname() { 
	tzname=$1
	while	:
	do	
		len=`expr "$tzname" : ".*"`
		matched=`expr "$tzname" : "[^0-9+\;-]\{1,9\}"`
		[ $len -gt 0 -a $len -le 9 -a $matched -eq $len ] || { 
			error "Please enter 1-9 characters (characters 0-9,-,+,and ; are not allowed)."
			return 1
		}
		break
	done
	return 0
}
	
# Break the hours into hh, mm and ss components. Return non-zero if the value
# for hh is not within the values given by the second and third arguments or
# if the values given for mm and ss (if any) are not in the range 0-59
parsehours() { 
	hours=$1
	brange=$2
	erange=$3
	hh=`expr "$hours" : "+*\(-*[0-9]\{1,2\}\).*"`
	[ $hh -ge $brange -a $hh -le $erange ] || { 
		error "Please answer with a number between $brange and $erange"
		return $?
	}
	#mm=`expr "$hours" : ".*:\([0-9]\{0,2\}\):\{0,1\}.*"`
	mm=`echo $hours | awk -F: '{ print $2 }'`
	if [ $mm ]
	then
		[ $mm -ge 0 -a $mm -le 59 ] || { 
			error "Value for mm out of range (0-59)"
			return $?
		}
	fi
	ss=`expr "$hours" : ".*:.*:\([0-9]*\)"`
	if [ $ss ]
	then
		[ $ss -ge 0 -a $ss -le 59 ] || { 
			error "Value for ss out of range (0-59)"
			return $?
		}
	fi
	return 0
}
	
# Get the value for hours as requested by the first argument. Return non-zero
# if the value entered is not in the format hh:mm:ss
sethours() { 
	hours="$1"
	# brange and erange are passed along to parsehours
	brange=$2
	erange=$3
	while :
	do
		case $hours in
		[0-9]*:[0-9]*:[0-9]*|[+-][0-9]*:[0-9]*:[0-9]*)
			len=`expr "$hours" : ".*"`
			matched=`expr "$hours" : "[-+]\{0,1\}[0-9]\{0,2\}:[0-9]\{0,2\}:[0-9]\{0,2\}"`
			[ $len -gt 0 -a $matched -eq $len ] || { 
				error "Please use hh:mm:ss format for hours"
				return 1
			}
			;;
		[0-9]*:[0-9]*:|[+-][0-9]*:[0-9]*:)
			len=`expr "$hours" : ".*"`
			matched=`expr "$hours" : "[-+]\{0,1\}[0-9]\{0,2\}:[0-9]\{0,2\}:"`
			[ $len -gt 0 -a $matched -eq $len ] || { 
				error "Please use hh:mm:ss format for hours"
				return 1
			}
			;;
		[0-9]*:[0-9]*|[+-][0-9]*:[0-9]*)
			len=`expr "$hours" : ".*"`
			matched=`expr "$hours" : "[-+]\{0,1\}[0-9]\{0,2\}:[0-9]\{0,2\}"`
			[ $len -gt 0 -a $matched -eq $len ] || { 
				error "Please use hh:mm:ss format for hours"
				return 1
			}
			;;
		[0-9]*:|[+-][0-9]*:)
			len=`expr "$hours" : ".*"`
			matched=`expr "$hours" : "[-+]\{0,1\}[0-9]\{0,2\}:"`
			[ $len -gt 0 -a $matched -eq $len ] || { 
				error "Please use hh:mm:ss format for hours"
				return 1
			}
			;;
		[0-9]*|[+-][0-9]*)
			len=`expr "$hours" : ".*"`
			matched=`expr "$hours" : "[-+]\{0,1\}[0-9]\{0,2\}"`
			[ $len -gt 0 -a $matched -eq $len ] || { 
				error "Please use hh:mm:ss format for hours"
				return 1
			}
			;;
		*)
			error "Please use hh:mm:ss format for hours"
			return 1
			;;
		esac
			
		parsehours $hours $brange $erange || return 1
		break
	done
	return 0
}

# Prompt for method used for conversion between standard and summer time
setmethod() {
	direction=$1
	while :
	do
		prompt "\t1. Week of the year (1-52).
	2. Week of a specific month (eg. 1st week of April).
	3. Day of the year, ie. Julian date (1-365).

Select the method your time zone uses to convert from
$direction\n" || exit 0
		choice=$cmd
		case $choice in
	    	1)
			pref=W
			break
			;;
		2)	pref=M
			break
			;;
	    	3)
			pref=J
			break
			;;
	    	*)
			echo "\nPlease enter a '1', '2' or '3'" >&2
	    	esac
	    done
	    return 0
}

# Prompt for the conversion date
setdate() { 
	direction=$1
	while :
	do
		case $pref in
		W)
			prompt "Enter the week (1-52) and day of week (0-6, Sunday is 0) when your time zone\nconverts from $direction. If you\nenter only the week the default of 0 will be used for the day of the week.\nEnter W[.d] " || exit 0
			convdate=$cmd
			case $convdate in
			[0-9].[0-9]|[0-9][0-9].[0-9])
				day=`expr "$convdate" : ".*\.\([0-9]\)"`
				[ $day -ge 0 -a $day -le 6 ] || { 
					error "Value for day out of range (0-6)"
					continue
				}
				;;
			[0-9]|[0-9][0-9]|[0-9].|[0-9][0-9].)
				# OK use default day of Sunday
				;;
			*)
				error "Enter the week (1-52) and day of week (0-6) in the format W[.d]"
				continue
				;;
			esac
			week=`expr "$convdate" : "\([0-9]\{1,2\}\).*"`
			[ $week -gt 0 -a $week -le 53 ] || { 
				error "Value for week out of range (1-53)"
				continue
			}
			;;
		M)
			prompt "Enter the month (1-12), week (1-5) and day of week (0-6, Sunday is 0) when your\ntime zone converts from $direction.\nFor example use 4.5.1 to specify Monday of the last week in April. If you enter\nonly the month and week the default of 0 will be used for the day of the week.\nEnter M.W[.d] " || exit 0
			convdate=$cmd
			case $convdate in
			[0-9].[0-9].[0-9]|[0-9][0-9].[0-9].[0-9])
				day=`expr "$convdate" : ".*\.[0-9]\.\([0-9]\)"`
				[ $day -ge 0 -a $day -le 6 ] || { 
					error "Value for day out of range (0-6)"
					continue
				}
				;;

			[0-9].[0-9]|[0-9][0-9].[0-9]|[0-9].[0-9].|[0-9][0-9].[0-9].)
				# OK use default day of Sunday
				;;
			*)
				error "Enter a month (1-12), week (1-5) and day (0-6)"
				continue
				;;
			esac
			month=`expr "$convdate" : "\([0-9]*\).*"`
			[ $month -gt 0 -a $month -le 12 ] || { 
				error "Value for month out of range (1-12)"
				continue
			}
			#week=`expr "$convdate" : ".*\.\([0-9]\).*"`
			week=`echo $convdate | awk -F. '{ print $2 }'`
			[ $week -gt 0 -a $week -le 5 ] || { 
				error "Value for week out of range (1-5)"
				continue
			}
			;;
		J)
	    		prompt "Enter the Julian date (1-365, Feb 29 is not counted) when your time zone\nconverts from $direction.\n" || exit 0
			convdate=$cmd
			len=`expr "$convdate" : ".*"`
		    	matched=`expr "$convdate" : "[0-9]\{1,3\}"`
			[ $len -gt 0 -a $len = $matched ] || { 
				error "Enter a Julian date (1-365)"
				continue
			}
    			[ $convdate -ge 1 -a $convdate -le 365 ] || { 
				error "Enter a number between 1 and 365"
				continue
			}
			;;
		esac
		break
	done
	return 0
}

# Converts hh:mm:ss to seconds or seconds to hh:mm:ss
convtime() { 
	time=$1
	conv=$2
	sign=`expr "$time" : "\(.\).*"`
	[ "$sign" = "-" ] || sign=+
	case $conv in
	htos)	# Convert hh:mm:ss to seconds
		parsehours $time -24 24 || return 1
		hrsecs=`expr $hh \* 3600`
		minsecs=`expr ${mm:-0} \* 60`
		convsecs=`expr $hrsecs $sign $minsecs $sign ${ss:-0}`
		;;
	stoh)	# Convert seconds to hh:mm:ss
		len=`expr "$time" : ".*"`
		matched=`expr "$time" : "[-+]\{0,1\}[0-9]*`
		[ $matched -eq $len ] || { 
			error "Non-numeric value for seconds"
			return 1
		}
		[ $time -ge -86400 -a $time -le 86400 ] || { 
			error "Value for seconds out of range"
			return 1
		}
		[ "$sign" = "+" ] && sign=
		time=`expr $time \* ${sign}1`
		rem=`expr $time % 3600`
		if [ $rem -gt 0 ]
		then
			mins=":`expr $rem / 60`"
			rem=`expr $rem % 60`
			[ $rem -gt 0 ] && secs=":$rem"
		fi
		hrs=`expr $time / 3600`
		convtime=$sign$hrs$mins$secs
		;;
	esac
	return 0
}

# Calculates the amount of adjustment for summer time
setsmhours() { 
	hours=$1
	convtime $hours htos || return 1
	adjsecs=$convsecs
	convtime $sthours htos|| return 1
	stsecs=$convsecs
	smsecs=`expr $stsecs - $adjsecs`
	convtime $smsecs stoh|| return 1
	smhours=$convtime
}

# Set North American values
setna() {
	tname="daylight saving time (summer time)"
	while	:
	do
		prompt "\t1. NST	- Newfoundland Standard Time
	2. AST	- Atlantic Standard Time
	3. EST	- Eastern Standard Time
	4. CST	- Central Standard Time
	5. MST	- Mountain Standard Time
	6. PST	- Pacific Standard Time
	7. YST	- Yukon Standard Time
	8. HST	- Hawaiian/Alaskan Standard Time
	9. NST	- Nome Standard Time

Enter the number that represents your time zone " || exit 0

		case $cmd in
		1)	ZONE=N offset=3:30	;;
		2)	ZONE=A offset=4		;;
		3)	ZONE=E offset=5		;;
		4)	ZONE=C offset=6		;;
		5)	ZONE=M offset=7		;;
		6)	ZONE=P offset=8		;;
		7)	ZONE=Y offset=9		;;
		8)	ZONE=H offset=10	;;
		9) 	ZONE=N offset=11	;;
		*)	echo "\nPlease answer either 1,2,3,4,5,6,7,8 or 9." >&2
			continue
			;;
		esac
	break
	done
	getyn "Does $tname apply at your location?" && dst=y
	tz=${ZONE}ST$offset${dst:+${ZONE}DT}
	return 0
}

# Set time zone values for other locations
setelse() { 
	tname="summer time (daylight saving time)"
	# Else prompt for standard time zone name
	while :
	do
		prompt "What is the abbreviation of your standard time zone?\nEnter 1-9 characters " || exit 0
		setzname $cmd || continue
		# tzname gets set in setzname
		stname=$tzname
		break
	done

	# Prompt for hours west of GMT
	while :
	do 	prompt "How many hours west of Greenwich Mean Time are you?\nEnter hh[:mm:ss] (eg. 10:30:00 or 10:30, use negative\nnumbers for locations east of GMT) " || exit 0
		sethours $cmd -12 12 || continue
		sthours=$hours
		sthh=$hh
		stmm=$mm
		stss=$ss
		break
	done
	getyn "Does $tname apply at your location?" && dst=y
	if [ "$dst" ]
	then
		# Prompt for summer time zone name
		while :
		do
			prompt "What is the summer abbreviation of your time zone?\nEnter 1-9 characters " || exit 0
			setzname $cmd || continue
			# tzname gets set in setzname
			smname=$tzname
			break
		done

		# setmethod for starting date
		setmethod "standard time to $tname"

		# setdate sets sdate (starting date of summer time)
		setdate "$direction"
		sdate=$convdate

		while :
		do
			prompt "At what time of day is the conversion made (use 24 hour clock)?\nEnter hh[:mm:ss] or press RETURN for default value of 2 am.\n" 2 || exit 0
			sethours $cmd 0 24 || continue
			cshour="/$hours"
			[ $hours = 2 ] && cshour=
			sdate="$pref$convdate$cshour"
			break
		done

		# setmethod for ending date
		setmethod "$tname to standard time"

		# setdate sets edate (ending date of summer time)
		setdate "$direction"
		edate=$convdate

		# Set time of day for conversion, if default set to null
		while :
		do
			prompt "At what time of day is the conversion made (use 24 hour clock)?\nEnter hh[:mm:ss] or press RETURN for default value of 2 am.\n" 2 || exit 0
			sethours $cmd 0 24 || continue
			cehour="/$hours"
			[ $hours = 2 ] && cehour=
			edate="$pref$convdate$cehour"
			break
		done

		# Prompt for and calculate summer hours
		while :
		do
			prompt "How many hours does your timezone adjust for $tname\nEnter hh:mm:ss or press RETURN for the default value of 1 hour\n" 1 || exit 0
			sethours $cmd -24 24 || continue
			break
		done
		[ $hours = 1 ] || setsmhours $hours
	fi
	tz="$stname$sthours${dst:+$smname$smhours;$sdate,$edate}"
	return 0
}

# Edit /etc/TIMEZONE

editfiles() { 
	seterror=0
	umask	0033

	while :
	do
		cp $dest/etc/TIMEZONE $dest/etc/TIMEZONE.00 || { 
			error "Failed to back up /etc/TIMEZONE"
			break
		}
		grep -s "^TZ=$tz$" $dest/etc/TIMEZONE > /dev/null
		case $? in
		0)	# already there
			break
			;;
		1) 	# change TZ in /etc/TIMEZONE
			awk '
			{
				if (substr($0, 1, 3) == "TZ=")
					printf("TZ=\"%s\"\n", tzstr)
				else
					print $0
			}
			' tzstr="$tz" $dest/etc/TIMEZONE > $tmp.TZ
			if [ $? -ne 0 ]
			then
				error "Failed to edit /etc/TIMEZONE"
				seterror=1
				break
			fi
			;;
		2)	error "$0: error reading /etc/TIMEZONE"
			seterror=1
			break
			;;
		esac

		trap "" 1 2 3 15
		cp $tmp.TZ $dest/etc/TIMEZONE || { 
			error "Failed to copy /etc/TIMEZONE"
			mv $dest/etc/TIMEZONE.00 $dest/etc/TIMEZONE
			seterror=1
		}
		trap 1 2 3 15
		break
	done

	rm -f $tmp* $dest/etc/TIMEZONE.00
	return $seterror
}

#
# main()
#
while :
do
	echo "\nTime zone initialization" >&2
	if getyn "Are you in North America?"
	then
		# Set North American values
		setna
	else
		# Set values for other locations
		setelse
	fi

	# Edit /etc/TIMEZONE
	editfiles && break
	getyn "Do you wish to try again" || break
done
echo

exit $OK
# End SCO_BASE 
