#!/bin/bash

# $Id: proof,v 1.2 2002/09/30$

################################################################################
#            Proof - A simple but smart editor interface to common             #
#              TeX/LaTeX and METAFONT/MetaPost processing tasks                #
#                                                                              #
#                    (Copyright (C) 2002 Klaus Bosau)                          #
################################################################################

function info
{
echo \
"Usage: proof [-P] [-abfp123xyzs] [-u routine] file
   or: proof -T file
   or: proof [-hv]

Process and view TeX, LaTeX, METAFONT and MetaPost sourcecode  

  -P   enter preview mode
  -T   leave preview mode and exit

  -a   leave auxiliary files untouched
  -b   create back-up file
  -f   use formatfile 'mylatex'
  -p   use PostScript-Type-1 fonts
  -n   METAFONT proofing mode n, where n can be 1, 2 or 3
  -x   generate dvi-file
  -y   generate ps-file (default)
  -z   generate pdf-file
  -u   call userdefined conversion routine
  -s   don't call viewer

  -h   print this help message
  -v   print version information

Options -abfp123xyzus may be handed over also in the sourcecode itself.
(see Proof manual page on how to do this) However, if you'd like to make
use of this feature, you should keep in mind that this would make both ad-
ditional small letter commandline options (except -h and -v) and existing
environmental settings ineffective.

Please send bug reports or suggestions to kbosau@web.de."
}

configfile=~/.proofrc                      # default configuration file

function error
{
  echo -e "--> ${0##*/}: $1"
  [ "$2" ] && { [ "$action" = 'watch' ] && read; exit 1; }
}

[ ! -e $configfile ] && error "Can't find configuration file!" halt

# global initializations

. $configfile

optionlist=':PTfbap123xyzu:sv'             # 'getopts' option list
nfsscmd='\table'                           # 'nfssfont' test command
FORMATFILEDIR=$(eval echo $FORMATFILEDIR)  # assign absolute path

# dvips
if [ "${DVI2PS%% *}" = 'dvips' ]; then
  PDF_OPT='-P pdf -G0'                     # enable distilling to PDF
  PSFONTS_OPT='-P cmz -P amz'              # use PostScript-Type-1 fonts
fi

# TeX
comp='tex'
init='tex --ini &latex mylatex.ltx'
virt='virtex --fmt latex'
myvirt='virtex --fmt mylatex'
formatfile='latex.fmt'
myformatfile='mylatex.fmt'
if [ "$USE_ETEX" = 'yes' ]; then
  comp='etex'
  init='etex --ini &elatex mylatex.ltx'
  virt='evirtex --efmt elatex'
  myvirt='evirtex --efmt mylatex'
  formatfile='elatex.efmt'
  myformatfile='mylatex.efmt'
fi

# pdfTeX
pdfcomp='pdftex'
pdfvirt='pdfvirtex --fmt pdflatex'
if [ "$USE_ETEX" = 'yes' ]; then
  pdfcomp='pdfetex'
  pdfvirt='pdfevirtex --efmt pdfelatex'
fi


function option  # checks if option is set
{
  local opt

  function opts
  {
    local line

    if line=$(head -1 $_source$_ext | grep '(-'); then
      echo $line | sed 's/^.*(\(-[^)]*\)).*/\1/'  # document
    elif [ "$OPTS" ]; then
      echo $OPTS                                  # commandline
    else
      echo $PROOFOPTS                             # environment
    fi
  }

  [ ! -e $_source$_ext ] && return 1

  if [ "$1" = 'y' ]; then
    ! option x && ! option z && return 0  # enables default output filetype
  else
    OPTIND=1
    while getopts $optionlist opt $(opts); do
      [ $opt = $1 ] && return 0
    done
  fi
  return 1
}


function tex2dvi
{
  local cmd=$comp fdir=${FORMATFILEDIR:-$PWD}

  function _mv
  {
    [ $2 = $PWD ] && return 0
    mv $1 $2
  }

  if grep '^\(\\documentclass\|\\documentstyle\)' >/dev/null; then
    cmd=$virt  # LaTeX source
    if option f; then
      if [ ! -e $fdir/$myformatfile ]; then
        echo "${0##*/}: Building formatfile..."
        [ ! -d $fdir ] && \
          { error "Can't find directory $fdir!"; return 1; }
        $init $_source && _mv $myformatfile $fdir || \
          { error 'Unable to install formatfile!'; return 1; }
        rm -f mylatex.log; new_ff=yes
      fi
      cmd=$myvirt  # read formatfile
    fi
  fi
  $cmd --interaction=$REPORTMODE $1
} <$_source$_ext


