#!/bin/bash
# set -o nounset
 Version=3.07
  Myname="${0##*/}"

<<'DOC'
= mk - a TeX and LaTeX maker

= Synopsis
mk [options] [file]	

== General options:
-h,--help		print this help and exit
-H,--Help		print full documentation via less and exit
-V,--version		print version and exit
-v,--verbose		be verbose
   --noverbose		be quiet (this is the default)
-r,--rc			use STRING as an rc file, instead of |~/.mkrc|.
			See the section /RC file and customization/ for more information.
   --norc		don´t read the |~/.mkrc| file

== mk-specific options:
-f,--formatter=STRING	Use STRING as the formatter: tex, latex, pdflatex, et cetera
-e,--edit=STRING	use STRING as the file to be edited; by default, the file containing the
			compilation error is presented for editing, or, if there are no errors,
			the main source file.
-C,--Clean		Remove all files generated by the compilation
-c,--clean		Same, except for the pdf or postscript files

== vpp-related options:
-b,--batch=STRING	run in batch using STRING as a printing command for vpp. See the section
			/Running in batch mode/ for more information.
-p,--printer=STRING	print to printer named STRING
    --view		view the document after compilation
    --noview		do not view
    --print		offer printing interaction after viewing
    --noprint		do not offer printing interaction

texlog_extract related options:
-w,--web		web colouring instead of ANSI
-n,--nocolor		no colouring instead of ANSI

== Defaults:
    --print --view --noverbose main

Arguments for short options are given without a separator, so you can write
either |--rc=myrc| or |-rmyrc|.

= Description
mk is a Bash script that, in close collaboration with vpp (short for
View and Print PDF/PostScript), is helpful in the cyclic process of
editing, compiling, viewing, and printing a tex document. Essentially,
mk uses texi2dvi for compilation, vpp for viewing and printing. Any
TeX formatter can be handled, with the exception of those starting
with |ht| (such as httex and htlatex) and context (handle those with
texexec).

Having an existing TeX document, say |main.tex| (see the section
/Locating the source/ for the creation of new documents and for other
extensions than |.tex|), you run mk by typing:

    $ mk main

or, since /main/ happens to be mk´s default filename:

    $ mk

Now, if |main.tex| is a valid TeX source, mk compiles it, including
any table of contents, indices, bibliography references, included
files, and so on, and /vpp/ takes over and displays the resulting
/PDF/ or /PostScript/ output. When you leave the viewer you will see a
prompt:

    vpp command (h for help):

If you are satisfied with the displayed output, you can now decide to
print all or part of your document (see the section /Page selection/),
or you can simply quit by typing 'q'. On the other hand, if you decide
that you want to change the source and have another try, you can edit
the source by typing 'e' to get back to mk and (re)edit your source.
After saving your work and leaving your editor, another compilation
and display cycle will be performed, based on the new source.

Essentially, mk uses /texi2dvi/ for compilation. /texi2dvi/ always
runs TeX at least once, even though this may be unnecessary.
Therefore, TeX will be run with the |--recorder| option, which reports
all the target´s dependencies in a |.fls| file. In every cycle, mk
analyzes the |.fls| and the tex and bibtex |.log| files to see if a
compilation is needed. When errors have occurred, mk uses the log
files to find out which file has to be edited, and at which line. This
can also be a file read with |\input|, a style file, or any other file on
which the target depends. However, files in the TEXMFMAIN tree are
excluded.

= Editor

vpp uses the contents of environment variable EDITOR to find your
editor. If that variable is empty, /vim/ is used. Note that your
editor should not fork off your shell, so if you specify /gvim/, for
example, specify it with the option |--nofork|.

= Page selection

As said in the introduction, after a successful compilation and
display of the resulting /PDF/ or /PostScript/ output, the user is
prompted with:

    vpp command (h for help):

on typing 'h' /vpp/ displays examples of possible commands:

   Examples of print commands:
       5       to print page 5
       5-      to print pages 5 through the end
       5-7     to print pages 5, 6 and 7
       -7      to print the first 7 pages
       5-7,19- to print pages 5, 6, 7 and 19 through the end
       a       to print the whole document
       -       to print the whole document
       a x3    to print 3 copies of the document
       x3      the same
       5 x3    to print 3 copies of page 5
       t       print the whole document two sided
       t 2-    print two sided starting at page 2
       b       to print the whole document as an a5 size booklet
       b -12   to print the first 12 pages as an a5 size booklet
   Other commands:
       e       (if called by mk) edit the tex source and rerun mk
       c       (if called by mk) rerun mk
       v       (re)view the ps/pdf file
       oxyz    send pdf output to file xyz.pdf instead of printer
       pxyz    print to printer xyz
       h       display this help
       ?       display this help
       q       quit

