#!/bin/bash
####################################################################
#                  MCB                                             #
# MCTDH Code Browser. Finds routines and files and loads them      #
# into less, vi, emacs or xemacs.                                  #
#                                                                  #
# HDM 02/01                                                        #
####################################################################

#--  set awk1 to awk or nawk if gawk is not available ---#
if [ -z "$awk1" ] ; then
   exist=`which gawk | cut -d' ' -f1 | fgrep '/bin'`
   if [ -n "$exist" ] ; then
      awk1=gawk
   fi
fi
if [ -z "$awk1" ] ; then
   exist=`which nawk | cut -d' ' -f1 | fgrep '/bin'`
   if [ -n "$exist" ] ; then
      awk1=nawk
   fi
fi
if [ -z "$awk1" ] ; then
   exist=`which awk | cut -d' ' -f1 | fgrep '/bin'`
   if [ -n "$exist" ] ; then
      awk1=awk
   fi
fi

cd $MCTDH_DIR
task=o
ms=$MCTDH_DIR/source

while getopts ":hvVgleopkxnfuFPRmt" opt; do
    case $opt in
    h  ) help=h ;;
    v  ) task=v ;;
    V  ) task=V ;;
    g  ) task=g ;;
    l  ) task=l ;;
    e  ) task=e ;;
    o  ) task=o ;;
    p  ) task=p ;;
    k  ) task=k ;;
    x  ) task=x ;;
    n  ) task=n ;;
    u  ) unmodified=1  ;;
    f  ) task=f ;;
    m  ) make=m ;;
    F  ) ppf=0  ;;
    P  ) ppf=1  ;;
    R  ) ppf=1  ;;
    t  ) tagmode=1 ;;
    \? ) echo ' Unkown option!'
         echo ' '
         task=h ;;
  esac
done
shift $(($OPTIND - 1))

if [ -z "$unmodified" ] && [ "$MCTDH_KB" = german ]; then
   pattern=` echo "$1" | sed -e  's/\./\\\./g'  -e  's/\:/\./g' \
             -e  's/,/\.\*/g'   -e  's/ä/\*/g'  -e  's/ö/\.\*/g' `
elif [ -z "$unmodified" ] && [ "$MCTDH_KB" = french ]; then
   pattern=` echo "$1" | sed -e  's/\./\\\./g'  -e  's/\:/\./g' \
             -e  's/,/\.\*/g'   -e  's/à/\*/g'  -e  's/ù/\.\*/g' `
else
   pattern=$1
fi
tag="$pattern"

# Print HELP text
if [ "$help" = "h" ] ; then
   cat <<END
---------------------------------------------------------------------
MCTDH Code Browser.
Purpose: Show FORTRAN programs, shell scripts or MCTDH files.
Usage: mcb [ -h -t -m -o -v -V -g -l -e -x -n -p -k -f -F -R -P ] program/routine/tag
-h     : print this help text.
-m     : Make new hash files source/SUBR and source/SFILES, or a new tags file.
-t     : Switch to tags mode.
-o     : Use less   to display the program or file (default).
-v     : Use vi     to display the program or file.
-V     : Use vim    to display the program or file.
-g     : Use gvim   to display the program or file.
-l     : Use nedit  to display the program or file.
-e     : Use emacs  to display the program or file.
-x     : Use xemacs to display the program or file.
-n     : Use emacs -nw to display the program or file.
-p     : Use pico   to display the program or file.
-k     : Use kate   to display the program or file.
-f     : Act as filter. Print the absolute path of the file containg the program/routine/tag.'
END
  if [ "$MCTDH_KB" = german ] || [ "$MCTDH_KB" = french ] ; then
    echo  '-u     : Do not modify the regex-input. (unmodified, see below).'
  fi
  cat <<END
-F     : Search for files only (search SFILES).
-R     : Search for routines only (search SUBR).
-P     : Same as -R. Search for programs only (search SUBR).