function tex2pdf
{
  local cmd=$pdfcomp
  grep '^\(\\documentclass\|\\documentstyle\)' >/dev/null && cmd=$pdfvirt
  $cmd --interaction=$REPORTMODE $1
} <$_source$_ext


function pid  # returns PID's matching given pattern
{
  $PROCSTAT | grep -v 'grep' | grep "$*" | sed 's/^\ *\([0-9]*\)\ .*$/\1/'
}


function view_file
{
  local cmd

  function tell  # starts process only if not yet running (untested)
  {
    # force reread
    [ -n "$(pid ghostview $2)" ] && return 0
    kill -SIGUSR1 $(pid xdvi.bin $2) >/dev/null 2>&1 && return 0
    kill -SIGHUP $(pid gv $2) >/dev/null 2>&1 && return 0

    # start anew
    $* &
  }

  case "${1##*.}" in
    dvi)  cmd=$DVI_VIEWER; last=x;;
    ps )  cmd=$PS_VIEWER; last=y;;
    pdf)  cmd=$PDF_VIEWER; last=z;;
  esac
  option s || tell $cmd $1
}


function view  # conversion and viewing
{

  local dvi2ps=$DVI2PS

function mk_test
{
$virt --interaction=$REPORTMODE $1 <<EOF
  $(echo $2 | tr ':' '\012')
EOF
if option x; then
  view_file $1.dvi
else
  $dvi2ps $1.dvi && \
  view_file $1.ps
fi
}

  [ $TERM != 'xterm' ] && { error 'No graphical user interface!'; return 1; }

  [ "$last" ] && { option $last || cleanup; }

  option p && dvi2ps="$dvi2ps $PSFONTS_OPT"

  if option u; then
    if type $OPTARG &>/dev/null; then
      $OPTARG $_source && view_file $_source.${ftypes##*->}
    else
      error "Can't find conversion routine $OPTARG!"
    fi
    return
  fi

  case $_ext in

    .mf )  if option 2; then
             mk_test nfssfont "${_source}:${nfsscmd}:\bye"
             ftypes='*pk,tfm->'
           elif option 3; then
             mk_test fontsmpl "${_source}"
           else
             $MF --interaction=$REPORTMODE "\mode=proof; input $_source;" && \
             gftodvi $_source.*gf && \
             if option x; then
               view_file $_source.dvi
               ftypes='log,*gf,*pk,tfm,dvi->'
             else
               $dvi2ps $_source.dvi && \
               view_file $_source.ps
               ftypes='log,*gf,*pk,tfm,dvi,ps->'
             fi
           fi;;

    .mp )  $MP --interaction=$REPORTMODE $_source && \
           tex2dvi "mproof $(ls $_source.[0-9]*)" && \
           if option x; then
             view_file mproof.dvi
           else
             $dvi2ps mproof.dvi && \
             view_file mproof.ps
           fi
           ftypes='log,mpx->[0-9]';;

    .tex)  if option x; then
             tex2dvi $_source.tex && \
             view_file $_source.dvi
             ftypes='log,aux->dvi'
           elif option z; then
             case "$TEXPDFMODE" in
               2)  tex2dvi $_source.tex && \
                   $DVI2PDF $_source.dvi
                   ftypes='log,aux,dvi->pdf';;
               3)  tex2dvi $_source.tex && \
                   $DVI2PS $PDF_OPT $_source.dvi && \
                   $PS2PDF $_source.ps
                   ftypes='log,aux,dvi->pdf';;
               *)  tex2pdf $_source.tex
                   ftypes='log,aux->pdf';;
             esac && view_file $_source.pdf
           else
             tex2dvi $_source.tex && \
             $dvi2ps $_source.dvi && \
             view_file $_source.ps
             ftypes='log,dvi,aux,idx->ps'
           fi;;

    .dvi)  if option y; then
             $dvi2ps $_source.dvi && \
             view_file $_source.ps
             ftypes='->ps'
           elif option z; then
             case "$DVIPDFMODE" in
               2)  $DVI2PS $PDF_OPT $_source.dvi && \
                   $PS2PDF $_source.ps
                   ftypes='ps->pdf';;
               *)  $DVI2PDF $_source.dvi
                   ftypes='->pdf';;
             esac && view_file $_source.pdf
           else
             view_file $_source.dvi
           fi;;

    .ps )  if option z; then
             $PS2PDF $_source.ps && \
             view_file $_source.pdf
             ftypes='->pdf'
           else
             view_file $_source.ps
           fi;;

    .pdf)  view_file $_source.pdf;;

  esac

}


