#!/bin/bash
#
#  whizzytex --- WhizzyTeX, a WYSIWIG environment for TeX
#  Copyright (C) 2001, 2002 Didier Rmy
#
#  Author         : Didier Remy <Didier.Remy@inria.fr>
#  Version        : 1.1.3
#  Bug Reports    : whizzytex-bugs@pauillac.inria.fr
#  Web Site       : http://pauillac.inria.fr/whizzytex
#  
#  WhizzyTeX 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 2, or (at your option)
#  any later version.
#
#  WhizzyTeX 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 
#  (enclosed in the file GPL).
#
#  See the file COPYING enclosed with the distribution.
#############################################################################
#  File whizzytex (Bash shell-script)
#############################################################################

### Configuration (manual or automatic)

VERSION="1.1.3"

# name (or full path) of the dump latex2e package file (without the extension)
PACKAGE=whizzytex.sty

# Set to false if grep does not accept option -B
GREPB=true

# Translatin DVI to Postscript
DVIPS=dvips

# Translatin DVI to DVI, may be set to DVICOPY
DVICOPY=mv

# Default LaTeX implementation
INITEX=initex
LATEX=latex
FORMAT=latex
FMT=fmt
BIBTEX=bibtex

# Default place for configuration files

CONFIGFILE=/etc/whizzytex/whizzytex.conf 

### Configuration (manual only)

# limit the length of error messages sent back to stdout.
TEXERRORMAXLINES=50
TEXERRORAROUND=20

# time limit for running initex in case this could loop, undetectedly!
LIMIT=20

# Signals telling gv and xdvi/advi to refresh
SIGDVI=SIGUSR1
SIGPS=SIGHUP

# Option to read command, could be use to send timeout, in case, whizzytex 
# should watch other files that those controlled by emacs
# READOPTION=-t 5
READOPTION=

### End of configuration variables

TRACE=false

case $# in
 0) echo 'Usage: whizzytex [ OPTIONS ] FILENAME'; exit 1;;
esac

COMMAND="$0"
for i in "$@"; do COMMAND="$COMMAND"'
'"$i"; done
TOUCH=false
MKSLICE=defaultmkslice
MKFILE=defaultmkfile

defaultmkslice () { mv $SLICE $WHIZZY$EXT 2>/dev/null; }
defaultmkfile () { true; }


DO=normal
DOMARKS=false
MAKEINDEX=makeindex
DOINDEX=true
DVI=dvi
VIEW=wdvi
VIEWCOMMAND=advi
NEWFILES=newfile
MULTIPLE=true
DUPLEX=false
ADVI=0

# Load config files
for CONFIG in "$CONFIGFILE" {"$HOME",.}/.whizzytexrc
do
  if [ -f "$CONFIG" ]
  then
    . "$CONFIG"
  fi
done

while true
do
  case $1 in
   -kill) DO=kill;;
   -wakeup) DO=wakeup;;
   -reformat) DO=format;;
   -mkslice) MKSLICE="$2"; shift;;
   -mkfile) MKFILE="$2"; shift;;
   -duplex) DUPLEX=true;;
   -trace) TRACE=true;; 
   -marks) DOMARKS=true;;
   -makeindex) 
      case "$2" in
        false) DOINDEX=false;;
        *) MAKEINDEX="$2";;
      esac 
      shift;;
   -bibtex) BIBTEX="$2"; shift;;
   -ext) EXT="$2"; shift;;
   -initex) INITEX="$2"; shift;;
   -latex) LATEX="$2"; shift;;
   -format) FORMAT="$2"; shift;;
   -fmt) FMT="$2"; shift;;
   -dvicopy) DVICOPY="$2"; shift;;
   -watch) NEWFILES=newfiles;;
   -dvi)
       VIEW=wdvi;
       case "$2" in
       .) VIEWCOMMAND=xdvi;;
       *) VIEWCOMMAND="$2";;
       esac
       shift;;
   -advi)
       ADVI=1
       VIEW=wdvi;
       case "$2" in
       .) VIEWCOMMAND=advi;;
       *) VIEWCOMMAND="$2";;
       esac
       shift;;
   -ps)
       VIEW=ps
       case "$2" in
       .) VIEWCOMMAND=gv;;
       *) VIEWCOMMAND="$2";;
       esac
       shift;;
   -display)
       export DISPLAY="$2"
       shift;;
   -*)
       echo 'Unrecognized argument '"$1"'

