#! /bin/sh
# $Id: osql,v 1.12 2011-05-11 03:08:13 jklowden Exp $
#
# Check odbc.ini, odbcinst, and, optionally, freetds.conf, 
# then execute isql (assume it's unixODBC's isql).  
#

USAGE="Syntax: $(basename "$0") -S server -U user -P password"

while getopts I:S:U:P: OPTION
do
	case ${OPTION} in
		I)	OVER_DIR=${OPTARG}	# override
			;;
		S)	DSN=${OPTARG}
			;;
		U)	USERNAME=${OPTARG}
			;;
		P)	PASSWORD=${OPTARG}
			;;
		\?)     echo "$USAGE"
			exit 1
			;;
	esac
done

#cho ${DSN} ${USERNAME} ${PASSWORD}
if [ -z "${DSN}" ] || [ -z "${USERNAME}" ] || [ -z "${PASSWORD}" ]
then 
	echo "$USAGE"
	exit 1
fi

ISQL=$(command -v isql)
if [ -z "${ISQL}" ]
then
	echo "$(basename "$0"): error: no \"isql\" command found.  Is unixODBC installed?"
	exit 1
fi

# Establish ODBC prefix directory

ISQL_DIR=$(strings "${ISQL}" | grep ^/ | grep -v elf | grep -v '\.so\.' | head -1 | sed 's/lib$/etc/' )

INI_DIRNAME="/tmp/$(basename "$0").$$"
exec 3> "${INI_DIRNAME}"

# Check the libraries, too. 
if [ -z "${OVER_DIR}" ]
then 
	libcmd='ldd "${ISQL}" | awk "/libodbc\./ {print \$3}"'
	if ! command -V ldd >/dev/null 2>&1 && command -V otool >/dev/null 2>&1
	then
		libcmd='otool -L "${ISQL}" | awk "/libodbc\./ {print \$1}"'
	fi
	echo "checking shared odbc libraries linked to isql for default directories..."
	(echo ${ISQL_DIR}; eval "$libcmd") \
	| while read L
	  do 
		strings "$L" | grep '^/' | grep -v '/lib' \
		| while read D 
		  do 
			if [ ! -s "${INI_DIRNAME}" ]
			then
				printf "	trying %s ... " "$D"
				if [ -d "$D" ] && [ -r "${D}/odbc.ini" ]
				then
					echo "$D" >&3
					printf "OK"
				else
					printf "no"
				fi
				printf "\n"
			fi
		done
	done
fi
exec 3>&-

ODBC_DIR=$(cat "${INI_DIRNAME}") && rm "${INI_DIRNAME}"

if [ -z "${ODBC_DIR}" ] && [ -z "${OVER_DIR}" ]
then
	echo "$(basename "$0"): problem: no potential directory strings in \"$(command -v isql)\""
	echo "$(basename "$0"): advice: use \"osql -I DIR\" where DIR unixODBC\'s install prefix e.g. /usr/local"
	echo "isql strings are:"
	strings "${ISQL}" | grep ^/ | sed 's/^/+ /'
	#xit 1
fi

if [ "${OVER_DIR}" ]
then
	if [ -d "${ODBC_DIR}" ]
	then
		echo "\"${ODBC_DIR}\" is a directory, overridden by"
	else 
		echo "\"${ODBC_DIR}\" is NOT a directory, overridden by"
	fi
	echo "\"${OVER_DIR}\"."
	
	if [ -d "${OVER_DIR}" ]
	then
		ODBC_DIR=${OVER_DIR}
	else
		echo "$(basename "$0"): error: \"${OVER_DIR}\" is not a directory"
		exit 1
	fi
fi

# Look for server entry in odbc.ini 
echo 'checking odbc.ini files'
for F in "${HOME}/.odbc.ini" "${ODBC_DIR}/odbc.ini"
do
	if [ ! -d "$(dirname "$F")" ]
	then
		echo "warning: $(dirname "$F") is not a directory" 
		continue
	fi
	
	if [ -r "$F" ]
	then
		echo "	reading $F"
	else
		echo "	cannot read \"$F\""
		continue
	fi

	grep -F "[${DSN}]" "$F" > /dev/null

	if [ $? -eq 0 ]
	then
		echo "[${DSN}] found in $F"
		ODBC_INI=$F
		break
	else
		echo "[${DSN}] not found in $F"
	fi
done

if [ -z "${ODBC_INI}" ]
then
	echo "$(basename "$0"): error: unable to locate ${DSN} in any odbc.ini"
	exit 1
fi

# Report finding of server entry
echo found this section:
SED_CMD="/^\[${DSN}\]/,/^[[:space:]]*$/ { s/^/	/; p; }"
sed -ne "${SED_CMD}" "${ODBC_INI}"

#
# Examine server entry in odbc.ini 
#

