/
#!/usr/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}"