summaryrefslogtreecommitdiff
path: root/support/latex_maker/mk
blob: 61d108f8c84493cd0b34bd8edb4bbcf37db8c8e0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
#!/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