#! /bin/sh
# -----------------------------------------------------------------------------
#     Title: A script to invoke the various CL implementations in a uniform way
#   Created: 1999-01-16 16:41
#    Author: Gilbert Baumann <unk6@rz.uni-karlsruhe.de>
#   License: LGPL (See file GNU-LGPL for details)
# -----------------------------------------------------------------------------
#  (c) copyright 1999 by Gilbert Baumann, Sam Steingold, Bruno Haible
# $CLOCC-Id: run-lisp,v 1.15 2001/12/14 19:06:01 sds Exp $
# $CLOCC-Source: /cvsroot/clocc/clocc/bin/run-lisp,v $

# Prior to invocation the LISPTYPE environment variable must been set to
# one of these:
#
#    clisp      uses ${CLISP} if set, else "clisp"
#    cmucl      uses ${CMUCL} if set, else "lisp"
#    acl43      uses ${ACL43} if set, else "cl"
#    acl5       uses ${ACL5}  if set, else "cl"
#    gcl        uses ${GCL}   if set, else "gcl"
#    sbcl       uses ${SBCL}  if set, else "sbcl"

usage(){
    cat <<\EOF
Usage:
 run-lisp [clause ...] [argument ...]
     clause ::= -i file    ; load file `file'
              | -c file    ; compile file `file'
              | -x form    ; execute form `form'
              | -I image   ; use image file `image'
              | -d image   ; dump to image `image'
              | --safety n ; set safety level for compilation
              | --speed n  ; set speed level for compilation

     Note: Lisp specific extensions (e.g. .dxl on ACL or .mem for CLISP)
           are not to be included into the image argument

     Anything else is stuffed into the Lisp variable 'argv', which is
     available in the lexical environment forms given to -x are
     evaluated in.

 run-lisp -run image
   interactively run `image'
   (user:run) will be called upon start up.

Makefile support

 run-lisp -faslext
   echo the fasload file extension to stdout.
   This is useful for Makefile; you could then say e.g.
   FAS:=$(shell $(TOP)/bin/run-lisp -faslext)

 run-lisp -dumpext
   echo the memory image extension _with_ dot.
   usage: (like above)
   DUMP:=$(shell $(TOP)/bin/run-lisp -dumpext)

 run-lisp -cat [fasl-file ...]
   Cat all the given fasl files together.

EXAMPLE

   $ run-lisp -x "(print argv)" foo bar baz "Hallo Welt" foo\"bar
   ("foo" "bar" "baz" "Hallo Welt" "foo\"bar")
   $
EOF
    exit 0;
}

fail(){
    echo "$0: $*" 1>&2
    exit 1
}

