#!/bin/bash

# cccl
# Wrapper around MS's cl.exe to make it act more like Unix cc
#
# Copyright (C) 2000-2022 by contributors listed in the AUTHORS file.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.


usage()
{
    cat <<EOF
Usage: cccl [--cccl-link] [--cccl-muffle] [--cccl-verbose] [--help] [--version]
       [OPTIONS]

cccl is a wrapper around Microsoft's cl.exe compiler.  It translates parameters
given by [OPTIONS] that Unix cc understands into parameters that cl understands.
EOF
}

case $MACHTYPE in
    *-msys)
        slash="-"
        ;;
    *)
        slash="/"
        ;;
esac

# prog specifies the program that should be run cl.exe
prog=cl
# opts specifies the command line to pass to the MSVC program
clopt=("${slash}nologo")
linkopt=("${slash}link")
debug=
# gotparam is 0 if we didn't ever see a param, in which case we show usage()
gotparam=
muffle=
verbose=
shared_index=-1

processargs()
{
### Run through every option and convert it to the proper MS one
while test $# -gt 0; do
    case "$1" in
    -D*) optarg= ;;
    -*=*) optarg=`echo "$1" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
    *) optarg= ;;
    esac
    gotparam=1

    case "$1" in
    --help)
        usage
        exit 0
        ;;

    --cccl-link)
        # One single option for the linker
        shift
        case "$1" in
        /*)
            linkopt+=("${slash}${1:1}")
            ;;

        *)
            linkopt+=("$1")
            ;;
        esac
        ;;

    --cccl-muffle)
        # Remove the unnecessary junk that the compiler outputs to stdout
        muffle=1
        ;;

    --cccl-verbose)
        verbose=1
        ;;

    --version)
        cat <<EOF
cccl 1.3
EOF
        exit 0;
        ;;

    -ansi)
        clopt+=("${slash}Za")
        ;;

    -c)
        # -c (compile only) is actually the same, but for clarity...
        clopt+=("${slash}c")
        ;;

    -g[0-9] | -g)
        # cl only supports one debugging level
        clopt+=("${slash}Zi")
        debug=1
        ;;

    -O0)
        clopt+=("${slash}Ot")
        ;;

    -O2)
        clopt+=("${slash}O2")
        ;;

    -L)
        shift
        path=`echo "$1"`
        linkopt+=("${slash}LIBPATH:$path")
        ;;

    -L*)
        path=`echo "$1" | sed 's/-L//'`
        linkopt+=("${slash}LIBPATH:$path")
        ;;

    -link)
        # Libtool compatibility which is trying to pass linker options to cl
        # Same behaviour as cl - all options after -link are linker options
        shift
        while test $# -gt 0; do
            case "$1" in
            -*)
                linkopt+=("${slash}${1:1}")
                ;;

            /*)
                linkopt+=("${slash}${1:1}")
                ;;

            *)
                linkopt+=("$1")
                ;;
            esac
            shift
        done
        ;;

    -l*)
        lib=`echo "$1" | sed 's/-l//'`
        lib="$lib.lib"
        clopt+=("$lib")
        ;;

    -m386)
        clopt+=("${slash}G3")
        ;;

    -m486)
        clopt+=("${slash}G4")
        ;;

    -mpentium)
        clopt+=("${slash}G5")
        ;;

    -mpentiumpro)
        clopt+=("${slash}G6")
        ;;

    -o)
        # specifying output file, is it an object or an executable
        shift
        case "$1" in
        *.o | *.obj)
            clopt+=("${slash}Fo$1")
            ;;

        *.exe | *.dll | *.pyd)
            clopt+=("${slash}Fe$1")
            ;;
        esac
        ;;

    -std=*)
        clopt+=("${slash}std:${1:5}")
        case "$1" in
        -std=c++*)
            clopt+=("${slash}Zc:__cplusplus")
            ;;
        esac
        ;;

    -pedantic)
        #ignore pedantic
        ;;

    -Wl,*)
        IFS=',' read -ra linkopt2 <<< "$(echo $1 | sed 's/-Wl,//')"
        for linkarg in ${linkopt2[@]}; do
            linkopt+=("${linkarg}")
        done
        ;;

    -W*)
        #ignore warnings
        ;;

    -fno-strict-aliasing*)
        #ignore aliasing
        ;;

    -isystem)
        shift
        clopt+=("${slash}I$1")
        ;;

    -I)
        shift
        clopt+=("${slash}I$1")
        ;;

    -rpath)
        #ignore this arg and the path
        shift
        ;;

    -MT)
        exit 0
        ;;

    -mno-cygwin)
        ;;

    -shared | -dll)
        shared_index=${#clopt[@]}
        clopt+=("${slash}LD")
        ;;

    -*)
        # Remaining '-' options are passed to the compiler
        if test x$optarg != x ; then
            clopt+=("${slash}${1:1}=$optarg")
        else
            clopt+=("${slash}${1:1}")
        fi
        ;;

    *.cc | *.cxx | *.C)
        # C++ source file with non .cpp extension, make sure cl understand 
        # that it is C++
        clopt+=("${slash}Tp$1")
        ;;

    /link)
        # Same behaviour as cl - all options after /link are linker options
        shift
        while test $# -gt 0; do
            case "$1" in
            -*)
                linkopt+=("${slash}${1:1}")
                ;;

            /*)
                linkopt+=("${slash}${1:1}")
                ;;

            *)
                linkopt+=("$1")
                ;;
            esac
            shift
        done
        ;;

    /*)
        # All '/' options are assumed to be for cl and are passed through
        clopt+=("${slash}${1:1}")
        ;;

    *)
        clopt+=("$1")
        ;;

    esac
    shift
done
}

# Whitespace in paths is dealt with by setting IFS and using bash arrays
# Except additional arguments in CCCL_OPTIONS need to be space separated
processargs $CCCL_OPTIONS
IFS=""
processargs $@

if test $shared_index -ge 0 -a -n "$debug"; then
    clopt[$shared_index]="${slash}LDd"
fi

if test x$gotparam = x ; then
    usage
    exit 1
fi

if test ${#linkopt[@]} -eq 1 ; then
    linkopt=()
fi

if test x$V = x1 ; then
    verbose=1
fi

if test -n "$verbose" ; then
  echo -n "$prog"
  for opt in "${clopt[@]}" ; do
    echo -n " \"$opt\""
  done
  for opt in "${linkopt[@]}" ; do
    echo -n " \"$opt\""
  done
  echo ""
fi

if test -z "$muffle" ; then
    exec $prog ${clopt[@]} ${linkopt[@]}
else
  # tr needed below for $ in regex to work (simple alternative to dos2unix)
    exec $prog ${clopt[@]} ${linkopt[@]} | tr -d '\r' | grep -v -e "\.cpp$" -e "\.cxx$" -e "\.cc$" -e "\.C$" -e "\.c$" -e "^   Creating library" -e "^Generating Code"
    exit ${PIPESTATUS[0]}
fi