Usage whizzytex: 

  whizzytex <option> ... <option> <file>

where <option> is 

    -kill 
    -reformat
    -duplex
    -makeindex <makeindex command>
    -ext <latex extension>
    -initex <initex command>
    -latex <latex command>
    -format <latex format file name>
    -bibtex <bibtex command>
    -fmt <format extension>
    -marks
    -pre <preprocess-command>
    -advi <advi-like previewer command> 
    -dvi <dvi-like previewer command> 
    -ps <Postscript previewer command> 
    -display <X display> 
  ' 1>&2
       exit 1;;
   *) break;;
  esac
  shift
done

if $TRACE; then set -o xtrace; fi

HERE=$(/bin/pwd)
case "$1" in 
  *" "*) 
      echo 'Cannot WhizzyTeX a file '"$1"' whose name contain a space'
      echo '<Quitting>'
      exit 1;;
esac
FULLNAME=$1
BASENAME=$(basename $1)
FILEEXT=$(expr "$BASENAME" : '.*\(\.[^.]*\)')
NAME=$(basename "$BASENAME" "$FILEEXT")
case "$EXT" in
 "") EXT=$FILEEXT;;
esac
WHIZZY=_whizzy_$NAME
WHIZZYDIR=${WHIZZY}_d
WFORMAT=\&$WHIZZY
LOCK=.$WHIZZY$EXT
SLICE=$WHIZZY.new
INPUT=$HERE/$WHIZZYDIR/input
OUTPUT=$HERE/$WHIZZYDIR/output
TMP=$HERE/$WHIZZYDIR/tmp
SECTIONS=$OUTPUT/sections
LOG=$TMP/log
ERR=$TMP/err
# for persistent log
# WLOG=$NAME.wlog

errlog () { echo "$*" 1>&2; }
fatal () { 
    errlog '*** Fatal error: ' "$*"
    [ -f $ERR ] && cat $ERR
    echo '<Quitting>' 
    cleanquit "$1"
}
warning () {
  echo "Warning: " $*
}  

quit () { 
  echo '<Quitting>'
  cleanquit viewer
}

log () {
  if [ -f $OUTPUT/$1 ] 
  then cat $OUTPUT/$1
  fi
  false
}

GREPB=false
texlog () { 
  if [ -f $OUTPUT/$1 ] 
  then
    if $GREPB; then
    grep -B 3 -A $TEXERRORAROUND -e '^!' $OUTPUT/$1 | head -$TEXERRORMAXLINES
    else sed -n -e '/^#/p' -e '/^!/,$p' $OUTPUT/$1 | head -$TEXERRORAROUND 
    fi
  fi
  false; 
}
errlog "$COMMAND"

$MKFILE $FULLNAME
if [ -f $NAME$EXT ]
then 
   :
else
   echo "File $NAME$EXT does not exist"
   exit 1
fi

clean () {
   [ -f $TMP/pids ] && kill -TERM $(cat  $TMP/pids) 2>/dev/null
   rm -f $WHIZZY.* ${WHIZZY}_nil.* $NAME.$FMT $NAME.$VIEW
   if $TRACE; then : ; else rm -rf $WHIZZYDIR; fi
   rm -f $LOCK
}

cleanquit () {
   trap - SIGTERM
   echo "Quitting ($1)"
   clean
   kill -TERM 0
   exit 0
}
trap "cleanquit SIGTERM" SIGTERM
trap "cleanquit SIGHUP" SIGHUP
trap "cleanquit SIGQUIT" SIGQUIT

cleankill () {
   if [ -f $LOCK ] 
   then
      PID=$(cat $LOCK)
      clean 
      kill -KILL $PID
   else
      clean
   fi
   exit 2
}

wakeup () { [ -f $LOCK ] && kill -CONT $(cat $LOCK); }
runinitex () { ( ulimit -t $LIMIT; $INITEX "$@" ); }
runlatex  () { ( ulimit -t $[ 3 * $LIMIT ] ; $LATEX  "$@" ); }
runslice  () { ( ulimit -t $LIMIT ; $LATEX  "$@" ); }


# Making format

format () {
 ( runinitex "&$FORMAT" '\nonstopmode\makeatletter\def\SourceFile{\def\whizzy@master}\makeatother\let\Documentclass\documentclass\renewcommand{\documentclass}[2][]{\Documentclass[#1]{#2}\let\WhizzyAdvi'"$ADVI"'\def\WhizzyExt{'"$EXT"'}\input{'"$PACKAGE"'}}\input{'"$NAME$EXT"'}' )
}