With these examples, no further explanation should be necessary, except that,
when two sided (|t|) or booklet (|b|) printing is selected for a single-sided
printer, printing will be performed in two shifts, one for the front side and
one for the backside. Between the shifts, another prompt appears:

    printer ready? then turn stack and type return

You will have to arrange your printer such that, with the printed sides up, the
first page printed will be at the bottom of the stack, and the last page
printed will be on top. Normally you will then have your output come out the
back of your printer. 'Turn the stack' then means: rotate it over the long side
of the paper and feed it back into the printer for the other side to be
printed.

For further information on /vpp/, look in its manpage by typing

    $ vpp --help

or read the /vpp/ documentation.

= Locating the source

mk locates the LaTeX source in several steps: (here the source
extension |.tex| is supposed, but |.ltx|, |.drv| and |.dtx| will also
be tried)

- If you supply no arguments, the file |main.tex| in the current directory
  is assumed.
- If you supply an argument (say /myfile/), mk adds a |.tex| extension if
  it isn´t there and looks for |myfile.tex| in the current directory.
- If |myfile.tex| is not found in the current directory, mk looks in the
  'alternate directory' (say |/Docs|) if you have defined one (see the
  section 'RC files').
- If the source was not found in |/Docs|, mk thinks that you may have a
  subdirectory /myfile/ in |/Docs| where the source may live under the name
  |main.tex|
- If that file is not there, mk now concludes that the source does not
  yet exist and reports this, telling at the same time which files have
  been tried.
- Finally, if all the above did not lead to a source file, mk dies.

= The TeX formatter to be used

mk determines the TeX formatter needed to compile the source as follows:
- if the |--formatter| option was used, its argument will be used;
- if the first line of the source starts with |%!|, the rest of that line
  specifies the formatter; for example:

    %!xelatex

- if the |--formatter| option was not used and the first line does not
  start with |%!|, mk looks for a |\usepackage{fontspec}| or
  |\RequirePackage{fontspec}| and, if found, uses /lualatex/. Those calls
  may have options, but they must be on the same line.  
- if still no decision could be made, mk looks for |\documentclass| and
  chooses /pdflatex/; 
- finally, if no match was found, /pdftex/ is assumed.  
= Running in batch mode

If you don´t need to edit the source and to view the output, but just want
to compile and maybe print the result, the |--batch| option is useful.

The |--batch=command| option prevents mk display the output and to
interrogate the user about pages to be printed. Instead the document is
printed according to the mandatory /command/, which mus be quoted if it
contains spaces. Thus the command

  mk --batch='2-3 x3' test

prints 3 copies of pages 2 and 3 of |test.tex|, without viewing. If you
just want to compile without printing anything, use the |q| command:

  mk --batch=q test

or

  mk -bq test

= RC file and customization

Unless the option |--norc| has been used, the file |~/.mkrc| will be
sourced, if it exists, before reading the command line options.

You can use this rc file to set the default values for the options, by
setting the global shell variable named after the long version of the
options. For example:

    verbose=true # run in verbose mode

So if you usually like mk to work in verbose mode, you can indicate so in
your rc file and change your mind in some cases by using the |--noverbose|
option.

Other variables, not having a corresponding command line option, that can
be set in the rc files, and their default values, are:

EDITOR=

	sets your editor; You can also set your EDITOR in your system startup
	files. Note that your editor should not fork; so if you set it to gvim, do:

	  EDITOR='gvim --nofork'

extraoptions=	
	
	adds one or more extra options to the /tex/ (/latex, xelatex/ et
	cetera) command. Example: |extraoptions='-shell-escape -quiet'|
	
othercleans=	
	
	can be set to a file regular expression; in the cleaning operation,
	caused by the |--clean| option, this variable will be eval'ed, and
	the resulting files will be removed. This is useful, for example,
	when the |gnuplottex| package is used; this package generates
	intermediate files named |$base-gnuplottex-fig*|, where the
	variable |$base| contains the basename (without extension) of your
	tex source file. So after adding:
	
	   othercleans='$base-gnuplottex-fig*'
	
	to your |./mkrc| file, the cleaning operation will get rid of these
	files, too.
	
texi2dviquiet=false	
	
	Normally, in verbose mode, you also see the complete tex log
	output, because texi2dvi will be verbose, too. This obscures most
	other output. You can keep texi2dvi quiet in verbose mode by
	setting this variable to true:
	
	   texi2dviquiet=true
	