In tags mode, the argument is interpreted as a source code tag (i.e. name
of a routine, variable, type definition, ...) and looked up in the tags
file produced by exuberant-ctags (see http://ctags.sourceforge.net/). The
viewer or editor is then started directly at the location of this tag.
Many editors can make additional use of the tags information and allow e.g.
jumping to the definition of the variable or subroutine under the cursor;
see the documentation of your editor for more information.

END
  if [ "$MCTDH_KB" = german ] ; then
    cat <<END
NB: To make the input simpler (no quoting, no backslash), there apply
    the following transformation rules:
     . -> \.  ;   : -> .  ;   ä -> *  ;   ö -> .*
    These transformations are switched off by the option -u
    Example:  mcb a:b.cdäfö    is equivalent to:    mcb -u 'a.b\.cd*f.*'

Examples: mcb rdoper    (displays the subroutine rdoper)
          mcb rdoper.F  (displays the file rdoper.F)
          mcb compile   (displays the shell script compile)
          mcb nocl1.op  (displays the file nocl1.op)
          mcb ö.op   or  mcb -u '.*\.op' (displays a list of all operator files)
          mcb zeroö  or  mcb -u 'zero.*' (displays a list of all routines
                                          that start with the string zero)
          mcb -R ö  or  mcb -R -u '.*'   (displays a list of all routines)
          mcb -F ö  or  mcb -F -u '.*'   (displays a list of all files)
END
  elif [ "$MCTDH_KB" = french ] ; then
    cat <<END
NB: To make the input simpler (no quoting, no backslash), there apply
    the following transformation rules:
     . -> \.  ;   : -> .  ;  à -> *  ;  ù -> .*
    These transformations are switched off by the option -u
    Example:  mcb a:b.cdàfù    is equivalent to:    mcb -u 'a.b\.cd*f.*'

Examples: mcb rdoper    (displays the subroutine rdoper)
          mcb rdoper.F  (displays the file rdoper.F)
          mcb compile   (displays the shell script compile)
          mcb nocl1.op  (displays the file nocl1.op)
          mcb ù.op   or  mcb -u '.*\.op' (displays a list of all operator files)
          mcb zeroù  or  mcb -u 'zero.*' (displays a list of all routines
                                          that start with the string zero)
          mcb -R ù  or  mcb -R -u '.*'   (displays a list of all routines)
          mcb -F ù  or  mcb -F -u '.*'   (displays a list of all files)
END
  else
    cat <<END
NB: If you are working on a german keyboard, set the environment variable:
    "export MCTDH_KB=german" and run "mcb -h" again.

Examples: mcb rdoper    (displays the subroutine rdoper)
          mcb rdoper.F  (displays the file rdoper.F)
          mcb compile   (displays the shell script compile)
          mcb nocl1.op  (displays the file nocl1.op)
          mcb '.*\.op'  (displays a list of all operator files)
          mcb 'zero.*'  (displays a list of all routines
                         that start with the string zero)
          mcb -R '.*'   (displays a list of all routines)
          mcb -F '.*'   (displays a list of all files)
END
  fi
  cat <<END

Tags Examples: mcb -t -m      (creates new tags file)
               mcb -t inputml (displays the subroutine inputml)
               mcb -t node_t  (displays the type node_t)
               mcb -t maxdef  (displays the global variable maxdef)
---------------------------------------------------------------------
END
  exit
fi

# Switch to tags mode if requested.

if [ "$tagmode" = 1 ]; then
   cd "$ms"
   if [ ! -f tags ]; then
      echo "No tags file found, I will create it." >&2
      make="m"
   fi
   if [ "$make" = "m" ]; then
      # check ctags version
      if ! ctags --version 2>/dev/null | grep -qi exuberant; then
         echo "You need Exuberant Ctags, see http://ctags.sourceforge.net/" >&2
         exit 1
      fi
      # create tags file
      echo -n 'Creating the tags file... ' >&2
      # run ctags on all source files but skip SVN directories
      ctags --recurse --exclude=.svn --fortran-kinds=+i --langmap=Fortran:+.inc
      echo "done." >&2
   fi
   if [ "$tag" ]; then
      # open editor at the requested tag
      case "$task" in
         o)  less -t "$tag" ;;
         v)  vi -t "$tag" ;;
         V)  vim -t "$tag" ;;
         g)  gvim -t "$tag" ;;
         f)  grep "^$tag\s" tags | awk '{print $2}' | sed -e "s|^|${ms}/|" ;;
         *)  echo "Don't know how to start this editor at a given tag!" >&2
             exit 1 ;;
      esac
   elif [ "$make" != "m" ]; then
      echo 'No tag given. Try "mcb -h".' >&2
      exit 1
   fi
   exit 0
fi

# Generate SUBR and SFILES if not existing