############################################################################
# BEGIN sections

makebibtex () {
  $BIBTEX $NAME 1>$OUTPUT/bibtex
  [ -f $NAME.bbl ] && cp $NAME.bbl $WHIZZY.bbl
  true
}

batchmarks () {
    if runlatex $WFORMAT '\WhizzytexInput{'"$NAME"'}' 1>$OUTPUT/latex
    then
      $DVICOPY $NAME.$DVI $NAME.$VIEW 2>$OUTPUT/dvicopy
      [ -f $NAME.toc ] && cp $NAME.toc $WHIZZY.toc
      if grep -e '\(LaTeX Warning: Citation\|No file [^ ]*\.bbl\)' $NAME.log
      then
        makebibtex
      fi
      if $DOINDEX && grep 'Writing index file' $NAME.log
      then
         $MAKEINDEX $NAME.idx 1>$OUTPUT/makeindex 
         [ -f $NAME.ind ] && cp $NAME.ind $WHIZZY.ind
         true
      fi
    else 
       rm -f $WHIZZY.pag; false
    fi
}

# END sections

############################################################################
# BEGIN Worddiff

wordify () { 
  awk '{for (i=1; i<=NF; i++) {print $i}}'
}


worddiff () { 
  FST=$1
  SND=$2
  DIF=$WHIZZY.dif
  diff $FST $SND > $DIF
  if [ $(grep -v -e '^[-><]' $DIF | wc -l) -eq 1 ] && \
     [ $(wc -l < $DIF) -lt 3 ] && \
       grep -e '^[1-9][0-9,]*[ac]' $DIF > $DIF.lin
  then
    sed $DIF -n -e '/^< /s/^< //p' > $FST.lin
    sed $DIF -n -e '/^> /s/^> //p' > $SND.lin

    wordify < $FST.lin > $FST.wrd
    wordify < $SND.lin > $SND.wrd

    diff $FST.wrd $SND.wrd > $DIF.wrd

    if [ $(grep -v -e '^[-><]' $DIF.wrd | wc -l) -eq 1 ] && \
         grep -e '^[1-9][0-9,]*[ac]' $DIF.wrd > /dev/null
    then
       (
        echo '<Error in Line'
        cat $DIF.lin
        echo Word
        grep -e '^[0-9]' $DIF.wrd
        echo ':'
        sed $DIF.wrd -n -e 's/^> \(.*\)$/\1/p'; 
        echo '>'
       ) | tr '\n' ' '
       echo     
    else
      false
    fi
  else 
    false
  fi
}

# END Wordify

############################################################################
# So what should we do

case $DO in
  kill) cleankill && exit 0 || exit 1;;
  wakeup) wakeup && exit 0 || exit 1;;
  format) format && exit 0 || exit 1;;
  normal) 
    # To ensure that only one deamon is running on the spool file.
    if [ -f $LOCK ] && kill -CONT $(cat $LOCK) 2>/dev/null 
    then
      fatal 'Remove running WhizzyTeX process first'
    else
       echo $$ > $LOCK
    fi
   ;;
esac

############################################################################
# We go for good

# These directories are normally created by Emacs
mkdir $WHIZZYDIR $INPUT $OUTPUT $TMP 2>/dev/null
echo "$COMMAND"  > $OUTPUT/command

if [ -f $NAME.$FMT ] && [ $NAME$EXT -ot $NAME.$FMT ]
then 
  :
else
  echo -n '<Initial formating '
  if { format >$OUTPUT/format 2>$ERR || \
       { echo 'failed>'; 
         echo Removing $NAME.aux; 
         echo '<Retrying '; 
         rm -f $NAME.aux && format >$OUTPUT/format 2>$ERR; }; } \
     && [ -f $NAME.$FMT ] 
  then
     mv $NAME.$FMT $WHIZZY.$FMT
     echo 'succeeded>'
  else
     rm -f $NAME.$FMT
     echo 'failed>'
     set +o xtrace
     texlog format
     echo '<Initialization failed>'; 
     { echo '
        ******************************************************
        An error occured while building the initial format.
        Are you sure that your file can be correctly LaTeX-ed? 
        To keep log files, start WhizzyTeX in debug mode. 
        ******************************************************
'         
#         echo; echo '*** Command was:'; echo; 
#         echo $COMMAND; 
#         echo; echo '*** Formatting log:' ; echo; 
#         cat $OUTPUT/format; 
#         echo; echo '*** Other errors:' ; echo; 
#         cat $ERR; 
     }