skip_pattern=	
	
	can be set to a file wild card pattern. Files matching this pattern
	on which the /(la)tex/ source file may depend will not be checked
	for changes. For example, if you use a write-protected TeX-tree in
	the directory mytextree it makes sense to set
	|skip_pattern=mytextree| unless you set |skip_pattern| explicitly,
	it will be set to match the TEXMFMAIN tree.
	
altdir=	
	
	If |altdir| is non-empty and a file to be compiled does not exist
	in the current directory, it will be given another try after
	prefixing it with the contents of |altdir|. So if you like to have
	your LaTeX file in
	|/Docs/myfile.tex| you can set |altdir| to |/Docs| and run
	mk from any directory with:
	
	   $ mk myfile
	
	However, a directory like |/Docs| does not make much sense if many
	of your LaTeX documents do not consist of a single file, but are
	constituted of an ensemble of a main LaTeX source and one or more
	files read with |\include| or with |\input| such as graphics.
        You will then probably prefer to have a subdirectory in |/Docs| for
        every LaTeX document. Therefore, if mk does not find |myfile.tex| in the
        alternate directory, it will assume that /myfile/ is a subdirectory with
        a main LaTeX source in it, called |main.tex|.
	
default=main	
	
	This is the default for the base name of your LaTeX document.
	
warnings_to_skip=()	
	
	Warnings appearing in the log file will be reported after a
	successful run. Warnings matching any of the regular expressions
	in this array will be skipped, however. For example, one could
	enter here:
	
	    warnings_to_skip=(
	      'Package hyperref Warning: Token not allowed in a PDFDocEncoded string'
	      'Package array Warning: Column [XY] is already defined on '
	    )
	
	The first message appears when the /hyperref/ package is used and
	section titles contain LaTeX-commands, the second message appears
	when the /ctable/ package is used, because it intentionally changes
	the X and Y column specifiers.

= TeXWorks and mk

mk can be used for one-click typesetting:

- edit -> preferences -> Typesetting
- add a new tool |mk| and give it three parameters:
    --noview
    --noprint
    $basename
- Deselect "Auto-hide output panel unless errors occur"
- If you need an other formatter than the default, pdflatex, use the 
  |%!| line in your source, or define separate tools in TeXWorks with 
  an extra |--formatter=...| parameter. 

mk runs pdflatex with the |--synctex=1| option, so you will be
able to jump between source and pdf-ouput.

= Bugs
If, during the compile-edit-cycle, the |%!| line is changed, mk should
respond to it by changing the formatter.

Currently, mk is only available for Linux. It depends on /texi2dvi/. Spaces
in the basename of TeX sources are not allowed (neither does the texi2dvi
script on which mk is based.)

Required other programs:
vpp >= 3.07		[CTAN](ctan.org/tex-archive/support/view_print_ps_pdf/vpp)
texi2dvi >= 6290	[Texinfo](ftp.gnu.org/gnu/texinfo/texi2dvi)
tex and friends		CTAN or /texlive/ package
kpsewhich		from texlive
texi2dvi		version 1.152 or greater, from texinfo
readlink		from coreutils
getopt			from util-linux

= Changes
Changes with respect to version 3.06:
- Option --doublesided removed; vpp does not need them anymore


= Author
[Wybo Dekker](wybo@dekkerdocumenten.nl)

= Copyright
Released under the [GNU General Public License](www.gnu.org/copyleft/gpl.html)
DOC

# globals:

# env vars: EDITOR HOME PWD IFS

# possibly set in rc file, but never here - prevent shellcheck 
# issuing warning:
extraoptions='' othercleans=''
export extraoptions othercleans

             Err='\e[31;1m' # ] light red
             Fil='\e[33m'   # ] brown            
             Com='\e[34;1m' # ] light blue       
             Lin='\e[32;1m' # ] light green      
             War='\e[35;1m' # ] light magenta    
             Nor='\e[0m'    # ] reset color      

          altdir=.
            base=''
           batch=''
         bibdeps=
           Clean=false
           clean=false
        cleanext=( 4ct 4dx 4ix 4tc aux bbl blg chk dvi dvi
                   ent fls glg glo gls hd idv idx ilg ind ist lg
                   lof log lot out soc synctex.gz tmp toc tui tuo xdv xdy xref)
     compileexit=98
         default=main
             dir=''
           dvips=dvips
            edit=''
        editexit=99
             ext=''
       formatter=''
           print=true
         printer=''
             pwd=$(pwd)
              rc=''
    skip_pattern=^$(kpsewhich --expand-var \$TEXMFMAIN)
          target=''
       targetext=pdf
      texcommand=''
         texdeps=()
   texi2dviquiet=false
         verbose=false
            view=true
    vpptargetext=pdf