# Find the driver in the servername or default section
for D in "${DSN}" 'default' 
do
	echo "looking for driver for DSN [$D] in ${ODBC_INI}"
	grep "$D" "${ODBC_INI}" > /dev/null
	if [ $? -eq 0 ]
	then
		CMD="/^\[$D\]/,/^[[:space:]]*$/ { s/^/	/; p; }"
		DRIVER_LINE=$(sed -ne "${CMD}" "${ODBC_INI}" \
				| grep -Ei '^[[:space:]]*driver[[:space:]]*=')
		if [ -z "${DRIVER_LINE}" ]
		then
			echo "  no driver mentioned for [$D] in $(basename "${ODBC_INI}")"
			continue
		fi

		echo "  found driver line: \"${DRIVER_LINE}\""
		DRIVER=$(echo "${DRIVER_LINE}" \
		    | awk -F ' *= *' '{print $2}' \
		    | sed 's/[[:space:]][[:space:]]*//g')
		if [ "${DRIVER}" ]
		then
			echo "  driver \"${DRIVER}\" found for [$D] in $(basename "${ODBC_INI}")"
			break
		else
			echo "  driver line for [$D] incomplete in $(basename "${ODBC_INI}")"
			continue
		fi
	fi
done

if [ -z "${DRIVER}" ]
then
	echo "$(basename "$0"): error: no driver found for [${DSN}] in $(basename "${ODBC_INI}")"
	exit 1
fi

# get filename of driver
echo "found driver named \"${DRIVER}\""
if [ -d "${DRIVER}" ] || [ ! -x "${DRIVER}" ]
then
	# not a filename, look it up
	DRIVERNAME=${DRIVER}
	ODBC_INST="${ODBC_DIR}/odbcinst.ini"
	echo "\"${DRIVERNAME}\" is not an executable file"
	echo "looking for entry named [${DRIVERNAME}] in ${ODBC_INST}"

	grep "${DRIVERNAME}" "${ODBC_INST}" > /dev/null
	if [ $? -ne 0 ]
	then
		if [ $? -eq 1 ]
		then
			echo "$(basename "$0"): error: no driver entry [${DRIVERNAME}] in ${ODBC_INST}"
		fi
		exit 1;
	fi
	
	CMD="/^\[${DRIVERNAME}\]/,/^[[:space:]]*$/ { s/^/	/; p; }"
	DRIVER_LINE=$(sed -ne "${CMD}" "${ODBC_INST}" \
				| grep -Ei '^[[:space:]]*driver[[:space:]]*=')
	if [ -z "${DRIVER_LINE}" ]
	then
		echo "$(basename "$0"): no driver mentioned for [${DRIVERNAME}] in $(basename "${ODBC_INST}")"
		exit 1
	fi

	echo "  found driver line: \"${DRIVER_LINE}\""
	DRIVER=$(echo "${DRIVER_LINE}" | awk -F ' *= *' '{print $2}')
	if [ -z "${DRIVER}" ]
	then
		echo "$(basename "$0"): driver line incomplete for [${DRIVERNAME}] in $(basename "${ODBC_INST}")"
		exit 1
	fi

	echo "  found driver ${DRIVER} for [${DRIVERNAME}] in $(basename "${ODBC_INST}")"
fi

if [ -z "${DRIVER}" ]
then
	echo "$(basename "$0"): error: sorry, failed sanity check: \${DRIVER} is null"
	exit 1
fi

if [ -x "${DRIVER}" ]
then
	echo "${DRIVER} is an executable file"
else
	echo "${DRIVER} is not an executable file"
	echo "$(basename "$0"): error: no driver found for ${DSN}"
	exit 1
fi

# find the server/servername
SERVER_LINE=$(sed -ne "${SED_CMD}" "${ODBC_INI}" \
		| grep -Ei '^[[:space:]]*server(name)*[[:space:]]*=')

ATTRIBUTE=$(echo "${SERVER_LINE}" | awk -F' *= *' '{print $1}')

if [ -z "${ATTRIBUTE}" ]
then
	echo "$(basename "$0"): neither \"Server\" nor \"Servername\" found for [${DSN}] in $(basename "${ODBC_INI}")"
	exit 1
fi

echo "${SERVER_LINE}" | grep -i servername >/dev/null