if [ ! -f "$ms/SUBR" ] || [ ! -f "$ms/SFILES" ] || [ "$make" = "m" ] ; then
    if [ "$make" = "m" ] ; then
      echo 'Creating the hash files:'
      echo "$ms/SUBR  SFILES"
    else
      echo 'The file(s):'
      echo "$ms/SUBR  SFILES"
      echo 'do not exist! I am creating them.'
    fi

    f77files=`find $ms -name "*.[Ff]" -type f -print | egrep -v 'source/.*\.(new|old)/'`
    f90files=`find $ms -name "*.[Ff]90" -type f -print | egrep -v 'source/.*\.(new|old)/'`
    ffiles=`echo -e "$f77files\n$f90files"`
    cfiles=`find $MCTDH_DIR -name "*.[Cc]" -type f -print`

    egrep -in "^      *(subroutine|program) " $f77files \
             | sed  's/(/ (/' | tr -s ' ' | sort > $ms/SUBR
    egrep -in "^     ( comp.*| dou.*| real\*8 | integer | )function" \
             $f77files | sed  's/(/ (/' | tr -s ' ' | sort >> $ms/SUBR
    echo "$f90files" | ( while read fn; do
        # ignore stuff inside interface sections; this would be too cryptic with sed & co.
        perl -ne 'if ($ii) { $ii=0 if (/^\s*end\s*interface/i); } else { $ii=1 if (/^\s*interface/i); if (s/^\s*((subroutine|program|function).*)$/\1/i) { s/\(/ (/; print $ARGV,":",$.,": ",$_ }; }' "$fn" ; done ) \
        | sort >> $ms/SUBR
    if [ -n "$cfiles" ]; then
      egrep -in "C-routine" $cfiles | sort >> $ms/SUBR
    fi
    egrep -n '^#!.*/bin/' $MCTDH_DIR/bin/*  2> /dev/null \
        | sed -e 's?:#!.*/bin/?: script ?' -e '/~:/d' \
          -e '/_bk/d' -e '/_md/d' >> $ms/SUBR
    egrep -n '^#!.*/bin/' $MCTDH_DIR/install/* 2> /dev/null \
        | sed -e 's?:#!.*/bin/?: script ?' -e '/~:/d' \
          -e '/_bk/d' -e '/_md/d' >> $ms/SUBR
    egrep -n '^#-#!.*/bin/' $MCTDH_DIR/bin/*  2> /dev/null \
        | sed -e 's?:#-#!.*/bin/?: script ?' -e '/~:/d' \
          -e '/_bk/d' -e '/_md/d' >> $ms/SUBR
    egrep -n '^#-#!.*/bin/' $MCTDH_DIR/install/* 2> /dev/null \
        | sed -e 's?:#-#!.*/bin/?: script ?' -e '/~:/d' \
          -e '/_bk/d' -e '/_md/d' >> $ms/SUBR
    egrep -n '^#!.*/bin/' $MCTDH_DIR/install/AdvancedUser/* 2> /dev/null \
        | sed -e 's?:#!.*/bin/?: script ?' -e '/~:/d' \
          -e '/_bk/d' -e '/_md/d' >> $ms/SUBR
    echo "$MCTDH_DIR/install/Makefile:1: Makefile" >> $ms/SUBR

    echo -e "$ffiles \n$cfiles" | sed  's?.*/?& ?' > $ms/SFILES
    find $MCTDH_DIR/doc -name "*.html" -type f -print  \
             | sed  's?.*/?& ?' >> $ms/SFILES
    find $MCTDH_DIR/doc -name "*.tex"  -type f -print  \
             | sed  's?.*/?& ?' >> $ms/SFILES
    ls $ms/include/*.inc | sed  's?.*/?& ?' >> $ms/SFILES
    ls $MCTDH_DIR/*inputs/*.inp | sed  's?.*/?& ?' >> $ms/SFILES
    ls $MCTDH_DIR/operators/{*.op,*.srf} | sed  's?.*/?& ?' >> $ms/SFILES
    ls $MCTDH_DIR/bin/python/*/*.py | sed  's?.*/?& ?' >> $ms/SFILES
    echo "DONE Installation of SUBR and SFILES."
    echo " "
    if [ -z "$pattern" ] ; then exit ; fi
fi
# End of generation of SUBR and SFILES.


if [ -z "$pattern" ] ; then
   echo 'No program or file given. Try "mcb -h".'
   exit
fi


# Distinguish between files and routines.

if [ -z "$ppf" ] ; then
    if [ -z "${pattern##*.[fFcC]}" ] || [ -z "${pattern##*.inc}" ] ||\
       [ -z "${pattern##*.f90}" ] || [ -z "${pattern##*.F90}" ] || \
       [ -z "${pattern##*.inp}" ] || [ -z "${pattern##*.op}" ] || \
       [ -z "${pattern##*.srf}" ] || [ -z "${pattern##*.html}" ] || \
       [ -z "${pattern##*.tex}" ] || [ -z "${pattern##*.py}" ]; then
        ppf=0
    fi
fi

unset vspath v2path v3
# Search for $pattern.

if [ "$ppf" = 0 ] ; then
    vsname=`sed -e "s?$MCTDH_DIR/??" $ms/SFILES | egrep "/ $pattern"\
            | sed 's!/ !/!' `

    vsnam1=`echo "$vsname "  | tr "\n" ' ' `
    vspath=`echo  $vsnam1    | cut -d' ' -f1`
    v2path=`echo  $vsnam1' ' | cut -d' ' -f2`
    if [ -z "$vspath" ] ; then
        echo " Unknown file: $pattern"
        exit
    else
        vsnumber=1
    fi