warnings_to_skip=()

    die() { echo -e "$Myname: $Err${*}$Nor"; exit 1; } 1>&2
   Warn() { echo -e "$Myname: $War${*}$Nor"; } 1>&2
   warn() { $verbose && Warn "$@"; }
helpsrt() { sed -n '/^= Synopsis/,/^= /p' "$0"|sed '1s/.*/Usage:/;/^= /d'; exit; }
helpall() { sed -n '/^<<.DOC.$/,/^DOC$/p' "$0"|sed -n '1d;$d;p'|less; exit; }
install() { which instscript>&/dev/null && instscript --zip --pdf --markdown "$Myname"; exit; }

<<'DOC' #------ function findsource --------------------------------------------
= findsource
parameters	the script´s first and only argument, maybe nil
description	find the file to be compiled. If the argument has no extension,
		look for tex, ltx, drv, dtx, in that order. 
		If the argument is nil, look for main
		If the argument is xxx, look for xxx or xxx/main, in that order, 
		in the current or the alternate directory.
		If the argument is xxx.ext, look for xxx.ext in the current or
		in the alternate directory.
globals set:	   base dir ext 
globals used:	   IFS PWD altdir Nor
returns:	0 on succes, 1 otherwise
DOC
#-------------------------------------------------------------------------------
findsource() {
   local tries fullpath arg
   arg="${1%.}" # remove final . (may be there by auto completion)
   [[ $arg =~ \  ]] &&
      die "mk cannot (yet) handle filenames with spaces, because texi2dvi can´t"
   if [[ -z $arg ]]; then 			 # nil argument
      tries=(./"$default".{tex,ltx,drv,dtx})
   elif [[ ! $arg =~ [/.] ]]; then 		 # argument has no path and no extension
      tries=({.,"$altdir"}/{"$arg","$arg"/"$default"}.{tex,ltx,drv,dtx})
   elif [[ $arg =~ \.(tex|ltx|drv|dtx)$ ]]; then # argument has valid extension
      tries=("$arg")
   else						 # try valid extensions
      tries=("$arg".{tex,ltx,drv,dtx})
   fi
   for i in "${tries[@]}"; do
      [[ -s "$i" ]] && {
         fullpath=$(readlink -f "$i")
         warn "source: $fullpath"
         dir="${fullpath%/*}"
         ext="${fullpath##*.}"
         base="${fullpath:${#dir}+1:${#fullpath}-${#ext}-${#dir}-2}"
         return
      }
   done
   die "not found after trying:$Nor\n$(printf "%s\n" "${tries[*]}")"
}

<<'DOC' #------ function run ---------------------------------------------------
= run
parameters:	command to be run, with its parameters
description:	Run a command; show what´s run if |$verbose|.
		If the command exits with 1, that´s considered an error,
		other values have a special meaning and are supposed to be a
		success
globals  set:	    
globals used:	   Com Err Lin Nor
returns:	the exit value of the command
DOC
#-------------------------------------------------------------------------------
run() {
   eval "$@"
   local ev=$?
   if [ $ev -eq 0 ]; then
      warn "$Nor${Fil}sys call: $Com$1$Lin - succeeded$Nor, exit status=$Err$ev"
   else
      warn "$Nor${Fil}sys call: $Com$1$Err - failed"
   fi
   return $ev
}

<<'DOC' #------ function settexdeps --------------------------------------------
= settexdeps
parameters:	-
description:	Scans |$base.fls| for tex dependencies and places those in the
		array texdeps. Any dependencies with future timestamps are
		touched in order to prevent mk from looping.
globals  set:	   texdeps
globals used:	   base PWD skip_pattern
returns:	0
DOC
#-------------------------------------------------------------------------------
settexdeps() {
   texdeps=()
   local fls="$base.fls" n=0 c f tmp
   test -e "$fls" || {
      warn "no file $fls in $PWD"
      return
   }
   while IFS=' ' read -r c f; do
      let n++
      [[ $n == 1 && $c != PWD ]] && die "$fls is not a TeX fls file"
      [[ $c == INPUT && ! $f =~ $skip_pattern ]] && texdeps+=($f)
   done <"$fls"
   texdeps=($(echo "${texdeps[@]}"|xargs -n1|sed 's/^\.\///'|sort -u))

   tmp=$(mktemp -t mk.XXXXXXXXXX)
   warn "tex dependencies:"
   for i in "${texdeps[@]}"; do
      warn "	$i"
      if [[ $i -nt $tmp ]]; then
         Warn "Touching file with future filestamp: $i"
         touch "$i"
      fi 
   done
   rm "$tmp"
}

