#!/usr/bin/bash


##  tab2exe
##  =======

#   From a TABLO source file, generate an executable program.


##  Set up.
##  ------

self='tab2exe'

function warn { #Warn but do not exit.
    echo "WARNING: $self: $1" 1>&2
}

function itcess { #Exit unsuccessfully.
    echo "ERROR: $self: $1" 1>&2
    exit 1
}

function itkeep { #Exit unsuccessfully, keeping work directory.
    echo "ERROR: $self: $1" 1>&2
    echo "Not removing directory $workrect" 1>&2
    exit 1
}

declare sfileread #stored input file name, as read
declare stifile #stored input file name, regularized
declare outrect #target (output) directory name, as read
declare tfileread #TABLO file name, as read
declare tabfile #TABLO file name, regularized
declare tbasestem #stem of base of name of TABLO source file
declare tabrect #directory containing TABLO source file
declare workrect #work directory
declare itrect="$(pwd)" #initial working directory


##  Check working environment.
##  -------------------------

#   Require Bash version 4 or higher, so that we can declare "outstems" an
#   associative array.
declare -i bvermin=4 #required minimum bash version
declare -i bashver="${BASH_VERSINFO[0]}" #bash version number
[ "$bashver" -ge "$bvermin" ]\
    || itcess "Found Bash version \"$bashver\", but require at least \"$bvermin\"."
unset bashver bvermin

#   Check that "cygpath" is available.
which cygpath > /dev/null 2>/dev/null\
    || itcess "Can't find required command \"cygpath\"."


##  Process options.
##  ---------------

function version {
    cat <<-EOF
	tab2exe, version 1.0
	authors: R.A. McDougall, Badri Narayanan G
	date: 2013-01
	EOF
}

function usage {
    cat <<-EOF
	Usage: tab2exe [-s STIFILE] [-S SIZE] [-t DIRECTORY] FILE

	From a TABLO source file, generate an executable program.

	  -s STIFILE, --sti=STIFILE
	       Supply stored input to TABLO from STIFILE.
	  -S SIZE, --stack=SIZE
	       [LF95] Set the size of the stack area.
	  -t DIRECTORY, --target_directory=DIRECTORY
	       Create output files in DIRECTORY.
	  --help
	       Display this usage information, and exit.
	  --version
	       Output version information, and exit.
	EOF
}

while [ $# -gt 0 ] ; do
    case $1 in
    -s* | --sti=*)
        if <<<"$1" grep -q '^-s.'; then
            sfileread="${1#-s}"
        elif <<<"$1" grep -q '^-s'; then
            shift
            sfileread="$1"
        else
            sfileread="${1#--*=}"
        fi
        [ -n "$sfileread" ] || itcess "Can't find stored input file argument."
        stifile="$(cygpath -u -- "$sfileread")"
        [ -f "$stifile" ] || itcess "Can't find stored input file \"$sfileread\"."
        declare rparlines #directory-separator-containing lines
        rparlines="$(grep -n '^[^!\\/]*[\\/]' "$stifile")"
        [ -n "$rparlines" ]\
            && itcess "Found directory part(s) in stored input file \"$sfileread\" at lines:"$'\n'"$(<<<"$rparlines" sed 's/^/    /;')"
        unset rparlines
        ;;
    --sti)
        itcess "Can't find stored input file argument."
        ;;
    -S* | --stack=* | --stac=* | --sta=*)
        if <<<"$1" grep -q '^-S.'; then
            stacksize="${1#-S}"
        elif <<<"$1" grep -q '^-S'; then
            shift
            stacksize="$1"
        else
            stacksize="${1#--*=}"
        fi
        [ -n "$stacksize" ] || itcess "Can't find stack size."
        <<<"$stacksize" grep -q '^[[:digit:]]\+$'\
            || itcess "Read invalid stack size '$stacksize'."
        ;;
    --stack | --stac | --sta)
        itcess "Can't find stack size."
        ;;
    -t* | --target-directory=* | --target-director=* | --target-directo=*\
        | --target-direct=* | --target-direc=* | --target-dire=*\
        | --target-dir=* | --target-di=* | --target-d=* | --target-=*\
        | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
        declare orectread #target (output) directory name, regularized
        if <<<"$1" grep -q '^-t.'; then
            orectread="${1#-t}"
        elif <<<"$1" grep -q '^-t'; then
            shift
            orectread="$1"
        else
            orectread="${1#--*=}"
        fi
        [ -n "$orectread" ] || itcess "Can't find target directory argument."
        outrect="$(cygpath -au -- "$orectread")"
        [ -d "$outrect" ]\
            || itcess "Can't find target directory \"$orectread\"."
        unset orectread
        ;;
    --target-directory | --target-director | --target-directo\
        | --target-direct | --target-direc | --target-dire | --target-dir\
        | --target-di | --target-d | --target- | --target | --targe | --targ\
        | --tar | --ta | --t)
        itcess "Can't find target directory argument."
        ;;
    --help | --hel | --he | --h)
        usage; exit
        ;;
    --version | --versio | --versi | --vers | --ver | --ve | --v)
        version; exit
        ;;
    --st=* | --s=* | --st | --s)
        itcess "Found ambiguous option \"$1\"."
        ;;
    --)
        shift; break
        ;;
    -*)
        itcess "Found unknown option \"$1\"."
        ;;
    *)
        break
        ;;
    esac
    shift
