#!/bin/sh -h
# -*- ksh -*-
# imprimer: `printer' gnrique
#
### 1.0 12/09/89 - 30/05/94 Philippe Dax
# commande gnrique pour les imprimantes, Test de l'existence des
# fichiers, Imprimantes LaserWriter par printcap, Compatibilit avec
# Solaris2.x
### 2.0 25/05/96 Akim Demaille
# segmentation des gros ps avec pssplit, pretty-printing avec a2ps,
# plusieurs pages par feuilles avec psnup, troff par groff, dvi par
# dvips, segmentation gros dvi par dvips, affichage quand $0=show,
# unification des imprimantes de cal, dcompression par gunzip,
# annulation des impressions si interrompu, prise en compte
# orientation
### 2.1 20/06/96 Akim Demaille
# selection des pages par dvips (dvi) et psselect (le reste)
### 2.2 17/07/96 Akim Demaille
# texinfo par texi2dvi
### 2.3 04/10/96 Akim Demaille
# le type d'un fichier ascii est directement trait par a2ps 4.6
# prise en compte des imprimantes recto-verso, affichage en `bg'
### 2.4 Akim Demaille
# exploitation des capacits d'a2ps 4.8
### programmes ncessaires:
# a2ps pssplit psnup psselect dvips xdvi ghostview file cut tee xargs
# basename rm mkdir (lpr et lprm) ou (lp et cancel) groff wc awk fgrep
# sed texi2dvi egrep strings
### Pour porter sur un autre site
# L'essentiel du travail devrait tre de mettre  jour le nom des
# imprimantes, et leur type: voir ci-dessous la clause case $lp


