#!/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