done
unset -f usage version


##  Process arguments.
##  -----------------

declare tfstemread #stem of TABLO file name, as read
[ -n "$1" ] || itcess "Can't find argument."
if <<<"$1" grep -iq '\.tab$'; then
    tfileread="$1"
    tfstemread="$(<<<"$1" sed 's/\.tab$//I;')"
else
    tfstemread="$1"
    tfileread="$1.tab"
fi
tabfile="$(cygpath -au -- "$tfileread")"
[ -f "$tabfile" ] || itcess "Can't find source file \"$tfileread\"."
tabrect="$(dirname -- "$tabfile")"
outrect="${outrect:-"$tabrect"}"
tbasestem="$(basename -- "$(cygpath -u -- "$tfstemread")")"
unset tfstemread


##  Set up working directory.
##  ------------------------

workrect="$(mktemp -d)"
cp -- "$tabfile" "$workrect" || itkeep "Can't copy file \"$tfileread\"."
if [ -n "$stifile" ] ; then
    cp -- "$stifile" "$workrect" || itkeep "Can't copy file \"$sfileread\"."
fi
cd "$workrect"


##  Generate and deliver executable.
##  -------------------------------

#   Set up.
declare -i titstat #TABLO exit status
declare -i infok=1 #zero if information file found as expected
declare outstem #stem of output file name

#   Generate Fortran file.
declare stifileopt='' #"-sti" option to TABLO
declare wfpopt='' #"-wfp" option and filename argument
if [ -n "$stifile" ] ; then
    stifileopt="-sti \"$(basename -- "$stifile")\""
else
    wfpopt="-wfp \"$tbasestem\""
fi
eval tablo.exe $stifileopt $wfpopt >tablo.stdout 2>tablo.stderr
titstat="$?"
unset wfpopt stifileopt

#   Copy information file to source directory.
if [ -n "$stifile" ] ; then
    declare -i infcount=$(ls -- *.inf 2>/dev/null | wc -l)
        #number of information files found
    if [ "$infcount" -eq 0 ] ; then
        warn "Found no information file."
    else
        cp -- *.inf "$tabrect"
        if [ "$infcount" -gt 1 ] ; then
            warn "Found more than one information file."
        else
           infok=0
        fi
    fi
    unset infcount
else
    declare infname="$tbasestem".inf #information file name
    if [ -f "$infname" ] ; then
        cp -- "$infname" "$tabrect"
        infok=0
    else
        warn "Can't find information file \"$infname\"."
    fi
    unset infname
fi

#   If TABLO failed, exit.
if [ "$titstat" -ne 0 ] ; then
    declare terrmess="Can't translate TABLO to Fortran." #TABLO error message
    if [ "$infok" -eq 0 ] ; then
        cd -- "$itrect"
        rm -r "$workrect"
        itcess "$terrmess"
    else
        itkeep "$terrmess"
    fi
    unset terrmess
fi

#   Determine stem of output files
if [ -n "$stifile" ] ; then
    declare -Ai outstems #stems of output file names
    for tens in axs axt min; do
        declare -i tenscount=$(ls -- *."$tens" 2>/dev/null | wc -l)
            #number of files with extension "$tens"
        [ "$tenscount" -gt 1 ] && itkeep "Found more than one \"$tens\" file."
        [ "$tenscount" -lt 1 ] && itkeep "Can't find \"$tens\" file."
        declare tensname=$(ls -- *."$tens")
            #name of file with extension "$tens"
        let $(( outstems[${tensname%.$tens}] += 1 ))
    done; unset tensname tenscount tens
    [ ${#outstems[@]} -eq 1 ] || itkeep "Found more than one output file stem."
    outstem="${!outstems[@]}" #stem of output file names
else
    outstem="$tbasestem"
fi
[ -f "$outstem".for ] || itkeep "Can't find Fortran file \"$outstem.for\"."
unset outstems

#   Generate executable.
ssizeopt=''
[ -n "$stacksize" ] && ssizeopt="-S\"$stacksize\""
eval for2exe $ssizeopt \"$outstem\" || itkeep "Can't compile \"$outstem\"."
unset ssizeopt

#   Deliver executable and auxiliary files.
declare tens #file name extension
for tens in axs axt min exe; do
    cp -- "$outstem.$tens" "$outrect"\
        || itkeep "Can't retrieve output file '$outstem.$tens'."
done; unset tens

#   Clean up.
unset outstem infok titstat


##  Clean up.
##  --------

cd -- "$itrect" || itkeep "Can't return to original directory."
rm -r "$workrect" || itcess "Can't remove work directory '$workrect'."
unset itrect workrect tabrect tbasestem tabfile tfileread outrect stifile\
    sfileread
unset -f itkeep itcess
unset self


##  end of script
##  -------------
