#!/bin/sh
# Usage: clisp-link command [more args]
# where   command = link, create-module-set, add-module-set, join-modules
# For more usage information, see file `doc/module.txt' or section 99.3
# of file `doc/impnotes.txt'.
# Bruno Haible 19.10.1994

# This could as well be written in Lisp, for portability. But I don't like
# the idea to have one Lisp process running while the next one is built.

usage () {
echo "Usage: $0 [ link | create-module-set | add-module-set | join-modules | add-module-sets ] ..." 1>&2
exit 1
}

link () {
# Make a link from $1 to $2. Try symbolic link, file copying.
dirname=`echo "$1" | sed -e 's,/[^/]*$,,'`
test -n "$dirname" || dirname='/'
basename=`echo "$1" | sed -e 's,^.*/,,'`
absolute_dirname=`cd "$dirname" ; /bin/pwd`
if ln -s "$absolute_dirname"/"$basename" "$2"; then
  :
else
  cp "$1" "$2"
fi
}

echotab () {
cat <<!!
	$1
!!
}

# Print the commands being executed
vecho () {
  echo "$@"
}

verbose () {
  echo "$@"
  "$@"
}

# Remove the comment to Set debugging output on
#set -x

# Exit immediately if some command fails.
set -e

# Check number of arguments. Need at least one argument.
if [ $# = 0 ] ; then
  usage
fi

# Where is the link kit?
if [ -n "$CLISP_LINKKIT" ] ; then
  linkkitdir="$CLISP_LINKKIT"
else
  linkkitdir=./linkkit
fi
if [ ! -r "$linkkitdir"/modules.d -o ! -r "$linkkitdir"/modules.c -o ! -r "$linkkitdir"/clisp.h ] ; then
  echo "$0: No link kit found in $CLISP_LINKKIT" 1>&2
  exit 1
fi
absolute_linkkitdir=`cd "$linkkitdir" ; /bin/pwd`

# Dispatch according to the first argument.
case "$1" in

  link)
    # This is actually obsolete because it is easier done by a simple
    # "make" w.r.t. to the distmakefile.
    # Usage: clisp-link link dir
    if [ $# != 2 ] ; then
      echo "Usage: $0 link dir" 1>&2
      exit 1
    fi
    dir="$2"
    # What to do if we abort.
    trap 'rm -f "$dir"/lisp.run "$dir"/wlisp.run' 1 2 15
    # Read the variables CC, CFLAGS, CLFLAGS, LIBS, WLIBS, X_LIBS, RANLIB, FILES
    . "$dir"/makevars
    vecho "$0: Entering directory \`$dir'"
    # Generate new modules.o, compiled from modules.c, includes modules.h
    (cd "$dir" ; link "$absolute_linkkitdir"/modules.c modules.c ; verbose ${CC} ${CFLAGS} -I"$absolute_linkkitdir" -c modules.c ; rm -f modules.c)
    # Generate new lisp.run
    (cd "$dir" ; verbose ${CC} ${CFLAGS} ${CLFLAGS} modules.o ${LIBS} ${X_LIBS} -o lisp.run)
    # Same thing in wide mode.
    if test -r "$dir"/wlispinit.mem; then
      (cd "$dir" ; link "$absolute_linkkitdir"/modules.c wmodules.c ; verbose ${CC} ${CFLAGS} -DWIDE -I"$absolute_linkkitdir" -c wmodules.c ; rm -f wmodules.c)
      (cd "$dir" ; verbose ${CC} ${CFLAGS} ${CLFLAGS} wmodules.o ${WLIBS} ${X_LIBS} -o wlisp.run)
    fi
    vecho "$0: Leaving directory \`$dir'"
    # Done.
    trap '' 1 2 15
    ;;

  create-module-set)
    # Usage: clisp-link create-module-set moduledir {file}*
    case $# in
      0 | 1) echo "Usage: $0 create-module-set moduledir file ..." 1>&2
             exit 1 ;;
    esac
    moduledir="$2"
    shift
    shift
    files="$*"
    if [ -r "$moduledir" ] ; then
      if [ -d "$moduledir" ] ; then
        echo "$0: $moduledir already exists" 1>&2
        exit 1
      else
        echo "$0: $moduledir is not a directory" 1>&2
        exit 1
      fi
    fi
    mkdir "$moduledir"
    modulename=`echo "$moduledir" | sed -e 's,^.*/,,'`
    files_c=''
    files_o=''
    wfiles_o=''
    for file in $files; do
      file=`echo "$file" | sed -e 's,\.c$,,'`.c
      filename=`echo "$file" | sed -e 's,^.*/,,'`
      case "$file" in
        /*) relative_file="$file" ;;
        *)  case "$moduledir" in
              /*) relative_file="$file" ;;
              *)  relative_file=`echo "$moduledir"/ | sed -e 's,[^/][^/]*/*/,../,g'`"$file" ;;
            esac ;;
      esac
      ln -s "$relative_file" "$moduledir"/"$filename" || ln "$file" "$moduledir"/"$filename"
      files_c="$files_c"' '"$filename"
      files_o="$files_o"' '`echo "$filename" | sed -e 's,\.c$,,'`.o
      wfiles_o="$wfiles_o"' 'w`echo "$filename" | sed -e 's,\.c$,,'`.o
    done
    if false; then
      # No Makefile
      (echo "file_list=''"
       echo "wfile_list=''"
       for fc in $files_c; do
         fo=`echo "$fc" | sed -e 's,\.c$,,'`.o
         echo 'if test -r '"$fc"'; then'
         echo "  if test '"'!'"' -f $fo || test $fo -ot $fc; then"
         echo '    ${CC} ${CFLAGS} -I"$absolute_linkkitdir" -c '"$fc"
         echo '  fi'
         echo '  if test -n "$with_wide"; then'
         echo "    if test '"'!'"' -f w$fo || test w$fo -ot $fc; then"
         echo '      ${CC} ${CFLAGS} -DWIDE -I"$absolute_linkkitdir" -c '"$fc -o w$fo"
         echo '    fi'
         echo '  fi'
         echo '  file_list="$file_list"'"' $fo'"
         echo '  wfile_list="$wfile_list"'"' w$fo'"
         echo 'fi'
       done
       echo 'NEW_FILES="$file_list"'
       echo 'NEW_LIBS="$file_list"'
       echo 'NEW_WLIBS="$wfile_list"'
       echo 'if test -n "$with_wide"; then'
       echo '  NEW_FILES=$NEW_FILES"$wfile_list"'
       echo 'fi'
       echo "TO_LOAD=''"
      ) > "$moduledir"/link.sh
    else
      # With Makefile
      (echo "# Makefile for CLISP module set $modulename"
       echo
       echo "CC ="
       echo "CFLAGS ="
       echo "INCLUDES="
       echo
       echo "CLISP ="
       echo
       echo "SHELL = /bin/sh"
       echo
       for fc in $files_c; do
         fo=`echo "$fc" | sed -e 's,\.c$,,'`.o
         echo "$fo : $fc"
         echotab '$(CC) $(CFLAGS) -I$(INCLUDES) -c '"$fc"
         echo
         echo "w$fo : $fc"
         echotab '$(CC) $(CFLAGS) -DWIDE -I$(INCLUDES) -c '"$fc -o w$fo"
         echo
       done
       echo "clisp-module :$files_o"
       echo
       echo "clisp-module-wide :$files_o$wfiles_o"
       echo
      ) > "$moduledir"/Makefile
      (echo "file_list=''"
       echo "wfile_list=''"
       echo "mod_list=''"
       for fc in $files_c; do
         fo=`echo "$fc" | sed -e 's,\.c$,,'`.o
         mod=`echo "$fc" | sed -e 's,\.c$,,' | sed -e 's,[^A-Za-z0-9_],_,g'`
         # The last sed command must agree with foreign1.lsp:to-module-name.
         echo 'if test -r '"$fc"'; then'
         echo '  file_list="$file_list"'"' $fo'"
         echo '  wfile_list="$wfile_list"'"' w$fo'"
         echo '  mod_list="$mod_list"'"' $mod'"
         echo 'fi'
       done
       echo 'make `if test -n "$with_wide"; then echo clisp-module-wide; else echo clisp-module; fi` CC="${CC}" CFLAGS="${CFLAGS}" INCLUDES="$absolute_linkkitdir"'
       echo 'NEW_FILES="$file_list"'
       echo 'NEW_LIBS="$file_list"'
       echo 'NEW_WLIBS="$wfile_list"'
       echo 'if test -n "$with_wide"; then'
       echo '  NEW_FILES=$NEW_FILES" $wfile_list"'
       echo 'fi'
       echo 'NEW_MODULES="$mod_list"'
       echo "TO_LOAD=''"
      ) > "$moduledir"/link.sh
    fi
    ;;

  add-module-set)
    # Usage: clisp-link add-module-set moduledir source-dir destination-dir
    if [ $# != 4 ] ; then
      echo "Usage: $0 add-module-set moduledir source-dir destination-dir" 1>&2
      exit 1
    fi
    moduledir="$2"
    sourcedir="$3"
    destinationdir="$4"
    if [ ! -d "$moduledir" ] ; then
      echo "$0: $moduledir is not a directory" 1>&2
      exit 1
    fi
    if [ ! -d "$sourcedir" ] ; then
      echo "$0: $sourcedir is not a directory" 1>&2
      exit 1
    fi
    if [ -r "$destinationdir" ] ; then
      if [ -d "$destinationdir" ] ; then
        echo "$0: $destinationdir already exists" 1>&2
        exit 1
      else
        echo "$0: $destinationdir is not a directory" 1>&2
        exit 1
      fi
    fi
    mkdir "$destinationdir"
    absolute_currentdir=`/bin/pwd`
    absolute_sourcedir=`cd "$sourcedir" ; /bin/pwd`
    absolute_destinationdir=`cd "$destinationdir" ; /bin/pwd`
    # What to do if we abort.
    trap 'rm -rf "$absolute_destinationdir"' 1 2 15
    if [ "$absolute_sourcedir" = "$absolute_destinationdir" ] ; then
      echo "$0: directories $sourcedir and $destinationdir may not be the same" 1>&2
      exit 1
    fi
    if [ ! -r "$sourcedir"/lisp.a -o ! -x "$sourcedir"/lisp.run -o ! -r "$sourcedir"/lispinit.mem -o ! -r "$sourcedir"/modules.h -o ! -r "$sourcedir"/makevars ] ; then
      echo "$0: directory $sourcedir does not contain a clisp linking set" 1>&2
      exit 1
    fi
    if [ ! -r "$moduledir/link.sh" ] ; then
      echo "$0: directory $moduledir does not contain a clisp module" 1>&2
      exit 1
    fi
    modulename=`echo "$moduledir" | sed -e 's,^.*/,,'`
    # Read the variables CC, CFLAGS, CLFLAGS, LIBS, WLIBS, X_LIBS, RANLIB, FILES
    . "$sourcedir"/makevars
    # Prepare the module directory and read the variables NEW_FILES, NEW_LIBS, NEW_WLIBS
    NEW_FILES=''
    NEW_LIBS=''
    NEW_WLIBS=''
    NEW_MODULES=''
    TO_PRELOAD=''
    TO_LOAD=''
    if [ -r "$sourcedir"/wlisp.a -a -x "$sourcedir"/wlisp.run -a -r "$sourcedir"/wlispinit.mem ] ; then
      with_wide=yes
    else
      with_wide=''
    fi
    cd "$moduledir"
    . ./link.sh
    cd "$absolute_currentdir"
    # Generate new modules.h
    (cat "$sourcedir"/modules.h
     for mod in $NEW_MODULES ; do
       echo 'MODULE('"$mod"')'
     done) > "$destinationdir"/modules.h
    # Generate new lisp.a
    for f in ${FILES}; do
      link "$sourcedir"/$f "$destinationdir"/$f
    done
    # Generate other libraries
    for f in ${NEW_FILES}; do
      link "$moduledir"/$f "$destinationdir"/$f
    done
    # Update the LIBS, WLIBS and FILES variables
    LIBS=${NEW_LIBS}' '${LIBS}
    WLIBS=${NEW_WLIBS}' '${WLIBS}
    FILES=${FILES}' '${NEW_FILES}
    # Generate new modules.o, compiled from modules.c, includes new modules.h
    (cd "$destinationdir" ; link "$absolute_linkkitdir"/modules.c modules.c ; verbose ${CC} ${CFLAGS} -I"$absolute_linkkitdir" -c modules.c ; rm -f modules.c)
    # Generate new lisp.run
    (cd "$destinationdir" ; verbose ${CC} ${CFLAGS} ${CLFLAGS} modules.o ${LIBS} ${X_LIBS} -o lisp.run)
    if [ -n "$TO_PRELOAD" ] ; then
      # Generate new preliminary lispinit.mem
      to_preload=''
      for f in $TO_PRELOAD; do
        to_preload="$to_preload $moduledir/$f"
      done
      verbose "$sourcedir"/lisp.run -M "$sourcedir"/lispinit.mem -q -i $to_preload -x "(saveinitmem \"$destinationdir/lispinit.mem\")"
      lispinitdir="$destinationdir"
    else
      lispinitdir="$sourcedir"
    fi
    # Generate new lispinit.mem
    to_load=''
    for f in $TO_LOAD; do
      to_load="$to_load $moduledir/$f"
    done
    verbose "$destinationdir"/lisp.run -M "$lispinitdir"/lispinit.mem -q -i $to_load -x "(saveinitmem \"$destinationdir/lispinit.mem\")"
    # Same thing in wide mode.
    if test -n "$with_wide"; then
      (cd "$destinationdir" ; link "$absolute_linkkitdir"/modules.c wmodules.c ; verbose ${CC} ${CFLAGS} -DWIDE -I"$absolute_linkkitdir" -c wmodules.c ; rm -f wmodules.c)
      (cd "$destinationdir" ; verbose ${CC} ${CFLAGS} ${CLFLAGS} wmodules.o ${WLIBS} ${X_LIBS} -o wlisp.run)
      if [ -n "$TO_PRELOAD" ] ; then
        verbose "$sourcedir"/wlisp.run -M "$sourcedir"/wlispinit.mem -q -i $to_preload -x "(saveinitmem \"$destinationdir/wlispinit.mem\")"
      fi
      verbose "$destinationdir"/wlisp.run -M "$lispinitdir"/wlispinit.mem -q -i $to_load -x "(saveinitmem \"$destinationdir/wlispinit.mem\")"
    fi
    # Generate new makevars
    sed -e "s,^LIBS=.*\$,LIBS='${LIBS}'," -e "s,^WLIBS=.*\$,WLIBS='${WLIBS}'," -e "s,^FILES=.*\$,FILES='${FILES}'," < "$sourcedir"/makevars > "$destinationdir"/makevars
    # Done.
    trap '' 1 2 15
    ;;

  add-module-sets)
    # This is functionally the same as multiple add-module-set commands,
    # but is faster and requires less disk space.
    # Usage: clisp-link add-module-sets source-dir destination-dir moduledir...
    if [ $# -lt 3 ] ; then
      echo "Usage: $0 add-module-sets source-dir destination-dir moduledir..." 1>&2
      exit 1
    fi
    sourcedir="$2"
    destinationdir="$3"
    shift
    shift
    shift
    moduledirs="$@"
    if [ ! -d "$sourcedir" ] ; then
      echo "$0: $sourcedir is not a directory" 1>&2
      exit 1
    fi
    if [ -r "$destinationdir" ] ; then
      if [ -d "$destinationdir" ] ; then
        echo "$0: $destinationdir already exists" 1>&2
        exit 1
      else
        echo "$0: $destinationdir is not a directory" 1>&2
        exit 1
      fi
    fi
    for moduledir in $moduledirs; do
      if [ ! -d "$moduledir" ] ; then
        echo "$0: $moduledir is not a directory" 1>&2
        exit 1
      fi
    done
    mkdir "$destinationdir"
    absolute_currentdir=`/bin/pwd`
    absolute_sourcedir=`cd "$sourcedir" ; /bin/pwd`
    absolute_destinationdir=`cd "$destinationdir" ; /bin/pwd`
    # What to do if we abort.
    trap 'rm -rf "$absolute_destinationdir"' 1 2 15
    if [ "$absolute_sourcedir" = "$absolute_destinationdir" ] ; then
      echo "$0: directories $sourcedir and $destinationdir may not be the same" 1>&2
      exit 1
    fi
    if [ ! -r "$sourcedir"/lisp.a -o ! -x "$sourcedir"/lisp.run -o ! -r "$sourcedir"/lispinit.mem -o ! -r "$sourcedir"/modules.h -o ! -r "$sourcedir"/makevars ] ; then
      echo "$0: directory $sourcedir does not contain a clisp linking set" 1>&2
      exit 1
    fi
    for moduledir in $moduledirs; do
      if [ ! -r "$moduledir/link.sh" ] ; then
        echo "$0: directory $moduledir does not contain a clisp module" 1>&2
        exit 1
      fi
    done
    # Read the variables CC, CFLAGS, CLFLAGS, LIBS, WLIBS, X_LIBS, RANLIB, FILES
    . "$sourcedir"/makevars
    if [ -r "$sourcedir"/wlisp.a -a -x "$sourcedir"/wlisp.run -a -r "$sourcedir"/wlispinit.mem ] ; then
      with_wide=yes
    else
      with_wide=''
    fi
    if [ -z "$moduledirs" ] ; then
      # Just make links from $destinationdir to $sourcedir
      for f in lisp.run lispinit.mem `if test -n "$with_wide"; then echo wlisp.run wlispinit.mem; fi` modules.h makevars ${FILES}; do
        link "$sourcedir"/$f "$destinationdir"/$f
      done
      if [ -r "$sourcedir"/modules.o ] ; then
        link "$sourcedir"/modules.o "$destinationdir"/modules.o
        if test -n "$with_wide"; then
          link "$sourcedir"/wmodules.o "$destinationdir"/wmodules.o
        fi
      fi
    else
      # Prepare the module directories and read their variables
      allmodulevars=''
      for moduledir in $moduledirs; do
        modulename=`echo "$moduledir" | sed -e 's,^.*/,,'`
        # Prepare the module directory and read the variables NEW_FILES, NEW_LIBS, NEW_WLIBS
        NEW_FILES=''
        NEW_LIBS=''
        NEW_WLIBS=''
        NEW_MODULES=''
        TO_PRELOAD=''
        TO_LOAD=''
        cd "$moduledir"
        . ./link.sh
        cd "$absolute_currentdir"
        # This is a crazy way to build doubly nested lists.
        allmodulevars="$allmodulevars""@@SEP1@@""moduledir=\""`echo "$moduledir" | sed -e 's,",\\\\",g' -e 's, ,@@SEP3@@,g'`"\""
        allmodulevars="$allmodulevars""@@SEP2@@""modulename=\""`echo "$modulename" | sed -e 's,",\\\\",g' -e 's, ,@@SEP3@@,g'`"\""
        allmodulevars="$allmodulevars""@@SEP2@@""NEW_FILES=\""`echo "$NEW_FILES" | sed -e 's,",\\\\",g' -e 's, ,@@SEP3@@,g'`"\""
        allmodulevars="$allmodulevars""@@SEP2@@""NEW_LIBS=\""`echo "$NEW_LIBS" | sed -e 's,",\\\\",g' -e 's, ,@@SEP3@@,g'`"\""
        allmodulevars="$allmodulevars""@@SEP2@@""NEW_WLIBS=\""`echo "$NEW_WLIBS" | sed -e 's,",\\\\",g' -e 's, ,@@SEP3@@,g'`"\""
        allmodulevars="$allmodulevars""@@SEP2@@""NEW_MODULES=\""`echo "$NEW_MODULES" | sed -e 's,",\\\\",g' -e 's, ,@@SEP3@@,g'`"\""
        allmodulevars="$allmodulevars""@@SEP2@@""TO_PRELOAD=\""`echo "$TO_PRELOAD" | sed -e 's,",\\\\",g' -e 's, ,@@SEP3@@,g'`"\""
        allmodulevars="$allmodulevars""@@SEP2@@""TO_LOAD=\""`echo "$TO_LOAD" | sed -e 's,",\\\\",g' -e 's, ,@@SEP3@@,g'`"\""
      done
      # Generate new modules.h
      (cat "$sourcedir"/modules.h
       for modulevars in `echo $allmodulevars | sed -e 's,@@SEP1@@, ,g'`; do
         for assignment in `echo $modulevars | sed -e 's,@@SEP2@@, ,g'`; do
           eval `echo $assignment | sed -e 's,@@SEP3@@, ,g'`
         done
         for mod in $NEW_MODULES ; do
           echo 'MODULE('"$mod"')'
         done
       done) > "$destinationdir"/modules.h
      # Generate new lisp.a
      for f in ${FILES}; do
        link "$sourcedir"/$f "$destinationdir"/$f
      done
      PRELOAD=''
      LOAD=''
      for modulevars in `echo $allmodulevars | sed -e 's,@@SEP1@@, ,g'`; do
        for assignment in `echo $modulevars | sed -e 's,@@SEP2@@, ,g'`; do
          eval `echo $assignment | sed -e 's,@@SEP3@@, ,g'`
        done
        # Generate other libraries
        for f in ${NEW_FILES}; do
          link "$moduledir"/$f "$destinationdir"/$f
        done
        # Update the LIBS, WLIBS and FILES variables
        LIBS=${NEW_LIBS}' '${LIBS}
        WLIBS=${NEW_WLIBS}' '${WLIBS}
        FILES=${FILES}' '${NEW_FILES}
        for f in $TO_PRELOAD; do
          PRELOAD=${PRELOAD}' '"$moduledir/$f"
        done
        for f in $TO_LOAD; do
          LOAD=${LOAD}' '"$moduledir/$f"
        done
      done
      # Generate new modules.o, compiled from modules.c, includes new modules.h
      (cd "$destinationdir" ; link "$absolute_linkkitdir"/modules.c modules.c ; verbose ${CC} ${CFLAGS} -I"$absolute_linkkitdir" -c modules.c ; rm -f modules.c)
      # Generate new lisp.run
      (cd "$destinationdir" ; verbose ${CC} ${CFLAGS} ${CLFLAGS} modules.o ${LIBS} ${X_LIBS} -o lisp.run)
      if [ -n "$PRELOAD" ] ; then
        # Generate new preliminary lispinit.mem
        verbose "$sourcedir"/lisp.run -M "$sourcedir"/lispinit.mem -q -i ${PRELOAD} -x "(saveinitmem \"$destinationdir/lispinit.mem\")"
      fi
      # Generate new lispinit.mem
      if [ -n "$PRELOAD" ] ; then
        verbose "$destinationdir"/lisp.run -M "$destinationdir"/lispinit.mem -q -i ${LOAD} -x "(saveinitmem \"$destinationdir/lispinit.mem\")"
      else
        verbose "$destinationdir"/lisp.run -M "$sourcedir"/lispinit.mem -q -i ${LOAD} -x "(saveinitmem \"$destinationdir/lispinit.mem\")"
      fi
      # Same thing in wide mode.
      if test -n "$with_wide"; then
        (cd "$destinationdir" ; link "$absolute_linkkitdir"/modules.c wmodules.c ; verbose ${CC} ${CFLAGS} -DWIDE -I"$absolute_linkkitdir" -c wmodules.c ; rm -f wmodules.c)
        (cd "$destinationdir" ; verbose ${CC} ${CFLAGS} ${CLFLAGS} wmodules.o ${WLIBS} ${X_LIBS} -o wlisp.run)
        if [ -n "$PRELOAD" ] ; then
          verbose "$sourcedir"/wlisp.run -M "$sourcedir"/wlispinit.mem -q -i ${PRELOAD} -x "(saveinitmem \"$destinationdir/wlispinit.mem\")"
          verbose "$destinationdir"/wlisp.run -M "$destinationdir"/wlispinit.mem -q -i ${LOAD} -x "(saveinitmem \"$destinationdir/wlispinit.mem\")"
        else
          verbose "$destinationdir"/wlisp.run -M "$sourcedir"/wlispinit.mem -q -i ${LOAD} -x "(saveinitmem \"$destinationdir/wlispinit.mem\")"
        fi
      fi
      # Generate new makevars
      sed -e "s,^LIBS=.*\$,LIBS='${LIBS}'," -e "s,^WLIBS=.*\$,WLIBS='${WLIBS}'," -e "s,^FILES=.*\$,FILES='${FILES}'," < "$sourcedir"/makevars > "$destinationdir"/makevars
    fi
    # Done.
    trap '' 1 2 15
    ;;

  *) usage;;
esac