else
    ppf=1
    vsname1=`sed -e "s?$MCTDH_DIR/??" $ms/SUBR | egrep -i \
      " (subroutine|program|function|C-routine) ${pattern}( |$)"`
    vsname2=`sed -e "s?$MCTDH_DIR/??" $ms/SUBR | \
              egrep -i "(bin|install|install/AdvancedUser)/${pattern}:"`
    if [ -n "$vsname2" ] ; then
        vsname=`echo -e "$vsname1 \n $vsname2"`
    else
        vsname=$vsname1
    fi
    vsnumber=`echo $vsname | cut -d: -f2`
    vspath=`echo $vsname | cut -d: -f1`
    v3=`echo $vsname | cut -d: -f4`

    if [ -z "$vspath" ] ; then
        echo " Unknown (sub)routine, function or script: $pattern"
        echo " Trying Files."
        ppf=0
        vsname=`sed -e "s?$MCTDH_DIR/??" $ms/SFILES | egrep "/ $pattern"\
               | sed 's!/ !/!' `
        vsnam1=`echo "$vsname "  | tr "\n" ' ' `
        vspath=`echo  $vsnam1    | cut -d' ' -f1`
        v2path=`echo  $vsnam1' ' | cut -d' ' -f2`
        if [ -z "$vspath" ] ; then
            echo " Unknown file: $pattern"
            exit
        else
            vsnumber=1
        fi
    fi
fi

# The pattern (regex) is not unique.
# Display the possible files or routines.

if [ -n "$v2path" ] ; then
    echo '###################################################################'
    echo 'WARNING: The name of the file is not unique.'
    echo "Paths relative to MCTDH_DIR = $MCTDH_DIR"
    echo '###################################################################'
    echo "$vsname" | $awk1 '{ printf("%4i  %-22s%s\n",NR,$1,$2) }'
    echo '-------------------------------------------------------------------'
    if [ "$task" = "f" ] ; then exit ; fi
    echo 'Which file do you want to see? Type number (0=exit)'
    read ans
    if [ -z "$ans" ] || [ "$ans" = 0 ] || [ "$ans" = q ] ; then exit ; fi
    vspath=`echo $vsname | tr "\n" ' ' | cut -d" " -f$ans `
    vsnumber=1
    echo '###################################################################'
fi
if [ -n "$v3" ] ; then
    echo '###################################################################'
    echo 'WARNING: The name of the (sub)routine is not unique.'
    echo "Paths relative to MCTDH_DIR = $MCTDH_DIR"
    echo '###################################################################'
    echo "$vsname" | sed  's?:? ?g' | $awk1 \
      '{ printf("%4i%35s%5i %12s %-s %s %s %s\n",NR,$1,$2,$3,$4,$5,$6,$7) }' \
      | cut -c1-80
    echo '-------------------------------------------------------------------'
    if [ "$task" = "f" ] ; then exit ; fi
    echo 'Which program, (sub)routine or script do you want to see?' \
         'Type number (0=exit)'
    read ans
    if [ -z "$ans" ] || [ "$ans" = 0 ] || [ "$ans" = q ] ; then exit ; fi
    a2=$((2*$ans))
    a1=$(($a2-1))
    vsname1=`echo $vsname | cut -d: -f$a1-$a2 `
    vsname=${vsname1##* }
    vsnumber=`echo $vsname | cut -d: -f2`
    vspath=`echo $vsname | cut -d: -f1 `
    echo '###################################################################'
fi

# Call less or vi or emacs or xemacs or ...


if [ "$task" = "o" ] ; then
    vsnumber=$((vsnumber - 11))
    if [ $vsnumber -lt 1 ] ; then  vsnumber=1 ; fi
    less +"$vsnumber"  "$vspath"
elif [ "$task" = "v" ] ; then
     vi +"$vsnumber"  "$vspath"
elif [ "$task" = "V" ] ; then
     vim +"$vsnumber"  "$vspath"
elif [ "$task" = "g" ] ; then
     gvim +"$vsnumber"  "$vspath"
elif [ "$task" = "e" ] ; then
     emacs  -geometry 80x55+3+80  +"$vsnumber"  "$vspath" &
elif [ "$task" = "x" ] ; then
     xemacs -geometry 80x55+3+80  +"$vsnumber"  "$vspath" &
elif [ "$task" = "l" ] ; then
     nedit +"$vsnumber"   "$vspath" &
elif [ "$task" = "n" ] ; then
     xterm -exec emacs -nw +"$vsnumber" "$vspath" &
elif [ "$task" = "k" ] ; then
     xterm -exec kate --nofork --line "$vsnumber" "$vspath" &
elif [ "$task" = "p" ] ; then
     xterm -exec pico +"$vsnumber" "$vspath" &
elif [ "$task" = "f" ] ; then
     echo "$MCTDH_DIR/$vspath"
fi

exit