### Aide  l'utilisation d'imprimer
# $1: nom d'appel
usage()
{
echo "usage:
     $1 files/options ...

options:
    -1/-2/-4/-8/-9 print 1/[2]/4/8/9 pages per sheet
    -  print standard input
    -h this help message
    -help complete documentation of $1 is printed
    +n/-n number/[don't] the lines in text files
    -l/-p set default orientation to landscape/[portrait]
    +f/-f force/[don't] the printing of very big text files
    +d/-d print [double]/single sided when possible
    +k/-k [pretty print]/don't, source keywords
    +s/-s convert/[don't] special symbols
    -P(pages) to select the pages to print

files accepted:
    - PostScript, dvi, man, TeXinfo, text
    - ada, c, c++, caml, common-lisp, claire, eiffel, fortran, java, lace, 
      mailfolder, modula-3, o2c, pascal, perl, promela, python, sather,
      scheme, sdl, sh, tcl, tk, zsh
    - compressed files of the above types

examples, customization, _known_problems_ and even more:
    - \`man imprimer'

comments, suggestions and problems:
      mail to demaille@inf.enst.fr"
}

if [ $# = 0 ]; then
  usage `basename $0`
  exit 1
fi

#
#
# Set up the variables used
#
# Common variables
PATH=$PATH:/usr/local/bin:/usr/ucb:/usr/bin:/bin
export PATH

lp=`basename $0`
# Device dependent variables
# lptype: ascii/postscript for ascii/postscript printers, display for screen
# psmaxsize: a ps file bigger than this has to be split.

# first, resolve aliases
case $lp in
  lps|qms|calpup|calrdc|calmez1|calb205)lp=${lp}ps 		;;
  imprimer*|imp|lw)			lp=${PRINTER:=imprimer}	;;
  voir)					lp=show			;;
esac

# second, characteristics of devices
case $lp in
  # Display
  show|voir)
      lptype=display ; operation="Displaying" ;;

  # Ascii device
  janus|pan|clio)
      lptype=ascii ; operation="Printing" ;;	

  # Postscript but small memory
  lisa|pascale|bulle|iris|petula|aurore|penelope|iseut|tournesol|neva|pomme|cerise)
      lptype=postscript ; operation="Printing" ; psmaxsize=1000000;;

  # double sided capabilities
  sigfried)
      lptype=postscript ; operation="Printing" ;psmaxsize=3000000;
      lp_two_sided_opt="-odouble" ;;

  # Postscript but big memory
  dominique|arlette)
      lptype=postscript ; operation="Printing" ; psmaxsize=3000000;;

  # Postscript but put a header page (hence we cannot split)
  # For no limit, do not define psmaxsize
  lpsps|qmsps|calpupps|calrdcps|calmez1ps|calb205ps)
      lptype=postscript ; operation="Printing" ;;

  # Call default printer, supposed to be ps.
  imprimer)
      lptype=postscript ; operation="Printing" ; psmaxsize=3000000;;

  *)
      error "${lp} is an unknown device."
      exit 1
      ;;
esac

# Common variables
RM="/bin/rm -f"
TMPDIR=/tmp/${lp}.$$
OS=`uname -s``uname -r`
A2PS="a2ps -qm"
DVIPS="dvips -q -t a4 -i"

# Vars depending upon the OS
case ${OS} in
  SunOS4.*) FILE="/usr/bin/file -L"	;;
  SunOS5.*) FILE="/usr/ucb/file -L"	;;
  HP-UXA.*) FILE="/usr/bin/file"	;;
  *)        FILE="file"			;;
esac

# Vars depending upon the device type
case ${lptype} in
  display) # Set up the display environment
     LP="ghostview -quiet"
     SAVE_REQUESTS=""
     CANCEL_REQUESTS=""
     ;;
  *) # Set up the print environnemment
     REQUESTS=${TMPDIR}/requests-ids
     MULTI="psnup";
     # -q is for quiet mode, -i is to split in sections
     case ${OS} in
       SunOS4.*)
          [ $lp != imprimer ] && LP="lpr -h -P${lp}" || LP="lpr -h"
	  SAVE_REQUESTS="| tee -a ${REQUESTS}"
	  CANCEL_REQUESTS="lprm -P$lp -"
	  ;;
       SunOS5.*|HP-UXA.*)
          [ $lp != imprimer ] && LP="lp -c -d ${lp}" || LP="lp -c"
	  SAVE_REQUESTS="| tee -a ${REQUESTS}"
	  CANCEL_REQUESTS="cat ${REQUESTS} | awk '{print \$4}' | xargs cancel"
	  ;;
       *)
	  [ $lp != imprimer ] && LP="lpr -h -P${lp}" || LP="lpr -h"
	  SAVE_REQUESTS="| tee -a ${REQUESTS}"
	  CANCEL_REQUESTS="cat ${REQUESTS}"
	  ;;
     esac
     ;;
esac

error()
{
  echo "${lp}: $*" >&2
}

# If interrupted, remove the temporary files
trap clean_up 1 2 3 13 14 15

### trapped - Cancel prints and remove tmp files
clean_up()
{
  error "$operation interrupted."
  # If there are some requests ids, cancel them
  if [ "${REQUESTS}" -a -f "${REQUESTS}" ]; then
    error "Canceling as many prints as possible."
    eval ${CANCEL_REQUESTS} 2>/dev/null
  fi
  # Kill background proccesses used (coz' tmp files are removed)
  [ "$childProc" ] && kill -9 $childProc 2> /dev/null
  cd /	# To be sure that we were not inside $TMPDIR
  ${RM} -r $TMPDIR
  exit 1
}
#
### print_text - print an TEXT file, check if the source is too big.
# $1 = file to print
print_text() {
  echo "  Processing text data." >&2

  # If printing, verify if it is not too big (could be postscript ?)
  if [ $lptype != "display" ]; then
    size=`wc -c $1 | awk '{print $1}'`
    if [ "${size}" -gt "100000" ]; then
      if [ "${FORCE}" ]; then
	error "$1 (${size} characters) is printed as text file."
      else
	error "$1 is very big : ${size} characters."
	echo "If you want to print it, type \`${lp} +f ${filename}'." >&2
	return
      fi
    fi
  fi
  # print it
  pa_tmpfile=${TMPDIR}/${filename}.ps
  ${A2PS} $a2ps_opt $1 -o${pa_tmpfile}
  print_postscript ${pa_tmpfile} 1
  [ $lptype != display ] && ${RM} ${pa_tmpfile}
}

### print_troff - print a troff file
# $1: the troff file
print_troff() 
{
  echo "  Processing troff data." >&2
  pt_tmpfile=${TMPDIR}/${filename}.ps
  #  tbl $1 | troff -man | tscript > ${pt_tmpfile}
  groff -man $1 > ${pt_tmpfile}
  print_postscript ${pt_tmpfile} ${MINIPAGES} portrait
  [ $lptype != display ] &&  ${RM} ${pt_tmpfile}
}

### print_dvi - print a dvi file  
# $1: the dvi file, $2: orientation
print_dvi()
{
  echo "  Processing dvi data." >&2
  # If the device is a screen AND pstricks were not used,
  # then we use xdvi. Otherwise, let print_postscript deal
  # with it.
  if [ $lptype = display -a -z "`strings $1 | egrep '^header=pstricks'`" ]; then
    case $2 in
      portrait)  xdvi -paper a4  -hush $1 2>/dev/null & ;;
      landscape) xdvi -paper a4r -hush $1 2>/dev/null & ;;
    esac
    childProc="$! $childProc"
  else
    dvifile=`basename $1`
    header=${TMPDIR}/`basename $1`
    # The directory is set where the file is, to avoid problems with
    # the included files (figures etc.)
    dir=`pwd`
    cd `dirname $1`
    # select pages to print
    if [ $pages_ref ]; then
      pages_opt=-pp$pages_ref
      pages_ref=""
    fi
    # Depending of the orientation of the dvi file, command changes
    case $2 in
      portrait)
	case ${MINIPAGES} in
	  1|8|9) ${DVIPS} $pages_opt $DVIPAGES -f $dvifile -o $header.ps ;;
	  2|4) ${DVIPS} $pages_opt $DVIPAGES -h twoup -t landscape -f $dvifile -o $header.ps ;;
	esac
	;;
      landscape)
	case ${MINIPAGES} in
	  1|8|9) ${DVIPS} $pages_opt $DVIPAGES -t landscape -f $dvifile -o ${header}.ps ;;
	  2|4) ${DVIPS} $pages_opt $DVIPAGES -h twoup -f $dvifile -o ${header}.ps ;;
	esac
	;;
    esac
    # The postscript is split: print the pieces in the right order
    sections=`ls ${header}.[0-9][0-9][0-9]`
    for section in ${sections}
    do
      print_postscript ${section} ${POSTPAGES}
    done
    cd ${dir}
    ${RM} ${sections}
    pages_opt=""
  fi
}

### print_texinfo - print a texinfo file
# $1: the texinfo file $filepath: full path to the file
print_texinfo()
{
  echo  "  Processing texinfo data." >&2
  # goto to tmpdir, coz' texinfo produces the dvi where it is.
  dir_texinfo=`pwd`
  texi_dir=`dirname $filepath`
  # Moreover, since it may call some files around, cp them in $TMPDIR
  for i in `ls $texi_dir/*.texi $texi_dir/*.texinfo $texi_dir/texinfo.tex 2> /dev/null`
  do
    ln -s $i $TMPDIR/`basename $i` || cp -p $i $TMPDIR/`basename $i`
  done
  cd $TMPDIR
  tmp_texinfo=$TMPDIR/`echo $filename | sed -e 's/\.[^\.]*$//'`.dvi
  texi2dvi $filepath > $TMPDIR/texlog
  cd $dir_texinfo
  print_dvi $tmp_texinfo portrait
  [ $lptype != display ] && $RM $tmp_texinfo $TMPDIR/*.texi
}
#
###########################################################################
#                         Postscript routines
###########################################################################

### print_postscript - select pages and print
# $1: ps file, $2, #pages/sheet, $pages_ref: pages to
# print
print_postscript () {
  echo "  Processing postscript data." >&2
  case $lptype in
    display)
	ghostview -quiet -a4 $1  > /dev/null 2>/dev/null & 
	childProc="$! $childProc"
	;;
    *)
  	if [ $pages_ref ]; then
	  pp_tmpfile=${TMPDIR}/`basename $1`.select.ps
	    psselect -p$pages_ref $1 $pp_tmpfile 2> /dev/null;
	    format_postscript $pp_tmpfile $2
	    $RM $pp_tmpfile
	    pages_ref=""
	else
	  format_postscript $1 $2
	fi
	;;
  esac
}

### format_postscript - format pages and print
# $1: ps file, $2, #pages/sheet
format_postscript() {
  case $2 in
    1) send_postscript $1 ;;
    2|4|8|9)
       fp_tmpfile=${TMPDIR}/`basename $1`.ps
       ${MULTI} -$2 $1 > ${fp_tmpfile} 2> /dev/null;
       send_postscript ${fp_tmpfile}
       ${RM} ${fp_tmpfile}
       ;;
  esac
}

### send_postscript - send a postscript file to the printer, split if too big
# $1: file to send, ${filename}: original name
send_postscript()
{
  size=`wc -c $1 | awk '{print $1}'`
  # If the file is not larger than $psmaxsize chars, print it without split
  if [ -z "$psmaxsize" -o "$size" -lt "$psmaxsize" ]; then
    eval ${LP} $1 ${SAVE_REQUESTS}
  else
    echo "  Splitting postscript." >&2
    header=${TMPDIR}/${filename}
    pssplit -p ${header} $1 2>/dev/null
    if [ $? = 0 ]; then
      # Print the pages in the correct order
      files="`ls ${header}[0-9][0-9].ps`"
      files="${files} `ls ${header}[0-9][0-9][0-9].ps 2> /dev/null`"
      if [ "$DOUBLE" ]; then
	lp_opt=$lp_two_sided_opt;
      else
	lp_opt=""
      fi
      for page in ${files}
      do
	eval ${LP} $lp_opt ${page} ${SAVE_REQUESTS}
	${RM} ${page}
      done
    else
      error "$filename could not be split. Trying to print it anyway."
      eval ${LP} $lp_opt $1 ${SAVE_REQUESTS}
    fi
  fi
}

#
### get_type - get simple type
# $1: file to type, $file_type: text, postscript...
get_type()
{
  case `${FILE} $1 | awk '{print $2 $3}'` in
    *text|executable/bin/awk|executable/usr/local/bin/gawk|assemblerprogram|cprogram|executableshell|executablec-shell|shellcommands|c-shellcommands|c-shellscript|shellscript|executable*/jsh|executable*/bash|executable*/zsh|executable*/perl|executable*/tclsh|executable*/wish|executable*/pythonscript|mailfolder)
       case `basename $1` in
	 # Those that are not to print with a2ps
	 *.texi|*.texinfo)	file_type=texinfo	;;
	 # Those that a2ps should print
	 *)			file_type=text		;;
       esac
       ;;
    \[nt\]roff,tbl,)
	file_type=troff ;;
    PostScript*)
	file_type=postscript ;;
    gzipcompressed|compresseddata)
	file_type=compressed ;;
      *)
       case `basename $1` in
	 *.landscape.dvi|*.landscape.DVI|*-2up.dvi|*-2up.DVI)
			file_type=dvilandscape  ;;	   
	 *.dvi|*.DVI)	file_type=dvi  ;;
	 *.Z|*.gz) 	file_type=compressed ;;
	 *)
	    if [ "`${FILE} $1|fgrep PostScript`" ]; then
	      error "Strange branch is used."
	      file_type=postscript
	    else
	      file_type=unknown
	    fi
	    ;;
       esac
  esac
}
#
main()
{
  for file in `echo ${IMPRIMER:="-2 -n -p +k +d"} $*`
  do
    case ${file} in
      #####################
      # The various options
      -1) MINIPAGES=1; POSTPAGES=1 ; a2ps_opt="$a2ps_opt -1" ;
	  DVIPAGES="-S 32";					continue ;;
      -2) MINIPAGES=2; POSTPAGES=1 ; a2ps_opt="$a2ps_opt -2" ;
	  DVIPAGES="-S 32";					continue ;;
      -4) MINIPAGES=4; POSTPAGES=2 ; a2ps_opt="$a2ps_opt -4" ; 	
	  DVIPAGES="-S 32";					continue ;;
      -8) MINIPAGES=8; POSTPAGES=8 ; a2ps_opt="$a2ps_opt -8" ; 	
	  DVIPAGES="-S 32";					continue ;;
      -9) MINIPAGES=9; POSTPAGES=9 ; a2ps_opt="$a2ps_opt -9" ; 	
	  DVIPAGES="-S 27";					continue ;;
      -l) DEFAULT_ORIENTATION="landscape"; 			continue ;;
      -p) DEFAULT_ORIENTATION="portrait"; 			continue ;;
      +f) FORCE="true"; 					continue ;;
      -f) FORCE=""; 						continue ;;
      +d) DOUBLE="true"; 					continue ;;
      -d) DOUBLE=""; 						continue ;;
      +k) a2ps_opt="$a2ps_opt -K1";				continue ;;
      -k) a2ps_opt="$a2ps_opt -K0";				continue ;;
      +n) a2ps_opt="$a2ps_opt -C1" ; 				continue ;;
      -n) a2ps_opt="$a2ps_opt -C0" ; 				continue ;;
      +s) a2ps_opt="$a2ps_opt -g1" ;  				continue ;;
      -s) a2ps_opt="$a2ps_opt -g0" ; 				continue ;;
      -P*) pages_ref=`echo $file | cut -c3-`;  			continue ;;
      -D) LP="echo ${lp}:${LP}"; # Useful for debugging: does not print
	  SAVE_REQUESTS="";
	  RM="echo ${lp}:rm "; 	
	  continue ;;
      -X) set -x; continue ;;
      -h) usage ${lp}; exit 1 ;;
      -help) echo "Printing documentation on imprimer."
	     filename=imprimer.1
	     print_troff /usr/local/man/man1/imprimer.1
	     echo "The documentation is sent to the printer."
	     exit 1
	     ;;
      ######################      
      # And now really print
      *)
	 # Printing from stdin ?
	 if [ ${file} = "-" ]; then
	   file=${TMPDIR}/stdin
	   cat > ${file}
	 elif [ ! -f "${file}" ]; then
	   error "${file} not found"
	   continue
	 fi
	 get_type ${file}
	 # if compressed file, uncompress
	 if [ ${file_type} = "compressed" ]; then
	   cp ${file} ${TMPDIR}
	   file=`gunzip -v ${TMPDIR}/\`basename $file\` 2>&1 | awk '{print $6}'`
	   get_type ${file}
	 fi
	 if [ $file_type = unknown ]; then
	   error "type of $file is unknown. Could not be processed."
	   if [ $lptype != display ]; then
	     echo "  If you are sure this is text file, try:" >&2
	     echo "     a2ps $file -P${lp}" >&2
	   fi
	   continue
	 fi
	 # what is the absolute position of the file, and its name ?
	 case $file in
	   /*)	filepath=$file;;
	   *)	filepath=`pwd`/$file ;;
	 esac
	 filename=`basename ${file}`
	 if [ $file_type != text ]; then
	   echo "$operation ${filename} ($file_type file)." >&2
	 else
	   echo "$operation ${filename} (`a2ps --guess $file|sed 's/.*(//;s/).*//'` text file)." >&2
	 fi
	 # Printing is done according the architecture
	 case ${lptype} in
	   ascii)
	     case ${file_type} in
	       text)
		  error "executing ${LP} ${file}"
		  eval ${LP} ${file} ${SAVE_REQUESTS}
		  ;;
	       *)
		  error "WARNING ${file} is not an ascii document"
		  eval ${LP} ${file} ${SAVE_REQUESTS}
		  ;;
	     esac
	     ;;
	   postscript)
	      case ${file_type} in
		text)		print_text ${file}			;;
		postscript)	print_postscript ${file} ${MINIPAGES}	;;
		dvi)		print_dvi ${file} $DEFAULT_ORIENTATION	;;
		dvilandscape)	print_dvi $file landscape 		;;
		texinfo)	print_texinfo $file			;;
		troff)		print_troff ${file}			;;
	      esac
	      ;;
	   display)
	      MINIPAGES=1
	      DVIPAGES=""
	      a2ps_opt="$a2ps_opt -1R"
	      case ${file_type} in
		text)       	print_text ${file}  			;;
		troff)      	print_troff ${file}  			;;
		postscript) 	print_postscript ${file}		;;
		dvi)		print_dvi ${file} $DEFAULT_ORIENTATION	;;
		dvilandscape)	print_dvi $file landscape 		;;
		texinfo)	print_texinfo $file			;;
	      esac
	      ;;
	 esac # case ${lptype}
    esac # case ${file}
  done
}

# Create the temporary directory
mkdir ${TMPDIR}

# Print the files
main $*

# Wait for the background processes sent (ghostview ...)
wait

# Remove the temporary files
${RM} -r ${TMPDIR}

# Untrap the signals
trap 1 2 3 13 14 15