case "$1" in
  "--help" | "-h" | "-?" )
      usage
      ;;
  "-cat" )
      shift
      case "$LISPTYPE" in
        clisp | cmucl | acl43 | acl5 | sbcl)
            cat "$@"
            ;;
        * )
            fail "Sorry, option -cat not supported for LISPTYPE=${LISPTYPE}."
            ;;
      esac
      ;;
  "-faslext" )
      # (pathname-type (compile-file-pathname "foo.lisp"))
      shift
      case "$LISPTYPE" in
        clisp )
            # This is (pathname-type (car (system::*compiled-file-types*))).
            echo 'fas'
            ;;
        cmucl )
            # This can be found via
            #    (c:backend-fasl-file-type c:*target-backend*),
            # but for speed we look at the uname first.
            case `uname -m 2>/dev/null` in
                i[3-6]86 )
                   echo 'x86f'
                   ;;
                * )
                  # Call .
                  ${CMUCL-lisp} -noinit \
                         -eval "(progn \
                                  (write-string (c:backend-fasl-file-type c:*target-backend*))
                                  (terpri)
                                  (ext:quit 0))"
                  ;;
            esac
            ;;
        acl43 | acl5 )
            echo 'fasl'
            ;;
        gcl )
            echo 'o' # but also 'data' on same platforms
            ;;
	sbcl )
	    echo 'fasl'
	    ;;
        * )
            # Since make does not stop when the exit status is 1, we simply
            # echo LISPTYPE_NOT_SET here.
            echo LISPTYPE_NOT_SET
            fail "Sorry, option -faslext not supported for LISPTYPE=${LISPTYPE}."
            ;;
      esac
      ;;
  "-dumpext" )
      shift 1
      case "$LISPTYPE" in
          clisp)
              echo .mem
          ;;
          acl43)
              echo ""
          ;;
          acl5)
              echo .dxl
          ;;
          cmucl)
              echo .core
          ;;
	  sbcl)
	      echo .core
	  ;;
          *)
              # Since make does not stop when the exit status is 1, we simply
              # echo LISPTYPE_NOT_SET here.
              echo LISPTYPE_NOT_SET
              fail "Sorry, option -dumpext not supported for LISPTYPE=${LISPTYPE}."
          ;;
      esac
    ;;
  "-run" )
    # we special case on '-run' for now
    shift 1
    case "$LISPTYPE" in
        clisp)
            ${CLISP-clisp} -M "$1".mem
        ;;
        cmucl )
            ${CMUCL-lisp} -core "$1".core
            ;;
        acl43 )
            # Why "$1"? ACL4.3 dumps executables
            "$1" -e '(unwind-protect (run) (excl:exit))'
            ;;
        acl5 )
            ${ACL5-cl} -I "$1".dxl -e '(unwind-protect (run) (excl:exit))'
            ;;
        gcl )
            "$1" -eval '(run)'
            ;;
	sbcl )
            ${SBCL:-sbcl} -core "$1".core
	    ;;
        *)
            fail "Sorry, option -run not supported for LISPTYPE=${LISPTYPE}."
        ;;
    esac
    ;;
  * )
    # Multiple arguments.
    unset image
    todo=""             # list of forms to execute
    args=""             # list of arguments (strings) to pass
    while [ $# != 0 ]; do
      case "$1" in
        -i)
            if [ $# = 1 ]; then
              fail "missing argument for $1"
            fi
            shift
            backslashify='s,\(["\\]\),\\\1,g'
            arg=`echo "$1" | sed -e "$backslashify"`
            todo=$todo" (load \"${arg}\")"
            shift
            ;;
        -x)
            if [ $# = 1 ]; then
              fail "missing argument for $1"
            fi
            shift
            todo=$todo" $1"
            shift
            ;;
        -c)
            if [ $# = 1 ]; then
              fail "missing argument for $1"
            fi
            shift
            backslashify='s,\(["\\]\),\\\1,g'
            arg=`echo "$1" | sed -e "$backslashify"`
            # todo=$todo" (compile-file \"${arg}\" :print nil)"
            # truename helps, when using Franz' emacs interface
            todo=$todo" (compile-file (truename \"${arg}\") :print nil)"
            shift
            ;;
        -I)
            if [ $# = 1 ]; then
              fail "missing argument for $1"
            fi
            shift
            image="$1"
            shift
            ;;
        -d)
            if [ $# = 1 ]; then
              fail "missing argument for $1"
            fi
            shift
            backslashify='s,\(["\\]\),\\\1,g'
            arg=`echo "$1" | sed -e "$backslashify"`
            case "$LISPTYPE" in
              clisp )
                  todo=$todo" (#+lisp=cl ext:saveinitmem #-lisp=cl lisp:saveinitmem \"${arg}.mem\")"
                  ;;
              cmucl )
                  todo=$todo" (ext:save-lisp \"${arg}.core\")"
                  ;;
              acl43 )
                  todo=$todo" (excl:dumplisp :name \"${arg}\")"
                  ;;
              acl5 )
                  todo=$todo" (excl:dumplisp :name \"${arg}.dxl\")"
                  ;;
              gcl )
                  todo=$todo" (si:save-system \"${arg}\")"
                  ;;
              sbcl )
	          todo=$todo" (sb-ext:save-lisp-and-die \"${arg}.core\")"
		  ;;
              * )
                  fail "Sorry, option -d not supported for LISPTYPE=${LISPTYPE}."
                  ;;
            esac
            shift
            ;;
        --safety)
            if [ $# = 1 ]; then
              fail "missing argument for $1"
            fi
            shift 1
            todo=$todo" (proclaim (quote (optimize (safety "$1"))))"
            shift 1
        ;;
        --speed)
            if [ $# = 1 ]; then
              fail "missing argument for $1"
            fi
            shift 1
            todo=$todo" (proclaim (quote (optimize (speed "$1"))))"
            shift 1
        ;;
        *)
            backslashify='s,\(["\\]\),\\\1,g'
            arg=`echo "$1" | sed -e "$backslashify"`
            args=$args" \"${arg}\""
            shift
            ;;
      esac
    done

    # done with collecting the arguments

    todo="(progn${todo})"
    args="(${args})"
    todo="(let ((argv '$args)) (declare (ignorable argv)) $todo (values))"

    case "$LISPTYPE" in
      clisp )
          todo="(progn (setq #+lisp=cl ext:*load-paths* #-lisp=cl lisp:*load-paths* '(#P\"\")) ${todo})"
          test -z "$image" || image="-M ${image}.mem";
          exec ${CLISP-clisp} -norc -q ${image} -x "$todo"
          ;;
      cmucl )
          # we have to convince CMUCL to return a proper exit status.
          todo="(let (.res (.cond t))
                  (unwind-protect
                    (multiple-value-setq (.res .cond)
                       (ignore-errors (progn $todo
                                        (fresh-line) (finish-output))))
                    (unix:unix-exit (if .cond 1 0))))"
          test -z "$image" || image="-core ${image}.core";
          exec ${CMUCL-lisp} -noinit ${image} -eval "$todo"
          ;;
      acl43 )
          exec echo "$todo" | ${image-${ACL43-cl}} -batch
          ;;
      acl5 )
          test -z "$image" || image="-I ${image}.dxl";
          exec echo "$todo" | ${ACL5-cl} ${image+"-I ${image}.dxl"} -batch
          ;;
      gcl )
          exec echo "$todo" | ${image-${GCL-gcl}} -batch
          ;;
      sbcl )
          # see comment for CMUCL
          todo="(let (.res (.cond t))
                  (unwind-protect
                    (multiple-value-setq (.res .cond)
                       (ignore-errors (progn $todo
                                        (fresh-line) (finish-output))))
                    (sb-ext:quit :unix-status (if .cond 1 0))))"
          test -z "$image" || image="-core ${image}.core";
          exec ${SBCL:-sbcl} --userinit /dev/null ${image} --eval "$todo"
          ;;
      * )
          if [ -n "$LISPTYPE" ] ; then
            fail "Sorry, LISPTYPE=${LISPTYPE} is not supported"
          else
            fail "LISPTYPE environment variable is not set"
          fi
          ;;
    esac

esac