#     > $WLOG
     fatal initialization 'Could not build format ---See message above'
  fi
fi

# Initial file

echo '\begin{document}\WhizzyEmptyPage\end{document}' > ${WHIZZY}_nil$EXT

# Texing...

echo $VIEWCOMMAND 1>&2

# GV does not refresh if the dates are equal (i.e. up 1 second) even when  
# signal with $SIGPS
ANTIDATE=$(date +%m%d%H%M.%S)

case $VIEW in
  ps)
    preview () {
        $DVIPS -o $WHIZZY.pst $WHIZZY.$DVI 2>/dev/null && \
        { [ ! $WHIZZY.pst -nt $WHIZZY.ps ] && touch -t $ANTIDATE $WHIZZY.pst
          mv $WHIZZY.pst $WHIZZY.ps ; }
    }
    SIG=$SIGPS
    ;;   
  wdvi)
    preview () { $DVICOPY $WHIZZY.$DVI $WHIZZY.$VIEW; }
    SIG=$SIGDVI
    ;;   
esac

newfiles () {
  mv $(find $INPUT -type f -print || cleanquit Find) $HERE/  2>/dev/null
}
newfile () {
#  if [ -f $INPUT/$SLICE ]; then echo YES; else echo NO; fi
  mv $INPUT/$SLICE $HERE/  2>/dev/null
}

preprocess () {
  if $MKSLICE $WHIZZY$EXT >$OUTPUT/mkslice 2>$ERR && [ -f $WHIZZY$EXT ]
  then 
     true
  else
     echo '<Preprocessing failed>'
     [ -f $ERR ] && cat $ERR && cat $ERR >> $OUTPUT/mkslice
     log mkslice
     false
  fi
}

PROCESS=false
process () {
  PROCESS=false
#  This line was added for some reason that I forgot...
#  Maybe for not reloading subfiles?
  rm -f $WHIZZY.aux
  echo '<Recompiling>'
  if { runslice $WFORMAT $WHIZZY$EXT && preview; } 1>$OUTPUT/slice
  then
     echo "<Compilation succeeded>"
     ln -f $WHIZZY$EXT $WHIZZY.last
  else
     echo "<Recompilation failed>"
     if [ -f $WHIZZY.last ] && worddiff  $WHIZZY.last $WHIZZY$EXT
     then 
       :
     else
       texlog slice
       [ -f $WHIZZY.last ] && echo 'l.'$[ $(wc -l < $WHIZZY.last) - 1 ]' ' 
     fi
     echo '<Continuing>'
     false
  fi
}

############################################################################
# Initial run

# We ensure that the dvi file exists 
if runslice $WFORMAT ${WHIZZY}_nil$EXT > $OUTPUT/nil
then
  mv ${WHIZZY}_nil.$DVI $WHIZZY.$DVI
  preview || fatal 'Cannot preview initial page'
else
  cat $OUTPUT/nil
  fatal 'Cannot build initial page'
fi

# To give it a chance to see citations and other global information.

[ -f $NAME.bbl ] && cp $NAME.bbl $WHIZZY.bbl
[ -f $NAME.ind ] && cp $NAME.ind $WHIZZY.ind
[ -f $NAME.toc ] && cp $NAME.toc $WHIZZY.toc

# process $WHIZZY$EXT is present. Will override $WHIZZY.$DVI if it succeeds

if $NEWFILES; then preprocess && process; fi

# lauch the previewer(s) in background, storing its pid in $ID
# and putting a handler on its termination. 


rm -f $TMP/ID

case $ADVI in
  1) ;;
  0) MULTIPLE=false;;
esac
if $MULTIPLE
then
   # we are going to launch $NAME.VIEW as a secondary file
   [ -f $NAME.$VIEW ] || cp $WHIZZY.$VIEW $NAME.$VIEW
   CLIENTVIEW=$NAME.$VIEW
else
   CLIENTVIEW=''
fi