function cleanup
{
  local aux all ign="\($_ext\|,v\|~\)$" files

  # clean up!
  aux=${ftypes%->*}
  ! option a && [ "$aux" ] && eval rm -f "$_source.{$aux,zzz}"
  rm -f {fontsmpl,nfssfont,mproof}.* tmp*

  # list files!
  all="\($(echo $ftypes | tr -d '*' | sed 's/->/,/g' | \
      sed 's/\(^,\|,$\)//;s/,/\\\|/g')\)$"
  if [ "$ftypes" ] && files=$(ls $_source.* | grep -v "$ign" | grep "$all"); then
    echo "${0##*/}: $PWD <-- "$files
  else
    echo "${0##*/}: no output"
  fi
  [ "$new_ff" ] && echo "${0##*/}: ${FORMATFILEDIR:-$PWD} <-- $myformatfile"

  # ftypes='aux,dvi->ps' --> 
  # --> delete {aux,dvi} if requested and show {aux,dvi,ps} if found
}


function backup
{
  function backup_file
  {
    declare -i n=$BACKUP_FILES
  
    cp $1 $1.0~
    until [ -e $1.$n~ ]; do
      n=$((n-1))
    done
    n=$((n+1))
    
    declare -i i=$n
    while [ $i -gt 0 ]; do
      mv $1.$((i-1))~ $1.$i~
      i=$((i-1))
    done
  
    [ $n -gt $BACKUP_FILES ] && rm -f $1.$n~
  }

  if [ "$USE_RCS" = 'yes' ] && type 'ci' &>/dev/null; then
    # call RCS
    echo "${0##*/}: RCS:"; ci -l $_source$_ext
  else
    # default back-up method
    [ "$USE_RCS" = 'yes' ] && \
      error "Can't find RCS! Using default back-up procedure instead..."
    backup_file $_source$_ext
    echo "${0##*/}: $PWD <-- $_source$_ext.1~"
  fi
}


function terminate  # log out procedure
{
  cleanup
  option b && backup
  [ "$action" = 'watch' ] && read
}


# process options!
while getopts $optionlist opt; do
  case $opt in
    P                    )  action=watch;;
    T                    )  action=quit;;
    v                    )  echo 'proof v1.2'; exit 0;;
    \?                   )  info; exit 1;;
  esac
  OPTS=$*
done
OPTS="${OPTS% *}"
shift $(($OPTIND - 1))

# check syntax!
[ -z "$1" ] && error 'Please specify your document!' halt
! echo $1 | grep '\(.mf\|.mp\|.tex\|.dvi\|.ps\|.pdf\)$' >/dev/null && \
  error "Unknown file type!" halt
[ "$action" != 'quit' ] && [ ! -e "$1" ] && error "Can't find file $1!" halt

# go!
case "$action" in
 
   quit )  kill -TERM $(pid "${0##*/}"'.*'-P'.*'"$1") >/dev/null 2>&1;;
 
   *    )  # set shell variables!
           cd $(dirname $1) || exit 1
           _ext=".${1##*.}"
           _source=$(basename $1 $_ext)
           current=/tmp/proof-$$  # preserves st_mtime

           # set signal trap!
           trap 'rm -f $current; terminate; exit 0' INT TERM
 
           # wait until file exists!
           [ ! -e "$1" ] && until [ -e $_source$_ext ]; do sleep 1; done
 
           view
 
           # view only?
           [ -z "$action" ] && { terminate; exit 0; }
 
           # watch!
           echo >$current
           while :; do
             until [ $_source$_ext -nt $current ]; do
               sleep 1
             done
             view; echo >$current
           done;;
 
esac