#
# Find the server's hostname 
#
if [ $? -eq 0 ] # ODBC-Combined
then
	TDS_SERVER=$(echo "${SERVER_LINE}" | awk -F ' *= *' '{print $2}')
	echo 'Using ODBC-Combined strategy'
	echo "DSN [${DSN}] has servername \"${TDS_SERVER}\" (from ${ODBC_INI})"
	if [ -z "${TDS_SERVER}" ]
	then
		exit 1
	fi

	# Look for $TDS_SERVER in freetds.conf 
	
	FREETDS_DIR=$(tsql -C | grep 'freetds.conf directory' | awk -F: '{print $2}' | sed 's/^ *//')
	if [ -z "${FREETDS_DIR}" ]
	then
		echo "$(basename "$0"): error: unable to locate directory for freetds.conf using \"$(command -v tsql)\""
		exit 1
	fi
	
	for F in "${HOME}/.freetds.conf" "${FREETDS_DIR}/freetds.conf"
	do
		if [ -r "$F" ]
		then
			echo "$F is a readable file"
		else
			echo "cannot read \"$F\""
			continue
		fi

		echo "looking for [${TDS_SERVER}] in $F"
		grep -F "[${TDS_SERVER}]" "$F" > /dev/null

		if [ $? -eq 0 ]
		then
			FREETDS_CONF=$F
			break
		else
			echo "[${TDS_SERVER}] not found in $F"
		fi
	done
	
	if [ -z "${FREETDS_CONF}" ]
	then
		echo "$(basename "$0"): error: unable to locate ${TDS_SERVER} in any freetds.conf"
		exit 1
	fi

	# Examine server entry in freetds.conf 

	echo found this section:
	SED_CMD="/^\[${TDS_SERVER}\]/,/^[[:space:]]*$/ { s/^/	/; p; }"
	sed -ne "${SED_CMD}" "${FREETDS_CONF}"

	SERVER_LINE=$(sed -ne "${SED_CMD}" "${FREETDS_CONF}" \
			| grep -Ei '^[[:space:]]*host[[:space:]]*=')

	# No character class support in mawk, 
	# cf. "Mawk does not support Posix character classes in expressions"
	# https://bugs.launchpad.net/ubuntu/+source/mawk/+bug/69724
	HOST=$(echo "${SERVER_LINE}" | awk -F' *= *' '{print $2}')

	if [ -z "${HOST}" ]
	then
		echo "$(basename "$0"): no \"host\" entry found for [${TDS_SERVER}] in $(basename "${FREETDS_CONF}")"
		exit 1
	fi
	
else # odbc.ini contains a "server", a DNS host
	HOST=$(echo "${SERVER_LINE}" | awk -F ' *= *' '{print $2}')
	echo "\"${ATTRIBUTE}\" found, not using freetds.conf"
	echo "${ATTRIBUTE} is \"${HOST}\""
	if [ -z "${HOST}" ]
	then
		echo "$(basename "$0"): no value found for \"${ATTRIBUTE}\" entry in $(basename "${ODBC_INI}")"
		exit 1
	fi
fi

# If the "host" is an ip address, look up the name, for neatness, e.g. 
## $ host 10.81.36.39
## 39.36.81.10.IN-ADDR.ARPA domain name pointer ntc5003.eg.com
if [ "${HOST}" = "$(echo "${HOST}" | sed 's/[^.0-9]*//')" ]
then 
	ADDRESS=${HOST}
	echo "looking up hostname for ip address ${ADDRESS}"
	HOST=$(host "${HOST}" | awk '/domain/ {print $5}' | sed 's/\.$//')
	if [ -z "${HOST}" ]
	then
		echo "$(basename "$0"): warning: no DNS hostname found for \"${ADDRESS}\""
		HOST=${ADDRESS}	# restore host address string
	fi
fi

# Now we have a DNS hostname for the server in HOST
if [ "${HOST}" != "${ADDRESS}" ]
then
    ADDRESS=$(host "${HOST}" | awk '/has address/ {print $4}' | head -1)
fi
if [ -z "${ADDRESS}" ]
then
	echo "$(basename "$0"): no IP address found for \"${HOST}\""
	exit 1
fi

#cho ${HOST} has address ${ADDRESS}

# Report what we know and exec isql 

printf "\n"
printf "Configuration looks OK.  Connection details:\n\n"
printf "%22s:\t%-30s\n"  DSN "${DSN}"
printf "%22s:\t%-30s\n"  odbc.ini "${ODBC_INI}"
printf "%22s:\t%-30s\n"  Driver "${DRIVER}"
printf "%22s:\t%-30s\n" "Server hostname" "${HOST}"
printf "%22s:\t%-30s\n"  Address "${ADDRESS}"
printf "\n"

echo "Attempting connection as ${USERNAME} ..."

if [ -z "${TDSDUMP}" ]
then
	TDSDUMP_AUTO="/tmp/$(basename "$0").dump.$$"
	export TDSDUMP=${TDSDUMP_AUTO}
fi

( set -x; isql "${DSN}" "${USERNAME}" "${PASSWORD}" -v ) \
	|| sed -ne 's/Connecting/FAILED &/p' "${TDSDUMP}"

test "${TDSDUMP_AUTO}" && rm -f "${TDSDUMP_AUTO}"