{
  $VIEWCOMMAND $WHIZZY.$VIEW $CLIENTVIEW 2>$OUTPUT/view &
  echo $! >> $TMP/pids
  ID=$!
  echo $ID > $TMP/ID
  wait $ID && quit || fatal 'Viewing process terminated abnormally'
} &

checkviewer () {
  kill -$SIG $ID || fatal 'Previewer is not responding to signal' $SIG
}

whole () {
   echo '<Recompiling whole document>'
   if batchmarks $1 </dev/null >$LOG
   then
      echo '<Whole document updated>'
      if $1; then echo '<Pages and sections updated>'; fi
      [ -f $TMP/DID ] && kill -$SIG $(cat $TMP/DID) || rm -f $TMP/DID
      true
   else
      echo '<Whole document recompilation failed>'
   fi
}
# So as to start immediately...
whole $DOMARKS

## Reformating

reformat () {
  echo '<Reformating>'
  touch -r $NAME$EXT $WHIZZY.$FMT
  if format 1>$OUTPUT/format 2>$ERR
  then
      mv -f $NAME.$FMT $WHIZZY.$FMT
      echo '<Reformatting succeeded>'
      PROCESS=true
      # do the slice with new format (quick)
      # process
      whole $DOMARKS
  else
      echo '<Reformatting failed>'
      texlog format
      echo '<Continuing with the old format>'
  fi
}

## Duplex

duplex () {
  [ -f $NAME.$VIEW ] || \
  { cp $WHIZZY.$VIEW $NAME.$VIEW; 
    warning '(duplex) Using ' $WHIZZY.$VIEW ' instead of missing ' $NAME.$VIEW
  }
  $VIEWCOMMAND $NAME.$VIEW &
  echo $! > $TMP/DID
  echo $! >> $TMP/pids
}

if $DUPLEX; then duplex; fi

## wait for viewer, this is here, so luckily one does not need to wait

while [ ! -f $TMP/ID ];
do if [ -x /bin/usleep ]; then /bin/usleep 10; else /bin/sleep 1; fi; done
ID=$(cat $TMP/ID)


## The loop watching changes

checkMKfile () {
  case "$1" in
    "") ;;
    *) 
        if  $MKFILE "$1" >$OUTPUT/mkfile 2>$ERR
        then true
        else 
          echo '<Postprocessing mkfile failed>'
          [ -f $ERR ] && cat $ERR
          log mkfile
          false
        fi;;
  esac
}

# to be fixed
viewps () {
  slice=$1
  if $slice; then file=$WHIZZY; else file=$NAME; fi
  if [ -f $file.wdvi ]
  then 
    ( $DVIPS -o $file.ps $1 && gv $TMP/$1.ps ) >$OUTPUT/viewps 2>$ERR &
  else echo "$file.wdvi not found"
  fi
}

echo '<Initialization succeeded, entering loop>'
while true
do
  if [ ! -d $WHIZZYDIR ]
  then fatal 'No more _whizzy_ directory'
################## Reformating now done from Emacs.
#    elif [ $WHIZZY.$FMT -ot $NAME$EXT ]
#    then reformat
  elif $NEWFILES && preprocess || $PROCESS
  then process && checkviewer
  else
     echo '<Waiting>'
     read $READOPTION COMMAND REST
     case "$COMMAND" in
       duplex) duplex ;;
       reformat) checkMKfile $REST; reformat ;;
       reslice) process && checkviewer ;;
       whole) checkMKfile $REST; whole $DOMAKS;;
       bibtex) makebibtex;;
       viewps-slice) viewps true;;
       viewps-whole) viewps false;;
#       bash) bash -c "$REST";;
       info) 
        echo "
        COMMAND=$COMMAND
        EXT=$EXT
        MKSLICE=$MKSLICE
        MKFILE=$MKFILE
        INITEX=$INITEX
        LATEX=$LATEX
        FORMAT=$FORMAT
        DUPLEX=$DUPLEX
        DOMARKS=$DOMARKS
        VIEW=$VIEW
        VIEWCOMMAND=$VIEWCOMMAND
        ADVI=$ADVI

        BASENAME=$BASENAME
        EXT=$EXT
        NAME=$NAME

        TRACE=$TRACE
";;
       trace)
          case $REST in
              on) set -o xtrace; TRACE=true;; 
             off) set +o xtrace; TRACE=false;; 
          esac
          echo "<trace is $TRACE>"
          ;;
       exit) break ;;
     esac
  fi
done

cleanquit Exit

# whizzytex ends here