<<'DOC' #------ function setbibdeps --------------------------------------------
= setbibdeps
parameters:	-
description:	Scan aux file (|$base.aux|) for bib-files needed and places those
		in the array bibdeps. Any dependencies with future timestamps are
		touched in order to prevent mk from looping.
globals  set:	   bibdeps
globals used:	   base
returns:	-
DOC
#-------------------------------------------------------------------------------
setbibdeps() {
   local i tmp
   test -e "$base.aux" || return
   bibdeps=$(sed -n '
        /\\bibdata{.*}/{
          s/.*\\bibdata{\(.*\)}.*/\1/   # a.bib,b,c.bib
          s/\.bib//g                    # a,b,c
          s/,/ /g                       # a b c
          p
        }' "$base.aux")
   bibdeps=($bibdeps)
   [[ ${#bibdeps[@]} -gt 0 ]] || return

   for i in "${!bibdeps[@]}"; do
      local b=${bibdeps[i]}
      # replace each bib with its full path found by kpsewhich
      bibdeps[i]=$(kpsewhich "$b.bib") || die "bib file $b.bib not found"
   done
   bibdeps=($(echo "${bibdeps[@]}"|xargs -n1|sed 's/^\.\///'|sort -u))

   tmp=$(mktemp -t mk.XXXXXXXXXXe)
   warn "bib dependencies:"
   for i in "${bibdeps[@]}"; do
      warn "	$i"
      if [[ $i -nt $tmp ]]; then
         Warn "Touching file with future filestamp: $i"
         touch "$i"
      fi 
   done
   rm "$tmp"
}

<<'DOC' #------ function compile --------------------------------------------
= compile
parameters:	-
description:	runs the command in |texcommand|
globals  set:	-
globals used:	   base bibdeps target texcommand texdeps 
returns:	0 on success, else 1
DOC
#-------------------------------------------------------------------------------
compile() {
   local i go
   # If there is an .fls file, and the target is there, then compile only if
   # there are texdeps or bibdeps that are newer than target.
   # If there are bibdeps newer than target, remove .bbl, forcing texi2dvi to run bibtex
   # Returns true if a compilation was actually performed.
   go=true
   if [[ -e $base.fls && -e $target ]]; then
      go=false
      setbibdeps
      settexdeps
      for i in "${bibdeps[@]}"; do
         if [ "$i" -nt "$target" ]; then
            rm -f "$base.bbl"
            warn "newer than target: $i"
            go=true
            break
         fi
      done
      if ! $go; then
         for i in "${texdeps[@]}"; do
            if [ "$i" -nt "$target" ]; then
               warn "newer than target: $i"
               go=true
               break
            fi
         done
      fi
   fi
   if $go; then
      rm -f "$target" # if compilation generates no output, we want to be able to detect that
      run "$texcommand" || return 1
      # texi2dvi claims to be successful if it produces no output:
      if [ -e "$target" ]; then
         test "$targetext" = dvi && $dvips "$target"
         return 0
      else
         Warn "After compilation, $target was not found;"
         Warn "were there any output generating statements in your source?"
         return 1
      fi
   else
      warn "No compilation needed"
      return 0
   fi
}

<<'DOC' #------ function show_error_and_edit -----------------------------------
= show_error_and_edit
parameters:	-
description:	Show compilation errors via texlog_extract and (unless edit is empty)
		edit the source file where the error is in, opening the editor at the line
		where the error is..
globals  set:	   IFS
globals used:	   Lin base bibdeps edit target warnings_to_skip 
returns:	0
DOC
#-------------------------------------------------------------------------------
show_error_and_edit() {
   local e m n i errorfile linenum ifsold=$IFS
   test -e "$target" -a -n "$edit" && rm -f "$target"

   warn "${Lin}warnings_to_skip:"
   for i in "${warnings_to_skip[@]}"; do warn "\t$i"; done
   i="texlog_extract $tleoptions '$base'"
   IFS=$'\n' m=($(eval "$i"))
   n=true
   for i in "${m[@]}"; do
      if $n; then
         IFS=$' ' read -r linenum errorfile <<<"$i"
         n=false
      else
          echo "$i"
      fi
   done
   IFS=$ifsold

   test -z "$edit" && exit

   : "${errorfile:="$edit"}"
   errorfile="${errorfile%%[\{ ]*}" # remove {...} and spaces at the end
   if [[ $errorfile =~ $base.bbl ]]; then
      setbibdeps
      if [[ ${#bibdeps[@]} -gt 1 ]]; then
         # isolate the fragment containing the error. 
         # m[-2] contains something like: line nnn: error fragment
         e=$(cut -d' ' -f 3- <<<"${m[-2]}")
         # find the bib file containing the error fragment
         for i in "${bibdeps[@]}"; do
            if grep -F "$e" "$i" >/dev/null; then
               edit "$i" 1 true && break
            fi
         done
      else
         edit "${bibdeps[0]}" 1 true && exit
      fi
   else
      edit "$errorfile" "$linenum" true && exit
   fi
}

<<'DOC' #------ function edit --------------------------------------------------
= edit
parameters:	1 file to be edited; if empty: contents of edit variable is used
		2 line number where edit should start; if empty, use 1
		3 true if an error was detected in the file and user must decide 
		if file shall be edited 
description:	Start the user´s editor to edit the file in argument 1; if the 
		call was induced by the detection of an error in that file, the
		user will be asked if he want to edit the file, or to quit.
globals  set:	-
globals used:	   edit
returns:	1 if the file was edited, else 0
DOC
#-------------------------------------------------------------------------------
edit() { # edfile,linenum,error
   local edfile="$1" linenum=$2 error=$3
   if $error; then
      Warn "error in $edfile"
      test -t 1 || return 0 # return if stdout is not to a terminal
      while true; do
         echo -n "=====> e(dit) q(uit) "
         read -r x
         case "$x" in
         (q) return;;
         (e) break;;
         (*) echo you must type e or q
         esac
      done
   fi
   : "${linenum:=1}"
   : "${edfile:=$edit}"
   test -e "$edfile" || edfile="$edit" # this happens when tex finds unexpected EOF
   eval "$EDITOR +$linenum \"$edfile\""
   # user may have edited a bibfile:
   setbibdeps
   for i in "${bibdeps[@]}"; do
      if [[ $i -nt $base.aux ]]; then
         rm -f "$base".{bbl,aux} # force texi2dvi to rerun bibtex
         break
      fi
   done
   return 1
}

<<'DOC' #------ function handle_options ----------------------------------------
= handle_options
parameters:	uses script´s arguments
description:	Handles the options
globals  set:	  Clean batch clean dvips edit input norc print
		   printer rc tleoptions verbose view
globals used:	   HOME verbose
returns:	0
DOC
#-------------------------------------------------------------------------------
handle_options() {
   local options
   if ! options=$(getopt \
      -n "$Myname" \
      -o Ccf:e:b::p:vr:nwhHVI \
      -l Clean,clean,formatter:,ps,edit:,batch::,printer:,view,noview,\
print,noprint,verbose,noverbose,rc:,norc,nocolor,web,help,Help,version -- "$@"
   ); then exit 1; fi
   eval set -- "$options"
   while [ $# -gt 0 ]; do
      case $1 in
      # General options:
      (-h|--help)    	 # print this help and exit
			 helpsrt
                         ;;
      (-H|--Help)    	 # print full documentation via less and exit
                         helpall
                         ;;
      (-V|--version) 	 # print version and exit
			 echo $Version
			 exit
                         ;;
      (-v|--verbose)	 # be verbose
                         verbose=true
                         shift
                         ;;
      (   --noverbose)	 # be quiet (this is the default)
                         verbose=false
                         shift
                         ;;
      (-r|--rc)		 # use STRING as an rc file, instead of |~/.mkrc|.
                         # See the section /RC file and customization/ for more information.
                         rc="$2"
                         shift 2
                         test -e "$rc" ||
   			    die "RC-file $rc, given with the --rc option, does not exist"
   			 ;;
      (   --norc)	 # don´t read the |~/.mkrc| file
                         shift
                         # --norc is detected before option handling, so it can be skipped here
                         ;;
      # mk specific options:
      (-f|--formatter)	 # Use STRING as the formatter: tex, latex, pdflatex, et cetera
                         formatter="$2"
                         shift 2
                         ;;
      (-e|--edit)	 # use STRING as the file to be edited; by default, the file containing the
                         # compilation error is presented for editing, or, if there are no errors,
                         # the main source file.
                         edit="${2/\~/$HOME}"
                         shift 2
                         ;;
      (-C|--Clean)	 # Remove all files generated by the compilation
                         Clean=true
                         shift
                         ;;
      (-c|--clean)	 # Same, except for the pdf or postscript files
                         clean=true
                         shift
                         ;;
      (   --ps)		 die "The --ps option has been removed; use --formatter";;
      # vpp-related options:
      (-b|--batch)	 # run in batch using STRING as a printing command for vpp.
                         # See the section /Running in batch mode/ for more information.
                         batch="$2"
                         shift 2
                         ;;
      (-p|--printer)	 # print to printer named STRING
                         printer="$2"
                         shift 2
                         ;;
      (   --view)	 # view the document after compilation
                         view=true
                         shift
                         ;;
      (   --noview)	 # do not view
                         view=false
                         shift
                         ;;
      (   --print)	 # offer printing interaction after viewing
                         print=true
                         shift
                         ;;
      (   --noprint)	 # do not offer printing interaction
                         print=false
                         shift
                         ;;
      # texlog_extract-related options:
      (-w|--web)	 # web colouring instead of ANSI
                         tleoptions+=' --web'
                         shift
                         ;;
      (-n|--nocolor)	 # no colouring instead of ANSI
                         tleoptions+=' --nocolor'
                         shift
                         ;;
      (-I)           	 install;;
      (--)		 shift;	break;;
      (*)		 break;;
      esac
   done
   # verbose is now set:
   $verbose || dvips='dvips -q'
   # remaining argument, if any, is the input file:
   input="$1"
}

# if --rc was used, source its argument
# if not, execute ~/.${Myname}rc if it exists

<<'DOC' #------ function read_rc -----------------------------------------------
= read_rc
parameters:	-
description:	If the |--norc| option was used, does nothing. Otherwise, sources
		the file in variable |rc|; if that is empty, use the |~/.mkrc| if
		it exists.
globals  set:	-
globals used:	   HOME Myname rc
returns:	0
DOC
#-------------------------------------------------------------------------------
read_rc() {
   : "${rc:=$HOME/.${Myname}rc}"
   # shellcheck disable=SC1090
   [[ -n $rc && -s $rc ]] && { warn "Sourcing $rc"; source "$rc"; }
}

# do we have all executables needed?

<<'DOC' #------ function check_needs -------------------------------------------
= check_needs
parameters:	-
description:	Checks if all executables needed are available.
globals  set:	   EDITOR
globals used:	   EDITOR print view
returns:	1 if there are missing executables, else 0
DOC
#-------------------------------------------------------------------------------
check_needs() {
   local j i needed=(kpsewhich texi2dvi $formatter)
   [[ $targetext = dvi ]] && needed+=(dvips)
   $print || $view && needed+=(vpp)
   for i in "${!needed[@]}"; do
      which "${needed[$i]}" >/dev/null && unset needed["$i"]
   done
   [[ ${#needed[@]} -gt 0 ]] && die "missing executables: ${needed[*]}"
   # test texi2dvi´s version:
   i=$(texi2dvi --version|head -1|sed -e "s/.* //")
   test "$i" -ge 6290 ||
      die "Your texi2dvi is too old ($i);$Nor Get a new version with:\n$Com"\
          "   wget ftp.gnu.org/gnu/texinfo/texi2dvi && chmod 755 texi2dvi$Nor\n"\
          "   and move texi2dvi to a directory in your PATH."
   # make sure we have an editor
   : "${EDITOR:=vim}"
}


<<'DOC' #------ function setformatter --------------------------------------------
= setformatter
parameters:	-
description:	set |$formatter| to the tex formatter to be used
globals  set:	   formatter targetext vpptargetext
globals used:	   base ext
returns:	
DOC
#-------------------------------------------------------------------------------
setformatter () {
   local preamble
   if [[ -n $formatter ]]; then
      # nothing to do if the |--formatter| option was used
      warn "formatter ($formatter) was set by the --formatter option"
   elif [[ $(head -1 "$base.$ext") =~ ^%!([[:alnum:]]+) ]]; then
      # found formatter in first line of source:
      formatter=${BASH_REMATCH[1]}
      warn "formatter found in the first line of the source"
   else
      # still no formatter? Try the preamble
      preamble=$(sed -n -e '
         /%.*/d
         /\s*\\usepackage.*fontspec/p
         /^\s*\\begin{document}/q
         /^\s*\\documentclass/p
      ' "$base.$ext")
      
      if [[ $preamble =~ fontspec ]]; then
         formatter=lualatex
      elif [[ $preamble =~ documentclass ]]; then
         formatter=pdflatex
      else
         formatter=pdftex
      fi
      warn "I guessed from the preamble that $formatter should be used to format $base.$ext\n"\
           "   If this is incorrect, then use the --formatter option\n"\
           "   or specify the formatter in line 1 of your source"
   fi
   if [[ $formatter == tex || $formatter == latex ]]; then 
      targetext=dvi vpptargetext=ps
   elif [[ $formatter =~ ^ht ]]; then
      targetext=html vpptargetext='' 
   fi
}     

<<'DOC' #------ function set_vpp_options --------------------------------------------
= set_vpp_options
parameters:	-
description:	set options for vpp call
globals  set:	 vppoptions
globals used:	 pwd verbose printer 
returns:	-
DOC
#-------------------------------------------------------------------------------
set_vpp_options () {
   vppoptions="--mk='$pwd'" # tell vpp it´s called from mk and must write to $pwd
       $verbose && vppoptions+=" --verbose"
   test -n "$printer" && vppoptions+=" --printer=$printer"
   
   # validate --batch option:
   if [[ -n $batch ]]; then
      view=false # --batch implies --noview
      j=($batch)
      for i in "${j[@]}"; do
         [[ $i =~ ^(q|b|t|a|x[0-9]+|([0-9]+-?[0-9]*|[0-9]*-?[0-9]+),?)+$|^o[a-zA-Z0-9_-]+$ ]] ||
            die "Illegal argument ($i) in --batch argument ($batch)\n"\
                "use q if you want no printout"
      done
      vppoptions+=" --batch='$batch'"
      [[ $batch == *q ]] && print=false
   fi
   
   $print && vppoptions+=" --print" || vppoptions+=" --noprint"
    $view && vppoptions+=" --view"  || vppoptions+=" --noview"
}

[[ "$*" =~ --norc ]] || read_rc
handle_options "$@"
check_needs
findsource "$input"
set_vpp_options

cd "$dir" || die "Could not cd to $dir"

: "${edit:=$base.$ext}"
if [ -n "$edit" ]; then # if empty, then no editing
   test -e "$edit" || die "can´t find source file ($edit)"
   test -r "$edit" || die "can´t read source file ($edit)"
   test -w "$edit" || die "can´t write source file ($edit)"
fi

setformatter

target="$base.$targetext"
vpptarget="$base.$vpptargetext"
if [[ -e $target && ! -w $target ]]; then
   die "Target $target is write-protected, so I cannot re-compile it!"
fi

if $clean || $Clean; then
   for i in "${cleanext[@]}"; do
      rm -f "$base.$i"
   done
   eval "othercleans=$othercleans" # othercleans may have been defined in rc file
   for i in $othercleans; do rm -f "$i"; done
   if $Clean; then
      # It may be useful to protect target, in order to prevent that they become
      # recompiled with changed style files:
      for i in $(echo "$target $vpptarget" | xargs -n1 |sort -u); do
         if [[ -e $i && ! -w $i ]]; then
            echo "$i is write-protected, so I'll keep it!" 1>&2
         else
            rm -f "$i"
         fi
      done
      [[ $formatter =~ ^ht ]] && rm -f idxmake.{dvi,log} "$base.css"
   fi
   exit
fi

if [[ $formatter =~ ^ht ]]; then
   die "mk can't handle the $formatter formatter, except with the --clean and --Clean options"
fi

# texi2dviquiet, if set to true in rc-file, keeps texi2dvi quiet, even in verbose mode
# extraoptions can be set in rc file; could be, e.g., -shell-escape
if [[ $texi2dviquiet || ! $verbose ]]; then q=--quiet; else q=''; fi

# For ltxdoc (.dtx) files, we don´t want to use the default index processor, texindy, but 
# we need makeindex, with the -s gind.ist option. 
if [ "$ext" = 'dtx' ]; then 
   indexprefix="TEXINDY=false MAKEINDEX='makeindex -s gind.ist'"
fi

texcommand="\
   $indexprefix \
   LATEX='$formatter -halt-on-error -recorder -synctex=1 $extraoptions' \
   texi2dvi --no-line-error $q '$base.$ext' 2>/dev/null"
while true; do
   if compile; then
      $view || $print && {
         run "vpp $vppoptions \"$vpptarget\""
         case $? in
         ($editexit)	edit "$edit" 1 false || continue ;;
         ($compileexit)	rm -f "$target"; continue ;;
         (0)		break ;;
         (*)		edit "$edit" 1 true || continue ;;
         esac
      }
   else
      show_error_and_edit
      continue
   fi
   break
done
edit=
# compilation was OK, but still show any warnings:
show_error_and_edit