summaryrefslogtreecommitdiff
path: root/Build
diff options
context:
space:
mode:
authorPeter Breitenlohner <peb@mppmu.mpg.de>2011-03-01 09:20:55 +0000
committerPeter Breitenlohner <peb@mppmu.mpg.de>2011-03-01 09:20:55 +0000
commit745b336f730a44982185b9218413f29352d55246 (patch)
treebece2f4f19ce54139b70dd29221b55d0d4e535de /Build
parent82e45746ba7c21a9b375025bcac04c5ab066cce1 (diff)
update linked scripts Master -> Build
git-svn-id: svn://tug.org/texlive/trunk@21563 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build')
-rwxr-xr-x[-rw-r--r--]Build/source/texk/texlive/linked_scripts/ebong/ebong.py346
-rwxr-xr-xBuild/source/texk/texlive/linked_scripts/epspdf/epspdf.rb2
-rwxr-xr-xBuild/source/texk/texlive/linked_scripts/epspdf/epspdftk.tcl2
-rwxr-xr-xBuild/source/texk/texlive/linked_scripts/epstopdf/epstopdf.pl19
-rwxr-xr-xBuild/source/texk/texlive/linked_scripts/fontools/autoinst250
-rwxr-xr-xBuild/source/texk/texlive/linked_scripts/fontools/font2afm190
-rwxr-xr-xBuild/source/texk/texlive/linked_scripts/fragmaster/fragmaster.pl688
-rwxr-xr-xBuild/source/texk/texlive/linked_scripts/glossaries/makeglossaries715
-rwxr-xr-x[-rw-r--r--]Build/source/texk/texlive/linked_scripts/installfont/installfont-tl585
-rwxr-xr-xBuild/source/texk/texlive/linked_scripts/latex2man/latex2man238
-rwxr-xr-xBuild/source/texk/texlive/linked_scripts/latexmk/latexmk.pl739
-rwxr-xr-xBuild/source/texk/texlive/linked_scripts/listings-ext/listings-ext.sh150
-rwxr-xr-xBuild/source/texk/texlive/linked_scripts/luaotfload/mkluatexfontdb.lua99
-rwxr-xr-x[-rw-r--r--]Build/source/texk/texlive/linked_scripts/mkgrkindex/mkgrkindex16
-rwxr-xr-xBuild/source/texk/texlive/linked_scripts/oberdiek/pdfatfi.pl4
-rwxr-xr-xBuild/source/texk/texlive/linked_scripts/pdfcrop/pdfcrop.pl1085
-rwxr-xr-xBuild/source/texk/texlive/linked_scripts/pdfjam/pdf1804
-rwxr-xr-xBuild/source/texk/texlive/linked_scripts/pdfjam/pdf2706
-rwxr-xr-xBuild/source/texk/texlive/linked_scripts/pdfjam/pdf904
-rwxr-xr-xBuild/source/texk/texlive/linked_scripts/pdfjam/pdfbook54
-rwxr-xr-xBuild/source/texk/texlive/linked_scripts/pdfjam/pdfflip4
-rwxr-xr-xBuild/source/texk/texlive/linked_scripts/pdfjam/pdfjam191
-rwxr-xr-xBuild/source/texk/texlive/linked_scripts/pdfjam/pdfjam-pocketmod13
-rwxr-xr-xBuild/source/texk/texlive/linked_scripts/pdfjam/pdfjam-slides3up10
-rwxr-xr-xBuild/source/texk/texlive/linked_scripts/pdfjam/pdfjam-slides6up10
-rwxr-xr-xBuild/source/texk/texlive/linked_scripts/pdfjam/pdfjoin9
-rwxr-xr-xBuild/source/texk/texlive/linked_scripts/pdfjam/pdfnup9
-rwxr-xr-xBuild/source/texk/texlive/linked_scripts/pdfjam/pdfpun17
-rwxr-xr-xBuild/source/texk/texlive/linked_scripts/perltex/perltex.pl503
-rwxr-xr-xBuild/source/texk/texlive/linked_scripts/pkfix-helper/pkfix-helper1362
-rwxr-xr-xBuild/source/texk/texlive/linked_scripts/ppower4/pdfthumb.tlu5
-rwxr-xr-xBuild/source/texk/texlive/linked_scripts/ppower4/ppower4.tlu4
-rwxr-xr-xBuild/source/texk/texlive/linked_scripts/pst2pdf/pst2pdf56
-rwxr-xr-xBuild/source/texk/texlive/linked_scripts/purifyeps/purifyeps186
-rwxr-xr-x[-rw-r--r--]Build/source/texk/texlive/linked_scripts/sty2dtx/sty2dtx.pl800
-rwxr-xr-xBuild/source/texk/texlive/linked_scripts/svn-multi/svn-multi.pl14
-rwxr-xr-xBuild/source/texk/texlive/linked_scripts/tetex/texdoctk.pl4
-rwxr-xr-xBuild/source/texk/texlive/linked_scripts/texlive/tlmgr.pl6373
-rwxr-xr-xBuild/source/texk/texlive/linked_scripts/texloganalyser/texloganalyser351
-rwxr-xr-xBuild/source/texk/texlive/linked_scripts/thumbpdf/thumbpdf.pl10
40 files changed, 14079 insertions, 1048 deletions
diff --git a/Build/source/texk/texlive/linked_scripts/ebong/ebong.py b/Build/source/texk/texlive/linked_scripts/ebong/ebong.py
index e69de29bb2d..c4162cf0ef6 100644..100755
--- a/Build/source/texk/texlive/linked_scripts/ebong/ebong.py
+++ b/Build/source/texk/texlive/linked_scripts/ebong/ebong.py
@@ -0,0 +1,346 @@
+#!/usr/bin/env python
+# look in newbong
+import sre
+A='A'
+B='B'
+S='S'
+s='s'
+F='F'
+X='X'
+
+NCLINE = 0
+global NCWORD,CWORD
+
+AKSAR={
+ 'k' :[B,'k'],
+ 'kh' :[B,'kh'],
+ 'g' :[B,'g'],
+ 'gh' :[B,'gh'],
+ 'ng' :[B,'NG'],
+
+ 'ch' :[B,'c'],
+ '^ch' :[B,'ch'],
+ 'j' :[B,'j'],
+ 'jh' :[B,'jh'],
+ '^y' :[B,'NJ'],
+ '_n' :[B,'NJ'],
+
+ 't' :[B,'T'],
+ '^th' :[B,'Th'],
+ 'd' :[B,'D'],
+ 'dh' :[B,'Dh'],
+ '^n' :[B,'N'],
+
+ '_t' :[B,'t'],
+ 'th' :[B,'th'],
+ '_d' :[B,'d'],
+ '_dh' :[B,'dh'],
+ 'n' :[B,'n'],
+
+ 'p' :[B,'p'],
+ 'ph' :[B,'ph'],
+ 'f' :[B,'ph'],
+ 'b' :[B,'b'],
+ 'bh' :[B,'bh'],
+ 'v' :[B,'bh'],
+ 'm' :[B,'m'],
+ 'M' :[F,'M'],
+
+ '^j' :[B,'J'],
+ 'J' :[B,'J'],
+ 'r' :[B,'r'],
+ 'R' :[F,'R'],
+ 'l' :[B,'l'],
+ 'L' :[F,'L'],
+ 'W' :[F,'W'],
+ 'V' :[F,'W'],
+ 'h' :[B,'H'],
+ 'kk' :[B,'kK'],
+ 'kkm' :[B,'kK/N'],
+
+ 'sh' :[B,'sh'],
+ '^s' :[B,'Sh'],
+ '^sh' :[B,'Sh'],
+ 's' :[B,'s'],
+
+ '^r' :[B,'rh'],
+ '^rh' :[B,'rhh'],
+ 'y' :[B,'y'],
+ 'Y' :[F,'Y'],
+ 'JY' :[F,'Y'],
+ '__t' :[B,'t//'],
+ '^ng' :[B,'NNG'],
+ ':h' :[B,'h'],
+ '^' :[F,'NN'],
+ '_' :[F,':/'],
+
+ 'A' :[S,'A'],
+ 'AA' :[S,'Aa'],
+ 'I' :[S,'I'],
+ 'II' :[S,'II'],
+ 'U' :[S,'U'],
+ 'UU' :[S,'UU'],
+ 'RI' :[S,'RR'],
+ 'E' :[S,'E'],
+ 'OI' :[S,'OI'],
+ 'O' :[S,'O'],
+ 'OU' :[S,'OU'],
+
+ 'a' :[X,'o',1],
+ 'aa' :[s,'a',1],
+ 'i' :[s,'i',-1],
+ 'ii' :[s,'ii',1],
+ 'u' :[s,'u',1],
+ 'uu' :[s,'uu',1],
+ 'RII' :[s,'rR',1],
+ 'e' :[s,'e',-1],
+ 'oi' :[s,'oi',-2],
+ 'oo' :[s,'oo',11],
+ 'o' :[X,'o',1],
+ 'ou' :[s,'ou',12],
+
+ '.' :[F,'.'],
+ '..' :[F,'..'],
+ '...' :[F,'...'],
+ '|' :[F,'|'],
+
+ '~' :[F,'~'],
+ '`' :[F,'`'],
+ '!' :[F,'!'],
+ '1' :[F,'1'],
+ '2' :[F,'2'],
+ 'at' :[F,'@'],
+ '#' :[F,'#'],
+ '3' :[F,'3'],
+ '$' :[F,'$'],
+ '4' :[F,'4'],
+ '%' :[F,'%'],
+ '5' :[F,'5'],
+ '6' :[F,'6'],
+ '&' :[F,'&'],
+ '7' :[F,'7'],
+ '*' :[F,'*'],
+ '8' :[F,'8'],
+ '(' :[F,'('],
+ '9' :[F,'9'],
+ ')' :[F,')'],
+ '0' :[F,'0'],
+ 'dash' :[F,'-'],
+ '+' :[F,'+'],
+ '=' :[F,'='],
+ '|' :[F,'|'],
+ '{' :[F,'{'],
+ '[' :[F,'['],
+ '}' :[F,'}'],
+ ']' :[F,']'],
+ ':' :[F,':'],
+ ';' :[F,';'],
+ '"' :[F,'"'],
+ "'" :[F,"'"],
+ '<' :[F,'<'],
+ ',' :[F,','],
+ '>' :[F,'>'],
+ '.' :[F,'.'],
+ '?' :[F,'?'],
+ '/' :[F,'/']}
+
+CATCODES = {'SS' :[S,'','','',1],
+ 'SB' :[B,'','','',1],
+ 'BS' :[S,'','','',1],
+ 'BB' :[B,'','/','',1],
+ 'BF' :[F,'','','',1],
+ 'Bs1' :[S,'','','',1],
+ 'Bs-1':[S,'\*','','*',1],
+ 'Bs-2':[S,'\*','','*{oi}',0],
+ 'Bs11':[S,'\*','','*ea',0],
+ 'Bs12':[S,'\*','','*eou',0],
+ 'Fs1' :[S,'','','',1],
+ 'Fs-1':[S,'\*','','*',1],
+ 'Fs-2':[S,'\*','','*{oi}',0],
+ 'Fs11':[S,'\*','','*ea',0],
+ 'Fs12':[S,'\*','','*eou',0],
+ 'FF' :[F,'','','',1],
+ 'AX' :[F,'','','',1]}
+
+def blocked(line):
+ #print '@ blocked', line , '->',
+ m = sre.findall('@[^@]+@',line)
+ outline = line
+ if not m :
+ #print outline
+ return(outline)
+ else:
+ for i in range(len(m)):
+ s=m[i][:-1].replace(' ','%X%')
+ outline = outline.replace(m[i],s,1)
+ #print outline
+ return(outline)
+
+def unblock(line):
+ #print '@unblock', line, '->',
+ m = sre.findall('@[^\s]+',line)
+ outline = line
+ if not m :
+ #print outline
+ return(outline)
+ else:
+ for i in range(len(m)):
+ s=m[i].replace('@','').replace('%X%',' ')
+ outline = outline.replace(m[i],s)
+ #print outline
+ return(outline)
+
+def printamp(line):
+ #print '@unblock', line, '->',
+ m = sre.findall('#AT',line)
+ outline = line
+ if not m :
+ #print outline
+ return(outline)
+ else:
+ for i in range(len(m)):
+ outline = outline.replace('#AT','@')
+ #print outline
+ return(outline)
+
+def readsyll(syll):
+ syllparts=[]
+ start = 0; end = len(syll)
+ while syll[start : end]:
+ slice = syll[start : end]
+ #print slice
+ if AKSAR.has_key(slice):
+ syllparts.append(AKSAR[slice])
+ start = start + len(slice)
+ end = len(syll)
+ else :
+ end = end -1
+ return(syllparts)
+
+def fuse(list1,list2):
+ global CCATCODE
+ #print list1,list2
+ Type1 = list1[0]
+ Type2 = list2[0]
+
+ if Type2 == s:
+ Type3 = str(list2[2])
+ elif Type2 == X:
+ Type1=A
+ Type3=''
+ else:
+ Type3 =''
+
+ Type = Type1+Type2+Type3
+
+ #print 'Type:', Type
+
+ try:
+ CATCODE = CATCODES[Type]
+ TARGET = CATCODE[0]
+ PREFIX = CATCODE[1]
+ MIDFIX = CATCODE[2]
+ POSTFIX = CATCODE[3]
+ FLAG = CATCODE[4]
+
+ #print 'TGT:', TARGET, PREFIX,MIDFIX,POSTFIX,FLAG
+ #print 'RAWC', AKSAR[list1[1]][1],AKSAR[list2[1]][1]
+
+ c1=list1[1]
+ c2=list2[1]
+
+ if FLAG == 1 :
+ c = PREFIX + c1 + MIDFIX + POSTFIX + c2
+ else :
+ c = PREFIX + c1 + MIDFIX + POSTFIX
+
+ fused = [TARGET,c]
+ #print CATCODE
+ return(fused)
+ except KeyError:
+ print '\n ERROR AT LINE:', NCLINE, 'WORD:',NCWORD, '(',CWORD,')'
+ return(['ERROR','UNKNOWN CATCODE'])
+
+def fuseatoms(syll):
+ slist=readsyll(syll);
+ #print slist
+ lslist=len(slist);
+ l0=slist[0];
+ for i in range(1,lslist):
+ nextitem = slist[i]
+ l0=fuse(l0,nextitem)
+
+ return(l0[1])
+
+def fuseword(wrd):
+ if wrd[0] == '@' :
+ return(wrd)
+ syllables = wrd.split('-')
+ w0=''
+ for eachsyll in syllables:
+ syll=eachsyll
+ thesyll = fuseatoms(syll)
+ w0 = w0 + thesyll
+ #print 'FUSED WORD',w0
+ return(w0)
+
+def fuseline(line):
+ global NCWORD,CWORD
+ NCWORD = 0
+ #line = blocked(line)
+ words = line.split()
+ l0=''
+ for eachword in words:
+ NCWORD=NCWORD+1
+ word = eachword
+ CWORD=word
+ theword=fuseword(word)
+ #print 'XX',theword
+ l0=l0+' '+theword
+ #print 'FUSED LINE', l0
+ return(l0)
+
+# The main program
+import sys
+OK=1
+finnam = sys.argv[1]
+foutnam = finnam.split('.')[0] + '.' + 'tex'
+
+fin = file(finnam,'rt')
+fout = file(foutnam,'wt')
+
+textin = fin.readlines()
+nlines = len(textin)
+
+textout = []
+
+fin.close()
+
+for eachline in textin:
+ NCLINE = NCLINE+1
+ if eachline[0] == '#' :
+ lineout = eachline[1:]
+ elif eachline[0] == '\\' :
+ lineout = eachline
+ elif eachline == '\n':
+ lineout = eachline
+ else :
+ line1 = eachline.strip()
+ line2 = blocked(line1)
+ lineout = fuseline(line2) + '\n'
+ lineout = lineout[1:]
+ #print ':::', lineout
+ if lineout.find('UNKNOWN CATCODE') == -1 :
+ lineout = unblock(lineout)
+ #print ':::', lineout
+ textout.append(printamp(lineout))
+ else :
+ OK = 0
+ fout.close()
+
+if OK == 1:
+ fout.writelines(textout)
+ fout.close()
+ print 'done'
+else:
+ print 'Unknown CATCODE, Fix The errors and try again'
diff --git a/Build/source/texk/texlive/linked_scripts/epspdf/epspdf.rb b/Build/source/texk/texlive/linked_scripts/epspdf/epspdf.rb
index 94da56c4c99..47a0c1f65b9 100755
--- a/Build/source/texk/texlive/linked_scripts/epspdf/epspdf.rb
+++ b/Build/source/texk/texlive/linked_scripts/epspdf/epspdf.rb
@@ -3,7 +3,7 @@
# epspdf conversion utility, main source
#####
-# Copyright (C) 2006, 2008, 2009, 2010 Siep Kroonenberg
+# Copyright (C) 2006, 2008, 2009, 2010, 2011 Siep Kroonenberg
# n dot s dot kroonenberg at rug dot nl
#
# This program is free software, licensed under the GNU GPL, >=2.0.
diff --git a/Build/source/texk/texlive/linked_scripts/epspdf/epspdftk.tcl b/Build/source/texk/texlive/linked_scripts/epspdf/epspdftk.tcl
index ff29eff893d..361ac83d6b1 100755
--- a/Build/source/texk/texlive/linked_scripts/epspdf/epspdftk.tcl
+++ b/Build/source/texk/texlive/linked_scripts/epspdf/epspdftk.tcl
@@ -3,7 +3,7 @@
# epspdf conversion utility, GUI frontend
#####
-# Copyright (C) 2006, 2008, 2009, 2010 Siep Kroonenberg
+# Copyright (C) 2006, 2008, 2009, 2010, 2011 Siep Kroonenberg
# n dot s dot kroonenberg at rug dot nl
#
# This program is free software, licensed under the GNU GPL, >=2.0.
diff --git a/Build/source/texk/texlive/linked_scripts/epstopdf/epstopdf.pl b/Build/source/texk/texlive/linked_scripts/epstopdf/epstopdf.pl
index d7662c96a88..9299839c41e 100755
--- a/Build/source/texk/texlive/linked_scripts/epstopdf/epstopdf.pl
+++ b/Build/source/texk/texlive/linked_scripts/epstopdf/epstopdf.pl
@@ -2,7 +2,7 @@ eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}' && eval 'exec perl -S $0 $
if 0;
use strict;
-# $Id: epstopdf.pl 17496 2010-03-18 17:57:31Z karl $
+# $Id: epstopdf.pl 18319 2010-05-17 16:34:21Z karl $
# (Copyright lines below.)
#
# Redistribution and use in source and binary forms, with or without
@@ -46,6 +46,9 @@ use strict;
#
# emacs-page
# History
+# 2010/05/09 v2.16 (Karl Berry)
+# * make --nogs dump edited PostScript to stdout by default
+# (report from Reinhard Kotucha).
# 2010/03/19 v2.15 (Karl Berry)
# * let --outfile override --filter again.
# * recognize MSWin64 as well as MSWin32, just in case.
@@ -146,7 +149,7 @@ use strict;
### program identification
my $program = "epstopdf";
-my $ident = '($Id: epstopdf.pl 17496 2010-03-18 17:57:31Z karl $) 2.15';
+my $ident = '($Id: epstopdf.pl 18319 2010-05-17 16:34:21Z karl $) 2.16';
my $copyright = <<END_COPYRIGHT ;
Copyright 2009-2010 Karl Berry et al.
Copyright 2002-2009 Gerben Wierda et al.
@@ -334,7 +337,7 @@ if (! $OutputFilename) {
}
} else {
debug "No Ghostscript: will write standard output";
- $OutputFilename = "-"; # no ghostscript, write to standard output
+ $OutputFilename = "-";
}
}
debug "Output filename:", $OutputFilename;
@@ -408,8 +411,14 @@ if ($::opt_gs) {
$outname = $GS;
}
else {
- open($OUT, '>', $OutputFilename) or error "Cannot write \"$OutputFilename\"";
- $outname = $OutputFilename;
+ debug "No Ghostscript: opening $OutputFilename";
+ if ($OutputFilename eq "-") {
+ $OUT = *STDOUT;
+ } else {
+ open($OUT, '>', $OutputFilename)
+ || error ("Cannot write \"$OutputFilename\": $!");
+ $outname = $OutputFilename;
+ }
}
binmode $OUT;
diff --git a/Build/source/texk/texlive/linked_scripts/fontools/autoinst b/Build/source/texk/texlive/linked_scripts/fontools/autoinst
index 7939246d91c..5c31de73c06 100755
--- a/Build/source/texk/texlive/linked_scripts/fontools/autoinst
+++ b/Build/source/texk/texlive/linked_scripts/fontools/autoinst
@@ -1,12 +1,16 @@
#!/usr/bin/perl
-=begin COPYRIGHT -------------------------------------------------------------
+=begin COPYRIGHT
- Copyright (c) 2005-2009 Marc Penninga.
+----------------------------------------------------------------------------
+
+ Copyright (C) 2005, 2006, 2007, 2009, 2010 Marc Penninga.
This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License
- as published by the Free Software Foundation.
+ it under the terms of the GNU General Public License
+ as published by the Free Software Foundation,
+ either version 2 of the License, or (at your option)
+ any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -21,7 +25,9 @@
Boston, MA 02111-1307,
USA
-=end -------------------------------------------------------------------------
+----------------------------------------------------------------------------
+
+=end COPYRIGHT
=cut
@@ -64,7 +70,7 @@ Please report any bugs or suggestions to <marc@penninga.info>.
END_USAGE
-=begin Comment ---------------------------------------------------------------
+=begin Comment
The next three tables map the names of weights, widths and shapes
to NFSS codes. New entries can be added without problems,
@@ -78,7 +84,7 @@ END_USAGE
families that have both italic and oblique shapes, but I doubt
whether these exist (apart from Computer Modern, of course).
-=end
+=end Comment
=cut
@@ -106,6 +112,7 @@ my %FD_WIDTH = (
ultracondensed => 'uc',
extracondensed => 'ec',
condensed => 'c',
+ cond => 'c',
semicondensed => 'sc',
regular => '',
semiextended => 'sx',
@@ -121,7 +128,7 @@ my %FD_SHAPE = (
romanii => 'it', # Map one of Silentium Pro's two roman shapes to 'it'
);
-=begin Comment ---------------------------------------------------------------
+=begin Comment
The following tables are used for deciding which font families and shapes
to generate. Each hash governs one of these aspects:
@@ -165,7 +172,7 @@ my %FD_SHAPE = (
name A string added to the name of the generated font,
to make it unique.
-=end
+=end Comment
=cut
@@ -324,7 +331,7 @@ END_PARSE_ERROR
else {
$already_seen{$unique} = $filename;
}
-
+
FIGURE_STYLE:
for my $figure_style (keys %FIGURE_STYLE) {
@@ -410,6 +417,15 @@ END_PARSE_ERROR
. "\"$CODING_SCHEME{$enc_name}\""
: ''),
(map { "--feature=$_" } @features),
+ # if there's no 'liga' feature,
+ # add ligkern commands for common ligatures
+ ((grep { $_ eq 'liga' } @features)
+ ? ''
+ : '--ligkern="f i =: fi" '
+ . '--ligkern="f l =: fl" '
+ . '--ligkern="f f =: ff" '
+ . '--ligkern="ff i =: ffi" '
+ . '--ligkern="ff l =: ffl" '),
$FIGURE_STYLE{$figure_style}{extra},
$SHAPE{$shape}{extra},
$ARGV{extra},
@@ -489,12 +505,25 @@ END_GENERAL_PARSE_ERROR
my $FD;
for my $fd_line_ref (@fd_data) {
my @fd_line = @{$fd_line_ref};
-
- write_style_file($fd_line[0], @fd_data) if $fd_line[0] ne $fd_prev[0];
+
+ my $safe = $fd_line[0];
+ $safe =~ s/1/One/xmsg;
+ $safe =~ s/2/Two/xmsg;
+ $safe =~ s/3/Three/xmsg;
+ $safe =~ s/4/Four/xmsg;
+ $safe =~ s/5/Five/xmsg;
+ $safe =~ s/6/Six/xmsg;
+ $safe =~ s/7/Seven/xmsg;
+ $safe =~ s/8/Eight/xmsg;
+ $safe =~ s/9/Nine/xmsg;
+ $safe =~ s/0/Zero/xmsg;
+
+ write_style_file($fd_line[0], $safe, @fd_data)
+ if $fd_line[0] ne $fd_prev[0];
if (grep { $fd_line[$_] ne $fd_prev[$_] } 0 .. 2) {
end_fd_file($FD) if $fd_prev[0];
- $FD = start_fd_file(@fd_line[0 .. 2]);
+ $FD = start_fd_file(@fd_line[0 .. 2], $safe);
}
if (grep { $fd_line[$_] ne $fd_prev[$_] } 0 .. 4) {
@@ -509,7 +538,7 @@ END_GENERAL_PARSE_ERROR
# Write fontshape entry
printf {$FD} " %s \\%s\@\@scale %s\n",
sprintf("%5s-%-5s", '<' . $fd_line[5], $fd_line[6] . '>'),
- $fd_line[0],
+ $safe,
$fd_line[7],
;
@@ -518,19 +547,19 @@ END_GENERAL_PARSE_ERROR
end_fd_file($FD) if $fd_prev[0];
- # Remove the --no-updmap option from the last command for each family
- map { $_->[-1] =~ s/--no-updmap//xms } values %commands;
-
# Print or execute the generated commands
if ($ARGV{manual}) {
while (my ($fam, $cmds_ref) = each %commands) {
open my $BAT, '>', "$fam.bat"
or die "ERROR: can't create '$fam.bat' - $!";
- map { print {$BAT} "$_\n" } @{$cmds_ref};
+ print {$BAT} "$_\n" for @{$cmds_ref};
close $BAT;
}
}
else {
+ # Remove the --no-updmap option from the last command for each family
+ map { $_->[-1] =~ s/--no-updmap//xms } values %commands;
+
for my $cmds_ref (values %commands) {
for my $cmd (@{$cmds_ref}) {
print "$cmd\n";
@@ -756,6 +785,17 @@ sub read_font_info {
}
close $OTFINFO;
+ # Check if there's a "kern" table
+ open $OTFINFO, '-|',"otfinfo --tables \"$fontfile\""
+ or die "ERROR: 'otfinfo --tables \"$fontfile\"' failed";
+ for my $line (<$OTFINFO>) {
+ if ($line =~ m/\d+ \s+ (\w{4}) \s+/xms && $1 eq 'kern') {
+ $fontinfo{features}{kern} = 1;
+ last;
+ }
+ }
+ close $OTFINFO;
+
if ($ARGV{verbose}) {
print <<"END_FONT_INFO";
@@ -780,14 +820,14 @@ END_FONT_INFO
}
-=begin Comment ---------------------------------------------------------------
+=begin Comment
Ornament glyph names vary across fonts, so we generate font-specific
encoding vectors.
This is skipped if a file `<family>_orn.enc' is found in the current
directory, so the user can provide her own vector if she wants to.
-=end
+=end Comment
=cut
@@ -834,17 +874,18 @@ sub make_ornament_encoding {
# ------------------------------------------------------------------------
sub write_style_file {
- my ($family, @fd_data) = @_;
-
- my (%seen_enc, %seen_style, %seen_series);
+ my ($family, $safe, @fd_data) = @_;
+
+ my (%seen_enc, %seen_style, %seen_series, %seen_shape);
LINE:
for my $line_ref (@fd_data) {
- my ($fam, $enc, $style, $series) = @{$line_ref}[0 .. 3];
+ my ($fam, $enc, $style, $series, $shape) = @{$line_ref}[0 .. 4];
next LINE if $fam ne $family;
$seen_enc{$enc} = 1;
$seen_style{$style} = 1;
$seen_series{$series} = 1;
+ $seen_shape{$shape} = 1;
}
open my $STYLE, '>', "$family.sty"
@@ -863,9 +904,13 @@ END_STY_HEADER
print {$STYLE} "\\RequirePackage[$textenc]{fontenc}\n" if $textenc;
print {$STYLE} "\\RequirePackage{textcomp}\n" if $seen_enc{TS1};
- print {$STYLE} <<'END_STY_FONTAXES';
+ print {$STYLE} <<'END_STY_FONTAXES_START';
\IfFileExists{fontaxes.sty}{
\RequirePackage{fontaxes}
+END_STY_FONTAXES_START
+
+ if ($seen_shape{tl} || $seen_shape{tlit}) {
+ print {$STYLE} <<'END_STY_FONTAXES_TL';
\providecommand{\tldefault}{tl}
\DeclareRobustCommand\tlshape{\not@math@alphabet\tlshape\relax
\fontsecondaryshape\tldefault\selectfont}
@@ -874,11 +919,21 @@ END_STY_HEADER
\fa@naming@exception{shape}{{n}{tl}}{tl}
\fa@naming@exception{shape}{{it}{tl}}{tlit}
+END_STY_FONTAXES_TL
+ }
+
+ if ($seen_shape{nw} || $seen_shape{sw}) {
+ print {$STYLE} <<'END_STY_FONTAXES_SW';
\DeclareRobustCommand\swshape{\not@math@alphabet\swshape\relax
\fontprimaryshape\itdefault\fontsecondaryshape\swdefault\selectfont}
\fa@naming@exception{shape}{{n}{sw}}{nw}
\fa@naming@exception{shape}{{it}{sw}}{sw}
+END_STY_FONTAXES_SW
+ }
+
+ if ($seen_style{Sup}) {
+ print {$STYLE} <<'END_STY_FONTAXES_SUP';
\fa@naming@exception{figures}{{superior}{proportional}}{Sup}
\fa@naming@exception{figures}{{superior}{tabular}}{Sup}
\def\sufigures{\@nomath\sufigures
@@ -886,6 +941,11 @@ END_STY_HEADER
\DeclareTextFontCommand{\textsu}{\sufigures}
\let\textsuperior\textsu
+END_STY_FONTAXES_SUP
+ }
+
+ if ($seen_style{Inf}) {
+ print {$STYLE} <<'END_STY_FONTAXES_INF';
\fa@naming@exception{figures}{{inferior}{proportional}}{Inf}
\fa@naming@exception{figures}{{inferior}{tabular}}{Inf}
\def\infigures{\@nomath\infigures
@@ -893,6 +953,11 @@ END_STY_HEADER
\DeclareTextFontCommand{\textin}{\infigures}
\let\textinferior\textin
+END_STY_FONTAXES_INF
+ }
+
+ if ($seen_style{Orn}) {
+ print {$STYLE} <<'END_STY_FONTAXES_ORN';
\fa@naming@exception{figures}{{ornament}{proportional}}{Orn}
\fa@naming@exception{figures}{{ornament}{tabular}}{Orn}
\def\ornaments{\@nomath\ornaments
@@ -900,20 +965,35 @@ END_STY_HEADER
\DeclareTextFontCommand{\textornaments}{\ornaments}
\providecommand{\ornament}[1]{\textornaments{\char##1}}
+END_STY_FONTAXES_ORN
+ }
+
+ if ($seen_style{Numr}) {
+ print {$STYLE} <<'END_STY_FONTAXES_NUMR';
\fa@naming@exception{figures}{{numerators}{proportional}}{Numr}
\fa@naming@exception{figures}{{numerators}{tabular}}{Numr}
+END_STY_FONTAXES_NUMR
+ }
+
+ if ($seen_style{Dnom}) {
+ print {$STYLE} <<'END_STY_FONTAXES_DNOM';
\fa@naming@exception{figures}{{denominators}{proportional}}{Dnom}
\fa@naming@exception{figures}{{denominators}{tabular}}{Dnom}
+
+END_STY_FONTAXES_DNOM
+ }
+
+ print {$STYLE} <<'END_STY_FONTAXES_END';
}{}
-END_STY_FONTAXES
+END_STY_FONTAXES_END
print {$STYLE} <<"END_STY_XKEYVAL";
\\IfFileExists{xkeyval.sty}{
- \\newcommand*{\\$family\@scale}{1}
+ \\newcommand*{\\$safe\@scale}{1}
\\RequirePackage{xkeyval}
- \\DeclareOptionX{scaled}{\\renewcommand*{\\$family\@scale}{##1}}
+ \\DeclareOptionX{scaled}{\\renewcommand*{\\$safe\@scale}{##1}}
}{
\\let\\DeclareOptionX\\DeclareOption
\\let\\ExecuteOptionsX\\ExecuteOptions
@@ -924,19 +1004,19 @@ END_STY_XKEYVAL
if ($seen_style{LF} or $seen_style{TLF}) {
print {$STYLE}
- "\\DeclareOptionX{lining}{\\edef\\$family\@figurestyle{LF}}\n";
+ "\\DeclareOptionX{lining}{\\edef\\$safe\@figurestyle{LF}}\n";
}
if ($seen_style{OsF} or $seen_style{TOsF}) {
print {$STYLE}
- "\\DeclareOptionX{oldstyle}{\\edef\\$family\@figurestyle{OsF}}\n";
+ "\\DeclareOptionX{oldstyle}{\\edef\\$safe\@figurestyle{OsF}}\n";
}
if ($seen_style{TLF} or $seen_style{TOsF}) {
print {$STYLE}
- "\\DeclareOptionX{tabular}{\\edef\\$family\@figurealign{T}}\n";
+ "\\DeclareOptionX{tabular}{\\edef\\$safe\@figurealign{T}}\n";
}
if ($seen_style{LF} or $seen_style{OsF}) {
print {$STYLE}
- "\\DeclareOptionX{proportional}{\\edef\\$family\@figurealign{}}\n";
+ "\\DeclareOptionX{proportional}{\\edef\\$safe\@figurealign{}}\n";
}
my $defaults
@@ -977,7 +1057,7 @@ END_STY_XKEYVAL
\\renewcommand*
{\\$ARGV{nfss}default}
- {$family-\\$family\@figurealign\\$family\@figurestyle}
+ {$family-\\$safe\@figurealign\\$safe\@figurestyle}
\\renewcommand*{\\familydefault}{\\$ARGV{nfss}default}
\\endinput
@@ -989,7 +1069,7 @@ END_STYLE_REST
# ------------------------------------------------------------------------
sub start_fd_file {
- my ($fam, $enc, $fig) = @_;
+ my ($fam, $enc, $fig, $safe) = @_;
open my $FD, '>', "$enc$fam-$fig.fd"
or die "ERROR: can't create '$enc$fam-$fig.fd' - $!";
@@ -1000,10 +1080,10 @@ sub start_fd_file {
\\ProvidesFile{$enc$fam-$fig.fd}
[$date (autoinst) Font definitions for $enc/$fam-$fig.]
-\\expandafter\\ifx\\csname $fam\@scale\\endcsname\\relax
- \\let\\$fam\@\@scale\\\@empty
+\\expandafter\\ifx\\csname $safe\@scale\\endcsname\\relax
+ \\let\\$safe\@\@scale\\\@empty
\\else
- \\edef\\$fam\@\@scale{s*[\\csname $fam\@scale\\endcsname]}%
+ \\edef\\$safe\@\@scale{s*[\\csname $safe\@scale\\endcsname]}%
\\fi
\\DeclareFontFamily{$enc}{$fam-$fig}{}
@@ -1056,10 +1136,12 @@ automatic mode) is quite complicated because it needs lots of long
command lines and doesn't generate the F<fd> and F<sty> files LaTeX needs.
B<autoinst> simplifies the font installation
process by generating and executing all commands for F<otftotfm>
-and by creating all necessary F<fd> and F<sty> files. All the user then needs
+and by creating all necessary F<fd> and F<sty> files.
+
+All the user then needs
to do is move these files to a suitable location
-(C<< $LOCALTEXMF/tex/latex/<Supplier>/<FontFamily>/ >> being the
-canonical choice) and update TeX's filename database.
+(F<< $LOCALTEXMF/tex/latex/<Supplier>/<FontFamily>/ >> is the
+official choice) and update TeX's filename database.
Given a family of font files (in either F<.ttf> or F<.otf> format),
B<autoinst> will create several LaTeX font families:
@@ -1198,8 +1280,7 @@ A typical name in this scheme is F<MinionPro-Regular-osf-sc-ly1>.
By default, all text families use the LY1 encoding. This has been chosen
over T1 (Cork) because many OpenType fonts contain additional ligatures
such as fj and Th, and LY1 has a number of empty slots to accommodate these.
-
-A different encoding can be selected using the B< --encoding>
+Different encodings can be selected using the B< --encoding>
command line option (see below).
@@ -1249,8 +1330,8 @@ shapes and styles. This package can be downloaded from the project's homepage
(F<http://developer.berlios.de/projects/minionpro>) or directly through
the CVS web interface
(F<http://cvs.berlios.de/cgi-bin/viewcvs.cgi/minionpro/MinionPro/tex/>),
-and is also available from CTAN as part of the archive F<base-v2.zip>
-(F<http://www.ctan.org/tex-archive/fonts/minionpro/base-v2.zip>).
+and is also available from CTAN as part of the archive F<metrics-base.zip>
+(F<http://www.ctan.org/tex-archive/fonts/minionpro/metrics-base.zip>).
Using the machinery set up by F<fontaxes>, the generated style file
also defines a few commands (which take the text to be typeset as argument)
@@ -1286,11 +1367,14 @@ running LaTeX on the file F<nfssfont.tex> (part of a standard
LaTeX installation) and specifying the ornament font
(e.g., I<MinionPro-Regular-orn-u>).
+These commands are only generated for shapes and number styles
+that actually exist; no commands are generated for shapes and styles
+that don't exist, or whose generation has been turned off.
+
This whole machinery builds on F<fontaxes>; if that package cannot be found,
the style file doesn't provide high-level access to the more `exotic'
font shapes and styles. In that case, you're limited to using the lower-level
-commands from standard NFSS, or even plain TeX's C<\font> primitive
-(and it's called `primitive' for a reason!)
+commands from standard NFSS.
=head2 Using multiple font families in one document
@@ -1305,8 +1389,6 @@ of B<autoinst> may not be able to coexist peacefully.
=head2 NFSS codes
In NFSS, weight and width are concatenated into a single `series' attribute.
-(I<Note:> versions of B<autoinst> before 2007-07-27 erroneously formed
-the series as `width plus weight' instead of the reverse.)
B<autoinst> maps widths, weights and shapes to NFSS codes using
the following tables. These are based on the tables in Lehman's
F<Font Installation Guide>, but some changes had to be made to avoid
@@ -1367,11 +1449,10 @@ cannot find a writable F<texmf> directory and leave all generated F<tfm>,
F<vf>, F<enc> and F<map> files in the current working directory.
In that case, you need to move these to their correct destinations.
You also need to tell the dvi-driver (F<dvips>, F<dvipdfm>, F<pdfTeX> etc.)
-about the new font map files; this usually means editing some
-configuration file.
+about the new font map files; see the documentation for how this is done.
-Furthermore, some OpenType fonts lead to F<pl> and F<vpl> files that are too
-big for MiKTeX's F<pltotf> and F<vptovf>; the versions that come with TeXLive
+Also, some OpenType fonts lead to F<pl> and F<vpl> files that are too
+big for MiKTeX's F<pltotf> and F<vptovf>; the versions from TeXLive
(F<http://tug.org/ftp/texlive/Contents/live/bin/win32/>) don't have this
problem.
@@ -1487,24 +1568,22 @@ being interpreted as options to B<autoinst>, it should be properly quoted.
Eddie Kohler's TypeTools (F<http://www.lcdf.org/type>).
Perl is usually pre-installed on Linux and Unix systems;
-for Windows, good (and free) Perl implementations are
-Strawberry Perl (F<http://strawberryperl.com>) and
-ActivePerl (available from F<http://www.activestate.com>);
+on Windows, try ActivePerl (available from F<http://www.activestate.com>)
+or Strawberry Perl (F<http://strawberryperl.com>).
-John Owens' F<otfinst> (F<http://www.ece.ucdavis.edu/~jowens/code/otfinst/>;
-also available from CTAN) is another wrapper around F<otftotfm>,
-and may work for you when B<autoinst> doesn't.
+XeTeX (F<http://www.tug.org/xetex>) is an extension of TeX
+that can natively use any font installed in the operating system
+(including both flavours of OpenType fonts)
+without additional support files.
-Ready-made support files for MinionPro, providing more options and features
-than B<autoinst> ever will (including math), are available from
-F<http://developer.berlios.de/projects/minionpro/>.
+John Owens' F<otfinst> (available from CTAN) is another wrapper
+around F<otftotfm>, and may work when B<autoinst> doesn't.
-XeTeX (F<http://scripts.sil.org/xetex>) is a TeX extension that can use
-any font installed in the operating system (including both flavours of
-OpenType fonts) without additional support files.
-It also isn't hindered by standard TeX's limitation to 8-bit fonts,
-so it is especially well suited to fonts with many ligatures and
-alternate glyphs, such as Bickham, Poetica and Zapfino.
+The MinionPro for LaTeX project
+(F<http://developer.berlios.de/projects/minionpro/>, also on CTAN)
+developed very complete support files for Minion Pro (including math),
+but the project appears to have been abandoned;
+these files don't work with recent versions of Minion Pro.
=head1 AUTHOR
@@ -1517,20 +1596,21 @@ running B<autoinst> with the B< --verbose> option.
Please make sure that this output includes all error messages (if any);
this can be done using the command
- autoinst I<(... all options and files ...)> >autoinst.log 2>&1
+C<< autoinst I<(... all options and files ...)> E<gt>autoinst.log 2>&1 >>
=head1 COPYRIGHT
-Copyright (c) 2005-2009 Marc Penninga.
+Copyright (C) 2005, 2006, 2007, 2009, 2010 Marc Penninga.
=head1 LICENSE
This program is free software; you can redistribute it and/or modify
-it under the terms of version 2 of the GNU General Public License
-as published by the Free Software Foundation.
-
+it under the terms of the GNU General Public License
+as published by the Free Software Foundation,
+either version 2 of the License, or (at your option)
+any later version.
A copy of the GNU General Public License is included with the I<fontools>
collection; see the file F<GPLv2.txt>.
@@ -1550,14 +1630,36 @@ GNU General Public License for more details.
=over 12
+=item I<2010-04-29>
+
+The C< --feature=kern> option is only used for fonts that
+contain either a I<kern> feature or a I<kern> table.
+Font feature selection commands in the F<.sty> file are only
+generated for shapes and figure styles that are supported
+by the current font, and whose generation has not been
+turned off using the command line options.
+Fixed the font info parsing to work with the Condensed fonts
+in the Minion Pro family.
+
+=item I<2010-04-23>
+
+Always provide the C< --feature=kern> option to F<otftotfm>,
+even if the font doesn't have a I<kern> feature;
+this will make F<otftotfm> use the I<kern> table if present.
+For fonts without a I<liga> feature, add C< --ligkern>
+options for the common f-ligatures to the F<otftotfm> command line,
+so that any ligatures present in the font will still be used.
+Bug fix: the generated F<.sty> files now work for font families
+with names containing digits.
+
=item I<2009-04-09>
Prefixed the filenames of the included encoding files with
-`fontools_', to prevent name clashes with other packages.
+I<fontools_>, to prevent name clashes with other packages.
=item I<2009-04-06>
-A small patch to the make_ornament_encoding subroutine;
+A small patch to the C<make_ornament_encoding> subroutine;
it now also recognises the I<bullet.xxx> ornament glyphs
in Adobe's Kepler Pro.
diff --git a/Build/source/texk/texlive/linked_scripts/fontools/font2afm b/Build/source/texk/texlive/linked_scripts/fontools/font2afm
index e69de29bb2d..b0a60420d23 100755
--- a/Build/source/texk/texlive/linked_scripts/fontools/font2afm
+++ b/Build/source/texk/texlive/linked_scripts/fontools/font2afm
@@ -0,0 +1,190 @@
+#!/usr/bin/perl
+
+=pod
+
+=head1 NAME
+
+font2afm - create font metrics (in F<afm> format) for (almost) any font file
+
+=head1 SYNOPSIS
+
+font2afm [options] I<font> ...
+
+font2afm [options] @I<filelist>
+
+=head1 DESCRIPTION
+
+B<font2afm> generates font metrics (in Adobe's F<afm> format) for Type1,
+TrueType and OpenType fonts.
+
+B<font2afm> is just a wrapper script around several utilities (B<cfftot1>,
+B<pf2afm>, B<ttf2afm>, B<pfm2kpx> and B<ot2kpx>) that do the real work.
+All these utilities need to be available on your system.
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<-f>
+
+Force overwriting of existing F<afm> files.
+
+=back
+
+=head1 FILES
+
+=over 4
+
+=item I<font>
+
+This can be any Type1 (in either F<pfa> or F<pfb> format), TrueType or
+OpenType (both PostScript- and TrueType-flavored) font.
+
+=item I<filelist>
+
+This should contain a newline-separated list of font filenames.
+
+=back
+
+=head1 SEE ALSO
+
+F<pf2afm> (part of I<GhostScript>), F<ttf2afm> (part of I<pdfTeX>),
+F<afm2afm>, F<autoinst>, F<cmap2enc>, F<ot2kpx>, F<pfm2kpx>, F<cfftot1>
+(part of Eddie Kohler's I<LCDF TypeTools>).
+
+=head1 AUTHOR
+
+Marc Penninga <marc@penninga.info>
+
+=head1 HISTORY
+
+=over 12
+
+=item I<2005-04-15>
+
+First version
+
+=item I<2005-04-29>
+
+Improved the documentation
+
+=item I<2005-05-23>
+
+Bugfix.
+
+=item I<2005-07-29>
+
+Added support for PostScript-flavored (F<otf>) OpenType fonts.
+
+=item I<2005-08-08>
+
+Bugfix.
+
+=item I<2010-07-19>
+
+Another small bugfix.
+
+=back
+
+=cut
+
+##############################################################################
+
+use Getopt::Std;
+use integer;
+use warnings; no warnings qw(uninitialized);
+
+getopts "f", \%options;
+
+$0 =~ s!.*/!!;
+die "Usage: $0 fontfile ...\n or: $0 \@fontlist\n" if @ARGV < 1;
+
+if ($ARGV[0] =~ m!^@(.+)!) {
+ open LIST, "<$1" or die "Error: can't open `$1' - $!\n";
+ chop(@ARGV = <LIST>);
+ map {m!\S+! and $_ = $&} @ARGV;
+}
+
+for (@ARGV) {
+ if (m!(.+)\.(pfb|pfa|ttf|otf)!) {
+ ($base, $ext) = ($1, $2);
+ }
+ else {
+ warn "Warning: unrecognised font file `$_' skipped\n";
+ next;
+ }
+
+ next if -e "${base}.afm" and !$options{f};
+
+ if ($ext =~ m!pf(?:a|b)!) {
+ if (-e "${base}.pfm") {
+ system "pfm2kpx '${base}.pfm'";
+ }
+ else {
+ system "pf2afm '$_'";
+ }
+ if (-e "${base}.otf") {
+ system "ot2kpx '${base}.otf' >'${base}.kpx'";
+ open AFM, "<${base}.afm" or
+ die "Error: can't open `${base}.afm' - $!\n";
+ open KPX, "<${base}.kpx" or
+ warn("Warning: can't open `${base}.kpx' - $!\n"), next;
+ {
+ local $/;
+ $afm = <AFM>;
+ $kpx = <KPX>;
+ }
+ close AFM;
+ open AFM, ">${base}.afm" or
+ die "Error: can't create `${base}.afm' - $!\n";
+
+ if ($afm =~ m!^(.+?)(?:StartKernData.+EndKernData\n)?
+ EndFontMetrics!sx)
+ {
+ print AFM "$1${kpx}EndFontMetrics\n";
+ }
+ else {
+ warn "Warning: `${base}.afm' seems rotten - please check\n";
+ print AFM $afm;
+ }
+ unlink "${base}.kpx";
+ }
+ }
+ elsif ($ext =~ m!ttf!) {
+ system "ttf2afm -o '${base}.afm' '$_'";
+ system "ot2kpx '${base}.ttf' >'${base}.kpx'";
+ open AFM, "<${base}.afm" or
+ die "Error: can't open `${base}.afm' - $!\n";
+ open KPX, "<${base}.kpx" or
+ warn("Warning: can't open `${base}.kpx' - $!\n"), next;
+ {
+ local $/;
+ $afm = <AFM>;
+ $kpx = <KPX>;
+ }
+ close AFM;
+ open AFM, ">${base}.afm" or
+ die "Error: can't create `${base}.afm' - $!\n";
+
+ if ($afm =~ m!^(.+?)(?:StartKernData.+EndKernData\n)?
+ EndFontMetrics!sx)
+ {
+ print AFM "$1${kpx}EndFontMetrics\n";
+ }
+ else {
+ warn "Warning: `${base}.afm' seems rotten - please check\n";
+ print AFM $afm;
+ }
+ unlink "${base}.kpx";
+ }
+ elsif ($ext =~ m!otf!) {
+ system "cfftot1 -b -o ${base}.pfb $_";
+ $_ = $base . ".pfb";
+ redo;
+ }
+ else {
+ die "Error: you've just found a bug in `$0' - congratulations!\n";
+ }
+}
+
+__END__
diff --git a/Build/source/texk/texlive/linked_scripts/fragmaster/fragmaster.pl b/Build/source/texk/texlive/linked_scripts/fragmaster/fragmaster.pl
index efb4c9aa892..35606bcbe08 100755
--- a/Build/source/texk/texlive/linked_scripts/fragmaster/fragmaster.pl
+++ b/Build/source/texk/texlive/linked_scripts/fragmaster/fragmaster.pl
@@ -1,13 +1,16 @@
#!/usr/bin/perl -w
######################################################################
-# $Id: fragmaster.pl,v 1.3 2006/09/26 08:59:30 tvogel Exp $
#
# fragmaster.pl
+#
# creates EPS and PDF graphics from source EPS and control files
# with \psfrag commands
#
+# Version: 1.6
+#
# Copyright (C) 2004 Tilman Vogel <tilman vogel web de> (dot at dot)
+# Copyright (C) 2011 Agustin Martin <agustin martin hispalinux es> (dot at dot)
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -19,139 +22,274 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-#
-# IMPORTANT: ALLOW DVIPS TO MAKE _PORTRAIT_ PS WITH WIDTH > HEIGHT
-# BY ADDING
-#
-# @ custom 0pt 0pt
-#
-# TO YOUR /usr/share/texmf/dvips/config/config.pdf
-# IF THIS ENTRY IS MISSING, DVIPS WILL GUESS ORIENTATION FROM
-# WIDTH / HEIGHT RATIO. THIS STILL CAN HAPPEN IN CASE YOUR INPUT EPS
-# MATCHES A STANDARD PAPER SIZE!
-#
-# Source files:
-# <graphics>_fm.eps
-# a source EPS file
-# <graphics>_fm
-# a control file containing \psfrag commands and optionally
-# special comments:
-# % fmclass: <class>
-# use <class> instead of "article"
-# % fmclassopt: <options>
-# use <options> as class options instead of "12pt"
-# % head:
-# % <texcode>
-# % end head
-# causes <texcode> to be put into the preamble
-# % fmopt: <option>
-# causes <option> to be given to \includegraphics as
-# optional parameter
-#
-# fragmaster.pl scans the current directory for files matching the
-# pattern "*_fm" and "*_fm.eps" and converts them to the respective
-# ".eps"- and ".pdf"-files if they are outdated.
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
-# Credits:
-#
-# This script was inspired by a posting from
-# Karsten Roemke <k roemke gmx de> (dot at dot)
-# with subject
-# "psfrag pdflatex, lange her"
-# in de.comp.text.tex on 2003-11-11 05:25:44 PST.
-#
-# Karsten Roemke was inspired for his solution by postings from
-# Thomas Wimmer.
+# See pod section at the end of this script for more information
+# ----------------------------------------------------------------------------
+
+use strict;
+use Cwd;
+use File::Temp qw(tempdir);
+use Pod::Usage;
+
+my $force;
+my $debug;
+my $global_dirfm_mtime;
+my $global_dirfm_file = "fragmaster.dfm";
+my $fragmaster_status = "fragmaster.sts";
+my $global_fmclass = "article";
+my $global_fmclassopt = "12pt";
+my $global_fmopt = "";
+my @global_fmhead = ();
+my @global_fmfile = ();
+my %dirdata_last_status = ();
-chomp($cwd = `pwd`);
-die "Current path contains whitespace. I am sorry, but LaTeX cannot handle this correctly, move somewhere else. Stopped"
- if $cwd =~ /\s/;
+foreach my $option ( @ARGV ){
+ if ( $option eq "--force" ){
+ $force++;
+ } elsif ( $option eq "--debug" ){
+ print STDERR "Debug enabled\n";
+ $debug++;
+ } elsif ( $option =~ s/^--dirfm(=|\#)// ) {
+ if ( -e $option ){
+ $global_dirfm_file = $option;
+ print STDERR "fragmaster: using \"$global_dirfm_file\" directory control file.\n"
+ if $debug;
+ } else {
+ die "fragmaster: Could not find given \"$option\" directory control file. Aborting ....\n";
+ }
+ } elsif ( $option eq "-h" || $option eq "--help" ) {
+ pod2usage(1);
+ exit 1;
+ } elsif ( $option eq "-m" || $option eq "--man" ) {
+ pod2usage(-exitstatus => 0, -verbose => 2);
+ exit 1;
+ } else {
+ print STDERR "\nfragmaster: Unsupported option \"$option\"\n\n";
+ pod2usage(1);
+ exit 1;
+ }
+}
+
+# Function definitions
-foreach $fm_file (<*_fm>) {
- ($base = $fm_file) =~ s/_fm$//;
- $source = "$fm_file.eps";
+# ----------------------------------------------------------------------------
+sub win32_which {
+# ----------------------------------------------------------------------------
+# Look if given basename matches a win32 executable under $PATH
+# ----------------------------------------------------------------------------
+ my $basename = shift;
+ my @extensions = (".exe");
- if(! -f $source) {
- print "Cannot find EPS file '$source' for fragmaster file '$fm_file'! Skipped.\n";
- next;
+ foreach my $path ( '.', split(';',$ENV{'PATH'}) ){
+ foreach my $ext ( @extensions ){
+ my $exec_location = "$path\\$basename$ext";
+ if ( -x "$exec_location" ){
+ print STDERR "fragmaster: Found gs executable at \"$exec_location\"\n"
+ if $debug;
+ return $basename;
+ } else {
+ print STDERR "fragmaster: No gs executable found at \"$exec_location\"\n"
+ if $debug;
+ }
+ }
}
+}
+
+# ----------------------------------------------------------------------------
+sub parse_fmfile {
+# ----------------------------------------------------------------------------
+ my $file = shift;
+ my %fmdata = ();
+ my @fmfile = (); # A copy of original file
+ my @fmhead = (); # Preamble to use
+ my $fmopt; # Options to \includegraphics
+ my $fmclass; # Class to use
+ my $fmclassopt; # Options for class invocation
+
+ die "fragmaster::parse_fmfile: No file to parse passed as argument. Aborting ...\n"
+ unless $file;
+
+ open FMFILE, "<$file"
+ or die "fragmaster: Cannot read \"$file\"!. Aborting ...\n";
-
- $dest_eps = "$base.eps";
- $dest_pdf = "$base.pdf";
-
- $do_it = 0;
-
- $do_it = 1
- if ! -f $dest_eps;
- $do_it = 1
- if ! -f $dest_pdf;
-
- if(! $do_it) {
- $oldest_dest = -M $dest_eps;
- $oldest_dest = -M $dest_pdf
- if -M $dest_pdf > $oldest_dest;
-
- $youngest_source = -M $fm_file;
- $youngest_source = -M $source
- if -M $source < $youngest_source;
- $do_it = 1
- if $oldest_dest > $youngest_source;
+ while (<FMFILE>) {
+ chomp;
+ push @fmfile, " $_%";
+ s/\s+$//; # Strip trailing whitespace
+ $fmopt = $1 if /fmopt:\s*(.*)/;
+ $fmclass = $1 if /fmclass:\s*(.*)/;
+ $fmclassopt = $1 if /fmclassopt:\s*(.*)/;
+ if (/head:/) {
+ while(<FMFILE>) {
+ chomp;
+ push @fmfile, " $_%";
+ last if /end head/;
+ # Remove comment prefix
+ s/^[\s%]*//;
+ push @fmhead, "$_%";
+ }
+ }
}
-
- if( $do_it ) {
- print "$fm_file, $source -> $dest_eps, $dest_pdf\n";
+ close FMFILE;
+ $fmdata{'fmfile'} = \@fmfile if scalar @fmfile;
+ $fmdata{'fmpreamble'} = \@fmhead if scalar @fmhead;
+ $fmdata{'fmclass'} = $fmclass if $fmclass;
+ $fmdata{'fmclassopt'} = $fmclassopt if $fmclassopt;
+ $fmdata{'fmopt'} = $fmopt if $fmopt;
+ print STDERR
+ "fragmaster::parse_fmfile: Keys found in \"$file\": ",
+ join(', ',sort keys %fmdata ), "\n"
+ if $debug;
+ return \%fmdata;
+}
- open FMFILE, "<$fm_file"
- or die "Cannot read $fm_file!";
+# ----------------------------------------------------------------------------
- $tempdir = `mktemp -d fm-$base.XXXXXX`
- or die "Cannot make temporary directory!";
+my $cwd = getcwd;
- chomp($tempdir);
+die "fragmaster: Current path contains whitespace. LaTeX cannot handle this correctly, move somewhere else. Aborting ... \n"
+ if $cwd =~ /\s/;
- print "using $tempdir/\n";
+# Get modification time for $global_dirfm_file if exists. Also used as existence flag.
+$global_dirfm_mtime = -M $global_dirfm_file if ( -e $global_dirfm_file );
- open TEXFILE, ">$tempdir/fm.tex"
- or die "Cannot write LaTeX file!";
+# Look for fragmaster _fm files.
+my %all_basenames = ();
+foreach (<*_fm>){
+ s/_fm$//;
+ $all_basenames{$_}++;
+}
+die "fragmaster: No fm_files found. Aborting ...\n"
+ unless ( scalar %all_basenames );
- $fmopt = "";
- @fmfile = ();
- @fmhead = ();
- $fmclass = "article";
- $fmclassopt = "12pt";
- while (<FMFILE>) {
- chomp;
- $fmopt = $1 if /fmopt:(.*)/;
- $fmclass = $1 if /fmclass:(.*)/;
- $fmclassopt = $1 if /fmclassopt:(.*)/;
- if (/head:/) {
- push @fmfile, " $_%\n";
- while(<FMFILE>) {
- chomp;
- last if /end head/;
- push @fmfile, " $_%\n";
- # Remove comment prefix
- s/^[\s%]*//;
- push @fmhead, "$_%\n";
+# Check if there is a status file for last processing with dir control file
+if ( -e $fragmaster_status ){
+ open my $LAST, "< $fragmaster_status"
+ or die "fragmaster: Could not open \"$fragmaster_status\" for read. Aborting ...\n";
+ while ( <$LAST> ){
+ next if m/^\s*\#/;
+ next if m/^\s*~/;
+ chomp;
+ if ( m/^([^:]+):(.*)$/ ) {
+ $dirdata_last_status{$1} = $2;
+ print STDERR "fragmaster: Last dfm used for \"$1\": \"$dirdata_last_status{$1}\"\n"
+ if $debug;
+ } else {
+ print STDERR "fragmaster: Wrongly formatted line in \"$fragmaster_status\": \"$_\". Ignoring ...\n";
+ }
+ }
+ close $LAST;
+}
+
+# Remove no longer present files from status file
+foreach my $base ( sort keys %dirdata_last_status ){
+ delete $dirdata_last_status{$base} unless ( defined $all_basenames{$base} );
+}
+
+# For each _fm file found, check if we need to rebuild
+my %doit_basenames = ();
+foreach my $base ( sort keys %all_basenames ) {
+ my $fm_file = "$base" . "_fm";
+ my $source = "$fm_file.eps";
+
+ if ( -f $source ) {
+ my $dest_eps = "$base.eps";
+ my $dest_pdf = "$base.pdf";
+ my $do_it = $force;
+
+ unless ( $do_it ) {
+ # Rebuild if there is any mismatch betwen last and current per-dir file.
+ if ( $global_dirfm_mtime ){
+ if ( defined $dirdata_last_status{$base} ){
+ $do_it++ unless ( $global_dirfm_file eq $dirdata_last_status{$base} );
+ } else {
+ $do_it++;
}
+ } elsif ( defined $dirdata_last_status{$base} ){
+ $do_it++;
+ }
+ }
+
+ unless ( $do_it ) {
+ if ( -f $dest_eps && -f $dest_pdf ){
+ # Sort after modification times. Redo if any source is newer than any dest.
+ my @dest_stamps = (-M $dest_eps, -M $dest_pdf);
+ my @sources_stamps = (-M $fm_file, -M $source );
+ push @sources_stamps, $global_dirfm_mtime if $global_dirfm_mtime;
+ my $oldest_dest = (sort { $b <=> $a } @dest_stamps)[0];
+ my $youngest_source = (sort { $a <=> $b } @sources_stamps)[0];
+ $do_it++ if $oldest_dest > $youngest_source;
+ } else {
+ $do_it++;
}
-
- push @fmfile, " $_%\n";
}
- print TEXFILE <<"EOF";
-\\documentclass[$fmclassopt]{$fmclass}
+ $doit_basenames{$base}++ if $do_it;
+ } else {
+ print "fragmaster: \"$fm_file\" skipped. Cannot find associated EPS file \"$source\".\n";
+ }
+}
+
+# Do nothing if targets are up to date and --force was not set
+unless ( scalar %doit_basenames ){
+ print STDERR "fragmaster: all targets are up-to-date.\n";
+ exit;
+}
+
+# Create master tempdir for the temporary files
+my $base_tempdir =
+ tempdir( "fragmaster.XXXXXX", # Use a fragmaster prefix for base tempdir
+ CLEANUP => ( ! $debug ), # Remove tempdir tree on exit unless $debug
+ DIR => $cwd ) # Create base tempdir under current dir
+ or die "fragmaster: Cannot make temporary directory!";
+print "fragmaster: Using $base_tempdir/\n";
+
+# Check which name to use for ghostscript.
+my $gs_prog = "gs";
+
+if ( $^O =~ /^MSWin/ ){
+ $gs_prog = win32_which("gswin32c") # Try first standard name
+ || win32_which("mgs") # Be kind with miktex
+ || "gswin32c"; # May be a wrapper with different extension. Try.
+}
+
+# If we have a per-directory control file with values set, use them to override defaults.
+if ( -e $global_dirfm_file ){
+ my $fmdata = parse_fmfile($global_dirfm_file);
+ $global_fmclass = $fmdata->{'fmclass'} if ( defined $fmdata->{'fmclass'} );
+ $global_fmclassopt = $fmdata->{'fmclassopt'} if ( defined $fmdata->{'fmclassopt'} );
+ $global_fmopt = $fmdata->{'fmopt'} if ( defined $fmdata->{'fmopt'} );
+ @global_fmhead = @{$fmdata->{'fmpreamble'}} if ( defined $fmdata->{'fmpreamble'} );
+}
+
+# (Re-)build targets
+foreach my $base ( sort keys %doit_basenames ) {
+ my $fm_file = "$base" . "_fm";
+ my $source = "$fm_file.eps";
+ my $dest_eps = "$base.eps";
+ my $dest_pdf = "$base.pdf";
+
+ print "fragmaster: $fm_file, $source -> $dest_eps, $dest_pdf\n";
+
+ # Parse per-graphic _fm control file
+ my $fmdata = parse_fmfile($fm_file);
+ my $fmclass = ( defined $fmdata->{'fmclass'} ) ? $fmdata->{'fmclass'} : $global_fmclass;
+ my $fmclassopt = ( defined $fmdata->{'fmclassopt'} ) ? $fmdata->{'fmclassopt'} : $global_fmclassopt;
+ my $fmopt = ( defined $fmdata->{'fmopt'} ) ? $fmdata->{'fmopt'} : $global_fmopt;
+ my @fmhead = ( defined $fmdata->{'fmpreamble'} ) ? @{$fmdata->{'fmpreamble'}} : @global_fmhead;
+ my @fmfile = ( defined $fmdata->{'fmfile'} ) ? @{$fmdata->{'fmfile'}} : @global_fmfile;
+
+ my $texstring = "\\documentclass[$fmclassopt]{$fmclass}
\\usepackage{graphicx,psfrag,color}
-\\usepackage{german}
-EOF
- print TEXFILE
- foreach(@fmhead);
- print TEXFILE <<'EOF';
+";
+ # Enter heading info
+ $texstring .= "$_\n" foreach (@fmhead);
+
+ $texstring .= '
\setlength{\topmargin}{-1in}
\setlength{\headheight}{0pt}
\setlength{\headsep}{0pt}
@@ -166,94 +304,266 @@ EOF
%\special{! statusdict /setpageparams undef }
\pagestyle{empty}
\newsavebox{\pict}
-EOF
- print TEXFILE "\\graphicspath{{../}}\n";
- print TEXFILE <<'EOF';
+\graphicspath{{../../}}
+
\begin{document}
\begin{lrbox}{\pict}%
-EOF
-
- print TEXFILE
- foreach (@fmfile);
-
- print TEXFILE " \\includegraphics[$fmopt]{$source}%\n";
- print TEXFILE <<'EOF';
- \end{lrbox}
+';
+ $texstring .= "$_\n" foreach (@fmfile);
+ $texstring .= " \\includegraphics[$fmopt]{$source}%\n";
+ $texstring .= ' \end{lrbox}
\special{papersize=\the\wd\pict,\the\ht\pict}
\usebox{\pict}
\end{document}
-EOF
-
- close TEXFILE;
+';
+
+ my $tempdir = "$base_tempdir/$base";
+ mkdir $tempdir;
- chdir($tempdir)
- or die "Cannot chdir to $tempdir!";
+ # Writing TeX file
+ open TEXFILE, ">$tempdir/fm.tex"
+ or die "fragmaster: Cannot write LaTeX file!";
+ print TEXFILE $texstring;
+ close TEXFILE;
- system("latex fm.tex") / 256 == 0
- or die "Cannot latex fm.tex!";
+ chdir($tempdir)
+ or die "fragmaster: Cannot chdir to \"$tempdir\"!. Aborting ...\n";
- # Using -E here, causes dvips to detect
- # the psfrag phantom stuff and to set the BoundingBox wrong
- system("dvips -E -P pdf fm.dvi -o fm.ps") / 256 == 0
- or die "Cannot dvips!";
-
- chdir("..")
- or die "Cannot chdir back up!";
+ system("latex fm.tex") == 0
+ or die "fragmaster: Cannot latex \"fm.tex\"!. Aborting ...\n";
+ # Using -E here, causes dvips to detect
+ # the psfrag phantom stuff and to set the BoundingBox wrong
+ system("dvips -E -P pdf fm.dvi -o fm.ps") == 0
+ or die "fragmaster: Cannot dvips!. Aborting ...\n";
+
+ chdir($cwd)
+ or die "fragmaster: Cannot chdir back up!. Aborting ...\n";
+
+ # Read original eps file as created by dvips -E.
+ my $psfile;
+ {
+ local $/ = undef;
open PS, "<$tempdir/fm.ps"
- or die "Cannot read fm.ps!";
-
- open EPS, ">$dest_eps"
- or die "Cannot write $dest_eps!";
-
- # Correct the bounding box by setting the left margin to 0
- # top margin to top of letterpaper!
- # (I hope that is general enough...)
- $saw_bounding_box = 0;
- while(<PS>) {
- if(! $saw_bounding_box) {
- # if(s/^\%\%BoundingBox:\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)/\%\%BoundingBox: 0 $2 $3 $4/) {
- if(s/^\%\%BoundingBox:\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)/\%\%BoundingBox: 0 $2 $3 792/) {
- $saw_bounding_box = 1;
- }
- }
- print EPS;
- }
-
- # Not using -E above causes
- # papersizes to be included into the PS
- # Strip off the specifications.
- # Otherwise gv doesn't show the BBox
- # and epstopdf won't detect the correct
- # PDF media size!
-
- # while(<PS>) {
- # s/^%!PS-Adobe.*/%!PS-Adobe-3.0 EPSF-3.0/;
-
- # next if /^\%\%DocumentPaperSizes:/;
- # if(/^\%\%BeginPaperSize:/) {
- # while(<PS>) {
- # last if /^\%\%EndPaperSize/;
- # }
- # next;
- # }
- # s/statusdict \/setpage known/false/;
- # s/statusdict \/setpageparams known/false/;
- # print EPS;
- # }
-
- close EPS;
+ or die "fragmaster: Cannot read \"fm.ps\"!. Aborting ...\n";
+ $psfile = <PS>;
close PS;
+ }
+
+ # Calculate real Bounding Box using ghostscript bbox device, also available
+ # in strippped-down texlive gs. Currently, it writes result only to STDERR.
+ my $bbox_line;
+ my $bbox_cmd = "$gs_prog -q -sDEVICE#bbox -dNOPAUSE -dBATCH \"$tempdir/fm.ps\" 2> \"$tempdir/fm.bb\"";
+ print STDERR "fragmaster: $bbox_cmd\n" if $debug;
+ system("$bbox_cmd") == 0
+ or die "fragmaster: Failed to obtain Bounding Box file with ghostscript bbox device.\n";
+ open BB,"<$tempdir/fm.bb"
+ or die "fragmaster: Could not read Bounding Box file \"$tempdir/fm.bb\". Aborting ...\n";
+ while (<BB>) {
+ if ( /^\%\%BoundingBox:/ ){
+ chomp;
+ $bbox_line = $_;
+ last;
+ }
+ }
+ close BB;
+ die "fragmaster: No Bounding Box entry found in \"$tempdir/fm.bb\". Aborting ...\n"
+ unless $bbox_line;
+ print STDERR "fragmaster: $bbox_line\n"
+ if $debug;
- system("epstopdf $dest_eps --outfile=$dest_pdf") / 256 == 0
- or die "Cannot epstopdf!";
+ # Fix Bounding Box and write corrected eps file.
+ $psfile =~ s/^\%\%BoundingBox:.*/$bbox_line/m;
+ open EPS, ">$dest_eps"
+ or die "fragmaster: Cannot write \"$dest_eps\"!. Aborting ...\n";
+ print EPS $psfile;
+ close EPS;
- system("rm -rf $tempdir") / 256 == 0
- or die "Cannot remove $tempdir!";
+ # Create pdf file after corrected ps file.
+ system("epstopdf $dest_eps --outfile=$dest_pdf") == 0
+ or die "fragmaster: Cannot epstopdf!. Aborting ...\n";
- close FMFILE;
+ # Update per-dir info in status file if everything was OK. We need to do this
+ # after processing each entry. Otherwise an error may leave things inconsistent.
+ if ( $global_dirfm_mtime ){
+ $dirdata_last_status{$base} = $global_dirfm_file; # Mark per-dir file used
+ } else {
+ delete $dirdata_last_status{$base}; # or leave empty otherwise
+ }
-
-
+ print STDERR
+ "fragmaster: \"$base\" processed, current files with dfm: \n [" ,
+ join(', ',sort keys %dirdata_last_status),
+ "]\n"
+ if $debug;
+ open my $LAST, "> $fragmaster_status"
+ or die "fragmaster: Could not open \"$fragmaster_status\" for write. Aborting ...\n";
+
+ print $LAST "\# This file is automatically generated by fragmaster. Do not edit.\n";
+ foreach ( sort keys %dirdata_last_status ){
+ print $LAST "$_:$dirdata_last_status{$_}\n";
}
+ close $LAST;
}
+
+__END__
+
+=pod
+
+=head1 NAME
+
+fragmaster - Using psfrag constructs with pdflatex
+
+=head1 SYNOPSIS
+
+ fragmaster [options]
+
+ Create EPS and PDF files with embedded psfrag substitutions.
+
+ Options:
+ -h,--help Brief usage guide.
+ -m,--man Show full man page (needs perldoc)
+ --debug Show more info and leave temporary files behind.
+ --force Rebuild everything ignoring modification times.
+ --dirfm=file Use given file as optional per-directory
+ fragmaster fm control file instead of default
+ "fragmaster.dfm".
+
+=head1 DESCRIPTION
+
+B<fragmaster> is a perl script that helps using psfrag constructs with
+B<pdflatex>.
+
+B<psfrag> is a LaTeX package which allows to replace text elements in
+included EPS graphics by arbitrary LaTeX output. Because B<psfrag>
+uses Postscript for making the replacements, in principle you can't
+use B<psfrag> with B<pdflatex> which doesn't have any interfaces to
+postscript.
+
+B<fragmaster> produces a new EPS from your original EPS which already
+contains all those B<psfrag> replacements. This new EPS graphic
+actually can be converted to PDF including all replacements. The
+resulting "encapsulated" PDF can then be used with pdflatex.
+
+B<fragmaster> will scan the current directory for files which end in
+F<_fm> and have a F<_fm.eps> counterpart.
+Looking at the modification dates, the script checks if the output
+files have to be rebuilt and does so if necessary
+(a little like "make" would do it).
+
+In your LaTeX document you can include the produced graphics using
+
+ \includegraphics{<graphics>}
+
+conveniently omitting file extension.
+B<latex> will choose the EPS, B<pdflatex> will choose the PDF.
+
+=head2 B<fragmaster> control file and other related files.
+
+To use the script you have to create two files per graphic:
+
+ * <graphics>_fm.eps: the EPS file itself,
+ * <graphics>_fm: a fragmaster control file.
+
+From these files the psfragged graphics will be created:
+
+ * <graphics>.eps,
+ * <graphics>.pdf
+
+The F<_fm> control file is basically a LaTeX file
+(with optionally special comments) and can look like this:
+
+ % Just an ordinary comment
+ %
+ % Some special comments:
+ % fmclass: book
+ % fmclassopt: 11pt
+ % fmopt: width=6cm
+ %
+ % Another special comment:
+ % head:
+ % \usepackage{amsmath}
+ % end head
+
+ % psfrag commands:
+ \psfrag{x}{$x$}
+ \psfrag{y}{$y = x^2$}
+
+Special comment C<fmclass:> will make the script use given class
+instead of default C<article> class.
+
+Special comment C<fmclassopt:> will make the script use given options
+as class options instead of default C<12pt>.
+
+The special comment C<fmopt:> will be evaluated such that the
+following text will by passed as optional argument to
+C<\includegraphics>.
+This way you can e.g. adjust the relation between graphics size
+and font size using something like C<fmopt: width=6cm>.
+No global default for this.
+
+The special comment construct C<head:/end head> causes the lines in
+between to be included in the preamble of the LaTeX temporary document
+after having the leading comment characters "%" stripped off.
+This way, you can include LaTeX packages, as in C<\usepackage{amsmath}>.
+No global default for this.
+
+=head2 Per-directory B<fragmaster> dir control file.
+
+You can set per-directory C<fmclass:>, C<fmclassopt:>, C<fmopt:> and
+C<head:/end head> options by means of a per-directory fragmaster control
+file F<fragmaster.dfm> with similar syntax as above.
+You can use another file by means of the B<--dirfm> option.
+Note that options set this way are mutually exclusive, any option set
+in per-file F<_fm> file will completely override associated option in
+per-directory file, and options set in per-directory file will
+override initial defaults (C<\documentclass[12pt]{article}>).
+Empty options are ignored.
+
+This is work in progress and still needs extensive checking. Double-check
+that modification date based rebuilds are working properly.
+
+=head1 KNOWN PROBLEMS
+
+In case the EPS will be produced as landscape graphics, i.e. B<gv> shows
+I<Landscape> instead of I<Portrait> in the menu bar, and the graphic will
+end up turned around 90 degrees in your document, then it is likely
+that your original EPS is wider than it is tall.
+In this case some (more recent) versions of B<dvips> make the "smart"
+assumption that your graphic is landscape, even though the graphic's
+proportions don't tell anything about the orientation of its contents...
+This still can happen in case your input EPS matches a standard paper size.
+
+Anyway, you can make B<dvips> behave nicer by specifying the following
+line in F</usr/share/texmf/dvips/config/config.pdf>
+(or a local equivalent inside F</usr/local/share/texmf>):
+
+@ custom 0pt 0pt
+
+In the likely case that you're wondering why, I'd recommend the
+B<dvipsk> sources warmly to you...
+
+=head1 AUTHOR
+
+Tilman Vogel <tilman vogel web de> (dot at dot) and
+Agustin Martin <agustin martin hispalinux es> (dot at dot)
+
+=head1 HISTORY
+
+This script was inspired by a posting from
+Karsten Roemke <k roemke gmx de> (dot at dot) with subject
+"psfrag pdflatex, lange her"
+in de.comp.text.tex on 2003-11-11 05:25:44 PST.
+
+Karsten Roemke was inspired for his solution by postings from
+Thomas Wimmer.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 2004 Tilman Vogel
+ Copyright (C) 2011 Agustin Martin
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
diff --git a/Build/source/texk/texlive/linked_scripts/glossaries/makeglossaries b/Build/source/texk/texlive/linked_scripts/glossaries/makeglossaries
index 3b9f1cb6b48..fcb3dfef031 100755
--- a/Build/source/texk/texlive/linked_scripts/glossaries/makeglossaries
+++ b/Build/source/texk/texlive/linked_scripts/glossaries/makeglossaries
@@ -2,8 +2,8 @@
# File : makeglossaries
# Author : Nicola Talbot
-# Version : 1.4 (2008/05/10)
-# Description: simple Perl script that calls makeindex.
+# Version : 1.9 (2010/06/14)
+# Description: simple Perl script that calls makeindex or xindy.
# Intended for use with "glossaries.sty" (saves having to remember
# all the various switches)
@@ -11,7 +11,7 @@
# Copyright 2007 Nicola L.C. Talbot
# This work may be distributed and/or modified under the
# conditions of the LaTeX Project Public License, either version 1.3
-# of this license of (at your option) any later version.
+# of this license or any later version.
# The latest version of this license is in
# http://www.latex-project.org/lppl.txt
# and version 1.3 or later is part of all distributions of LaTeX
@@ -26,8 +26,29 @@
# glossary-hypernav.sty, glossary-list.sty, glossary-long.sty,
# glossary-super.sty, glossaries.perl.
# Also makeglossaries and makeglossaries.
-#
+
+my $version="2.0 (2010-06-30)";
+
# History:
+# v2.0 (2010-06-30) :
+# * Made file handle local in &scan_aux
+# v1.9 (2010-06-14) :
+# * Check for \@input
+# v1.8 (2009-11-03) :
+# * Create an empty output file if the input file is empty
+# without calling xindy/makeindex
+# v1.7 (2009-09-23) :
+# * Issue warning rather than error when empty/non existant file
+# checks fail
+# v1.6 (2009-05-24) :
+# * main glossary no longer automatically added
+# (only added if information in aux file)
+# * if file extension is specified, check added to ensure it
+# corresponds to a known glossary extension.
+# * added file existance test and file empty test
+# v1.5 (2008-12-26) :
+# * added support for xindy
+# * picks up ordering information from aux file
# v1.4 (2008-05-10) :
# * added support for filenames with spaces.
# v1.3 (2008-03-08) :
@@ -44,15 +65,17 @@
use Getopt::Std;
use strict;
+use vars qw($opt_q $opt_t $opt_o $opt_s $opt_p $opt_g $opt_c $opt_r
+ $opt_l $opt_i $opt_L $opt_n $opt_C);
-$Getopt::Std::STANDARD_HELP_VERSION = 1;
-
-my $version="1.3 (2008-03-08)";
-my($opt_q, $opt_t, $opt_o, $opt_s, $opt_p, $opt_g, $opt_c, $opt_r,
- $opt_l, $opt_i)=("","","","","","","","","","");
+$Getopt::Std::STANDARD_HELP_VERSION = 1;
-getopts('s:o:t:p:ilqrcg');
+# v1.5 added -L <lang> for xindy (but language can be specified in
+# .tex file)
+# v1.5 added -n (print the command that would be issued but
+# don't actually run the command)
+getopts('s:o:t:p:L:C:ilqrcgn');
unless ($#ARGV == 0)
{
@@ -61,10 +84,18 @@ unless ($#ARGV == 0)
# define known extensions
+# v1.6: removed adding main glossary here as there's no guarantee
+# that it's been used. If it has been used, the information will
+# be picked up later in the aux file
my %exttype = (
- main => {in=>'glo', out=>'gls', 'log'=>'glg'},
+# main => {in=>'glo', out=>'gls', 'log'=>'glg'},
);
+# v1.5 define require languages for xindy
+
+my %language = ();
+my %codepage = ();
+
my $ext = '';
my $name = $ARGV[0];
@@ -85,42 +116,33 @@ if (length($ARGV[0]) > 3 and substr($ARGV[0],-4,1) eq ".")
}
}
-my $istfile = "$name.ist";
+my $istfile = "";
+
+# should letter ordering be used? (v1.5 added)
+
+my $letterordering = defined($opt_l);
# check aux file for other glossary types
# and for ist file name
-if (open AUXFILE, "$name.aux")
+&scan_aux($name);
+
+# has the style file been specified?
+unless ($istfile)
{
- while (<AUXFILE>)
- {
- if (m/\\\@newglossary\s*\{(.*)\}{(.*)}{(.*)}{(.*)}/
- and ($1 ne 'main'))
- {
- $exttype{$1}{'log'} = $2;
- $exttype{$1}{'out'} = $3;
- $exttype{$1}{'in'} = $4;
+ die "No \\\@istfilename found in '$name.aux'.\n",
+ "Did your LaTeX run fail?\n",
+ "Did your LaTeX run produce any output?\n",
+ "Did you remember to use \\makeglossaries?\n";
+}
- if (!$opt_q)
- {
- print "added glossary type '$1' ($2,$3,$4)\n";
- }
- }
+# v1.5 save the general xindy switches
- if (m/\\\@istfilename\s*{([^}]*)}/)
- {
- $istfile = $1;
+my $xdyopts = '';
- # check if double quotes were added to \jobname
- $istfile=~s/^"(.*)"\.ist$/$1.ist/;
- }
- }
-
- close AUXFILE;
-}
-else
+unless ($opt_L eq "")
{
- print STDERR "Unable to open $name.aux: $!\n";
+ $xdyopts .= " -L $opt_L";
}
# save all the general makeindex switches
@@ -132,14 +154,16 @@ if ($opt_i)
$mkidxopts .= " -i";
}
-if ($opt_l)
+if ($letterordering)
{
$mkidxopts .= " -l";
+ $xdyopts .= " -M ord/letorder";
}
if ($opt_q)
{
$mkidxopts .= " -q";
+ $xdyopts .= " -q";
}
if ($opt_r)
@@ -167,9 +191,20 @@ unless ($opt_s eq "")
$istfile = $opt_s;
}
+# Use xindy if style file ends with .xdy otherwise use makeindex
+
+my $usexindy = $istfile=~m/\.xdy\Z/;
+
if ($ext ne '')
{
- my %thistype = %{$exttype{'main'}}; #default
+ # an extension has been specified, so only process
+ # the specified file
+
+ # v1.6 %thistype is no longer given a default value
+ my %thistype;
+
+ my $thislang = "";
+ my $thiscodepage = "";
foreach my $type (keys %exttype)
{
@@ -177,10 +212,24 @@ if ($ext ne '')
{
%thistype = %{$exttype{$type}};
+ $thislang = $language{$type};
+
+ $thiscodepage = $codepage{$type};
+
last;
}
}
+ # v1.6 If %thistype hasn't been defined, then the given
+ # extension doesn't correspond to any known glossary type
+
+ unless (defined %thistype)
+ {
+ die "The file extension '$ext' doesn't correspond to any\n",
+ "known glossary extension. Try running makeglossaries\n",
+ "without an extension, e.g. makeglossaries \"$name\".\n";
+ }
+
my $outfile;
if ($opt_o eq "")
@@ -203,53 +252,186 @@ if ($ext ne '')
$transcript = $opt_t;
}
- &makeindex("$name.$ext",$outfile,$transcript,$istfile,
- $mkidxopts,$opt_q);
+ if ($usexindy)
+ {
+ &xindy("$name.$ext", $outfile, $transcript,$istfile,
+ $thislang, $thiscodepage, $xdyopts, $opt_q, $opt_n);
+ }
+ else
+ {
+ &makeindex("$name.$ext",$outfile,$transcript,$istfile,
+ $mkidxopts,$opt_q, $opt_n);
+ }
}
else
{
+ # no file extension specified so process all glossary types
+
foreach my $type (keys %exttype)
{
my %thistype = %{$exttype{$type}};
my $inputfile = "$name.$thistype{in}";
- if (-r $inputfile)
+ my $outfile;
+
+ if ($opt_o eq "")
{
- my $outfile;
+ $outfile = "$name.$thistype{out}";
+ }
+ else
+ {
+ $outfile = $opt_o;
+ }
- if ($opt_o eq "")
- {
- $outfile = "$name.$thistype{out}";
- }
- else
- {
- $outfile = $opt_o;
- }
+ # v1.7 print warnings to STDOUT instead of STDERR
- my $transcript;
+ # v1.6 added file existance test
+ unless (-e $inputfile)
+ {
+ print "Warning: File '$inputfile' doesn't exist.\n",
+ "*** Skipping glossary '$type'. ***\n";
+ next;
+ }
- if ($opt_t eq "")
+ unless (-r $inputfile)
+ {
+ print "Warning: No read access for '$inputfile' $!\n",
+ "*** Skipping glossary '$type'. ***\n";
+ next;
+ }
+
+ # v1.6 added file empty test
+ if (-z $inputfile)
+ {
+ print "Warning: File '$inputfile' is empty.\n",
+ "Have you used any entries defined in glossary '$type'?\n";
+
+ # create an empty output file and move on to the next glossary
+
+ if (open OFD, ">$outfile")
{
- $transcript = "$name.$thistype{'log'}";
+ print OFD "\\null\n";
+ close OFD;
}
else
{
- $transcript = $opt_t;
+ print STDERR "Unable to create '$outfile' $!\n";
}
- &makeindex($inputfile,$outfile,$transcript,
- $istfile,$mkidxopts,$opt_q);
+ next;
+ }
+
+ my $transcript;
+
+ if ($opt_t eq "")
+ {
+ $transcript = "$name.$thistype{'log'}";
}
else
{
- print STDERR "No read access for '$inputfile': $!\n";
+ $transcript = $opt_t;
+ }
+
+ if ($usexindy)
+ {
+ &xindy($inputfile,$outfile,$transcript,$istfile,
+ $language{$type},$codepage{$type},
+ $xdyopts,$opt_q,$opt_n);
+ }
+ else
+ {
+ &makeindex($inputfile,$outfile,$transcript,
+ $istfile,$mkidxopts,$opt_q,$opt_n);
}
}
}
+sub scan_aux{
+ my $name = shift;
+
+ # v2.0 added
+ local(*AUXFILE);
+
+ if (open AUXFILE, "$name.aux")
+ {
+ while (<AUXFILE>)
+ {
+ # v1.9 added
+ if (m/\\\@input{(.+)\.aux}/)
+ {
+ &scan_aux($1);
+ }
+
+ if (m/\\\@newglossary\s*\{(.*)\}{(.*)}{(.*)}{(.*)}/)
+ {
+ $exttype{$1}{'log'} = $2;
+ $exttype{$1}{'out'} = $3;
+ $exttype{$1}{'in'} = $4;
+
+ unless ($opt_q)
+ {
+ print "added glossary type '$1' ($2,$3,$4)\n";
+ }
+ }
+
+ if (m/\\\@istfilename\s*{([^}]*)}/)
+ {
+ $istfile = $1;
+
+ # check if double quotes were added to \jobname
+ $istfile=~s/^"(.*)"\.ist$/$1.ist/;
+ }
+
+ # v1.5 added
+ if (m/\\\@xdylanguage\s*{([^}]+)}{([^}]*)}/)
+ {
+ $language{$1} = $2;
+ }
+
+ # v1.5 added
+ if (m/\\\@gls\@codepage\s*{([^}]+)}{([^}]*)}/)
+ {
+ $codepage{$1} = $2;
+ }
+
+ # v1.5 added
+ # Allow -l switch to override specification in aux file
+ unless (defined($opt_l))
+ {
+ if (m/\\\@glsorder\s*{([^}]+)}/)
+ {
+ my $ordering = $1;
+
+ if ($ordering eq "word")
+ {
+ $letterordering = 0;
+ }
+ elsif ($ordering eq "letter")
+ {
+ $letterordering = 1;
+ }
+ else
+ {
+ print STDERR
+ "Unknown ordering '$letterordering'\n",
+ "Assuming word ordering\n";
+ $letterordering = 0;
+ }
+ }
+ }
+ }
+
+ close AUXFILE;
+ }
+ else
+ {
+ print STDERR "Unable to open $name.aux: $!\n";
+ }
+}
+
sub makeindex{
- my($in,$out,$trans,$ist,$rest,$quiet) = @_;
+ my($in,$out,$trans,$ist,$rest,$quiet,$dontexec) = @_;
my($name,$cmdstr,$buffer,$n,$i,$j);
my(@stuff,@item);
@@ -260,26 +442,321 @@ sub makeindex{
print "makeindex $cmdstr\n";
}
- `makeindex $cmdstr`;
+ unless ($dontexec)
+ {
+ open STATUS, "makeindex $cmdstr 2>&1 |"
+ or die "Can't fork: $!\n";
+
+ my $status = '';
+
+ while (<STATUS>)
+ {
+ $status .= $_;
+ }
+
+ close STATUS;
+
+ if ($?)
+ {
+ my $errno = $?;
+
+ if (open LOGFILE, ">>$trans")
+ {
+ print LOGFILE "\n$status";
+ close LOGFILE;
+ }
+ else
+ {
+ print STDERR "Unable to open '$trans' $!\n";
+ }
+
+ die "Call to makeindex failed (errno=$errno):\n", $status;
+ }
+
+ print $status;
+ }
+}
+
+sub xindy{
+ my($in,$out,$trans,$ist,$language,$codepage,$rest,$quiet,
+ $dontexec) = @_;
+ my($cmdstr, $langparam, $main);
+ my($module);
+
+ $module = $ist;
+ $module=~s/\.xdy\Z//;
+
+ # map babel names to xindy names
+ if ($language eq "frenchb")
+ {
+ $language = "french";
+ }
+ elsif ($language=~/^n?germanb?$/)
+ {
+ $language = "german";
+ }
+ elsif ($language eq "magyar")
+ {
+ $language = "hungarian";
+ }
+ elsif ($language eq "lsorbian")
+ {
+ $language = "lower-sorbian";
+ }
+ elsif ($language eq "norsk")
+ {
+ $language = "norwegian";
+ }
+ elsif ($language eq "portuges")
+ {
+ $language = "portuguese";
+ }
+ elsif ($language eq "russianb")
+ {
+ $language = "russian";
+ }
+ elsif ($language eq "slovene")
+ {
+ $language = "slovenian";
+ }
+ elsif ($language eq "ukraineb")
+ {
+ $language = "ukrainian";
+ }
+ elsif ($language eq "usorbian")
+ {
+ $language = "upper-sorbian";
+ }
+
+ if ($language)
+ {
+ $langparam = "-L $language";
+ }
+ else
+ {
+ $langparam = "";
+ }
+
+ # most languages work with xindy's default codepage, but
+ # some don't, so if the codepage isn't specific, check
+ # the known cases that will generate an error
+ # and supply a default. (For all other cases, it's up to the
+ # user to supply a codepage.)
+
+ if ($codepage eq '')
+ {
+ if ($language eq 'dutch')
+ {
+ $codepage = "ij-as-ij";
+ }
+ elsif ($language eq 'german')
+ {
+ $codepage = "din";
+ }
+ elsif ($language eq 'gypsy')
+ {
+ $codepage = "northrussian";
+ }
+ elsif ($language eq 'hausa')
+ {
+ $codepage = "utf";
+ }
+ elsif ($language eq 'klingon')
+ {
+ $codepage = "utf";
+ }
+ elsif ($language eq 'latin')
+ {
+ $codepage = "utf";
+ }
+ elsif ($language eq 'mongolian')
+ {
+ $codepage = "cyrillic";
+ }
+ elsif ($language eq 'slovak')
+ {
+ $codepage = "small";
+ }
+ elsif ($language eq 'spanish')
+ {
+ $codepage = "modern";
+ }
+ elsif ($language eq 'vietnamese')
+ {
+ $codepage = "utf";
+ }
+ }
+
+ my $codepageparam = "";
+
+ if ($codepage)
+ {
+ $codepageparam = "-C $codepage";
+ }
+
+ $main = join(' ',
+ "-I xindy",
+ "-M \"$module\"",
+ "-t \"$trans\"",
+ "-o \"$out\"",
+ "\"$in\"");
+
+ $cmdstr = join(' ', $rest, $langparam, $codepageparam, $main);
+
+ unless ($quiet)
+ {
+ print "xindy $cmdstr\n";
+ }
+
+ unless ($dontexec)
+ {
+ open STATUS, "xindy $cmdstr 2>&1 |" or die "Can't fork: $!\n";
+
+ my $status = '';
+ my $warnings = "";
+ my $retry ="";
+
+ while (<STATUS>)
+ {
+ $status .= $_;
+
+ $warnings .= $_ if /WARNING:/;
+ }
+
+ close STATUS;
+
+ if ($status=~/Cannot locate xindy module for language ([^\s]+) in codepage ([^\s]+)/)
+ {
+ $cmdstr = join(' ', $rest, $langparam, $main);
+
+ unless ($quiet)
+ {
+ my $message = "$&\nRetrying using default codepage.\n";
+
+ print STDERR $message;
+
+ $retry .= $message;
+
+ print "xindy $cmdstr\n";
+ }
+
+ open STATUS, "xindy $cmdstr 2>&1 |"
+ or die "Can't fork: $!\n";
+
+ $status = '';
+
+ while (<STATUS>)
+ {
+ $status .= $_;
+ }
+
+ close STATUS;
+ }
+
+ if ($status=~/Cannot locate xindy module for language ([^\s]+)/
+ and $1 ne 'general')
+ {
+ $cmdstr = join(' ', $rest, "-L general", $main);
+
+ unless ($quiet)
+ {
+ my $message = "$&\nRetrying with -L general\n";
+
+ print STDERR $message;
+ $retry .= $message;
+
+ print "xindy $cmdstr\n";
+ }
+
+ open STATUS, "xindy $cmdstr 2>&1 |"
+ or die "Can't fork: $!\n";
+
+ $status = '';
+
+ while (<STATUS>)
+ {
+ $status .= $_;
+ }
+
+ close STATUS;
+ }
+
+ if ($?)
+ {
+ my $errno = $?;
+
+ if (open LOGFILE, ">>$trans")
+ {
+ print LOGFILE "\n$status";
+ close LOGFILE;
+ }
+ else
+ {
+ print STDERR "Unable to open '$trans' $!\n";
+ }
+
+ if ($language)
+ {
+ die "Call to xindy failed (errno=$errno):\n", $status;
+ }
+ else
+ {
+ # If the language hasn't been set, then it may be
+ # because the document doesn't contain
+ # \printglossaries/\printglossary or it may be
+ # because the user has a customized style file that
+ # contains the language settings.
+
+ die "Call to xindy failed (errno=$errno):\n", $status,
+ "\n\nNo language detected.",
+ "\nHave you remembered to use \\printglossary\n",
+ "or \\printglossaries in your document?\n\n";
+ }
+ }
+
+ print $status;
+
+ if (open LOGFILE, ">>$trans")
+ {
+ print LOGFILE "\n$warnings";
+
+ if ($retry)
+ {
+ print LOGFILE "\nmakeglossaries messages:\n\n", $retry;
+ }
+
+ close LOGFILE;
+ }
+ else
+ {
+ print STDERR "Unable to open '$trans' $!\n";
+ }
+
+ }
}
sub HELP_MESSAGE{
print "\nSyntax : makeglossaries [options] <filename>\n\n";
print "For use with the glossaries package to pass relevant\n";
- print "files to makeindex\n\n";
+ print "files to makeindex or xindy\n\n";
print "<filename>\tBase name of glossary file(s). This should\n";
print "\t\tbe the name of your main LaTeX document without any\n";
print "\t\textension.\n";
- print "\nOptions:\n";
+ print "\n General Options:\n";
+ print "-o <gls>\tUse <gls> as the output file.\n";
+ print "-q\t\tQuiet mode\n";
+ print "-s <sty>\tEmploy <sty> as the style file\n";
+ print "-t <log>\tEmploy <log> as the transcript file\n";
+
+ print "\n Xindy Options:\n";
+ print "-L <language>\tUse <language>.\n";
+
+ print "\n Makeindex Options:\n";
print "-c\t\tCompress intermediate blanks\n";
print "-g\t\tEmploy German word ordering\n";
print "-l\t\tLetter ordering\n";
- print "-o <gls>\tUse <gls> as the output file.\n";
print "-p <num>\tSet the starting page number to be <num>\n";
- print "-q\t\tQuiet mode\n";
print "-r\t\tDisable implicit page range formation\n";
- print "-s <sty>\tEmploy <sty> as the style file\n";
- print "-t <log>\tEmploy <log> as the transcript file\n";
print "\nSee makeindex documentation for further details on these ";
print "options\n";
}
@@ -291,3 +768,107 @@ sub VERSION_MESSAGE{
}
1;
+
+=head1 NAME
+
+makeglossaries - Calls makeindex/xindy for LaTeX documents using glossaries package
+
+=head1 SYNOPSIS
+
+B<makeglossaries> [B<-o> I<file>] [B<-q>] [B<-s> I<file>]
+[B<-t> I<file>] [B<-L> I<language>] [B<-c>] [B<-g>] [B<-l>]
+[B<-p> I<num>] [B<-r>] [B<--version>] [B<--help>] I<filename>
+
+=head1 DESCRIPTION
+
+B<makeglossaries> is designed for use with LaTeX documents that
+use the glossaries package. The mandatory argument I<filename> should
+be the name of the LaTeX document without the .tex extension.
+B<makeglossaries> will read the auxiliary file to determine whether
+B<makeindex> or B<xindy> should be called. All the information
+required to be passed to the relevant indexing application should
+also be contained in the auxiliary file, but may be overridden by
+the option arguments to B<makeglossaries>.
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<-q>
+
+Quiet mode. Reduces chatter to standard output.
+
+=item B<-o> I<file>
+
+Use I<file> as the output file. (Only suitable for documents
+containing a single glossary, otherwise each glossary will be
+overridden.)
+
+=item B<-s> I<file>
+
+Use I<file> as the style file. Note that if you use this option,
+you need to know whether B<makeindex> or B<xindy> will be called, as
+they have different style files.
+
+=item B<-t> I<file>
+
+Use I<file> as the transcript file.
+
+=item B<-L> I<language>
+
+This option only has an effect if B<xindy> is called. Sets the
+language. See B<xindy> documentation for further details.
+
+=item B<-c>
+
+Compress intermediate blanks (B<makeindex> only).
+
+=item B<-g>
+
+Employ German word ordering (B<makeindex> only).
+
+=item B<-l>
+
+Letter ordering (B<makeindex> only).
+
+=item B<-p> I<num>
+
+Sets the starting page number to be I<num> (B<makeindex> only).
+
+=item B<-r>
+
+Disable implicit page range formation (B<makeindex> only).
+
+=item B<--version>
+
+Prints version number and exits.
+
+=item B<--help>
+
+Prints help message and exits.
+
+=back
+
+=head1 REQUIRES
+
+Perl, Getopt::Std, and makeindex or xindy (depending on glossaries
+package options).
+
+=head1 LICENSE
+
+This is free software distributed under the LaTeX Project Public
+License. There is NO WARRANTY.
+See L<http://www.latex-project.org/lppl.txt> for details.
+
+=head1 AUTHOR
+
+Nicola L. C. Talbot,
+L<http://theoval.cmp.uea.ac.uk/~nlct/>
+
+=head1 RECOMMENDED READING
+
+The glossaries manual:
+
+ texdoc glossaries
+
+=cut
diff --git a/Build/source/texk/texlive/linked_scripts/installfont/installfont-tl b/Build/source/texk/texlive/linked_scripts/installfont/installfont-tl
index e69de29bb2d..bd64a4dbc0f 100644..100755
--- a/Build/source/texk/texlive/linked_scripts/installfont/installfont-tl
+++ b/Build/source/texk/texlive/linked_scripts/installfont/installfont-tl
@@ -0,0 +1,585 @@
+#!/bin/bash
+#
+# installfont-tl [options]
+#
+# installs a LaTeX font family (Postscript Type1, Truetype, Opentype)
+# (font series from light to ultra bold + (faked) small caps + faked
+# slanted, but no expert fonts) for using them with LaTeX (TeXLive)
+# The script tries to rename the fonts automatically (optional).
+# Otherwise the *.afm and *.pfb files must be named in Karl Berry scheme
+# (e.g. 5bbr8a.pfb)
+# (tested with Ubuntu 9.10) v1.6a (03.11.2010)
+# (C) Josef Kleber, Christoph Bier 2009-2010 License: LPPL
+#
+VERSION="v1.6a (2010/11/03)"
+#
+FONTFAMILY="5bb"
+FONTNAME="Softmaker Barbedor"
+FONTCOMMAND="FFBarbedor"
+FONTSCALEPREFIX="Sbb"
+FONTTYPE="pst1"
+PACKAGENAME="barbedor"
+PACKAGEDATE="2010/03/05"
+PACKAGEVERSION="v1.0"
+MAPFILENAME=""
+OFN="false"
+LOCALTEXMF="`kpsexpand '$TEXMFHOME'`"
+FONTSUBDIR="MyFonts/Barbedor"
+# Font encoding default: 8a (AdobeStandardEncoding)
+FONTENCODING="8a"
+# Font cut default: r (regular) don't change!
+FONTCUT="r"
+#
+OTFFONTFEATURES="-fkern -fliga"
+UNINSTALL="false"
+INSTALLAS="rmdefault"
+SMFFSTY="%"
+LOG="false"
+#
+function printHelp()
+{
+ echo -e \
+ " `basename $0` [options]\n"\
+ " installs a font family for using it with LaTeX\n\n"\
+ " Options:\n\n"\
+ " -f, --fontfamily Name of the LaTeX font family, e.g. ptm (Adobe Times)\n\n"\
+ " -n, --fontname Font name, e.g. Adobe Times\n\n"\
+ " -c, --fontcommand Name of the command that will switch to the font\n\n"\
+ " -P, --fontscaleprefix prefix of the scale macro, which should be different\n"\
+ " for each font (no digits allowed)\n\n"\
+ " -t, --fonttype Font type (pst1(default)|ttf|otf)\n\n"\
+ " -p, --packagename Name of the LaTeX package\n\n"\
+ " -d, --packagedate Date of the LaTeX package\n\n"\
+ " -v, --packageversion Version of the LaTeX package\n\n"\
+ " -l, --localtexmf root of your local TeX tree, e.g. /cygdrive/c/LocalTeXMF\n\n"\
+ " -m, --mapfilename file name of the map file\n\n"\
+ " -O. --origfontname use original font names instead of Karl Berry names\n\n"\
+ " -s, --fontsubdir location where the fonts and support files should be\n"\
+ " installed, e.g. MyFonts/AdobeTimes\n\n"\
+ " -e, --fontencoding encoding of the font\n"\
+ " (default: 8a (AdobeStandardEncoding))\n\n"\
+ " -o, --otffontfeatures choosing Opentype font features\n"\
+ " (default: -fkern -fliga)\n\n"\
+ " -u, --uninstall will uninstall the font family. You must specify\n"\
+ " --localtexmf and --fontsubdir\n\n"\
+ " -R, --rmdefault installs font family as roman (serif) font family (default)\n\n"\
+ " -S, --sfdefault installs font family as sans serif font family\n\n"\
+ " -T, --ttdefault installs font family as typewriter font family\n\n"\
+ " -V, --version will print the version number of `basename $0`\n\n"\
+ " -L, --log will write the terminal output of the main programms into the\n"\
+ " LOG file instead of the screen\n\n"\
+ " -h, --help will print this help statement\n\n"\
+ " See http://latex.josef-kleber.de/index.htm or\n"\
+ " http://latex.josef-kleber.de/en_index.htm\n"\
+ " for more information"
+ exit 0;
+}
+function printVersion()
+{
+ echo "This is version $VERSION of `basename $0`"
+ exit 0;
+}
+#
+if [ $# -eq 0 ]
+then
+ echo -e "`basename $0`: Could not find command line arguments.\nUse the '--help' option for explanation."
+ echo -e "Continue with default script variables? (y/n)"
+ read ANSWER
+ if [ $ANSWER = "n" -o $ANSWER = "N" ]
+ then
+ exit 1
+ fi
+fi
+#
+LONGOPTS="fontfamily:,fontname:,fontcommand:,fontscaleprefix:,fonttype:,packagename:,packagedate:,packageversion:,mapfilename:,origfilename,localtexmf:,fontsubdir:,fontencoding:,otffontfeatures:,uninstall,rmdefault,sfdefault,ttdefault,log,version,help"
+SHORTOPTS="f:n:c:P:t:p:d:v:m:Ol:s:e:o:uRSTVLh"
+ARGS=$(getopt -l "$LONGOPTS" "$SHORTOPTS" "$@")
+if [ $? -ne 0 ]
+then
+ echo -e "`basename $0`: Could not process command line arguments.\nUse the '--help' option for explanation."
+ exit 1
+fi
+#
+eval set -- "$ARGS"
+#
+while [ $# -gt 0 ]; do
+ ARG=$1
+ case "$ARG" in
+ --help|-h) shift; printHelp;;
+ --version|-V) shift; printVersion;;
+ --rmdefault|-R) shift; INSTALLAS="rmdefault";;
+ --sfdefault|-S) shift; INSTALLAS="sfdefault";;
+ --ttdefault|-T) shift; INSTALLAS="ttdefault";;
+ --uninstall|-u) shift; UNINSTALL="true";;
+ --log|-L) shift; LOG="true";;
+ --fontfamily|-f) shift; FONTFAMILY="$1"; shift;;
+ --fontname|-n) shift; FONTNAME="$1"; shift;;
+ --fontcommand|-c) shift; FONTCOMMAND="$1"; shift;;
+ --fontscaleprefix|-P) shift; FONTSCALEPREFIX="$1"; shift;;
+ --fonttype|-t) shift; FONTTYPE="$1"; shift;;
+ --packagename|-p) shift; PACKAGENAME="$1"; shift;;
+ --packagedate|-d) shift; PACKAGEDATE="$1"; shift;;
+ --packageversion|-v) shift; PACKAGEVERSION="$1"; shift;;
+ --mapfilename|-m) shift; MAPFILENAME="$1"; shift;;
+ --origfontname|-O) shift; OFN="true";;
+ --localtexmf|-l) shift; LOCALTEXMF="$1"; shift;;
+ --fontsubdir|-s) shift; FONTSUBDIR="$1"; shift;;
+ --fontencoding|-e) shift; FONTENCODING="$1"; shift;;
+ --otffontfeatures|-o) shift; FONTFAMILY="$1"; shift;;
+ --) shift; break;;
+ *) break;;
+ esac
+done
+#
+rm LOG 2>>/dev/null;
+#
+if [ -z $MAPFILENAME ]
+then
+ MAPFILENAME=$FONTFAMILY
+fi
+#
+typeset -i STEP=0
+#
+if [ $UNINSTALL = "true" ]
+then
+ DIR="${LOCALTEXMF}/fonts/tfm/${FONTSUBDIR}/"
+ echo -e "$\n\n$DIR"
+ DIR="${LOCALTEXMF}/fonts/vf/${FONTSUBDIR}/"
+ echo $DIR
+ DIR="${LOCALTEXMF}/fonts/type1/${FONTSUBDIR}/"
+ echo $DIR
+ DIR="${LOCALTEXMF}/fonts/map/dvips/${FONTSUBDIR}/"
+ echo $DIR
+ DIR="${LOCALTEXMF}/tex/latex/${FONTSUBDIR}/"
+ echo $DIR
+ echo -e "\n\nDo you want to delete these directories? (y/n)"
+ read ANSWER
+ if [ $ANSWER = "y" -o $ANSWER = "Y" ]
+ then
+ DIR="${LOCALTEXMF}/fonts/tfm/${FONTSUBDIR}/"
+ rm -f -r $DIR
+ DIR="${LOCALTEXMF}/fonts/vf/${FONTSUBDIR}/"
+ rm -f -r $DIR
+ DIR="${LOCALTEXMF}/fonts/type1/${FONTSUBDIR}/"
+ rm -f -r $DIR
+ DIR="${LOCALTEXMF}/fonts/map/dvips/${FONTSUBDIR}/"
+ rm -f -r $DIR
+ DIR="${LOCALTEXMF}/tex/latex/${FONTSUBDIR}/"
+ rm -f -r $DIR
+ exit 0
+ else
+ exit 1
+ fi
+fi
+#
+if [ $FONTTYPE = "ttf" ]
+then
+ ((STEP++))
+ echo -e "\n\n$STEP) Convert font(s) to Postscript Type1\n\n"
+ for file in *.TTF;
+ do
+ mv "$file" "${file%TTF}ttf" 2>>/dev/null;
+ done
+ for file in *.ttf;
+ do
+ ttf2pt1 -a -b -l adobestd "$file";
+ done
+fi
+#
+if [ $FONTTYPE = "otf" ]
+then
+ ((STEP++))
+ echo -e "\n\n$STEP) Convert font(s) to Postscript Type1\n\n"
+ for file in *.OTF;
+ do
+ mv "$file" "${file%OTF}otf" 2>>/dev/null;
+ done
+ for file in *.otf;
+ do
+ otftotfm $OTFFONTFEATURES -e - "$file" 555;
+ done
+ for file in *.pfb;
+ do
+ pf2afm "$file";
+ done
+ rm *.tfm
+ rm *.enc
+fi
+#
+echo -e "\n\nTry to rename the font(s) automatically? (y/n)"
+read ANSWER
+if [ $ANSWER = "y" -o $ANSWER = "Y" ]
+then
+ ((STEP++))
+ echo -e "\n\n$STEP) Renaming fonts\n\n"
+ mkdir -p Backup
+ cp *.afm *.AFM ./Backup 2>>/dev/null
+ cp *.pfb *.PFB ./Backup 2>>/dev/null
+ for file in *.AFM;
+ do
+ mv "$file" "${file%AFM}afm" 2>>/dev/null;
+ done
+ for file in *.PFB;
+ do
+ mv "$file" "${file%PFB}pfb" 2>>/dev/null;
+ done
+ FONTNUMBER=$(ls -al *.pfb 2>>/dev/null | wc -l)
+ if [ $FONTNUMBER -le 1 ]
+ then
+ if [ $FONTNUMBER -eq 0 ]
+ then
+ echo "No font(s) to rename!"
+ exit 1
+ else
+ for file in *.afm;
+ do
+ mv "$file" ${FONTFAMILY}${FONTCUT}${FONTENCODING}.afm;
+ done
+ for file in *.pfb;
+ do
+ mv "$file" ${FONTFAMILY}${FONTCUT}${FONTENCODING}.pfb;
+ echo -e "s/<${FONTFAMILY}${FONTCUT}${FONTENCODING}/<${file%.pfb}/" >>map.sed;
+ done
+ fi
+ else
+ mkdir -p Rename
+ # rename small caps font(s)
+ FILES=$(grep -e "Smc" -e "Caps" -e "SCOsF" *.afm | cut -d':' -f1 | uniq)
+ for file in $FILES
+ do
+ FONTCUT="rc"
+ FWEIGHT=$(grep -e "Light" "$file" | wc -l)
+ if [ $FWEIGHT -gt 0 ]
+ then
+ FONTCUT="lc"
+ else
+ FWEIGHT=$(grep -e "Semibold" -e "SemiBold" "$file" | wc -l)
+ if [ $FWEIGHT -gt 0 ]
+ then
+ FONTCUT="sc"
+ else
+ FWEIGHT=$(grep -e "Demi" -e "Demibold" -e "DemiBold" "$file" | wc -l)
+ if [ $FWEIGHT -gt 0 ]
+ then
+ FONTCUT="dc"
+ else
+ FWEIGHT=$(grep -e "Extrabold" -e "ExtraBold" "$file" | wc -l)
+ if [ $FWEIGHT -gt 0 ]
+ then
+ FONTCUT="xc"
+ else
+ FWEIGHT=$(grep -e "Heavy" -e "Ultrabold" -e "UltraBold" "$file" | wc -l)
+ if [ $FWEIGHT -gt 0 ]
+ then
+ FONTCUT="uc"
+ else
+ FWEIGHT=$(grep -e "Bold" -e "bold" "$file" | wc -l)
+ if [ $FWEIGHT -gt 0 ]
+ then
+ FONTCUT="bc"
+ fi
+ fi
+ fi
+ fi
+ fi
+ fi
+ mv "$file" ./Rename/${FONTFAMILY}${FONTCUT}${FONTENCODING}.afm;
+ mv "${file%afm}pfb" ./Rename/${FONTFAMILY}${FONTCUT}${FONTENCODING}.pfb;
+ echo -e "s/<${FONTFAMILY}${FONTCUT}${FONTENCODING}/<${file%.afm}/" >>map.sed;
+ done
+ # rename the light font(s)
+ FILES=$(grep -e "Light" *.afm | cut -d':' -f1 | uniq)
+ for file in $FILES
+ do
+ ITALICANGLE=$(grep -e "ItalicAngle" "$file" | tr ' ' '+' | cut -d'+' -f2 | cut -d'.' -f1)
+ if [ $ITALICANGLE -lt 0 -o $ITALICANGLE -gt 0 ]
+ then
+ FONTCUT="li"
+ else
+ FONTCUT="l"
+ fi
+ mv "$file" ./Rename/${FONTFAMILY}${FONTCUT}${FONTENCODING}.afm;
+ mv "${file%afm}pfb" ./Rename/${FONTFAMILY}${FONTCUT}${FONTENCODING}.pfb;
+ echo -e "s/<${FONTFAMILY}${FONTCUT}${FONTENCODING}/<${file%.afm}/" >>map.sed;
+ SMFFSTY="\RequirePackage{softmakerfreefont}%"
+ done
+ # rename the semi(bold) font
+ FILES=$(grep -e "Semibold" -e "SemiBold" *.afm | cut -d':' -f1 | uniq)
+ for file in $FILES
+ do
+ ITALICANGLE=$(grep -e "ItalicAngle" "$file" | tr ' ' '+' | cut -d'+' -f2 | cut -d'.' -f1)
+ if [ $ITALICANGLE -lt 0 -o $ITALICANGLE -gt 0 ]
+ then
+ FONTCUT="si"
+ else
+ FONTCUT="s"
+ fi
+ mv "$file" ./Rename/${FONTFAMILY}${FONTCUT}${FONTENCODING}.afm;
+ mv "${file%afm}pfb" ./Rename/${FONTFAMILY}${FONTCUT}${FONTENCODING}.pfb;
+ echo -e "s/<${FONTFAMILY}${FONTCUT}${FONTENCODING}/<${file%.afm}/" >>map.sed;
+ SMFFSTY="\RequirePackage{softmakerfreefont}%"
+ done
+ # rename the demi(bold) font
+ FILES=$(grep -e "FontName" -e "FullName" *.afm | grep -e "Demi" -e "Demibold" -e "DemiBold" | cut -d':' -f1 | uniq)
+ for file in $FILES
+ do
+ ITALICANGLE=$(grep -e "ItalicAngle" "$file" | tr ' ' '+' | cut -d'+' -f2 | cut -d'.' -f1)
+ if [ $ITALICANGLE -lt 0 -o $ITALICANGLE -gt 0 ]
+ then
+ FONTCUT="di"
+ else
+ FONTCUT="d"
+ fi
+ mv "$file" ./Rename/${FONTFAMILY}${FONTCUT}${FONTENCODING}.afm;
+ mv "${file%afm}pfb" ./Rename/${FONTFAMILY}${FONTCUT}${FONTENCODING}.pfb;
+ echo -e "s/<${FONTFAMILY}${FONTCUT}${FONTENCODING}/<${file%.afm}/" >>map.sed;
+ SMFFSTY="\RequirePackage{softmakerfreefont}%"
+ done
+ # rename the extra bold font
+ FILES=$(grep -e "Extrabold" -e "ExtraBold" *.afm | cut -d':' -f1 | uniq)
+ for file in $FILES
+ do
+ ITALICANGLE=$(grep -e "ItalicAngle" "$file" | tr ' ' '+' | cut -d'+' -f2 | cut -d'.' -f1)
+ if [ $ITALICANGLE -lt 0 -o $ITALICANGLE -gt 0 ]
+ then
+ FONTCUT="xi"
+ else
+ FONTCUT="x"
+ fi
+ mv "$file" ./Rename/${FONTFAMILY}${FONTCUT}${FONTENCODING}.afm;
+ mv "${file%afm}pfb" ./Rename/${FONTFAMILY}${FONTCUT}${FONTENCODING}.pfb;
+ echo -e "s/<${FONTFAMILY}${FONTCUT}${FONTENCODING}/<${file%.afm}/" >>map.sed;
+ done
+ # rename the ultra bold/heavy font
+ FILES=$(grep -e "Heavy" -e "Ultrabold" -e "UltraBold" *.afm | cut -d':' -f1 | uniq)
+ for file in $FILES
+ do
+ ITALICANGLE=$(grep -e "ItalicAngle" "$file" | tr ' ' '+' | cut -d'+' -f2 | cut -d'.' -f1)
+ if [ $ITALICANGLE -lt 0 -o $ITALICANGLE -gt 0 ]
+ then
+ FONTCUT="ui"
+ else
+ FONTCUT="u"
+ fi
+ mv "$file" ./Rename/${FONTFAMILY}${FONTCUT}${FONTENCODING}.afm;
+ mv "${file%afm}pfb" ./Rename/${FONTFAMILY}${FONTCUT}${FONTENCODING}.pfb;
+ echo -e "s/<${FONTFAMILY}${FONTCUT}${FONTENCODING}/<${file%.afm}/" >>map.sed;
+ SMFFSTY="\RequirePackage{softmakerfreefont}%"
+ done
+ # rename the bold font
+ FILES=$(grep -e "Bold" -e "bold" *.afm | cut -d':' -f1 | uniq)
+ for file in $FILES
+ do
+ ITALICANGLE=$(grep -e "ItalicAngle" "$file" | tr ' ' '+' | cut -d'+' -f2 | cut -d'.' -f1)
+ if [ $ITALICANGLE -lt 0 -o $ITALICANGLE -gt 0 ]
+ then
+ FONTCUT="bi"
+ else
+ FONTCUT="b"
+ fi
+ mv "$file" ./Rename/${FONTFAMILY}${FONTCUT}${FONTENCODING}.afm;
+ mv "${file%afm}pfb" ./Rename/${FONTFAMILY}${FONTCUT}${FONTENCODING}.pfb;
+ echo -e "s/<${FONTFAMILY}${FONTCUT}${FONTENCODING}/<${file%.afm}/" >>map.sed;
+ done
+ # rename the regular font
+ FILES=$(grep -e "Regular" -e "Medium" -e "Book" --exclude=installfont * | grep -e ".afm" | cut -d':' -f1 | uniq)
+ for file in $FILES
+ do
+ ITALICANGLE=$(grep -e "ItalicAngle" "$file" | tr ' ' '+' | cut -d'+' -f2 | cut -d'.' -f1)
+ if [ $ITALICANGLE -lt 0 -o $ITALICANGLE -gt 0 ]
+ then
+ FONTCUT="ri"
+ else
+ FONTCUT="r"
+ fi
+ mv "$file" ./Rename/${FONTFAMILY}${FONTCUT}${FONTENCODING}.afm;
+ mv "${file%afm}pfb" ./Rename/${FONTFAMILY}${FONTCUT}${FONTENCODING}.pfb;
+ echo -e "s/<${FONTFAMILY}${FONTCUT}${FONTENCODING}/<${file%.afm}/" >>map.sed;
+ done
+ mv ./Rename/* .
+ rmdir ./Rename
+ fi
+ ls -al *.pfb *.afm
+ echo -e "\n\nPlease check the font name(s). Continue? (y/n)"
+ read ANSWER
+ if [ $ANSWER = "n" -o $ANSWER = "N" ]
+ then
+ rm *.afm
+ rm *.pfb
+ rm *.sed
+ mv ./Backup/* .
+ rmdir ./Backup
+ exit 1
+ fi
+fi
+#
+((STEP++))
+echo -e "\n\n$STEP) Creating metrics and virtual fonts\n\n"
+cat >${FONTFAMILY}-drv.tex <<EOF
+\input fontinst.sty
+\needsfontinstversion{1.926}
+\recordtransforms{${FONTFAMILY}-rec.tex}
+\latinfamily{${FONTFAMILY}}{}
+\endrecordtransforms
+\bye
+EOF
+if [ $LOG = "true" ]
+then
+ tex ${FONTFAMILY}-drv.tex >>LOG 2>&1
+else
+ tex ${FONTFAMILY}-drv.tex
+fi
+#
+((STEP++))
+echo -e "\n\n$STEP) Compiling metrics and virtual fonts\n\n"
+for file in *.pl;
+do
+ if [ $LOG = "true" ]
+ then
+ pltotf $file ${file%pl}tfm >>LOG 2>&1
+ else
+ pltotf $file ${file%pl}tfm
+ fi
+done
+for file in *.vpl;
+do
+ if [ $LOG = "true" ]
+ then
+ vptovf $file ${file%vpl}vf ${file%vpl}tfm >>LOG 2>&1
+ else
+ vptovf $file ${file%vpl}vf ${file%vpl}tfm
+ fi
+done
+#
+((STEP++))
+echo -e "\n\n$STEP) Creating the map file\n\n"
+cat >${FONTFAMILY}-map.tex <<EOF
+\input finstmsc.sty
+\resetstr{PSfontsuffix}{.pfb}
+\adddriver{dvips}{${FONTFAMILY}.map}
+\input ${FONTFAMILY}-rec.tex
+\donedrivers
+\bye
+EOF
+if [ $LOG = "true" ]
+then
+ tex ${FONTFAMILY}-map.tex >>LOG 2>&1
+else
+ tex ${FONTFAMILY}-map.tex
+fi
+if [ $OFN = "true" ]
+then
+ sed -f map.sed ${FONTFAMILY}.map >${MAPFILENAME}.map
+ unix2dos ${MAPFILENAME}.map 2>>/dev/null;
+ rm ${FONTFAMILY}.map
+ UMAP="${MAPFILENAME}"
+else
+ UMAP="${FONTFAMILY}"
+fi
+#
+((STEP++))
+echo -e "\n\n$STEP) Creating the style file"
+cat >${PACKAGENAME}.sty <<EOF
+\NeedsTeXFormat{LaTeX2e}%
+\ProvidesPackage{$PACKAGENAME}[$PACKAGEDATE $PACKAGEVERSION $FONTNAME]%
+\RequirePackage{textcomp}%
+\RequirePackage{xkeyval}%
+\define@key{${PACKAGENAME}.sty}{scaled}[1.0]%
+{%
+ \def\\${FONTSCALEPREFIX}@scale{#1}%
+}%
+\define@key{${PACKAGENAME}.sty}{leading}[1.0]%
+{%
+ \linespread{#1}%
+}%
+\ExecuteOptionsX{scaled}%
+\ProcessOptionsX\relax%
+\renewcommand*{\\${INSTALLAS}}{$FONTFAMILY}%
+\newcommand\\${FONTCOMMAND}{\fontfamily{$FONTFAMILY}\fontseries{m}\fontshape{n}\selectfont}%
+\endinput%
+EOF
+#
+((STEP++))
+echo -e "\n\n$STEP) Insert scale factor into the fd files"
+cat >installfont.sed <<EOF
+9 i\\
+\\n\\\\expandafter\\\\ifx\\\\csname ${FONTSCALEPREFIX}@scale\\\\endcsname\\\\relax\\n \\\\let\\\\${FONTSCALEPREFIX}@@scale\\\\@empty\\n\\\\else\\n \\\\edef\\\\${FONTSCALEPREFIX}@@scale{s*[\\\\csname ${FONTSCALEPREFIX}@scale\\\\endcsname]}\\n\\\\fi\\n
+s/<-> /<-> \\\\${FONTSCALEPREFIX}@@scale /
+EOF
+#
+for file in *.fd;
+do
+ mv $file ${file%fd}fdb;
+ sed -f installfont.sed ${file%fd}fdb >$file;
+done
+#
+((STEP++))
+echo -e "\n\n$STEP) Installing font(s) and support files"
+DIR="${LOCALTEXMF}/fonts/tfm/${FONTSUBDIR}/"
+if [ ! -d $DIR ]
+then
+ mkdir -p $DIR
+fi
+mv *.tfm $DIR
+DIR="${LOCALTEXMF}/fonts/vf/${FONTSUBDIR}/"
+if [ ! -d $DIR ]
+then
+ mkdir -p $DIR
+fi
+mv *.vf $DIR
+DIR="${LOCALTEXMF}/fonts/type1/${FONTSUBDIR}/"
+if [ $OFN = "true" ]
+then
+ cd Backup
+ if [ ! -d $DIR ]
+ then
+ mkdir -p $DIR
+ fi
+ cp *.pfb $DIR
+ cd ..
+else
+ if [ ! -d $DIR ]
+ then
+ mkdir -p $DIR
+ fi
+ cp *.pfb $DIR
+fi
+DIR="${LOCALTEXMF}/fonts/map/dvips/${FONTSUBDIR}/"
+if [ ! -d $DIR ]
+then
+ mkdir -p $DIR
+fi
+mv *.map $DIR
+DIR="${LOCALTEXMF}/tex/latex/${FONTSUBDIR}/"
+if [ ! -d $DIR ]
+then
+ mkdir -p $DIR
+fi
+mv *.fd $DIR
+DIR="${LOCALTEXMF}/tex/latex/${FONTSUBDIR}/"
+if [ ! -d $DIR ]
+then
+ mkdir -p $DIR
+fi
+mv *.sty $DIR
+#
+((STEP++))
+echo -e "\n\n$STEP) Include the map file"
+echo -e "\n\n"
+updmap --enable Map ${UMAP}.map
+rm *.mtx
+rm *.pl
+rm *.vpl
+rm *.log
+rm *.tex
+rm *.fdb
+rm *.sed
+if [ -d Backup ]
+then
+ rm *.afm
+ rm *.pfb
+ mv ./Backup/* .
+ rmdir ./Backup
+fi
+echo -e "\n\nThe font(s) should be installed now!"
+exit 0 \ No newline at end of file
diff --git a/Build/source/texk/texlive/linked_scripts/latex2man/latex2man b/Build/source/texk/texlive/linked_scripts/latex2man/latex2man
index 45da55fb578..5987a8c8c0c 100755
--- a/Build/source/texk/texlive/linked_scripts/latex2man/latex2man
+++ b/Build/source/texk/texlive/linked_scripts/latex2man/latex2man
@@ -3,7 +3,7 @@
# Descr: Latex --> MAN-page (groff -man), HTML and TexInfo;
# Language: PERL (>= 5.0)
# Author: Dr. Jürgen Vollmer, Juergen.Vollmer@informatik-vollmer.de
-# $Id: latex2man,v 1.151 2008/11/11 07:21:14 vollmer Exp $
+# $Id: latex2man,v 1.156 2010/12/22 12:44:30 vollmer Exp $
#
# Copyright (C) 1998 Dr. Juergen Vollmer
# Viktoriastrasse 15, D-76133 Karlsruhe, Germany
@@ -26,8 +26,8 @@ $CMD=`basename $0`; chop ($CMD);
$gen_date = `date`; chomp $gen_date; # date when the output was generated
sub date2str;
-$VERSION = "1.23";
-$DATE = date2str ('$Date: 2008/11/11 07:21:14 $' =~ m|(\d+/\d+/\d+)|);
+$VERSION = "1.24";
+$DATE = date2str ('$Date: 2010/12/22 12:44:30 $' =~ m|(\d+/\d+/\d+)|);
$tmp = "/tmp/$CMD.$$";
@@ -35,17 +35,19 @@ $tmp = "/tmp/$CMD.$$";
# check option and arguments
##################################################################
-getopts('o:t:VhMHTLC:D:a:'); # -D1: write each read line -D2: write each word
+getopts('o:t:c:VhMHTLC:D:a:'); # -D1: write each read line -D2: write each word
sub usage
{
print <<'END';
-usage: latex2man [-t transfile] [-HTML] [-C name] [-h] [-V] infile outfile.
+usage: latex2man [-t transfile] [-c cssfile] [-HTML] [-C name] [-h] [-V] infile outfile.
A tool to translate UNIX manual pages written with LaTeX into a format
understood by the UNIX man(1)-command.
Reads infile, writes outfile.
-t transfile: Translation for user defined LaTeX macros.
+ -c CSSfile: If -H is given, add a link to the \`CSSfile\' and use those
+ CSS definitions.
-M: Produce output suitable for the man(1) command (default).
-H: Instead of producing output suitable for the man(1) command,
HTML code is produced (despite of the name of the command).
@@ -396,68 +398,133 @@ $htmlMacro1a{'verb'} = '<tt>';
$htmlMacro1b{'verb'} = '</tt>';
$htmlMacro1a{'underline'} = '<u>';
$htmlMacro1b{'underline'} = '</u>';
-$htmlMacro1a{'section'} = '\n<h2>';
- $htmlMacro1b{'section'} = '</h2>\n';
-$htmlMacro1a{'subsection'} = '\n<h4>';
- $htmlMacro1b{'subsection'} = '</h4>\n';
-$htmlMacro1a{'subsubsection'} = '\n<h5>';
- $htmlMacro1b{'subsubsection'} = '</h5>\n';
-$htmlMacro1a{'Email'} = '\n<tt>';
- $htmlMacro1b{'Email'} = '</tt>';
-$htmlMacro1a{'URL'} = '\n<tt>';
- $htmlMacro1b{'URL'} = '</tt>';
-
-$htmlMacro1a{'Prog'} = '<tt>';
- $htmlMacro1b{'Prog'} = '</tt>';
-$htmlMacro1a{'File'} = '<tt>';
- $htmlMacro1b{'File'} = '</tt>';
-$htmlMacro1a{'Opt'} = '<b>';
- $htmlMacro1b{'Opt'} = '</b>';
-$htmlMacro1a{'oOpt'} = '[<b>';
- $htmlMacro1b{'oOpt'} = '</b>]';
-$htmlMacro1a{'Arg'} = '<i>';
- $htmlMacro1b{'Arg'} = '</i>';
-$htmlMacro1a{'oArg'} = '[<i>';
- $htmlMacro1b{'oArg'} = '</i>]';
+ $htmlMacro1a{'section'} = '\n<h2>';
+ $htmlMacro1b{'section'} = '</h2>\n';
+ $htmlMacro1a{'subsection'} = '\n<h4>';
+ $htmlMacro1b{'subsection'} = '</h4>\n';
+ $htmlMacro1a{'subsubsection'} = '\n<h5>';
+ $htmlMacro1b{'subsubsection'} = '</h5>\n';
+if ($opt_c) {
+ # use CSS
+ # thanks to Tom Brand <tbrand@manumit-systems.com>
+ $htmlMacro1a{'Email'} = '\n<font class="emailstyle">';
+ $htmlMacro1b{'Email'} = '</font>';
+ $htmlMacro1a{'URL'} = '\n<font class="urlstyle">';
+ $htmlMacro1b{'URL'} = '</font>';
+
+ $htmlMacro1a{'Prog'} = '<font class="progname">';
+ $htmlMacro1b{'Prog'} = '</font>';
+ $htmlMacro1a{'File'} = '<font class="filename">';
+ $htmlMacro1b{'File'} = '</font>';
+ $htmlMacro1a{'Opt'} = '<font class="optstyle">';
+ $htmlMacro1b{'Opt'} = '</font>';
+ $htmlMacro1a{'oOpt'} = '[<font class="optstyle">';
+ $htmlMacro1b{'oOpt'} = '</font>]';
+ $htmlMacro1a{'Arg'} = '<font class="argstyle">';
+ $htmlMacro1b{'Arg'} = '</font>';
+ $htmlMacro1a{'oArg'} = '[<font class="argstyle">';
+ $htmlMacro1b{'oArg'} = '</font>]';
+} else {
+ # don't use CSS
+ $htmlMacro1a{'Email'} = '\n<tt>';
+ $htmlMacro1b{'Email'} = '</tt>';
+ $htmlMacro1a{'URL'} = '\n<tt>';
+ $htmlMacro1b{'URL'} = '</tt>';
+
+ $htmlMacro1a{'Prog'} = '<tt>';
+ $htmlMacro1b{'Prog'} = '</tt>';
+ $htmlMacro1a{'File'} = '<tt>';
+ $htmlMacro1b{'File'} = '</tt>';
+ $htmlMacro1a{'Opt'} = '<b>';
+ $htmlMacro1b{'Opt'} = '</b>';
+ $htmlMacro1a{'oOpt'} = '[<b>';
+ $htmlMacro1b{'oOpt'} = '</b>]';
+ $htmlMacro1a{'Arg'} = '<i>';
+ $htmlMacro1b{'Arg'} = '</i>';
+ $htmlMacro1a{'oArg'} = '[<i>';
+ $htmlMacro1b{'oArg'} = '</i>]';
+}
# LaTeX macros with two arguments
-$htmlMacro2a{'Cmd'} = '<em>';
- $htmlMacro2b{'Cmd'} = '</em>(';
- $htmlMacro2c{'Cmd'} = ')';
-$htmlMacro2a{'OptArg'} = '<b>';
- $htmlMacro2b{'OptArg'} = '</b><i>';
- $htmlMacro2c{'OptArg'} = '</i>';
-$htmlMacro2a{'OptoArg'} = '<b>';
- $htmlMacro2b{'OptoArg'} = '</b>[<i>';
- $htmlMacro2c{'OptoArg'} = '</i>]';
-$htmlMacro2a{'oOptArg'} = '[<b>';
- $htmlMacro2b{'oOptArg'} = '</b><i>';
- $htmlMacro2c{'oOptArg'} = '</i>]';
-$htmlMacro2a{'oOptoArg'} = '[<b>';
- $htmlMacro2b{'oOptoArg'} = '</b>[<i>';
- $htmlMacro2c{'oOptoArg'} = '</i>]]';
+if (opt_c) {
+ $htmlMacro2a{'Cmd'} = '<font class="commandname">';
+ $htmlMacro2b{'Cmd'} = '</font>(';
+ $htmlMacro2c{'Cmd'} = ')';
+ $htmlMacro2a{'OptArg'} = '<font class="optstyle">';
+ $htmlMacro2b{'OptArg'} = '</font><font class="argstyle">';
+ $htmlMacro2c{'OptArg'} = '</font>';
+ $htmlMacro2a{'OptoArg'} = '<font class="optstyle">';
+ $htmlMacro2b{'OptoArg'} = '</font>[<font class="argstyle">';
+ $htmlMacro2c{'OptoArg'} = '</font>]';
+ $htmlMacro2a{'oOptArg'} = '[<font class="optstyle">';
+ $htmlMacro2b{'oOptArg'} = '</font><font class="argstyle">';
+ $htmlMacro2c{'oOptArg'} = '</font>]';
+ $htmlMacro2a{'oOptoArg'} = '[<font class="optstyle">';
+ $htmlMacro2b{'oOptoArg'} = '</font>[<font class="argstyle">';
+ $htmlMacro2c{'oOptoArg'} = '</font>]]';
+} else {
+ $htmlMacro2a{'Cmd'} = '<em>';
+ $htmlMacro2b{'Cmd'} = '</em>(';
+ $htmlMacro2c{'Cmd'} = ')';
+ $htmlMacro2a{'OptArg'} = '<b>';
+ $htmlMacro2b{'OptArg'} = '</b><i>';
+ $htmlMacro2c{'OptArg'} = '</i>';
+ $htmlMacro2a{'OptoArg'} = '<b>';
+ $htmlMacro2b{'OptoArg'} = '</b>[<i>';
+ $htmlMacro2c{'OptoArg'} = '</i>]';
+ $htmlMacro2a{'oOptArg'} = '[<b>';
+ $htmlMacro2b{'oOptArg'} = '</b><i>';
+ $htmlMacro2c{'oOptArg'} = '</i>]';
+ $htmlMacro2a{'oOptoArg'} = '[<b>';
+ $htmlMacro2b{'oOptoArg'} = '</b>[<i>';
+ $htmlMacro2c{'oOptoArg'} = '</i>]]';
+}
$htmlMacro2a{'setlength'} = '';
$htmlMacro2b{'setlength'} = '';
$htmlMacro2c{'setlength'} = '';
# we handle sections in HTML as having two arguments, 1. the number, 2. the name
-$htmlMacro2a{'section'} = '\n<h2><a name="section_';
- $htmlMacro2b{'section'} = '">';
- $htmlMacro2c{'section'} = '</a></h2>\n';
-$htmlMacro2a{'subsection'} = '\n<h4><a name="section_';
- $htmlMacro2b{'subsection'} = '">';
- $htmlMacro2c{'subsection'} = '</a></h4>\n';
-$htmlMacro2a{'subsubsection'} = '\n<h5><a name="section_';
- $htmlMacro2b{'subsubsection'} = '">';
- $htmlMacro2c{'subsubsection'} = '</a></h5>\n';
-
-# we handle Email and URL special in HTML, the LaTeX argument is doubled.
-$htmlMacro2a{'Email'} = '<a href ="mailto:';
- $htmlMacro2b{'Email'} = '"><tt>';
- $htmlMacro2c{'Email'} = '</tt></a>';
-$htmlMacro2a{'URL'} = '<a href ="';
- $htmlMacro2b{'URL'} = '"><tt>';
- $htmlMacro2c{'URL'} = '</tt></a>';
+if ($opt_c) {
+ # use CSS
+ # thanks to Tom Brand <tbrand@manumit-systems.com>
+ $htmlMacro2a{'section'} = '\n<h2 class="sectionname"><a name="section_';
+ $htmlMacro2b{'section'} = '">';
+ $htmlMacro2c{'section'} = '</a></h2>\n';
+ $htmlMacro2a{'subsection'} = '\n<h4 class="subsectionname"><a name="section_';
+ $htmlMacro2b{'subsection'} = '">';
+ $htmlMacro2c{'subsection'} = '</a></h4>\n';
+ $htmlMacro2a{'subsubsection'} = '\n<h5 class="subsubsectionname"><a name="section_';
+ $htmlMacro2b{'subsubsection'} = '">';
+ $htmlMacro2c{'subsubsection'} = '</a></h5>\n';
+
+ # we handle Email and URL special in HTML, the LaTeX argument is doubled.
+ $htmlMacro2a{'Email'} = '<a class="emailstyle" href ="mailto:';
+ $htmlMacro2b{'Email'} = '">';
+ $htmlMacro2c{'Email'} = '</a>';
+ $htmlMacro2a{'URL'} = '<a class="urlstyle" href ="';
+ $htmlMacro2b{'URL'} = '"><tt>';
+ $htmlMacro2c{'URL'} = '</tt></a>';
+
+}else{
+ # don't use CSS
+ $htmlMacro2a{'section'} = '\n<h2><a name="section_';
+ $htmlMacro2b{'section'} = '">';
+ $htmlMacro2c{'section'} = '</a></h2>\n';
+ $htmlMacro2a{'subsection'} = '\n<h4><a name="section_';
+ $htmlMacro2b{'subsection'} = '">';
+ $htmlMacro2c{'subsection'} = '</a></h4>\n';
+ $htmlMacro2a{'subsubsection'} = '\n<h5><a name="section_';
+ $htmlMacro2b{'subsubsection'} = '">';
+ $htmlMacro2c{'subsubsection'} = '</a></h5>\n';
+
+ # we handle Email and URL special in HTML, the LaTeX argument is doubled.
+ $htmlMacro2a{'Email'} = '<a href ="mailto:';
+ $htmlMacro2b{'Email'} = '"><tt>';
+ $htmlMacro2c{'Email'} = '</tt></a>';
+ $htmlMacro2a{'URL'} = '<a href ="';
+ $htmlMacro2b{'URL'} = '"><tt>';
+ $htmlMacro2c{'URL'} = '</tt></a>';
+}
########################################################################
# Translation for LaTeX macros for TexInfo
@@ -768,18 +835,31 @@ sub manVerbatimLine
sub htmlStart
{
Print "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">"; NL;
- Print "<!-- Manual page created with $CMD on $gen_date --"; NL;
- Print " -- Author of $CMD: Juergen.Vollmer\@informatik-vollmer.de --";NL;
- Print " -- NOTE: This file is generated, DO NOT EDIT. -->"; NL;
+ Print "<!-- Manual page created with $CMD on $gen_date"; NL;
+ Print " ** Author of $CMD: Juergen.Vollmer\@informatik-vollmer.de";NL;
+ Print " ** NOTE: This file is generated, DO NOT EDIT. -->"; NL;
Print "<html>"; NL;
- Print "<head><title>$Name</title></head>"; NL;
- Print "<body bgcolor=\"white\">"; NL;
- Print "<h1 align=center>"; NL;
+ Print "<head><title>$Name</title>"; NL;
+
+ if ($opt_c) {
+ Print '<link href="'.$opt_c.'" rel="stylesheet" type="text/css">'; NL;
+ Print "</head><body>"; NL;
+ Print '<h1 class="titlehead">'; NL;
+ interpret_line $title;
+ Print '</h1>'; NL;
+ Print '<h4 class="authorhead">'; interpret_word $author; Print '</h4>'; NL;
+ Print "<h4 class=\"datehead\">$date</h4>"; NL;
+ Print "<h4 class=\"versionhead\">Version $version</h4>"; NL;
+ } else {
+ Print "</head><body bgcolor=\"white\">"; NL;
+
+ Print "<h1 align=center>"; NL;
interpret_line $title;
Print "</h1>"; NL;
Print "<h4 align=center>"; interpret_word $author; Print "</h4>"; NL;
Print "<h4 align=center>$date</h4>"; NL;
Print "<h4 align=center>Version $version</h4>"; NL;
+ }
}
sub htmlEnd
{
@@ -873,14 +953,24 @@ sub htmlTableStart
{
my $columns = $_[0];
my $width = $_[1];
- NL; Print "<table>"; NL:
+ NL;
+ if ($opt_c) {
+ Print '<table summary="table" class="tablestyle">';
+ } else{
+ Print '<table summary="table">';
+ }
+ NL;
}
sub htmlTableSep
{
if ($first_column == 0) {
Print '</td>\n';
}
- Print '<td>';
+ if ($opt_c) {
+ Print '<td class="cellstyle">';
+ } else {
+ Print '<td>';
+ }
}
sub htmlTableEnd
{
@@ -1120,9 +1210,17 @@ sub interpret_word
return;
}
if (/^\\hline/) {
- Print '\n<tr><td colspan=' . $columns . '><hr></td></tr>\n';
+ if ($opt_c) {
+ Print '\n<tr class="rowstyle"><td class="cellstyle" colspan=' . $columns . '><hr></td></tr>\n';
+ } else {
+ Print '\n<tr><td colspan=' . $columns . '><hr></td></tr>\n';
+ }
+ }
+ if ($opt_c) {
+ Print '\n<tr class="rowstyle">\n<td class="cellstyle">';
+ } else {
+ Print '\n<tr>\n<td>';
}
- Print '\n<tr>\n<td>';
}
$first_column = 0;
}
diff --git a/Build/source/texk/texlive/linked_scripts/latexmk/latexmk.pl b/Build/source/texk/texlive/linked_scripts/latexmk/latexmk.pl
index 08fdc5b4ebe..4f0c9b13228 100755
--- a/Build/source/texk/texlive/linked_scripts/latexmk/latexmk.pl
+++ b/Build/source/texk/texlive/linked_scripts/latexmk/latexmk.pl
@@ -24,7 +24,6 @@ use warnings;
#=======================================
-
#?? Force mode doesn't appear to do force (if error in latex file)
#??? Get banner back in.
#?? CORRECT DIAGNOSTICS ON CHANGED FILES IF THEY DIDN'T EXIST BEFORE
@@ -33,7 +32,7 @@ use warnings;
#?? logfile w/o fdb file: don't set changed file, perhaps for generated exts.
# Reconsider
#?? Do proper run-stuff for bibtex, makeindex, cus-deps. OK I think
-# Parse and correctly find bst and ist files
+# Parse and correctly find ist files
# ATTEMPT TO ALLOW FILENAMES WITH SPACES:
@@ -108,8 +107,8 @@ use warnings;
$my_name = 'latexmk';
$My_name = 'Latexmk';
-$version_num = '4.16a';
-$version_details = "$My_name, John Collins, 18 May 2010";
+$version_num = '4.22e';
+$version_details = "$My_name, John Collins, 3 February 2011";
use Config;
@@ -176,40 +175,61 @@ else {
## 5. Parsing of log file instead of source file is used to
## obtain dependencies, by default.
##
-## Modification log from 22 Jan 2010 onwards in detail
-##
-## 18 May 2010, John Collins Remove unlink $$Pdest in do_viewfile
-## 17 May 2010, John Collins Correct sleep time to 1 sec
-## if s<0 (otherwise infinite delay)
-## or if 0<s<1 (otherwise zero delay)
-## 10 May 2010, John Collins Deal with filenames in log file that are
-## messed up by pdfTeX warnings not preceeded
-## by a new line
-## 18 Apr 2010, John Collins Clean up comment from previous mod.
-## 16 Apr 2010, John Collins V. 4.15c. Correct line wrapping issue.
-## 12 Apr 2010, John Collins V. 4.15b. Change
-## use Digest;
-## to
-## use Digest::MD5;
-## for TeX Live compatibility. Needed also to change
-## my $md5 = Digest->MD5;
-## to
-## my $md5 = Digest::MD5->new;
+## Modification log from 23 Jun 2010 onwards in detail
##
-## 8 Apr 2010, John Collins V. 4.15a. Better defaults for system
-## configuration file under cygwin
-## 7 Apr 2010, John Collins V. 4.15: Change defaults on
-## $latex_silent_switch and $pdflatex_silent_switch
-## to make them TeXLive compatible always
-## 26 Mar 2010, John Collins V. 4.14b. Make sure md5 calc
-## doesn't bomb out on directory
-## Parse log file: if apparent dependent
-## is directory, remove it from list.
-## 27 Feb 2010, John Collins V. 4.14a. List failed primaries
-## 22 Jan 2010, John Collins V. 4.14. Try to fix problem that with -pvc
-## failure on latex should force a wait until
-## a user file is changed. But latexmk keeps
-## doing dvips, dvipdf, etc.
+## 3 Feb 2011, John Collins Correct handling of errors in rc files
+## 23 Jan 2011, John Collins Fix detection of makeindex files with
+## MiKTeX v >= 2.8
+## 23 Jan 2011, John Collins Fix detection of biber use with MiKTeX
+## (which doesn't put openout lines in log file).
+## 9 Jan 2011, John Collins Correct parsing of blg file for biber 0.7.2
+## 3 Jan 2011, John Collins Small correction on reading .fdb_file
+## 1 Jan 2011, John Collins Biber implementation
+## 30 Dec 2010, John Collins Version number to 4.22
+## Start for biber
+## 28 Dec 2010, John Collins Version number to 4.21
+## 1. Improved handling of quotes in filenames in log file:
+## a. Clean double quotes in names from messages about files.
+## b. In ("..." construct, as in MiKTeX, the only character
+## prohibited is ". Previously I disallowed ( and ),
+## which are allowed special characters.
+## 2. Reversed deletion and reinsertion in dependents list
+## of name found by kpsewhich. Otherwise sometimes a name
+## could be deleted in error
+## 12 Nov 2010, John Collins Version number to 4.20
+## 11 Nov 2010, John Collins Fix problem that when bibtex finds no
+## citations, this is treated by bibtex as an
+## error, but latexmk should only treat it as
+## a warning but not an error
+## 8,9 Nov 2010, John Collins Get .bst file in source file list for bibtex
+## 30 Oct 2010, John Collins Allow clean-up to delete destination files of
+## custom-dependencies
+## Configuration variable $cleanup_includes_cusdep_generated to
+## whether this is done
+## 20 Sep 2010, John Collins First attempt to deal with no citations warning
+## Still have the problem that bibtex gives non-zero exit code
+## 19 Sep 2010, John Collins Orthogonalize option and command specification
+## a la Manuel Pegourie-Gonnard (email of 19 Sep 2010,
+## "[latexmk] latex engine and default options".)
+## Still needs documentation.
+## 19 Sep 2010, John Collins More general pattern for files to clean up,
+## Thanks to Manuel Pegourie-Gonnard for the code
+## Must check the use of %generated_exts_all in
+## sub rdb_file_change1.
+## 30 Aug 2010, John Collins Remove double quotes (if any) surrounding
+## output filename read from .log file.
+## (Avoids wrong diagnostic message.)
+## 19 Aug 2010, John Collins Remove use of --width option on $pscmd
+## on linux. It's no longer needed.
+## 30 Jul 2010, John Collins Timing information
+## 27 Jul 2010, John Collins Clean up some comments.
+## This is release version for 4.18.
+## 23 Jul 2010, John Collins Only produce "Removing no-longer-needed
+## dependent" messages when diagnostics are
+## on.
+## 23 Jun 2010, John Collins Add test in log file for natbib's
+## citation-undefined warning
+## 23 Jun 2010, John Collins V. 4.18
##
## 1998-2010, John Collins. Many improvements and fixes.
## See CHANGE-log.txt for full list, and CHANGES for summary
@@ -356,6 +376,11 @@ $log_wrap = 79;
## Commands to invoke latex, pdflatex
$latex = 'latex %O %S';
$pdflatex = 'pdflatex %O %S';
+
+## Default switches:
+$latex_default_switches = '';
+$pdflatex_default_switches = '';
+
## Switch(es) to make them silent:
$latex_silent_switch = '-interaction=batchmode';
$pdflatex_silent_switch = '-interaction=batchmode';
@@ -371,9 +396,11 @@ add_input_ext( 'latex', 'tex', 'eps' );
add_input_ext( 'pdflatex', 'tex', 'jpg', 'pdf' );
#show_input_ext( 'latex' ); show_input_ext( 'pdflatex' );
-## Command to invoke bibtex
+## Command to invoke biber & bibtex
+$biber = 'biber %O %B';
$bibtex = 'bibtex %O %B';
-# Switch(es) to make bibtex silent:
+# Switch(es) to make biber & bibtex silent:
+$biber_silent_switch = '--onlylog';
$bibtex_silent_switch = '-terse';
$bibtex_use = 1; # Whether to actually run bibtex to update bbl files
# 0: Never run bibtex
@@ -751,7 +778,10 @@ else {
# for SUSE 7.2).
# However the option is not available under other UNIX-type
# systems, e.g., Solaris 8.
- $pscmd = "ps --width 200 -f -u $ENV{USER}";
+ # But (19 Aug 2010), the truncation doesn't happen on RHEL4 and 5,
+ # unless the output is written to a terminal. So the --width
+ # option is now unnecessary
+ # $pscmd = "ps --width 200 -f -u $ENV{USER}";
}
elsif ( $^O eq "darwin" ) {
# OS-X on Macintosh
@@ -873,20 +903,26 @@ $cleanup_fdb = 0; # No removal of file for latexmk's file-database
$cleanup_only = 0; # When doing cleanup, do not go-on to making files
$cleanup_includes_generated = 0;
# Determines whether cleanup deletes files generated by
+ # custom dependencies
+$cleanup_includes_cusdep_generated = 0;
+ # Determines whether cleanup deletes files generated by
# (pdf)latex (found from \openout lines in log file).
$diagnostics = 0;
$dvi_filter = ''; # DVI filter command
$ps_filter = ''; # Postscript filter command
$force_mode = 0; # =1 to force processing past errors
-$force_include_mode = 0;# =1 to ignore non-existent files when testing
- # for dependency. (I.e., don't treat them as error)
$go_mode = 0; # =1 to force processing regardless of time-stamps
# =2 full clean-up first
$preview_mode = 0;
$preview_continuous_mode = 0;
$printout_mode = 0; # Don't print the file
+$show_time = 0;
+@timings = ();
+$processing_time1 = processing_time();
+
+
# Do we make view file in temporary then move to final destination?
# (To avoid premature updating by viewer).
$always_view_file_via_temporary = 0; # Set to 1 if viewed file is always
@@ -1222,8 +1258,6 @@ while ($_ = $ARGV[0])
elsif (/^-nodependents$/ || /^-dependents-$/) { $dependents_list = 0; }
elsif (/^-dvi$/) { $dvi_mode = 1; }
elsif (/^-dvi-$/) { $dvi_mode = 0; }
- elsif (/^-F$/) { $force_include_mode = 1; }
- elsif (/^-F-$/) { $force_include_mode = 0; }
elsif (/^-f$/) { $force_mode = 1; }
elsif (/^-f-$/) { $force_mode = 0; }
elsif (/^-g$/) { $go_mode = 1; }
@@ -1238,6 +1272,12 @@ while ($_ = $ARGV[0])
}
elsif (/^-l$/) { $landscape_mode = 1; }
elsif (/^-l-$/) { $landscape_mode = 0; }
+ elsif (/^-latex=(.*)$/) {
+ $latex = $1;
+ }
+ elsif (/^-pdflatex=(.*)$/) {
+ $pdflatex = $1;
+ }
elsif (/^-new-viewer$/) {
$new_viewer_always = 1;
}
@@ -1277,6 +1317,8 @@ while ($_ = $ARGV[0])
}
elsif (/^-pvc-$/) { $preview_continuous_mode = 0; }
elsif (/^-silent$/ || /^-quiet$/ ){ $silent = 1; }
+ elsif (/^-time$/) { $show_time = 1;}
+ elsif (/^-time-$/) { $show_time = 0;}
elsif (/^-v$/ || /^-version$/) {
print "\n$version_details. Version $version_num\n";
exit;
@@ -1302,7 +1344,6 @@ while ($_ = $ARGV[0])
process_rc_file( $ARGV[0] );
}
else {
- $! = 11;
die "$My_name: RC file [$ARGV[0]] does not exist\n";
}
shift;
@@ -1481,6 +1522,11 @@ if ( ($jobname ne '') && ($num_files > 1) ) {
# Normalize the commands, to have place-holders for source, dest etc:
&fix_cmds;
+# Add common options
+add_option( \$latex, $latex_default_switches );
+add_option( \$pdflatex, $pdflatex_default_switches );
+
+
# If landscape mode, change dvips processor, and the previewers:
if ( $landscape_mode )
{
@@ -1492,6 +1538,7 @@ if ( $landscape_mode )
if ( $silent ) {
add_option( \$latex, " $latex_silent_switch" );
add_option( \$pdflatex, " $pdflatex_silent_switch" );
+ add_option( \$biber, " $biber_silent_switch" );
add_option( \$bibtex, " $bibtex_silent_switch" );
add_option( \$makeindex, " $makeindex_silent_switch" );
add_option( \$dvips, " $dvips_silent_switch" );
@@ -1568,7 +1615,7 @@ elsif ( ($sleep_time < 1) && ( $sleep_time != 0 ) ) {
}
elsif ( $sleep_time == 0 ) {
warn "$My_name: sleep_time was configured to zero.\n",
- " Do you really want to do this? It will give 100% CPU usage.";
+ " Do you really want to do this? It will give 100% CPU usage.\n";
}
# Make convenient forms for lookup.
@@ -1585,11 +1632,6 @@ $quell_uptodate_msgs = $silent;
# Whether to quell informational messages when files are uptodate
# Will turn off in -pvc mode
-# Process for each file.
-# The value of $bibtex_mode set in an initialization file may get
-# overridden, during file processing, so save it:
-#?? Unneeded now: $save_bibtex_mode = $bibtex_mode;
-
$failure_count = 0;
@failed_primaries = ();
@@ -1646,9 +1688,17 @@ foreach $filename ( @file_list )
local %conversions = (); # (pdf)latex-performed conversions.
# Maps output file created and read by (pdf)latex
# to source file of conversion.
+
print "$My_name: Examining log file for generated files...\n";
&parse_logB;
+ print "$My_name: Examining fdb file for rules ...\n";
+ # Needed for cleanup of files generated by custom dependencies
+ if ( -e $fdb_file ) {
+ $rdb_errors = rdb_read( $fdb_file );
+ $have_fdb = ($rdb_errors == 0);
+ }
+
my @index_bibtex_generated = ();
my @aux_files = ();
my %other_generated = %generated_log;
@@ -1693,6 +1743,9 @@ foreach $filename ( @file_list )
if ($cleanup_includes_generated) {
unlink( keys %other_generated );
}
+ if ( $cleanup_includes_cusdep_generated) {
+ &cleanup_cusdep_generated;
+ }
if ( $cleanup_mode == 1 ) {
&cleanup1( 'dvi', 'dviF', 'ps', 'psF', 'pdf',
split(' ', $clean_full_ext)
@@ -1824,6 +1877,16 @@ continue {
}
&ifcd_popd;
}
+
+if ($show_time) { show_timing();}
+
+sub show_timing {
+ my $processing_time = processing_time() - $processing_time1;
+ print @timings, "Accumulated processing time = $processing_time\n";
+ @timings = ();
+ $processing_time1 = processing_time();
+}
+
# If we get here without going through the continue section:
if ( $do_cd && ($#dir_stack > -1) ) {
# Just in case we did an abnormal exit from the loop
@@ -1861,7 +1924,7 @@ sub fix_cmds {
# Source only
if ( $_ && ! /%/ ) { $_ .= " %O %S"; }
}
- foreach ($bibtex) {
+ foreach ($biber, $bibtex) {
# Base only
if ( $_ && ! /%/ ) { $_ .= " %O %B"; }
}
@@ -2019,9 +2082,14 @@ sub rdb_make_links {
#?? foreach (sort keys %from_rules) {print "D='$_' F='$from_rules{$_}\n";}
rdb_for_all(
0,
- sub{
- if ( exists $from_rules{$file} ) { $$Pfrom_rule = $from_rules{$file}; }
-#?? print "$rule: $file, $$Pfrom_rule\n";
+ sub{
+ # Set from_rule, but only if it isn't set or is invalid.
+ # Don't forget the biber v. bibtex issue
+ if ( exists $from_rules{$file}
+ && ( (!$$Pfrom_rule) || (! exists $rule_db{$$Pfrom_rule} ) )
+ )
+ { $$Pfrom_rule = $from_rules{$file};
+ }
}
);
rdb_for_all(
@@ -2288,7 +2356,6 @@ sub find_basename
#************************************************************
sub make_preview_continuousB {
- # Version for use with makeB
local @changed = ();
local @disappeared = ();
local @no_dest = (); # Non-existent destination files
@@ -2370,29 +2437,9 @@ CHANGE:
if ( $MSWin_fudge_break && ($^O eq "MSWin32") ) {
$SIG{BREAK} = $SIG{INT} = 'DEFAULT';
}
- if ( $failure > 0 ) {
- if ( !$failure_msg ) {
- $failure_msg = 'Failure to make the files correctly';
- }
- @pre_primary = (); # Array of rules
- @post_primary = (); # Array of rules
- @one_time = (); # Array of rules
- &rdb_classify_rules( \%possible_primaries, keys %requested_filerules );
- # There will be files changed during the run that are irrelevant.
- # We need to wait for the user to change the files.
-
- # So set the GENERATED files from (pdf)latex as up-to-date:
- rdb_for_some( [keys %current_primaries], \&rdb_update_gen_files );
-
- # And don't watch for changes for post_primary rules (ps and pdf
- # from dvi, etc haven't been run after an error in (pdf)latex, so
- # are out-of-date by filetime criterion, but they should not be run
- # until after another (pdf)latex run:
- foreach (@post_primary) { delete $rules_to_watch{$_}; }
-
- $failure_msg =~ s/\s*$//; #Remove trailing space
- warn "$My_name: $failure_msg\n",
- " ==> You will need to change a source file before I do another run <==\n";
+ # Start viewer if needed.
+ if ( ($failure > 0) && (! $force_mode) ) {
+ # No viewer yet
}
elsif ( ($view_file ne '') && (-e $view_file) && $updated && $viewer_running ) {
# A viewer is running. Explicitly get it to update screen if we have to do it:
@@ -2414,7 +2461,6 @@ CHANGE:
}
local $retcode = 0;
rdb_one_rule( 'view', sub { $retcode = &rdb_run1;} );
-# PREVIOUS local $retcode = rdb_makeB ( 'view' );
if ( $retcode != 0 ) {
if ($force_mode) {
warn "$My_name: I could not run previewer\n";
@@ -2427,14 +2473,39 @@ CHANGE:
$viewer_running = 1;
$$Pneed_to_get_viewer_process = 1;
} # end analyze result of trying to run viewer
- } # end start viewer
- rdb_show_rule_errors();
- if ( $first_time || $updated || $failure ) {
- print "\n=== Watching for updated files. Use ctrl/C to stop ...\n";
- }
- $waiting = 1; if ($diagnostics) { warn "WAITING\n"; }
- &catch_break;
- $have_break = 0;
+ } # end start viewer
+ if ( $failure > 0 ) {
+ if ( !$failure_msg ) {
+ $failure_msg = 'Failure to make the files correctly';
+ }
+ @pre_primary = (); # Array of rules
+ @post_primary = (); # Array of rules
+ @one_time = (); # Array of rules
+ &rdb_classify_rules( \%possible_primaries, keys %requested_filerules );
+ # There will be files changed during the run that are irrelevant.
+ # We need to wait for the user to change the files.
+
+ # So set the GENERATED files from (pdf)latex as up-to-date:
+ rdb_for_some( [keys %current_primaries], \&rdb_update_gen_files );
+
+ # And don't watch for changes for post_primary rules (ps and pdf
+ # from dvi, etc haven't been run after an error in (pdf)latex, so
+ # are out-of-date by filetime criterion, but they should not be run
+ # until after another (pdf)latex run:
+ foreach (@post_primary) { delete $rules_to_watch{$_}; }
+
+ $failure_msg =~ s/\s*$//; #Remove trailing space
+ warn "$My_name: $failure_msg\n",
+ " ==> You will need to change a source file before I do another run <==\n";
+ }
+ rdb_show_rule_errors();
+ if ($show_time && ! $first_time) { show_timing(); }
+ if ( $first_time || $updated || $failure ) {
+ print "\n=== Watching for updated files. Use ctrl/C to stop ...\n";
+ }
+ $waiting = 1; if ($diagnostics) { warn "WAITING\n"; }
+ &catch_break;
+ $have_break = 0;
WAIT: while (1) {
sleep( $sleep_time );
if ($have_break) { last WAIT; }
@@ -2445,6 +2516,8 @@ CHANGE:
}
last WAIT;
}
+ # Don't count waiting time in processing:
+ $processing_time1 = processing_time();
# Does this do this job????
local $new_files = 0;
rdb_for_some( [keys %current_primaries], sub{ $new_files += &rdb_find_new_filesB } );
@@ -2467,57 +2540,37 @@ CHANGE:
sub process_rc_file {
# Usage process_rc_file( filename )
+ # NEW VERSION
# Run rc_file whose name is given in first argument
- # Exit with code 11 if file could not be read.
- # (In general this is not QUITE the right error)
- # Exit with code 13 if there is a syntax error or other problem.
- # ???Should I leave the exiting to the caller (perhaps as an option)?
- # But I can always catch it with an eval if necessary.
- # That confuses ctrl/C and ctrl/break handling.
+ # Exit with code 0 on success
+ # Exit with code 1 if file could not be read.
+ # Stop if there is a syntax error or other problem.
my $rc_file = $_[0];
- warn "$My_name: Executing PERL code in file '$rc_file'...\n"
+ my $ret_code = 0;
+ warn "$My_name: Executing Perl code in file '$rc_file'...\n"
if $diagnostics;
- do( $rc_file );
- # The return value from the do is not useful, since it is the value of
- # the last expression evaluated, which could be anything.
- # The correct test of errors is on the values of $! and $@.
-
-# This is not entirely correct. On gluon2:
-# rc_file does open of file, and $! has error, apparently innocuous
-# See ~/proposal/06/latexmkrc-effect
-
- my $OK = 1;
- if ( $! ) {
- # Get both numeric error and its string, by forcing numeric and
- # string contexts:
- my $err_no = $!+0;
- my $err_string = "$!";
- warn "$My_name: Initialization file '$rc_file' could not be read,\n",
- " or it gave some other problem. Error code \$! = $err_no.\n",
- " Error string = '$err_string'\n";
- $! = 256;
- $OK = 0;
+ if ( ! -r $rc_file ) {
+ return 1;
}
+ do( $rc_file );
if ( $@ ) {
- $! = 256;
- # Indent the error message to make it easier to locate
- my $indented = prefix( $@, " " );
- $@ = "";
+ # Indent each line of possibly multiline message:
+ my $message = prefix( $@, " " );
warn "$My_name: Initialization file '$rc_file' gave an error:\n",
- "$indented";
- $OK = 0;
- }
- if ( ! $OK ) {
- die "$My_name: Stopping because of problem with rc file\n";
+ "$message\n";
+ die "$My_name: Stopping because of problem with rc file\n";
+ # Use the following if want non-fatal error.
+ return 2;
}
+ return 0;
} #END process_rc_file
#************************************************************
sub execute_code_string {
# Usage execute_code_string( string_of_code )
- # Run the PERL code contained in first argument
- # Exit with code 13 if there is a syntax error or other problem.
+ # Run the perl code contained in first argument
+ # Halt if there is a syntax error or other problem.
# ???Should I leave the exiting to the caller (perhaps as an option)?
# But I can always catch it with an eval if necessary.
# That confuses ctrl/C and ctrl/break handling.
@@ -2528,18 +2581,16 @@ sub execute_code_string {
eval $code;
# The return value from the eval is not useful, since it is the value of
# the last expression evaluated, which could be anything.
- # The correct test of errors is on the values of $! and $@.
+ # The correct test of errors is on the value of $@.
if ( $@ ) {
- $! = 256;
- my $message = $@;
- $@ = "";
- $message =~ s/\s*$//;
+ # Indent each line of possibly multiline message:
+ my $message = prefix( $@, " " );
die "$My_name: ",
"Stopping because executing following code from command line\n",
" $code\n",
"gave an error:\n",
- " $message\n";
+ "$message\n";
}
} #END execute_code_string
@@ -2547,10 +2598,42 @@ sub execute_code_string {
sub cleanup1 {
# Usage: cleanup1( exts_without_period, ... )
- foreach (@_) { unlink("$root_filename.$_"); }
+ foreach (@_) {
+ (my $name = /%R/ ? $_ : "%R.$_") =~ s/%R/$root_filename/;
+ unlink("$name");
+ }
} #END cleanup1
#************************************************************
+
+sub cleanup_cusdep_generated {
+ # Remove files generated by custom dependencies
+ rdb_for_all( \&cleanup_one_cusdep_generated );
+} #END cleanup_cusdep_generated
+
+#************************************************************
+
+sub cleanup_one_cusdep_generated {
+ # Remove destination file generated by one custom dependency
+ # Assume rule context, but not that the rule is a custom dependency.
+ # Only delete destination file if source file exists (so destination
+ # file can be recreated)
+ if ( $$Pcmd_type ne 'cusdep' ) {
+ # NOT cusdep
+ return;
+ }
+ if ( (-e $$Pdest) && (-e $$Psource) ) {
+ unlink $$Pdest;
+ }
+ elsif ( (-e $$Pdest) && (!-e $$Psource) ) {
+ warn "$My_name: For custom dependency '$rule',\n",
+ " I won't delete destination file '$$Pdest'\n",
+ " because the source file '$$Psource' doesn't exist,\n",
+ " so the destination file may not be able to be recreated\n";
+ }
+} #END cleanup_one_cusdep_generated
+
+#************************************************************
#************************************************************
#************************************************************
@@ -2659,8 +2742,6 @@ sub print_help
" -e <code> - Execute specified PERL code\n",
" -f - force continued processing past errors\n",
" -f- - turn off forced continuing processing past errors\n",
- " -F - Ignore non-existent files when testing for dependencies\n",
- " -F- - Turn off -F\n",
" -gg - Super go mode: clean out generated files (-CA), and then\n",
" process files regardless of file timestamps\n",
" -g - process regardless of file timestamps\n",
@@ -2672,12 +2753,16 @@ sub print_help
" implementations of latex/pdflatex.)\n",
" -l - force landscape mode\n",
" -l- - turn off -l\n",
+ " -latex=<program> - set program used for latex.\n",
+ " (replace '<program>' by the program name)\n",
" -new-viewer - in -pvc mode, always start a new viewer\n",
" -new-viewer- - in -pvc mode, start a new viewer only if needed\n",
" -nobibtex - never use bibtex\n",
" -nodependents - Do not show list of dependent files after processing\n",
" -pdf - generate pdf by pdflatex\n",
" -pdfdvi - generate pdf by dvipdf\n",
+ " -pdflatex=<program> - set program used for pdflatex.\n",
+ " (replace '<program>' by the program name)\n",
" -pdfps - generate pdf by ps2pdf\n",
" -pdf- - turn off pdf\n",
" -ps - generate postscript\n",
@@ -2696,7 +2781,9 @@ sub print_help
" -pvc- - turn off -pvc\n",
" -r <file> - Read custom RC file\n",
" -silent - silence progress messages from called programs\n",
- " -v - display program version\n",
+ " -time - show CPU time used\n",
+ " -time- - don't show CPU time used\n",
+ " -v - display program version\n",
" -verbose - display usual progress messages from called programs\n",
" -version - display program version\n",
" -view=default - viewer is default (dvi, ps, pdf)\n",
@@ -2721,6 +2808,7 @@ sub print_commands
warn "Commands used by $my_name:\n",
" To run latex, I use \"$latex\"\n",
" To run pdflatex, I use \"$pdflatex\"\n",
+ " To run biber, I use \"$biber\"\n",
" To run bibtex, I use \"$bibtex\"\n",
" To run makeindex, I use \"$makeindex\"\n",
" To make a ps file from a dvi file, I use \"$dvips\"\n",
@@ -2758,12 +2846,71 @@ sub view_file_via_temporary {
#************************************************************
#### Tex-related utilities
+#**************************************************
+
+sub check_biber_log {
+ # Check for biber warnings:
+ # Usage: check_biber_log( base_of_biber_run[, \@biber_source )
+ # return 0: OK, 1: biber warnings, 2: biber errors,
+ # 3: could not open .blg file.
+ # 10: only error is missing \citation commands.
+ # Side effect: add source files @biber_source
+ my $base = $_[0];
+ my $Pbiber_source = $_[1];
+ my $log_name = "$base.blg";
+ my $log_file = new FileHandle;
+ open( $log_file, "<$log_name" )
+ or return 3;
+ my $have_warning = 0;
+ my $have_error = 0;
+ my $missing_citations = 0;
+ my $no_citations = 0;
+ my $error_count = 0;
+ my $warning_count = 0;
+ while (<$log_file>) {
+ if (/> WARN /) {
+ print "Biber warning: $_";
+ $have_warning = 1;
+ $warning_count ++;
+ }
+ elsif (/> FATAL /) {
+ print "Biber error: $_";
+ $have_error = 1;
+ $error_count ++;
+ if ( /> FATAL - The file '[^']+' does not contain any citations!/ ) {
+ $no_citations++;
+ }
+ }
+ elsif ( /> INFO - Found .* '([^']+)'\s*$/
+ || /> INFO - Found '([^']+)'\s*$/
+ ) {
+ if ( defined $Pbiber_source ) {
+ push @$Pbiber_source, $1;
+ }
+ }
+ }
+ close $log_file;
+# print "$My_name: #Biber errors = $error_count, warning messages = $warning_count,\n ",
+# "missing citation messages = $missing_citations, no_citations = $no_citations\n";
+ if ( ! $have_error && $no_citations ) {
+ # If the only errors are missing citations, or lack of citations, that should
+ # count as a warning.
+ # HOWEVER: biber doesn't generate a new bbl. So it is an error condition.
+ return 10;
+ }
+ if ($have_error) {return 2;}
+ if ($have_warning) {return 1;}
+ return 0;
+} #END check_biber_log
+
+#**************************************************
sub check_bibtex_log {
# Check for bibtex warnings:
# Usage: check_bibtex_log( base_of_bibtex_run )
# return 0: OK, 1: bibtex warnings, 2: bibtex errors,
# 3: could not open .blg file.
+ # 10: only error is missing \citation commands.
my $base = $_[0];
my $log_name = "$base.blg";
@@ -2772,17 +2919,35 @@ sub check_bibtex_log {
or return 3;
my $have_warning = 0;
my $have_error = 0;
+ my $missing_citations = 0;
+ my $error_count = 0;
while (<$log_file>) {
- if (/Warning--/) {
+ if (/^Warning--/) {
#print "Bibtex warning: $_";
$have_warning = 1;
}
- if (/error message/) {
- #print "Bibtex error: $_";
+ elsif ( /^I found no \\citation commands---while reading file/ ) {
+ $missing_citations++;
+ }
+ elsif (/There (were|was) (\d+) error message/) {
+ $error_count = $2;
+ #print "Bibtex error: count=$error_count $_";
$have_error = 1;
}
}
close $log_file;
+ #print "Bibtex errors = $error_count, missing citations = $missing_citations\n";
+ if ($have_error && ($error_count <= $missing_citations )
+ && ($missing_citations > 0) ) {
+ # If the only error is a missing citation line, that should only
+ # count as a warning.
+ # But have to deal with the problem that bibtex gives a non-zero
+ # exit code. So leave things as they are so that the user gets
+ # a better diagnostic ??????????????????????????
+# $have_error = 0;
+# $have_warning = 1;
+ return 10;
+ }
if ($have_error) {return 2;}
if ($have_warning) {return 1;}
return 0;
@@ -2790,18 +2955,23 @@ sub check_bibtex_log {
#**************************************************
-sub clean_file_name{
- # Convert filename found in log file to true filename.
- # Used normally only by parse_logB, below
- # 1. For names of form
- # `"string".ext', which arises e.g., from \jobname.bbl:
+sub clean_file_name {
+ # Convert quoted filename as found in log file to filename without quotes
+ # Allows arbitrarily embedded double-quoted substrings, includes the
+ # cases
+ # 1. `"string".ext', which arises e.g., from \jobname.bbl:
# when the base filename contains spaces, \jobname has quotes.
# and from \includegraphics with basename specified.
+ # Also deals with filenames written by asymptote.sty
# 2. Or "string.ext" from \includegraphcs with basename and ext specified.
+ # and from MiKTeX logfile for input files with spaces.
+ # Doubled quotes (e.g., A""B) don't get converted.
+ # Neither do unmatched quotes.
my $filename = $_[0];
- $filename =~ s/^\"([^\"]*)\"(.*)$/$1$2/;
+ while ( $filename =~ s/^([^\"]*)\"([^\"]+)\"(.*)$/$1$2$3/ ) {}
return $filename;
}
+
# ------------------------------
sub parse_logB {
@@ -2983,6 +3153,10 @@ LINE:
warn "$My_name: $1 \n";
$bad_citation = 1;
}
+ if (/Package natbib Warning: (Citation[^\001]*undefined)./) {
+ warn "$My_name: $1 \n";
+ $bad_citation = 1;
+ }
if ( /^Document Class: / ) {
# Class sign-on line
next LINE;
@@ -2997,8 +3171,8 @@ LINE:
next LINE;
}
if ( /^Output written on\s+(.*)\s+\(\d+\s+page/ ) {
- $primary_out = $1;
- warn "$My_name: Log file says output to '$1'\n"
+ $primary_out = clean_file_name($1);
+ warn "$My_name: Log file says output to '$primary_out'\n"
unless $silent;
next LINE;
}
@@ -3011,15 +3185,15 @@ LINE:
next LINE;
}
if ( /^\\openout\d+\s*=\s*\`([^\']+)\'\.$/ ) {
- $generated_log{$1} = 1;
+ $generated_log{clean_file_name($1)} = 1;
next LINE;
}
# Test for conversion produced by package:
if ( /^Package (\S+) Info: Source file: <([^>]+)>/ ) {
# Info. produced by epstopdf (and possibly others)
# about file conversion
- $current_pkg = $1;
- $delegated_source = $2;
+ $current_pkg = clean_file_name($1);
+ $delegated_source = clean_file_name($2);
$block_type = 'conversion';
next LINE;
}
@@ -3088,9 +3262,9 @@ LINE:
next LINE;
}
}
- if ( /^File: ([^\s\[]*) Graphic file \(type / ) {
+ if ( /^File: (.+) Graphic file \(type / ) {
# First line of message from includegraphics/x
- $dependents{$1} = 1;
+ $dependents{clean_file_name($1)} = 1;
next LINE;
}
# Now test for generic lines to ignore, only after special cases!
@@ -3149,36 +3323,49 @@ LINE:
# filename (on VAX/VMS) may include '[' and ']' (directory
# separators)
# filenames (on MS-Win) commonly include space.
+ # filenames on UNIX can included space.
+ # Miktex quotes filenames
+ # But web2c doesn't. Then
+ # (string message
+ # is ambiguous: is the filename "string" or "string message".
+ # Allow both as candidates, since user filenames with spaces
+ # are rare. System filenames with spaces are common, but
+ # they are normally followed by a newline rather than messages.
# First step: replace $_ by whole of line after the '('
# Thus $_ is putative filename followed by other stuff.
$_ = $1;
- if ( /^\"([^\(^\)^\"]+)\"/ ) {
+ # Array of new candidate include files; sometimes more than one.
+ my @new_includes = ();
+ my $quoted = 0;
+ if ( /^\"([^\"]+)\"/ ) {
# Quoted file name, as from MikTeX
+ $quoted = 1;
}
- elsif ( /^([^\(^\)]*?)\s\[/ ) {
- # Terminator: space then '['
- # Use *? in condition: to pick up first ' [' as terminator
- # 'file [' should give good filename.
+ elsif ( /^([^\(^\)]*?)\s+[\[\{\<]/ ) {
+ # Terminator: space then '[' or '{' or '<'
+ # Use *? in condition: to pick up first ' [' (etc)
+ # as terminator
}
- elsif ( /^([^\(^\)]*)\s(?=\()/ ) {
+ elsif ( /^([^\(^\)]*)\s+(?=\()/ ) {
# Terminator is ' (', but '(' isn't in matched string,
# so we keep the '(' ready for the next match
}
elsif ( /^([^\(^\)]*)(\))/ ) {
# Terminator is ')'
}
- elsif ( /^([^\(^\)]*?)\s*\{/ ) {
- # Terminator: arbitrary space then '{'
- # Use *? in condition: to pick up first ' [' as terminator
- # 'file [' should give good filename.
- }
else {
#Terminator is end-of-string
}
$_ = $'; # Put $_ equal to the unmatched tail of string '
my $include_candidate = $1;
$include_candidate =~ s/\s*$//; # Remove trailing space.
+ if ( !$quoted && ($include_candidate =~ /(\S+)\s/ ) ){
+ # Non-space-containing filename-candidate
+ # followed by space followed by message
+ # (Common)
+ push @new_includes, $1;
+ }
if ( $include_candidate eq "[]" ) {
# Part of overfull hbox message
next INCLUDE_CANDIDATE;
@@ -3191,14 +3378,13 @@ LINE:
# treated this as a special case. For safety check here:
$include_candidate =~ s/^\"(.*)\"$/$1/;
- # Make list of new include files; sometimes more than one.
- my @new_includes = ($include_candidate);
+ push @new_includes, $include_candidate;
if ( $include_candidate =~ /^(.+)\[([^\]]+)\]$/ ) {
# Construct of form 'file1[file2]', as produced by pdflatex
if ( -e $1 ) {
# If the first component exists, we probably have the
# pdflatex form
- @new_includes = ($1, $2);
+ push @new_includes, $1, $2;
}
else {
# We have something else.
@@ -3266,8 +3452,8 @@ CANDIDATE:
# If the file still is not found, assume non-existent;
my @kpse_result = kpsewhich( $candidate );
if ($#kpse_result > -1) {
- $dependents{$kpse_result[0]} = 4;
delete $dependents{$candidate};
+ $dependents{$kpse_result[0]} = 4;
next CANDIDATE;
}
else {
@@ -3372,7 +3558,6 @@ CANDIDATE_PAIR:
foreach (@dependents) {
print " '$_' ";
if ( $dependents{$_} == 6 ) { print " written by (pdf)latex";}
- print "\n";
if ( $dependents{$_} == 7 ) { print " converted by (pdf)latex";}
print "\n";
}
@@ -3390,8 +3575,7 @@ CANDIDATE_PAIR:
}
if ( $misparse > 0 ) {
- print "$misparse\n";
- print "Apparent input files apparently from misunderstood lines in .log file:\n";
+ print "Possible input files, perhaps from misunderstood lines in .log file:\n";
foreach ( @misparse ) { print " $_\n"; }
}
}
@@ -3401,8 +3585,8 @@ CANDIDATE_PAIR:
#************************************************************
sub parse_aux {
- #Usage: parse_aux( $aux_file, \@new_bib_files, \@new_aux_files )
- # Parse aux_file (recursively) for bib files.
+ #Usage: parse_aux( $aux_file, \@new_bib_files, \@new_aux_files, \@new_bst_files )
+ # Parse aux_file (recursively) for bib files, and bst files.
# If can't open aux file, then
# Return 0 and leave @new_bib_files empty
# Else set @new_bib_files from information in the aux files
@@ -3416,8 +3600,10 @@ sub parse_aux {
my $aux_file = $_[0];
local $Pbib_files = $_[1];
local $Paux_files = $_[2];
+ local $Pbst_files = $_[3];
@$Pbib_files = ();
+ @$Pbst_files = ();
@$Paux_files = ();
parse_aux1( $aux_file );
@@ -3425,6 +3611,7 @@ sub parse_aux {
return 0;
}
@$Pbib_files = uniqs( @$Pbib_files );
+ @$Pbst_files = uniqs( @$Pbst_files );
if ( $#{$Pbib_files} == -1 ) {
warn "$My_name: No .bib files listed in .aux file '$aux_file' \n",
@@ -3433,6 +3620,8 @@ sub parse_aux {
my $bibret = &find_file_list1( $Pbib_files, $Pbib_files,
'.bib', \@BIBINPUTS );
@$Pbib_files = uniqs( @$Pbib_files );
+ &find_file_list1( $Pbst_files, $Pbst_files, '.bst' );
+ @$Pbst_files = uniqs( @$Pbst_files );
if ($bibret == 0) {
warn "$My_name: Found bibliography file(s) [@$Pbib_files]\n"
unless $silent;
@@ -3480,6 +3669,11 @@ AUX_LINE:
# (Without the '.bib' extension)
push( @$Pbib_files, split /,/, $1 );
}
+ elsif ( /^\\bibstyle\{(.*)\}/ ) {
+ # \\bibstyle{bst_file_name}
+ # (Without the '.bst' extension)
+ push( @$Pbst_files, $1 );
+ }
elsif ( /^\\\@input\{(.*)\}/ ) {
# \\@input{next_aux_file_name}
&parse_aux1( $1 );
@@ -3665,7 +3859,7 @@ LINE:
rdb_create_rule( $rule, 'cusdep', '', $PAnew_cmd, 1,
$source, $dest, $base, 0, $run_time, 1 );
}
- elsif ( $rule =~ /^(makeindex|bibtex)\s*(.*)$/ ) {
+ elsif ( $rule =~ /^(makeindex|bibtex|biber)\s*(.*)$/ ) {
my $rule_generic = $1;
if ( ! $source ) {
# If fdb_file was old-style (v. 1)
@@ -3681,6 +3875,10 @@ LINE:
$dest = "$base.bbl";
$source = "$base.aux";
}
+ elsif ($rule_generic eq 'biber') {
+ $dest = "$base.bbl";
+ $source = "$base.bcf";
+ }
}
warn "$My_name: File-database '$in_name': setting rule '$rule'\n"
if $diagnostics;
@@ -3932,6 +4130,7 @@ sub rdb_set_from_logB {
# $reference_changed, $bad_reference $bad_citation
&parse_logB;
+# ?? !! Should also deal with .run.xml file
# Handle result on output file:
# 1. Non-existent output file, which is because of no content.
@@ -3969,6 +4168,12 @@ sub rdb_set_from_logB {
if ($diagnostics > -1);
rdb_ensure_file( $rule, $ind_file, $from_rule );
}
+ # Make sure the .ind file is treated as a detected source file;
+ # otherwise if the log file has it under a different name (as
+ # with MiKTeX which gives full directory information), there
+ # will be problems with the clean-up of the rule concerning
+ # no-longer-in-use source files:
+ $dependents{$ind_file} = 4;
if ( ! -e $ind_file ) {
# Failure was non-existence of makable file
# Leave failure issue to other rules.
@@ -3980,31 +4185,50 @@ sub rdb_set_from_logB {
foreach my $bbl_file ( uniqs( @bbl_files ) ) {
my ($bbl_base, $bbl_path, $bbl_ext) = fileparseA( $bbl_file );
$bbl_base = $bbl_path.$bbl_base;
- my @new_bib_files;
- my @new_aux_files;
- &parse_aux( "$bbl_base.aux", \@new_bib_files, \@new_aux_files );
- my $from_rule = "bibtex $bbl_base";
- print "!!!===Dealing with rule '$from_rule'\n"
- if ($diagnostics);
+ my @new_bib_files = ();
+ my @new_aux_files = ();
+ my @new_bst_files = ();
+ my @biber_source = ( "$bbl_base.bcf" );
+ my $bib_program = 'bibtex';
+ if ( test_gen_file( "$bbl_base.bcf" ) ) {
+ $bib_program = 'biber';
+ }
+ my $from_rule = "$bib_program $bbl_base";
+ print "======= Dealing with '$from_rule'\n" if ($diagnostics);
+ if ($bib_program eq 'biber') {
+ check_biber_log( $bbl_base, \@biber_source );
+ # Remove OPPOSITE kind of bbl generation:
+ rdb_remove_rule( "bibtex $bbl_base" );
+ }
+ else {
+ parse_aux( "$bbl_base.aux", \@new_bib_files, \@new_aux_files, \@new_bst_files );
+ # Remove OPPOSITE kind of bbl generation:
+ rdb_remove_rule( "biber $bbl_base" );
+ }
if ( ! rdb_rule_exists( $from_rule ) ){
- print " ===Creating rule '$from_rule'\n"
- if ($diagnostics);
- rdb_create_rule( $from_rule, 'external', $bibtex, '', 1,
- "$bbl_base.aux", $bbl_file, $bbl_base, 1, 0);
+ print " ===Creating rule '$from_rule'\n" if ($diagnostics);
+ if ( $bib_program eq 'biber' ) {
+ rdb_create_rule( $from_rule, 'external', $biber, '', 1,
+ "$bbl_base.bcf", $bbl_file, $bbl_base, 1, 0);
+ }
+ else {
+ rdb_create_rule( $from_rule, 'external', $bibtex, '', 1,
+ "$bbl_base.aux", $bbl_file, $bbl_base, 1, 0);
+ }
}
local %old_sources = ();
rdb_one_rule( $from_rule, sub { %old_sources = %$PHsource; } );
- foreach my $source ( @new_bib_files, @new_aux_files ) {
+ foreach my $source ( @new_bib_files, @new_aux_files, @new_bst_files, @biber_source ) {
print " === Source file '$source' for '$from_rule'\n"
if ($diagnostics);
rdb_ensure_file( $from_rule, $source );
delete $old_sources{$source};
}
- if ($diagnostics>-1 ) {
+ if ($diagnostics) {
foreach ( keys %old_sources ) {
print "Removing no-longer-needed dependent '$_' from rule '$from_rule'\n";
}
- }
+ }
rdb_remove_files( $from_rule, keys %old_sources );
print " ===Source file '$bbl_file' for '$rule'\n"
if ($diagnostics);
@@ -4075,12 +4299,11 @@ NEW_SOURCE:
print "No new source files for rule '$rule':\n";
}
}
-
my @files_not_needed = ();
foreach (keys %$PHsource) {
if ( ! exists $dependents{$_} ) {
print "Removing no-longer-needed dependent '$_' from rule '$rule'\n"
- if $diagnostics>-1;
+ if $diagnostics;
push @files_not_needed, $_;
}
}
@@ -4090,6 +4313,24 @@ NEW_SOURCE:
#************************************************************
+sub test_gen_file {
+ # Usage: test_gen_file( filename )
+ # Tests whether the file was generated during a run of (pdf)latex.
+ # Used by rdb_set_from_logB.
+ # Assumes context for primary rule, and that %generated_log is set.
+ # The generated_log test works with TeXLive's tex, because it puts
+ # \openout lines in log file.
+ # But it doesn't work with MikTeX, which does NOT put \openout lines
+ # in log file.
+ # So we have a back up test: bcf file exists and is at least as new as
+ # the run time (so it should have been generated on the current run).
+ my $file = shift;
+ return exists $generated_log{$file}
+ || ( -e $file && ( get_mtime( $file ) >= $$Prun_time ));
+}
+
+#************************************************************
+
sub rdb_find_new_filesB {
# Call: rdb_find_new_filesB
# Assumes rule context for primary rule.
@@ -4546,7 +4787,7 @@ sub rdb_makeB {
"make new source files\n";
next PASS;
}
- elsif ( ! $failure ) {
+ elsif ( $force_mode || (! $failure) ) {
next PASS;
}
}
@@ -4647,7 +4888,7 @@ sub rdb_makeB1 {
# Carries out make at level of given rule (all data available).
# Assumes contexts for recursion, make, and rule, and
# assumes that source files for the rule are to be considered
- # up-to-date.
+ # up-to-date.
if ($diagnostics) { print " MakeB1 $rule\n"; }
if ($failure & ! $force_mode) {return;}
if ( ! defined $pass{$rule} ) {$pass{$rule} = 0; }
@@ -4663,7 +4904,7 @@ sub rdb_makeB1 {
# (and setting to do this test)
# 2 => don't run bibtex because of setting
my @missing_bib_files = ();
- if ( $rule =~ /^bibtex/ ) {
+ if ( $rule =~ /^(bibtex|biber)/ ) {
$bibtex_not_run = 0;
if ($bibtex_use == 0) {
$bibtex_not_run = 2;
@@ -4903,7 +5144,6 @@ sub rdb_classify1 {
# Helper routine for rdb_classify_rules
# Applied as rule_act1 in recursion over rules
# Assumes rule context, and local variables from rdb_classify_rules
- #print "??======= '$rule' $depth $state ========== \n";
push @classify_stack, [$state];
if ( exists $possible_one_time{$rule} ) {
# Normally, we will have already extracted the one_time rules,
@@ -4980,6 +5220,27 @@ sub rdb_run1 {
$$Plast_result = 2;
$$Plast_message = "Bug or configuration error; incorrect command type";
}
+ if ( $rule =~ /^biber/ ) {
+ my $retcode = check_biber_log($$Pbase);
+ if ($retcode == 3) {
+ $$Plast_result = 2;
+ $$Plast_message = "Could not open biber log file for '$$Pbase'";
+ push @warnings, $$Plast_message;
+ }
+ elsif ($retcode == 2) {
+ $$Plast_message = "Biber errors: See file '$$Pbase.blg'";
+ push @warnings, $$Plast_message;
+ }
+ elsif ($retcode == 1) {
+ push @warnings, "Biber warnings for '$$Pbase'";
+ }
+ elsif ($retcode == 10) {
+ push @warnings, "Biber found no citations for '$$Pbase'";
+ # Biber doesn't generate a bbl file in this situation. So I cannot
+ # ignore the error the way I do with bibtex.
+ #$return = 0;
+ }
+ }
if ( $rule =~ /^bibtex/ ) {
my $retcode = check_bibtex_log($$Pbase);
if ($retcode == 3) {
@@ -4994,6 +5255,11 @@ sub rdb_run1 {
elsif ($retcode == 1) {
push @warnings, "Bibtex warnings for '$$Pbase'";
}
+ elsif ($retcode == 10) {
+ push @warnings, "Bibtex found no citations for '$$Pbase'";
+ # But this is an innocuous error, so fix return code
+ $return = 0;
+ }
}
$updated = 1;
@@ -5590,6 +5856,15 @@ sub rdb_one_file {
#************************************************************
+sub rdb_remove_rule {
+ # rdb_remove_rule( rule, ... )
+ foreach my $key (@_) {
+ delete $rule_db{$key};
+ }
+}
+
+#************************************************************
+
sub rdb_create_rule {
# rdb_create_rule( rule, command_type, ext_cmd, int_cmd, test_kind,
# source, dest, base,
@@ -5998,6 +6273,13 @@ sub get_time_size_raw
#************************************************************
+sub processing_time
+{ my ($user, $system, $cuser, $csystem) = times();
+ return $user + $system + $cuser + $csystem;
+}
+
+#************************************************************
+
sub get_checksum_md5 {
my $source = shift;
my $input = new FileHandle;
@@ -6052,38 +6334,41 @@ sub find_file1 {
# On success: full_name = full name with path, retcode = 0
# On failure: full_name = given name, retcode = 1
- my $name = $_[0];
- # Make local copy of path, since we may rewrite it!
- my @path = @{$_[1]};
- if ( $name =~ /^\// ) {
- # Absolute path (if under UNIX)
- # This needs fixing, in general
- if (-e $name) { return( $name, 0 );}
- else { return( $name, 1 );}
- }
- foreach my $dir ( @path ) {
- #??print "-------------dir='$dir', ";
- # Make $dir concatenatable, and empty for current dir:
- if ( $dir eq '.' ) {
- $dir = '';
- }
- elsif ( $dir =~ /[\/\\:]$/ ) {
- #OK if dir ends in / or \ or :
- }
- elsif ( $dir ne '' ) {
- #Append directory separator only to non-empty dir
- $dir = "$dir/";
- }
- #?? print " newdir='$dir'\n";
- if (-e "$dir$name") {
- return("$dir$name", 0);
- }
- }
- my @kpse_result = kpsewhich( $name );
- if ($#kpse_result > -1) {
- return( $kpse_result[0], 0);
- }
- return("$name" , 1);
+ my $name = $_[0];
+ # Make local copy of path, since we may rewrite it!
+ my @path = ();
+ if ($_[1]) {
+ @path = @{$_[1]};
+ }
+ if ( $name =~ /^\// ) {
+ # Absolute path (if under UNIX)
+ # This needs fixing, in general
+ if (-e $name) { return( $name, 0 );}
+ else { return( $name, 1 );}
+ }
+ foreach my $dir ( @path ) {
+ #??print "-------------dir='$dir', ";
+ # Make $dir concatenatable, and empty for current dir:
+ if ( $dir eq '.' ) {
+ $dir = '';
+ }
+ elsif ( $dir =~ /[\/\\:]$/ ) {
+ #OK if dir ends in / or \ or :
+ }
+ elsif ( $dir ne '' ) {
+ #Append directory separator only to non-empty dir
+ $dir = "$dir/";
+ }
+ #?? print " newdir='$dir'\n";
+ if (-e "$dir$name") {
+ return("$dir$name", 0);
+ }
+ }
+ my @kpse_result = kpsewhich( $name );
+ if ($#kpse_result > -1) {
+ return( $kpse_result[0], 0);
+ }
+ return("$name" , 1);
} #END find_file1
#************************************************************
@@ -6431,7 +6716,11 @@ sub tempfile1 {
sub Run_msg {
# Same as Run, but give message about my running
warn_running( "Running '$_[0]'" );
- Run($_[0]);
+ my $time1 = processing_time();
+ my ($pid, $return) = Run($_[0]);
+ my $time = processing_time() - $time1;
+ push @timings, "'$_[0]': time = $time\n";
+ return ($pid, $return);
}
sub Run {
diff --git a/Build/source/texk/texlive/linked_scripts/listings-ext/listings-ext.sh b/Build/source/texk/texlive/linked_scripts/listings-ext/listings-ext.sh
index 49728fcfb9e..5e47ec064bf 100755
--- a/Build/source/texk/texlive/linked_scripts/listings-ext/listings-ext.sh
+++ b/Build/source/texk/texlive/linked_scripts/listings-ext/listings-ext.sh
@@ -1,10 +1,10 @@
#! /bin/sh
### listings-ext.sh ---
-## Author: Dr. Jobst Hoffmann <j.hoffmann@fh-aachen.de>
-## Version: $Id: listings-ext.dtx 48 2009-08-31 18:30:48Z ax006ho $
+## Author: Dr. Jobst Hoffmann <j.hoffmann_(at)_fh-aachen.de>
+## Version: $Id: listings-ext.dtx 67 2010-06-29 16:38:12Z ax006ho $
## Keywords: LaTeX, listings
-## Copyright: (C) 2008-2009 Jobst Hoffmann, <j.hoffmann@fh-aachen.de>
+## Copyright: (C) 2008-2010 Jobst Hoffmann, <j.hoffmann_(at)_fh-aachen.de>
##-------------------------------------------------------------------
##
## This file may be distributed and/or modified under the
@@ -26,56 +26,52 @@
## with the distribution package.
ERR_HELP=1
ME=$(basename $0)
-USAGE="usage:\n\t${ME} [-c|--command] [-h|--help] [-g|--debug] \\\\\n \
-\t\t[-n|--no-header] \\\\\n \
-\t\t[{-o|--output-file} [<output filename>]] <filename>, ...\n \
-\t-c:\tgenerate commands, which can be input and later used by\n \
-\t\t\\\\lstuse, optional\n \
-\t-g:\tdebug mode, create some additional output, optional\n \
-\t-h:\tshow this help, optional\n \
-\t-n:\twrite no header into the LaTeX code, optional; valid only,\n \
-\t\tif -c isn't used\n \
-\t-o [<output filename>]: if this argument is present, the output will\n \
-\t\tbe written into a file <output filename>; if the\n \
-\t\t<output filename> is omitted, the output is redirected\n \
-\t\tinto a file with a basename, which corresponds to the name\n \
-\t\tof the current directory, it has an extension of \".lst\".\n\n \
-remark:\n\tIf <filename> contains characters others than lower \
-and upper\n\tcase characters, \"-\" and \"_\", <output filename> can't be\
- directly\n\tprocessed by TeX.\n \
+USAGE="usage:\n\t${ME} [-c|--command] [-e|--ext] [-g|--debug] \\\\\n\
+\t\t[-h|--help] [-n|--no-header] \\\\\n\
+\t\t[{-o|--output-file} [<output filename>]] <filename>, ...\n\
+\t-c:\tgenerate commands, which can be input and later used by\n\
+\t\t\\\\lstuse, optional\n\
+\t-e:\tinclude file name extension into the identifier\n\
+\t-g:\tdebug mode, create some additional output, optional\n\
+\t-h:\tshow this help, optional\n\
+\t-n:\twrite no header into the LaTeX code, optional; valid only,\n\
+\t\tif -c isn't used\n\
+\t-o [<output filename>]: if this argument is present, the output will\n\
+\t\tbe written into a file <output filename>; if the\n\
+\t\t<output filename> is omitted, the output is redirected\n\
+\t\tinto a file with a basename corresponding to the name\n\
+\t\tof the current directory, it has an extension of \".lst\".\n\
"
+DEBUG_MODE=0
+EXTENSION=0
HEADER=1
-DEBUGMODE=0
-show_usage() # display help massage
-{
- echo -e ${USAGE}
+show_usage() { # display help massage
+ printf "${USAGE}"
exit ${ERR_HELP}
}
-print_header()
-{
+print_header() {
FILE=$1
- echo "%% -- file $1 generated on $(date) by ${ME}"
+ printf "%%%% -- file $1 generated on $(date) by ${ME}\n"
FILE=$(echo $(basename $1 .lst) | sed -e s/[_\-]//g)
- echo "\\csname ${FILE}LstLoaded\\endcsname"
- echo "\\let\\${FILE}LstLoaded\\endinput"
+ printf "\\\\csname ${FILE}LstLoaded\\\\endcsname\n"
+ printf "\\\\expandafter\\\\let\\\\csname ${FILE}LstLoaded\\\\endcsname\\\\endinput\n"
}
-do_the_job()
-{
+do_the_job() {
PATHCOMPONENT=$(pwd)
SOURCEFILE=$1
if [ ! -f ${SOURCEFILE} ]
then
- echo ${SOURCEFILE} is no valid file
+ printf "${SOURCEFILE} is no valid file\n"
return $ERR_NOFILE
fi
SOURCEFILE=${SOURCEFILE##${PATHCOMPONENT}/}
- grep -n "[/%;#!][/\* ;]\ *[cbe]e:" $1 | \
- awk -v pathcomponent="$PATHCOMPONENT" -v file="$SOURCEFILE" \
- -v header="$HEADER" -v command="$COMMAND" -v application="${ME}" \
- -v debugmode="$DEBUG_MODE" \
+ grep -n "^[[:space:]]*[/%;#!][/\* ;][[:space:]]*[cbe]e:" $1 | \
+ awk -v pathcomponent="${PATHCOMPONENT}" -v file="${SOURCEFILE}" \
+ -v header="${HEADER}" -v command="${COMMAND}" -v application="${ME}" \
+ -v debugmode="${DEBUG_MODE}" -v respect_extension="${EXTENSION}"\
-F : \
'
BEGIN {
@@ -95,24 +91,23 @@ BEGIN {
if ( debugmode ) printf("path: %s\n", pathcomponent) > "/dev/stderr"
if ( debugmode ) printf("file: %s\n", file) > "/dev/stderr"
- n = split(pathcomponent, parts, "/");
+ n = split(pathcomponent, partsa, "/");
curr_dir = parts[n]
n++;
- parts[n] = file;
- inputfile = "/" parts[2];
+ partsa[n] = file;
+ inputfile = "/" partsa[2];
for ( i = 3; i <= n; i++ ) {
- if ( length(inputfile) + length(parts[i]) < linelen - 1) {
- inputfile = inputfile "/" parts[i];
- } else {
- inputfile = inputfile "%\n /" parts[i];
- }
+ inputfile = inputfile "/" partsa[i];
}
- n = split(file, parts, "/")
- n = split(parts[n], parts, ".")
- identifier = parts[1]
- for ( i = 2; i < n; i++ )
- {
- identifier = identifier parts[i]
+ if ( debugmode ) printf("inputfile: %s\n", inputfile) > "/dev/stderr"
+ n = split(file, partsa, "/")
+ n = split(partsa[n], partsb, ".")
+ identifier = partsb[1]
+ for ( i = 2; i < n; i++ ) {
+ identifier = identifier partsb[i]
+ }
+ if ( respect_extension ) {
+ identifier = identifier partsb[n]
}
identifier = replace_characters(identifier)
if ( debugmode ) printf("identifier: %s\n", identifier) > "/dev/stderr"
@@ -127,10 +122,9 @@ BEGIN {
} else if ( match(text[1], "combine") ) {
combine = 1;
}
- for ( i = 2; i <= n; i++ ) {
- }
} else if ( match($2, /be/) > 0 ) {
opening_tag = $(NF)
+ gsub(/\r/, "", opening_tag);
is_opened = 1
start = $1 + 1; # entry $1 is the line number
} else if ( match($2, /ee/) > 0 ) {
@@ -145,12 +139,14 @@ BEGIN {
" not closed")
} else {
opening_tag = $(NF)
+ gsub(/\r/, "", opening_tag);
is_opened = 1
start = $1 + 1; # entry $1 is the line number
}
} else {
if ( match($2, /ee/) > 0 ) {
closing_tag = $(NF)
+ gsub(/\r/, "", closing_tag);
if ( !is_opened ) {
print_error($1, "missing \"be\" declaration")
} else if ( opening_tag == closing_tag ) {
@@ -163,7 +159,7 @@ BEGIN {
# setup the prefixes
len = linelen - addchars - length(caption);
- begin_prefix = "%";
+ begin_prefix = "%%";
cnt = 0;
while ( cnt < len) {
begin_prefix = begin_prefix "=";
@@ -171,7 +167,7 @@ BEGIN {
};
begin_prefix = begin_prefix ">";
end_prefix = begin_prefix;
- sub(/%/, "%==", end_prefix);
+ sub(/%%/, "%%==", end_prefix);
if ( join ) {
linerange = linerange ", " start "-" $1-1;
} else if ( combine ) {
@@ -182,7 +178,8 @@ BEGIN {
combine_array[closing_tag] = \
combine_array[closing_tag] ", " start "-" $1-1
}
- if ( debugmode ) printf("combine_array: >%s<\n", combine_array[closing_tag]) > "/dev/stderr"
+ if ( debugmode ) printf("combine_array: >%s<\n",\
+ combine_array[closing_tag]) > "/dev/stderr"
} else {
linerange = start "-" $1-1;
if ( command ) {
@@ -205,6 +202,9 @@ BEGIN {
}
}
END {
+ if ( is_opened ) {
+ print_error($1, "missing \"ee\" declaration")
+ }
caption = "\\lstinline|" file "|";
if ( join ) {
sub(/, /, "", linerange);
@@ -218,7 +218,7 @@ END {
for ( range in combine_array ) {
if ( debugmode ) printf("range: %s, combine_array[range]: >%s<\n", \
range, combine_array[range]) > "/dev/stderr"
- printf "%%-->> %s <<--\n", range
+ printf("%%%%-->> %s <<--\n", range)
if ( command ) {
print_command((identifier toB26(idPt++)), \
combine_array[range]);
@@ -229,8 +229,7 @@ END {
}
}
}
-function replace_characters(identifier)
-{
+function replace_characters(identifier) {
tmp = ""
toUppercase = 0
n = split(identifier, sequence, "") # split the string into an array
@@ -251,16 +250,14 @@ function replace_characters(identifier)
}
return tmp
}
-function print_command(identifier, linerange)
-{
+function print_command(identifier, linerange) {
if ( debugmode ) printf("print_command.linerange: >%s<\n", linerange) > "/dev/stderr"
print break_line("\\lstdef{" identifier "}{" inputfile "}{" \
linerange "}", linelen)
}
-function print_linerange(identifier, caption, linerange)
-{
- print begin_prefix "begin{" \
- identifier"}\n{%\n \\def\\inputfile{%\n " inputfile "\n }";
+function print_linerange(identifier, caption, linerange) {
+ print break_line(begin_prefix "begin{" \
+ identifier"}\n{%\n \\def\\inputfile{%\n " inputfile "%\n }");
local_style = "";
if ( header )
{
@@ -271,7 +268,7 @@ function print_linerange(identifier, caption, linerange)
local_style="style=localStyle, "
}
print " \\lstinputlisting[" local_style "linerange={" linerange "}]" \
- "%\n {%\n \\inputfile\n }\n" \
+ "{%\n \\inputfile\n }\n" \
"}%\n" end_prefix "end{"identifier"}";
}
function print_error(linenumber, error_message)
@@ -279,8 +276,7 @@ function print_error(linenumber, error_message)
printf "%--> error (line number %d): %s\n", \
linenumber, error_message > "/dev/stderr"
}
-function break_line(input_line, line_len)
-{
+function break_line(input_line, line_len) {
n = split(input_line, parts, "/");
output_line = parts[1];
len_curr = length(output_line)
@@ -288,24 +284,21 @@ function break_line(input_line, line_len)
len_next = length(parts[i])
if ( len_curr + len_next + 1 < linelen ) {
output_line = output_line "/" parts[i];
- len_curr += len_next + 1
+ len_curr += len_next + 1 # continue current line
} else {
output_line = output_line "%\n /" parts[i];
- len_curr = len_next + 7
+ len_curr = len_next + 7 # start a new line
}
}
return output_line
}
-function toB26(n10)
-{
+function toB26(n10) {
v26 = ""
-
do {
remainder = n10%26
v26 = idArray[remainder+1] v26
n10 = int(n10/26)
} while ( n10 != 0 )
-
return v26
}
'
@@ -316,8 +309,8 @@ if [ $# -eq 0 ]
then
show_usage
fi
-GETOPT=$(getopt -o cghno:: \
- --longoptions command,debug-mode,help,no-header,output-file:: \
+GETOPT=$(getopt -o ceghno:: \
+ --longoptions command,debug-mode,ext,help,no-header,output-file:: \
-n ${ME} -- "$@")
if [ $? -ne 0 ] # no success
@@ -331,6 +324,7 @@ while true
do
case "$1" in
-c|--command) COMMAND=1; HEADER=0; shift;;
+ -e|--ext) EXTENSION=1; shift;;
-g|--debug-mode) DEBUG_MODE=1; shift;;
-h|--help) show_usage ;;
-n|--no-header) HEADER=0; shift;;
@@ -347,7 +341,7 @@ if [ -n "${OUTFILE}" ]
then
if [ -f "${OUTFILE}" ]
then
- echo -e "%--> file \"${OUTFILE}\" already exists, creating backup"
+ printf "%s\n" "%--> file \"${OUTFILE}\" already exists, creating backup"
mv ${OUTFILE} ${OUTFILE}~
fi
exec > ${OUTFILE} # redirect stdout to ${OUTFILE}
@@ -357,8 +351,8 @@ fi
# now take all remaining arguments (should be all filenames) and do the job
for arg do
- echo -e "%-->" processing file \"$arg\" 1>&2 # echo the current
- # filename to stderr
+ printf "%s\n" "%%--> processing file \"$arg\"" 1>&2 # echo the current
+ # filename to stderr
do_the_job $arg
done
diff --git a/Build/source/texk/texlive/linked_scripts/luaotfload/mkluatexfontdb.lua b/Build/source/texk/texlive/linked_scripts/luaotfload/mkluatexfontdb.lua
index 5eac9ae06af..dc46ac09c22 100755
--- a/Build/source/texk/texlive/linked_scripts/luaotfload/mkluatexfontdb.lua
+++ b/Build/source/texk/texlive/linked_scripts/luaotfload/mkluatexfontdb.lua
@@ -18,11 +18,6 @@ local version = '1.07' -- same version number as luaotfload
local names = fonts.names
--- the directory in which the database will be saved, can be overwritten
-local output_directory = names.path.localdir
-
-local log = logs.report
-
local function help_msg()
texio.write(string.format([[
Usage: %s [OPTION]...
@@ -30,19 +25,16 @@ Usage: %s [OPTION]...
Rebuild the LuaTeX font database.
Valid options:
- -d --database-dir=DIRECTORY install the database in the specified directory
-f --force force re-indexing all fonts
-q --quiet don't output anything
-v --verbose=LEVEL be more verbose (print the searched directories)
-vv print the loaded fonts
-vvv print all steps of directory searching
- --fc-cache run fc-cache before updating database
- --sys install the database system-wide
- (default is only for the current user)
-V --version print version and exit
-h --help print this message
-Fonts are found on Windows or Linux with fontconfig (i.e., using fc-list).
+Fonts are searched in directories on Windows or Linux that have been
+searched with fontconfig.
On Mac OS X, only fonts located in the following standard font locations are
included in the font database:
@@ -51,18 +43,19 @@ included in the font database:
In particular, fonts installed in other locations with "Font Book" will not
be available.
-Rather than using fontconfig or, in Mac OS X, searching the default font
-locations, you may specify where fonts are located by setting the OSFONTDIR
-environment variable. Doing so will override the use of fontconfig or
-scanning the system font locations. If you wish to continue to include them
-while also including other font locations, simply include them in the list.
-For example (using a bash shell),
+Rather than using the fontconfig locations or, in Mac OS X, searching the
+default system font locations, you may specify where fonts are located by
+setting the OSFONTDIR environment variable. Doing so will override the use of
+fontconfig or scanning the system font locations. If you wish to continue to
+include them while also including other font locations, simply include them in
+the list. For example (using a bash shell),
export OSFONTDIR='/path/to/other/fonts:/Users/will/Library/Fonts:...'
-The output database file is named otfl-fonts.lua. By default it is placed
-in $TEXMFVAR/luatex/generic/luaotfload/names.
-]], name))
+The output database file is named otfl-fonts.lua and is placed under:
+
+ %s"
+]], name, names.path.localdir))
end
local function version_msg()
@@ -76,38 +69,15 @@ Here we fill cmdargs with the good values, and then analyze it.
--]]
local long_opts = {
- ['database-dir'] = "d",
force = "f",
quiet = "q",
help = "h",
- ['fc-cache'] = 0 ,
- sys = 0 ,
verbose = 1 ,
version = "V",
}
-local short_opts = "d:fqpvVh"
-
-local function do_run_fc_cache(c)
- --[[
- Function running fc-cache if needed.
- The argument is nil for default, 0 for no fc-cache and 1 for fc-cache.
- Default behaviour is to run fc-cache if available.
- --]]
- if c == 0 then return end
- if not c then
- -- TODO: detect if fc-cache is available
- end
- local toexec = 'fc-cache'
- if os.type == 'windows' then
- toexec = 'fc-cache.exe' -- TODO: to test on a non-cygwin Windows
- end
- log('executing %s...\n', toexec)
- os.execute(toexec)
-end
+local short_opts = "fqpvVh"
--- a temporary variable, containing the command line option concerning fc-cache
-local run_fc_cache = 0
local force_reload = nil
local function process_cmdline()
@@ -128,54 +98,19 @@ local function process_cmdline()
elseif v == "h" then
help_msg()
os.exit(0)
- elseif v == "d" then
- output_directory = optarg [i]
elseif v == "f" then
force_reload = 1
- elseif v == "fc-cache" then
- run_fc_cache = 1
- elseif v == "sys" then
- output_directory = names.path.systemdir
end
end
- if string.match(arg[0], '-sys') then
- output_directory = names.path.systemdir
- end
- output_directory = fonts.path_normalize(output_directory)
names.set_log_level(log_level)
end
-process_cmdline()
-do_run_fc_cache(run_fc_cache)
-
local function generate(force)
- log("generating font names database.")
- local savepath = output_directory
- if not lfs.isdir(savepath) then
- log("creating directory %s", savepath)
- dir.mkdirs(savepath)
- if not lfs.isdir(savepath) then
- texio.write_nl(string.format("Error: cannot create directory '%s', exiting.\n", savepath))
- os.exit(1)
- end
- end
- savepath = savepath .. '/' .. names.path.basename
- local fh = io.open(savepath, 'a+')
- if not fh then
- texio.write_nl(string.format("Error: cannot write file '%s', exiting.\n", savepath))
- os.exit(1)
- end
- fh:close()
- local fontnames
- if not force_reload and file.isreadable(savepath) then
- fontnames = dofile(savepath)
- else
- fontnames = nil
- end
+ local fontnames, saved
fontnames = names.update(fontnames, force)
- log("%s fonts in the database", #fontnames.mappings)
- io.savedata(savepath, table.serialize(fontnames, true))
- log("saved font names database in %s\n", savepath)
+ logs.report("%s fonts in the database", #fontnames.mappings)
+ saved = names.save(fontnames)
end
+process_cmdline()
generate(force_reload)
diff --git a/Build/source/texk/texlive/linked_scripts/mkgrkindex/mkgrkindex b/Build/source/texk/texlive/linked_scripts/mkgrkindex/mkgrkindex
index a4d30438f1b..f63f3316cfc 100644..100755
--- a/Build/source/texk/texlive/linked_scripts/mkgrkindex/mkgrkindex
+++ b/Build/source/texk/texlive/linked_scripts/mkgrkindex/mkgrkindex
@@ -55,7 +55,7 @@ if ($argc == 0 || $argc > 4 ) # no command line arguments or more than 3
}
else
{
- print "This is mkgrkindex (previously known as mkindex) version 2.0\n";
+ print "This is mkgrkindex (previously known as mkindex) version 2.2\n";
$style = 0;
$is_latin = 0;
@@ -116,14 +116,14 @@ $old_file="$index_file.old";
if ($is_latin == 0)
{
rename $index_file, $old_file;
- open(OLD, "$old_file") || die "Can't open file $old_file\n";
+ open(OLD, "<$old_file") || die "Can't open file $old_file\n";
open(NEW, ">$index_file") || die "Can't open file $index_file\n";
while (<OLD>)
{
#\x7B = {, \x7D = },\x40 = @, \x7C = |, \x21 = !, \x28 = (, \x29 = )
chomp;
- #Nikos Platis has suggested to replace the original code with a better
+ #Nikos Platis has suggested the replacement of the original code with a better
#parsing method. The code that follows is essentially Nikos's code with
#some minnor modifications, as explained in the documentation.
$newentry = "";
@@ -159,7 +159,7 @@ if ($is_latin == 0)
}
rename $index_file, $old_file;
-open(OLD, "$old_file") || die "Can't open file $old_file\n";
+open(OLD, "<$old_file") || die "Can't open file $old_file\n";
open(NEW, ">$index_file") || die "Can't open file $index_file\n";
while(<OLD>)
@@ -183,11 +183,11 @@ unlink $old_file;
if ($style < 0)
{
- system("$makeindex -s alpha.ist $file");
+ system("$makeindex -s lowercase-headers.ist $file");
}
elsif ($style > 0)
{
- system("$makeindex -s Alpha.ist $file");
+ system("$makeindex -s uppercase-headers.ist $file");
}
else
{
@@ -231,7 +231,7 @@ if ($style != 0)
if ($is_latin == 0)
{
rename $ind_file, $old_file;
- open(OLD, "$old_file") || die "Can't open file $old_file\n";
+ open(OLD, "<$old_file") || die "Can't open file $old_file\n";
open(NEW, ">$ind_file") || die "Can't open file $ind_file\n";
while (<OLD>)
@@ -253,7 +253,7 @@ if ($style != 0)
}
rename $ind_file, $old_file;
- open(OLD, "$old_file") || die "Can't open file $old_file\n";
+ open(OLD, "<$old_file") || die "Can't open file $old_file\n";
open(NEW, ">$ind_file") || die "Can't open file $ind_file\n";
while (<OLD>)
diff --git a/Build/source/texk/texlive/linked_scripts/oberdiek/pdfatfi.pl b/Build/source/texk/texlive/linked_scripts/oberdiek/pdfatfi.pl
index dec7f96defd..e33859ce598 100755
--- a/Build/source/texk/texlive/linked_scripts/oberdiek/pdfatfi.pl
+++ b/Build/source/texk/texlive/linked_scripts/oberdiek/pdfatfi.pl
@@ -26,8 +26,8 @@ $^W=1; # turn warning on
#
my $file = "pdfatfi.pl";
my $program = uc($&) if $file =~ /^\w+/;
-my $version = "2.4";
-my $date = "2008/07/29";
+my $version = "2.5";
+my $date = "2009/09/25";
my $author = "Heiko Oberdiek";
my $copyright = "Copyright (c) 2005, 2006 by $author.";
#
diff --git a/Build/source/texk/texlive/linked_scripts/pdfcrop/pdfcrop.pl b/Build/source/texk/texlive/linked_scripts/pdfcrop/pdfcrop.pl
index e69de29bb2d..5e43ec024fd 100755
--- a/Build/source/texk/texlive/linked_scripts/pdfcrop/pdfcrop.pl
+++ b/Build/source/texk/texlive/linked_scripts/pdfcrop/pdfcrop.pl
@@ -0,0 +1,1085 @@
+#!/usr/bin/env perl
+use strict;
+$^W=1; # turn warning on
+#
+# pdfcrop.pl
+#
+# Copyright (C) 2002, 2004, 2005, 2008-2010 Heiko Oberdiek.
+#
+# This program may be distributed and/or modified under the
+# conditions of the LaTeX Project Public License, either version 1.2
+# of this license or (at your option) any later version.
+# The latest version of this license is in
+# http://www.latex-project.org/lppl.txt
+# and version 1.2 or later is part of all distributions of LaTeX
+# version 1999/12/01 or later.
+#
+# See file "README" for a list of files that belong to this project.
+#
+# This file "pdfcrop.pl" may be renamed to "pdfcrop"
+# for installation purposes.
+#
+my $file = "pdfcrop.pl";
+my $program = uc($&) if $file =~ /^\w+/;
+my $version = "1.31";
+my $date = "2010/09/17";
+my $author = "Heiko Oberdiek";
+my $copyright = "Copyright (c) 2002-2010 by $author.";
+#
+# Reqirements: Perl5, Ghostscript
+# History:
+# 2002/10/30 v1.0: First release.
+# 2002/10/30 v1.1: Option --hires added.
+# 2002/11/04 v1.2: "nul" instead of "/dev/null" for windows.
+# 2002/11/23 v1.3: Use of File::Spec module's "devnull" call.
+# 2002/11/29 v1.4: Option --papersize added.
+# 2004/06/24 v1.5: Clear map file entries so that pdfTeX
+# does not touch the fonts.
+# 2004/06/26 v1.6: Use mgs.exe instead of gswin32c.exe for MIKTEX.
+# 2005/03/11 v1.7: Support of spaces in file names
+# (open("-|") is used for ghostscript call).
+# 2008/01/09 v1.8: Fix for moving the temporary file to the output
+# file across file system boundaries.
+# 2008/04/05 v1.9: Options --resolution and --bbox added.
+# 2008/07/16 v1.10: Support for XeTeX added with new options
+# --pdftex, --xetex, and --xetexcmd.
+# 2008/07/22 v1.11: Workaround for open("-|").
+# 2008/07/23 v1.12: Workarounds for the workaround (error detection, ...).
+# 2008/07/24 v1.13: open("-|")/workaround removed.
+# Input files with unsafe file names are linked/copied
+# to temporary file with safe file name.
+# 2008/09/12 v1.14: Error detection for invalid Bounding Boxes.
+# 2009/07/14 v1.15: Fix for negative coordinates in Bounding Boxes
+# (David Menestrina).
+# 2009/07/16 v1.16: Security fixes:
+# * -dSAFER added for Ghostscript,
+# * -no-shell-escape added for pdfTeX/XeTeX.
+# 2009/07/17 v1.17: Security fixes:
+# * Backticks and whitespace are forbidden
+# for options --(gs|pdftex|xetex)cmd.
+# * Validation of options --papersize and --resolution.
+# 2009/07/18 v1.18: * Restricted mode added.
+# * Option --version added.
+# 2009/09/24 v1.19: * Ghostscript detection rewritten.
+# * Cygwin: `gs' is preferred to `gswin32c'.
+# 2009/10/06 v1.20: * File name sanitizing in .tex file.
+# 2009/12/21 v1.21: * Option --ini added for IniTeX mode.
+# * Option --luatex and --luatexcmd added for LuaTeX.
+# 2009/12/29 v1.22: * Syntax description for option --bbox fixed
+# (Lukas Prochazka).
+# 2010/01/09 v1.23: * Options --bbox-odd and -bbox-even added.
+# 2010/08/16 v1.24: * Workaround added for buggy ghostscript ports
+# that print the BoundingBox data twice.
+# 2010/08/26 v1.25: * Fix for the case that the PDF file contains
+# an entry /CropBox different to /MediaBox.
+# * \pageinclude implemented for XeTeX.
+# * XeTeX: --clip does not die, but this option
+# is ignored, because XeTeX always clip.
+# 2010/08/26 v1.26: * XeTeX's \XeTeXpdffile expects keyword
+# `media', not `mediabox'.
+# * New option --pdfversion.
+# Default is `auto' that means the PDF version
+# is inherited from the input file. Before
+# pdfcrop has used the TeX engine's default.
+# * Option --luatex fixed (extra empty page at end).
+# 2010/09/03 v1.27: * Workaround of v1.24 fixed.
+# 2010/09/06 v1.28: * The Windows registry is searched if Ghostscript
+# is not found via PATH.
+# * Windows only: support of spaces in command
+# names in unrestricted mode.
+# 2010/09/06 v1.29: * Find the latest Ghostscript version in registry.
+# 2010/09/15 v1.30: * Warning of pdfTeX because of \pdfobjcompresslevel
+# avoided when reducing \pdfminorversion.
+# * Fix for TeX syntax characters in input file names.
+# 2010/09/17 v1.31: * Passing the input file name via hex string to TeX.
+# * Again input file names restricted for Ghostscript
+# command line, switch then to symbol link/copy
+# method.
+
+### program identification
+my $title = "$program $version, $date - $copyright\n";
+
+### error strings
+my $Error = "!!! Error:"; # error prefix
+
+### make ENV safer
+delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
+
+# Windows detection (no SIGHUP)
+my $Win = 0;
+$Win = 1 if $^O =~ /mswin32/i;
+$Win = 1 if $^O =~ /cygwin/i;
+
+### string constants for Ghostscript run
+# get Ghostscript command name
+$::opt_gscmd = '';
+sub find_ghostscript () {
+ return if $::opt_gscmd;
+ my $system = 'unix';
+ $system = "dos" if $^O =~ /dos/i;
+ $system = "os2" if $^O =~ /os2/i;
+ $system = "win" if $^O =~ /mswin32/i;
+ $system = "cygwin" if $^O =~ /cygwin/i;
+ $system = "miktex" if defined($ENV{"TEXSYSTEM"}) and
+ $ENV{"TEXSYSTEM"} =~ /miktex/i;
+ print "* System: $system\n" if $::opt_debug;
+ my %candidates = (
+ 'unix' => [qw|gs gsc|],
+ 'dos' => [qw|gs386 gs|],
+ 'os2' => [qw|gsos2 gs|],
+ 'win' => [qw|gswin32c gs|],
+ 'cygwin' => [qw|gs gswin32c|],
+ 'miktex' => [qw|mgs gswin32c gs|]
+ );
+ my %ext = (
+ 'unix' => '',
+ 'dos' => '.exe',
+ 'os2' => '.exe',
+ 'win' => '.exe',
+ 'cygwin' => '.exe',
+ 'miktex' => '.exe'
+ );
+ my $candidates_ref = $candidates{$system};
+ my $ext = $ext{$system};
+ use File::Spec;
+ my @path = File::Spec->path();
+ my $found = 0;
+ foreach my $candidate (@$candidates_ref) {
+ foreach my $dir (@path) {
+ my $file = File::Spec->catfile($dir, "$candidate$ext");
+ if (-x $file) {
+ $::opt_gscmd = $candidate;
+ $found = 1;
+ print "* Found ($candidate): $file\n" if $::opt_debug;
+ last;
+ }
+ print "* Not found ($candidate): $file\n" if $::opt_debug;
+ }
+ last if $found;
+ }
+ if (not $found and $Win) {
+ $found = SearchRegistry();
+ }
+ if ($found) {
+ print "* Autodetected ghostscript command: $::opt_gscmd\n" if $::opt_debug;
+ }
+ else {
+ $::opt_gscmd = $$candidates_ref[0];
+ print "* Default ghostscript command: $::opt_gscmd\n" if $::opt_debug;
+ }
+}
+
+sub SearchRegistry () {
+ my $found = 0;
+ eval 'use Win32::TieRegistry qw|KEY_READ REG_SZ|;';
+ if ($@) {
+ if ($::opt_debug) {
+ print "* Registry lookup for Ghostscript failed:\n";
+ my $msg = $@;
+ $msg =~ s/\s+$//;
+ foreach (split /\r?\n/, $msg) {
+ print " $_\n";
+ }
+ }
+ return $found;
+ }
+ my $open_params = {Access => KEY_READ(), Delimiter => '/'};
+ my $key_name_software = 'HKEY_LOCAL_MACHINE/SOFTWARE/';
+ my $current_key = $key_name_software;
+ my $software = new Win32::TieRegistry $current_key, $open_params;
+ if (not $software) {
+ print "* Cannot find or access registry key `$current_key'!\n"
+ if $::opt_verbose;
+ return $found;
+ }
+ print "* Search registry at `$current_key'.\n" if $::opt_debug;
+ my %list;
+ foreach my $key_name_gs (grep /Ghostscript/i, $software->SubKeyNames()) {
+ $current_key = "$key_name_software$key_name_gs/";
+ print "* Registry entry found: $current_key\n" if $::opt_debug;
+ my $key_gs = $software->Open($key_name_gs, $open_params);
+ if (not $key_gs) {
+ print "* Cannot open registry key `$current_key'!\n" if $::opt_debug;
+ next;
+ }
+ foreach my $key_name_version ($key_gs->SubKeyNames()) {
+ $current_key = "$key_name_software$key_name_gs/$key_name_version/";
+ print "* Registry entry found: $current_key\n" if $::opt_debug;
+ if (not $key_name_version =~ /^(\d+)\.(\d+)$/) {
+ print " The sub key is not a version number!\n" if $::opt_debug;
+ next;
+ }
+ my $version_main = $1;
+ my $version_sub = $2;
+ $current_key = "$key_name_software$key_name_gs/$key_name_version/";
+ my $key_version = $key_gs->Open($key_name_version, $open_params);
+ if (not $key_version) {
+ print "* Cannot open registry key `$current_key'!\n" if $::opt_debug;
+ next;
+ }
+ $key_version->FixSzNulls(1);
+ my ($value, $type) = $key_version->GetValue('GS_DLL');
+ if ($value and $type == REG_SZ()) {
+ print " GS_DLL = $value\n" if $::opt_debug;
+ $value =~ s|([\\/])([^\\/]+\.dll)$|$1gswin32c.exe|i;
+ if (-f $value) {
+ print " EXE found: $value\n" if $::opt_debug;
+ }
+ else {
+ print " EXE not found!\n" if $::opt_debug;
+ next;
+ }
+ my $sortkey = sprintf '%02d.%03d %s',
+ $version_main, $version_sub, $key_name_gs;
+ $list{$sortkey} = $value;
+ }
+ else {
+ print " Missing key `GS_DLL' with type `REG_SZ'!\n" if $::opt_debug;
+ }
+ }
+ }
+ foreach my $entry (reverse sort keys %list) {
+ $::opt_gscmd = $list{$entry};
+ print "* Found (via registry): $::opt_gscmd\n" if $::opt_debug;
+ $found = 1;
+ last;
+ }
+ return $found;
+}
+
+# restricted mode
+my $restricted = 0;
+if ($0 =~ /rpdfcrop/ or $0 =~ /restricted/) {
+ $restricted = 1;
+}
+
+# "null" device
+use File::Spec::Functions qw(devnull);
+my $null = devnull();
+
+### variables
+my $inputfile = "";
+my $outputfile = "";
+my $tmp = "tmp-\L$program\E-$$";
+
+### paper sizes
+
+my @papersizes = qw[
+ 11x17 ledger legal letter lettersmall
+ archE archD archC archB archA
+ a0 a1 a2 a3 a4 a4small a5 a6 a7 a8 a9 a10
+ isob0 isob1 isob2 isob3 isob4 isob5 isob6
+ c0 c1 c2 c3 c4 c5 c6
+ jisb0 jisb1 jisb2 jisb3 jisb4 jisb5 jisb6
+ b0 b1 b2 b3 b4 b5
+ flsa flse halfletter
+];
+my %papersizes;
+foreach (@papersizes) {
+ $papersizes{$_} = 1;
+}
+
+### option variables
+my @bool = ("false", "true");
+$::opt_version = 0;
+$::opt_help = 0;
+$::opt_debug = 0;
+$::opt_verbose = 0;
+$::opt_pdftexcmd = "pdftex";
+$::opt_xetexcmd = "xetex";
+$::opt_luatexcmd = "luatex";
+$::opt_tex = "pdftex";
+$::opt_margins = "0 0 0 0";
+$::opt_clip = 0;
+$::opt_hires = 0;
+$::opt_papersize = "";
+$::opt_resolution = "";
+$::opt_bbox = "";
+$::opt_bbox_odd = "";
+$::opt_bbox_even = "";
+$::opt_initex = 0;
+$::opt_pdfversion = "auto";
+
+sub usage ($) {
+ my $ret = shift;
+ find_ghostscript();
+ my $usage = <<"END_OF_USAGE";
+${title}Syntax: \L$program\E [options] <input[.pdf]> [output file]
+Function: Margins are calculated and removed for each page in the file.
+Options: (defaults:)
+ --help print usage
+ --version print version number
+ --(no)verbose verbose printing ($bool[$::opt_verbose])
+ --(no)debug debug informations ($bool[$::opt_debug])
+ --gscmd <name> call of ghostscript ($::opt_gscmd)
+ --pdftex | --xetex | --luatex
+ use pdfTeX | use XeTeX | use LuaTeX ($::opt_tex)
+ --pdftexcmd <name> call of pdfTeX ($::opt_pdftexcmd)
+ --xetexcmd <name> call of XeTeX ($::opt_xetexcmd)
+ --luatexcmd <name> call of LuaTeX ($::opt_luatexcmd)
+ --margins "<left> <top> <right> <bottom>" ($::opt_margins)
+ add extra margins, unit is bp. If only one number is
+ given, then it is used for all margins, in the case
+ of two numbers they are also used for right and bottom.
+ --(no)clip clipping support, if margins are set ($bool[$::opt_clip])
+ (not available for --xetex)
+ --(no)hires using `%%HiResBoundingBox' ($bool[$::opt_hires])
+ instead of `%%BoundingBox'
+ --(no)ini use iniTeX variant of the TeX compiler ($bool[$::opt_initex])
+Expert options:
+ --restricted turn on restricted mode ($bool[$restricted])
+ --papersize <foo> parameter for gs's -sPAPERSIZE=<foo>,
+ use only with older gs versions <7.32 ($::opt_papersize)
+ --resolution <xres>x<yres> ($::opt_resolution)
+ --resolution <res> pass argument to ghostscript's option -r
+ Example: --resolution 72
+ --bbox "<left> <bottom> <right> <top>" ($::opt_bbox)
+ override bounding box found by ghostscript
+ with origin at the lower left corner
+ --bbox-odd Same as --bbox, but for odd pages only ($::opt_bbox_odd)
+ --bbox-even Same as --bbox, but for even pages only ($::opt_bbox_even)
+ --pdfversion <1.x> | auto | none
+ Set the PDF version to 1.x, 1 < x < 8.
+ If `auto' is given as value, then the
+ PDF version is taken from the header
+ of the input PDF file.
+ An empty value or `none' uses the
+ default of the TeX engine. ($::opt_pdfversion)
+
+Examples:
+ \L$program\E --margins 10 input.pdf output.pdf
+ \L$program\E --margins '5 10 5 20' --clip input.pdf output.pdf
+In case of errors:
+ Try option --verbose first to get more information.
+In case of bugs:
+ Please, use option --debug for bug reports.
+END_OF_USAGE
+ if ($ret) {
+ die $usage;
+ }
+ else {
+ print $usage;
+ exit(0);
+ }
+}
+
+### process options
+my @OrgArgv = @ARGV;
+use Getopt::Long;
+GetOptions(
+ "help!",
+ "version!",
+ "debug!",
+ "verbose!",
+ "gscmd=s",
+ "pdftexcmd=s",
+ "xetexcmd=s",
+ "luatexcmd=s",
+ "pdftex" => sub { $::opt_tex = 'pdftex'; },
+ "xetex" => sub { $::opt_tex = 'xetex'; },
+ "luatex" => sub { $::opt_tex = 'luatex'; },
+ "initex!",
+ "margins=s",
+ "clip!",
+ "hires!",
+ "papersize=s",
+ "resolution=s",
+ "bbox=s",
+ "bbox-odd=s" => \$::opt_bbox_odd,
+ "bbox-even=s" => \$::opt_bbox_even,
+ "restricted" => sub { $restricted = 1; },
+ "pdfversion=s" => \$::opt_pdfversion,
+) or usage(1);
+!$::opt_help or usage(0);
+
+if ($::opt_version) {
+ print "$version\n";
+ exit(0);
+}
+
+$::opt_verbose = 1 if $::opt_debug;
+
+@ARGV >= 1 or usage(1);
+
+print $title;
+
+print "* Restricted mode: ", ($restricted ? "enabled" : "disabled"), "\n"
+ if $::opt_debug;
+
+$::opt_pdfversion =~ /^(|none|auto|1\.([2-7]))$/
+ or die "!!! Error: Invalid value `$::opt_pdfversion' for option `--pdfversion'!\n";
+print "* Option `pdfversion': $::opt_pdfversion\n" if $::opt_debug;
+$::opt_pdfversion = $2 if $2;
+$::opt_pdfversion = '' if $::opt_pdfversion eq 'none';
+
+find_ghostscript();
+
+if ($::opt_bbox) {
+ $::opt_bbox =~ s/^\s+//;
+ $::opt_bbox =~ s/\s+$//;
+ $::opt_bbox =~ s/\s+/ /;
+ if ($::opt_bbox =~ /^-?\d*\.?\d+ -?\d*\.?\d+ -?\d*\.?\d+ -?\d*\.?\d+$/) {
+ print "* Explicite Bounding Box: $::opt_bbox\n" if $::opt_debug;
+ }
+ else {
+ die "$Error Parse error (option --bbox \"$::opt_bbox\")!\n";
+ }
+}
+if ($::opt_bbox_odd) {
+ $::opt_bbox_odd =~ s/^\s+//;
+ $::opt_bbox_odd =~ s/\s+$//;
+ $::opt_bbox_odd =~ s/\s+/ /;
+ if ($::opt_bbox_odd =~ /^-?\d*\.?\d+ -?\d*\.?\d+ -?\d*\.?\d+ -?\d*\.?\d+$/) {
+ print "* Explicite Bounding Box for odd pages: $::opt_bbox_odd\n"
+ if $::opt_debug;
+ }
+ else {
+ die "$Error Parse error (option --bbox-odd \"$::opt_bbox_odd\")!\n";
+ }
+}
+if ($::opt_bbox_even) {
+ $::opt_bbox_even =~ s/^\s+//;
+ $::opt_bbox_even =~ s/\s+$//;
+ $::opt_bbox_even =~ s/\s+/ /;
+ if ($::opt_bbox_even =~ /^-?\d*\.?\d+ -?\d*\.?\d+ -?\d*\.?\d+ -?\d*\.?\d+$/) {
+ print "* Explicite Bounding Box for even pages: $::opt_bbox_even\n"
+ if $::opt_debug;
+ }
+ else {
+ die "$Error Parse error (option --bbox-even \"$::opt_bbox_even\")!\n";
+ }
+}
+
+@ARGV <= 2 or die "$Error Too many files!\n";
+
+### input file
+$inputfile = shift @ARGV;
+
+if (! -f $inputfile) {
+ if (-f "$inputfile.pdf") {
+ $inputfile .= ".pdf";
+ }
+ else {
+ die "$Error Input file `$inputfile' not found!\n";
+ }
+}
+
+print "* Input file: $inputfile\n" if $::opt_debug;
+
+if ($::opt_pdfversion eq 'auto') {
+ open(IN, '<', $inputfile) or die "!!! Error: Cannot open `$inputfile'!\n";
+ my $buf;
+ read(IN, $buf, 1024) or die "!!! Error: Cannot read the header of `$inputfile' failed!\n";
+ close(IN);
+ if ($buf =~ /%PDF-1.([0-7])\s/) {
+ $::opt_pdfversion = $1;
+ print "* PDF header: %PDF-1.$::opt_pdfversion\n" if $::opt_verbose;
+ $::opt_pdfversion = 2 if $::opt_pdfversion < 2;
+ }
+ else {
+ die "!!! Error: Cannot find PDF header of `$inputfile'!\n";
+ }
+}
+print '* Using PDF minor version: ',
+ ($::opt_pdfversion ? $::opt_pdfversion : "engine's default"),
+ "\n" if $::opt_debug;
+
+### output file
+if (@ARGV) {
+ $outputfile = shift @ARGV;
+}
+else {
+ $outputfile = $inputfile;
+ $outputfile =~ s/\.pdf$//i;
+ $outputfile .= "-crop.pdf";
+}
+
+print "* Output file: $outputfile\n" if $::opt_debug;
+
+### margins
+my ($llx, $lly, $urx, $ury) = (0, 0, 0, 0);
+if ($::opt_margins =~
+ /^\s*([\-\.\d]+)\s+([\-\.\d]+)\s+([\-\.\d]+)\s+([\-\.\d]+)\s*$/) {
+ ($llx, $lly, $urx, $ury) = ($1, $2, $3, $4);
+}
+else {
+ if ($::opt_margins =~ /^\s*([\-\.\d]+)\s+([\-\.\d]+)\s*$/) {
+ ($llx, $lly, $urx, $ury) = ($1, $2, $1, $2);
+ }
+ else {
+ if ($::opt_margins =~ /^\s*([\-\.\d]+)\s*$/) {
+ ($llx, $lly, $urx, $ury) = ($1, $1, $1, $1);
+ }
+ else {
+ die "$Error Parse error (option --margins)!\n";
+ }
+ }
+}
+print "* Margins: $llx $lly $urx $ury\n" if $::opt_debug;
+
+### papersize validation (security)
+if ($::opt_papersize ne '') {
+ $::opt_papersize =~ /^[0-9A-Za-z]+$/
+ or die "$Error Invalid papersize ($::opt_papersize)!\n";
+ $papersizes{$::opt_papersize}
+ or die "$Error Unknown papersize ($::opt_papersize),"
+ . " see ghostscript's documentation for option `-r'!\n";
+}
+
+### resolution validation (security)
+if ($::opt_resolution ne '') {
+ $::opt_resolution =~ /^\d+(x\d+)?$/
+ or die "$Error Invalid resolution ($::opt_resolution),"
+ . " see ghostscript's documentation!\n";
+}
+
+### command name validation (security)
+my %cmd = (
+ 'gscmd' => \$::opt_gscmd,
+ 'pdftexcmd' => \$::opt_pdftexcmd,
+ 'luatexcmd' => \$::opt_luatexcmd,
+ 'xetexcmd' => \$::opt_xetexcmd
+);
+foreach my $cmd (keys %cmd) {
+ my $cmd_ref = $cmd{$cmd};
+ $$cmd_ref =~ s/^\s+//;
+ $$cmd_ref =~ s/\s+$//;
+ my $val = $$cmd_ref;
+ next unless $val;
+ next unless $val;
+ if ($val =~ /`/) {
+ die "$Error Forbidden backtick for option `--$cmd' ($val)!\n";
+ }
+ if ($val =~ /\s/) {
+ my $err = 0;
+ if ($restricted or not $Win) {
+ $err = 1;
+ }
+ else {
+ $err = 1 if $val =~ /\.exe.*\s/i;
+ $err = 1 if $val =~ /\s[-\/@+]/;
+ }
+ die "$Error Forbidden whitespace for option `--$cmd' ($val)!\n" if $err;
+ }
+}
+if ($restricted) {
+ if ($::opt_pdftexcmd and $::opt_pdftexcmd ne 'pdftex') {
+ die "$Error pdfTeX program name must not be changed in restricted mode!\n";
+ }
+ if ($::opt_xetexcmd and $::opt_xetexcmd ne 'xetex') {
+ die "$Error XeTeX program name must not be changed in restricted mode!\n";
+ }
+ if ($::opt_luatexcmd and $::opt_luatexcmd ne 'luatex') {
+ die "$Error LuaTeX program name must not be changed in restricted mode!\n";
+ }
+ if ($::opt_gscmd) {
+ $::opt_gscmd =~ /^(gs|mgs|gswin32c|gs386|gsos2)$/
+ or $::opt_gscmd =~ /^gs[\-_]?(\d|\d[\.-_]?\d\d)c?$/
+ or die "$Error: Invalid Ghostscript program name in restricted mode!\n";
+ }
+}
+if ($Win and $::opt_gscmd =~ /\s/) {
+ $::opt_gscmd = "\"$::opt_gscmd\"";
+}
+
+### cleanup system
+my @unlink_files = ();
+my $exit_code = 1;
+sub clean {
+ print "* Cleanup\n" if $::opt_debug;
+ if ($::opt_debug) {
+ print "* Temporary files: @unlink_files\n";
+ }
+ else {
+ for (; @unlink_files>0; ) {
+ unlink shift @unlink_files;
+ }
+ }
+}
+sub cleanup {
+ clean();
+ exit($exit_code);
+}
+$SIG{'INT'} = \&cleanup;
+$SIG{'__DIE__'} = \&clean;
+
+### Calculation of BoundingBoxes
+
+# use safe file name for use within cmd line of gs (unknown shell: space, ...)
+# and XeTeX (hash, curly braces, ...)
+my $inputfilesafe = $inputfile;
+if (not $inputfile =~ /^[\w\d\.\-\:\/@]+$/) { # /[\s\$~'"#{}%]/
+ $inputfilesafe = "$tmp-img.pdf";
+ push @unlink_files, $inputfilesafe;
+ my $symlink_exists = eval { symlink("", ""); 1 };
+ print "* Input file name `$inputfile' contains special characters.\n"
+ . "* " . ($symlink_exists ? "Link" : "Copy")
+ . " input file to temporary file `$inputfilesafe'.\n"
+ if $::opt_verbose;
+ if ($symlink_exists) {
+ symlink($inputfile, $inputfilesafe)
+ or die "$Error Link from `$inputfile' to"
+ . " `$inputfilesafe' failed: $!\n";
+ }
+ else {
+ use File::Copy;
+ copy($inputfile, $inputfilesafe)
+ or die "$Error Copy from `$inputfile' to"
+ . " `$inputfilesafe' failed: $!\n";
+ }
+}
+
+my @gsargs = (
+ "-sDEVICE=bbox",
+ "-dBATCH",
+ "-dNOPAUSE"
+);
+push @gsargs, "-sPAPERSIZE=$::opt_papersize" if $::opt_papersize;
+push @gsargs, "-r$::opt_resolution" if $::opt_resolution;
+push @gsargs,
+ "-c",
+ "save",
+ "pop",
+ "-f",
+ $inputfilesafe
+;
+
+my $tmpfile = "$tmp.tex";
+push @unlink_files, $tmpfile;
+open(TMP, ">$tmpfile") or
+ die "$Error Cannot write tmp file `$tmpfile'!\n";
+print TMP <<'END_TMP';
+\catcode37 14 % percent
+\catcode33 12 % exclam
+\catcode34 12 % quote
+\catcode35 6 % hash
+\catcode39 12 % apostrophe
+\catcode40 12 % left parenthesis
+\catcode41 12 % right parenthesis
+\catcode45 12 % minus
+\catcode46 12 % period
+\catcode60 12 % less
+\catcode61 12 % equals
+\catcode62 12 % greater
+\catcode64 12 % at
+\catcode91 12 % left square
+\catcode93 12 % right square
+\catcode96 12 % back tick
+\catcode123 1 % left curly brace
+\catcode125 2 % right curly brace
+\catcode126 12 % tilde
+\catcode`\#=6 %
+\escapechar=92 %
+\def\IfUndefined#1#2#3{%
+ \begingroup\expandafter\expandafter\expandafter\endgroup
+ \expandafter\ifx\csname#1\endcsname\relax
+ #2%
+ \else
+ #3%
+ \fi
+}
+END_TMP
+my $pdffilehex = unpack 'H*', $inputfilesafe;
+$pdffilehex = "\U$pdffilehex\E";
+print TMP "\\def\\pdffilehex{$pdffilehex}\n";
+print TMP <<'END_TMP';
+\IfUndefined{pdfunescapehex}{%
+ \begingroup
+ \gdef\pdffile{}%
+ \def\do#1#2{%
+ \ifx\relax#2\relax
+ \ifx\relax#1\relax
+ \else
+ \errmessage{Invalid hex string, should not happen!}%
+ \fi
+ \else
+ \lccode`0="#1#2\relax
+ \lowercase{%
+ \xdef\pdffile{\pdffile0}%
+ }%
+ \expandafter\do
+ \fi
+ }%
+ \expandafter\do\pdffilehex\relax\relax
+ \endgroup
+}{%
+ \edef\pdffile{\pdfunescapehex{\pdffilehex}}%
+}
+\immediate\write-1{Input file: \pdffile}
+END_TMP
+if ($::opt_tex eq 'luatex') {
+ print TMP <<'END_TMP';
+\begingroup\expandafter\expandafter\expandafter\endgroup
+\expandafter\ifx\csname directlua\endcsname\relax
+ \errmessage{LuaTeX not found!}%
+\else
+ \begingroup
+ \newlinechar=10 %
+ \endlinechar=\newlinechar %
+ \ifnum0%
+ \directlua{%
+ if tex.enableprimitives then
+ tex.enableprimitives('TEST', {
+ 'luatexversion',
+ 'pdfoutput',
+ 'pdfcompresslevel',
+ 'pdfhorigin',
+ 'pdfvorigin',
+ 'pdfpagewidth',
+ 'pdfpageheight',
+ 'pdfmapfile',
+ 'pdfximage',
+ 'pdflastximage',
+ 'pdfrefximage',
+ 'pdfminorversion',
+ 'pdfobjcompresslevel',
+ })
+ tex.print('1')
+ end
+ }%
+ \ifx\TESTluatexversion\UnDeFiNeD\else 1\fi %
+ =11 %
+ \global\let\luatexversion\luatexversion %
+ \global\let\pdfoutput\TESTpdfoutput %
+ \global\let\pdfcompresslevel\TESTpdfcompresslevel %
+ \global\let\pdfhorigin\TESTpdfhorigin %
+ \global\let\pdfvorigin\TESTpdfvorigin %
+ \global\let\pdfpagewidth\TESTpdfpagewidth %
+ \global\let\pdfpageheight\TESTpdfpageheight %
+ \global\let\pdfmapfile\TESTpdfmapfile %
+ \global\let\pdfximage\TESTpdfximage %
+ \global\let\pdflastximage\TESTpdflastximage %
+ \global\let\pdfrefximage\TESTpdfrefximage %
+ \global\let\pdfminorversion\TESTpdfminorversion %
+ \global\let\pdfobjcompresslevel\TESTpdfobjcompresslevel %
+ \else %
+ \errmessage{%
+ Missing \string\luatexversion %
+ }%
+ \fi %
+ \endgroup %
+\fi
+END_TMP
+}
+if ($::opt_tex eq 'pdftex' or $::opt_tex eq 'luatex') {
+ print TMP <<'END_TMP_HEAD';
+\pdfoutput=1 %
+\pdfcompresslevel=9 %
+\csname pdfmapfile\endcsname{}
+\def\setpdfversion#1{%
+ \IfUndefined{pdfobjcompresslevel}{%
+ }{%
+ \ifnum#1<5 %
+ \pdfobjcompresslevel=0 %
+ \else
+ \pdfobjcompresslevel=2 %
+ \fi
+ }%
+ \IfUndefined{pdfminorversion}{%
+ \IfUndefined{pdfoptionpdfminorversion}{%
+ }{%
+ \pdfoptionpdfminorversion=#1\relax
+ }%
+ }{%
+ \pdfminorversion=#1\relax
+ }%
+}
+\def\page #1 [#2 #3 #4 #5]{%
+ \count0=#1\relax
+ \setbox0=\hbox{%
+ \pdfximage page #1 mediabox{\pdffile}%
+ \pdfrefximage\pdflastximage
+ }%
+ \pdfhorigin=-#2bp\relax
+ \pdfvorigin=#3bp\relax
+ \pdfpagewidth=#4bp\relax
+ \advance\pdfpagewidth by -#2bp\relax
+ \pdfpageheight=#5bp\relax
+ \advance\pdfpageheight by -#3bp\relax
+ \ht0=\pdfpageheight
+ \shipout\box0\relax
+}
+\def\pageclip #1 [#2 #3 #4 #5][#6 #7 #8 #9]{%
+ \count0=#1\relax
+ \dimen0=#4bp\relax \advance\dimen0 by -#2bp\relax
+ \edef\imagewidth{\the\dimen0}%
+ \dimen0=#5bp\relax \advance\dimen0 by -#3bp\relax
+ \edef\imageheight{\the\dimen0}%
+ \pdfximage page #1 mediabox{\pdffile}%
+ \setbox0=\hbox{%
+ \kern -#2bp\relax
+ \lower #3bp\hbox{\pdfrefximage\pdflastximage}%
+ }%
+ \wd0=\imagewidth\relax
+ \ht0=\imageheight\relax
+ \dp0=0pt\relax
+ \pdfhorigin=#6pt\relax
+ \pdfvorigin=#7bp\relax
+ \pdfpagewidth=\imagewidth
+ \advance\pdfpagewidth by #6bp\relax
+ \advance\pdfpagewidth by #8bp\relax
+ \pdfpageheight=\imageheight\relax
+ \advance\pdfpageheight by #7bp\relax
+ \advance\pdfpageheight by #9bp\relax
+ \pdfxform0\relax
+ \shipout\hbox{\pdfrefxform\pdflastxform}%
+}%
+\def\pageinclude#1{%
+ \pdfhorigin=0pt\relax
+ \pdfvorigin=0pt\relax
+ \pdfximage page #1 mediabox{\pdffile}%
+ \setbox0=\hbox{\pdfrefximage\pdflastximage}%
+ \pdfpagewidth=\wd0\relax
+ \pdfpageheight=\ht0\relax
+ \advance\pdfpageheight by \dp0\relax
+ \shipout\hbox{%
+ \raise\dp0\box0\relax
+ }%
+}
+END_TMP_HEAD
+ print TMP "\\setpdfversion{$::opt_pdfversion}\n" if $::opt_pdfversion;
+}
+else { # XeTeX
+ print TMP <<'END_TMP_HEAD';
+\expandafter\ifx\csname XeTeXpdffile\endcsname\relax
+ \errmessage{XeTeX not found or too old!}%
+\fi
+\def\page #1 [#2 #3 #4 #5]{%
+ \count0=#1\relax
+ \setbox0=\hbox{%
+ \XeTeXpdffile "\pdffile" page #1 media\relax
+ }%
+ \pdfpagewidth=#4bp\relax
+ \advance\pdfpagewidth by -#2bp\relax
+ \pdfpageheight=#5bp\relax
+ \advance\pdfpageheight by -#3bp\relax
+ \shipout\hbox{%
+ \kern-1in%
+ \kern-#2bp%
+ \vbox{%
+ \kern-1in%
+ \kern#3bp%
+ \ht0=\pdfpageheight
+ \box0 %
+ }%
+ }%
+}
+\def\pageclip #1 [#2 #3 #4 #5][#6 #7 #8 #9]{%
+ \page {#1} [#2 #3 #4 #5]%
+}
+\def\pageinclude#1{%
+ \setbox0=\hbox{%
+ \XeTeXpdffile "\pdffile" page #1 media\relax
+ }%
+ \pdfpagewidth=\wd0\relax
+ \pdfpageheight=\ht0\relax
+ \advance\pdfpageheight by \dp0\relax
+ \shipout\hbox{%
+ \kern-1in%
+ \vbox{%
+ \kern-1in%
+ \ht0=\pdfpageheight
+ \box0 %
+ }%
+ }%
+}
+END_TMP_HEAD
+}
+
+print "* Running ghostscript for BoundingBox calculation ...\n"
+ if $::opt_verbose;
+print "* Ghostscript call: $::opt_gscmd @gsargs\n" if $::opt_debug;
+
+my @bbox;
+my @bbox_all;
+my @bbox_odd;
+my @bbox_even;
+if ($::opt_bbox) {
+ $::opt_bbox =~ /([-\d\.]+) ([-\d\.]+) ([-\d\.]+) ([-\d\.]+)/;
+ @bbox_all = ($1, $2, $3, $4);
+}
+if ($::opt_bbox_odd) {
+ $::opt_bbox_odd =~ /([-\d\.]+) ([-\d\.]+) ([-\d\.]+) ([-\d\.]+)/;
+ @bbox_odd = ($1, $2, $3, $4);
+}
+if ($::opt_bbox_even) {
+ $::opt_bbox_even =~ /([-\d\.]+) ([-\d\.]+) ([-\d\.]+) ([-\d\.]+)/;
+ @bbox_even = ($1, $2, $3, $4);
+}
+
+sub getbbox ($$$$$) {
+ my $page = shift;
+ my $a = shift;
+ my $b = shift;
+ my $c = shift;
+ my $d = shift;
+ if ($page % 2 == 1) {
+ if ($::opt_bbox_odd) {
+ return @bbox_odd;
+ }
+ }
+ else {
+ if ($::opt_bbox_even) {
+ return @bbox_even;
+ }
+ }
+ if ($::opt_bbox) {
+ return @bbox_all;
+ }
+ return ($a, $b, $c, $d);
+}
+my $page = 0;
+my $gs_pipe = "$::opt_gscmd -dSAFER @gsargs 2>&1";
+$gs_pipe .= " 1>$null" unless $::opt_verbose;
+$gs_pipe .= "|";
+
+open(GS, $gs_pipe) or
+ die "$Error Cannot call ghostscript ($::opt_gscmd)!\n";
+my $bb = ($::opt_hires) ? "%%HiResBoundingBox" : "%%BoundingBox";
+my $previous_line = 'Previous line';
+# Ghostscript workaround for buggy ports that prints
+# the bounding box data twice on STDERR.
+while (<GS>) {
+ print $_ if $::opt_verbose;
+ if (/^$bb:\s*(-?[\.\d]+) (-?[\.\d]+) (-?[\.\d]+) (-?[\.\d]+)/o) {
+ }
+ else {
+ $previous_line = $_;
+ next;
+ }
+ next if $previous_line eq $_;
+ $previous_line = $_;
+ $page++;
+ @bbox = getbbox($page, $1, $2, $3, $4);
+
+ my $empty = 0;
+ $empty = 1 if $bbox[0] >= $bbox[2];
+ $empty = 1 if $bbox[1] >= $bbox[3];
+ if ($empty) {
+ print <<"END_WARNING";
+
+!!! Warning: Empty Bounding Box is returned by Ghostscript!
+!!! Page $page: @bbox
+!!! Either there is a problem with the page or with Ghostscript.
+!!! Recovery is tried by embedding the page in its original size.
+
+END_WARNING
+ print TMP "\\pageinclude{$page}\n";
+ next;
+ }
+
+ print "* Page $page: @bbox\n" if $::opt_verbose;
+
+ my @bb = ($bbox[0] - $llx, $bbox[1] - $ury,
+ $bbox[2] + $urx, $bbox[3] + $lly);
+
+ $empty = 0;
+ $empty = 1 if $bb[0] >= $bb[2];
+ $empty = 1 if $bb[1] >= $bb[3];
+ if ($empty) {
+ print <<"END_WARNING";
+
+!!! Warning: The final Bounding Box is empty!
+!!! Page: $page: @bb
+!!! Probably caused by too large negative margin values.
+!!! Recovery by ignoring margin values.
+
+END_WARNING
+ print TMP "\\page $page [@bbox]\n";
+ # clipping shouldn't make a difference
+ next;
+ }
+ if ($::opt_clip) {
+ print TMP "\\pageclip $page [@bbox][$llx $lly $urx $ury]\n";
+ }
+ else {
+ print TMP "\\page $page [@bb]\n";
+ }
+}
+close(GS);
+
+if ($? & 127) {
+ die sprintf "$Error Ghostscript died with signal %d!\n",
+ ($? & 127);
+}
+elsif ($? != 0) {
+ die sprintf "$Error Ghostscript exited with error code %d!\n",
+ $? >> 8;
+}
+
+print TMP "\\csname \@\@end\\endcsname\n\\end\n";
+close(TMP);
+
+if ($page == 0) {
+ die "$Error Ghostscript does not report bounding boxes!\n";
+}
+
+### Run pdfTeX/XeTeX
+
+push @unlink_files, "$tmp.log";
+my $cmd;
+my $texname;
+if ($::opt_tex eq 'pdftex') {
+ $cmd = $::opt_pdftexcmd;
+ $texname = 'pdfTeX';
+}
+elsif ($::opt_tex eq 'luatex') {
+ $cmd = $::opt_luatexcmd;
+ $texname = 'LuaTeX';
+}
+else {
+ $cmd = $::opt_xetexcmd;
+ $texname = 'XeTeX';
+}
+$cmd = "\"$cmd\"" if $Win and $cmd =~ /\s/;
+$cmd .= ' -no-shell-escape';
+if ($::opt_initex) {
+ $cmd .= ' --ini --etex';
+}
+if ($::opt_verbose) {
+ $cmd .= " -interaction=nonstopmode $tmp";
+}
+else {
+ $cmd .= " -interaction=batchmode $tmp";
+}
+print "* Running $texname ...\n" if $::opt_verbose;
+print "* $texname call: $cmd\n" if $::opt_debug;
+if ($::opt_verbose) {
+ system($cmd);
+}
+else {
+ `$cmd`;
+}
+if ($?) {
+ die "$Error $texname run failed!\n";
+}
+
+### Check pdf version of temp file
+if ($::opt_pdfversion) {
+ open(PDF, '+<', "$tmp.pdf") or die "!!! Error: Cannot open `$tmp.pdf'!\n";
+ my $header;
+ read PDF, $header, 9 or die "!!! Error: Cannot read header of `$tmp.pdf'!\n";
+ $header =~ /^%PDF-1\.(\d)\s$/ or die "!!! Error: Cannot find header of `$tmp.pdf'!\n";
+ if ($1 ne $::opt_pdfversion) {
+ seek PDF, 7, 0 or die "!!! Error: Cannot seek in `$tmp.pdf'!\n";
+ print PDF $::opt_pdfversion or die "!!! Error: Cannot write in `$tmp.pdf'!\n";
+ print "* PDF version correction in output file: 1.$::opt_pdfversion\n"
+ if $::opt_debug;
+ }
+ close(PDF);
+}
+
+### Move temp file to output
+if (!rename("$tmp.pdf", $outputfile)) {
+ use File::Copy;
+ move "$tmp.pdf", $outputfile or
+ die "$Error Cannot move `$tmp.pdf' to `$outputfile'!\n";
+}
+
+print "==> $page page", (($page == 1) ? "" : "s"),
+ " written on `$outputfile'.\n";
+
+$exit_code = 0;
+cleanup();
+
+__END__
diff --git a/Build/source/texk/texlive/linked_scripts/pdfjam/pdf180 b/Build/source/texk/texlive/linked_scripts/pdfjam/pdf180
index a9f4b7a250d..6e88947589b 100755
--- a/Build/source/texk/texlive/linked_scripts/pdfjam/pdf180
+++ b/Build/source/texk/texlive/linked_scripts/pdfjam/pdf180
@@ -4,8 +4,8 @@
##
## Author David Firth (http://go.warwick.ac.uk/dfirth)
##
-## This is a simple wrapper for pdfjam, version 2.01
+## This is a simple wrapper for pdfjam, version 2.08
##
-pdfjam --suffix rotated180 --angle 180 --fitpaper true "$@"
+exec pdfjam --suffix rotated180 --angle 180 --fitpaper true "$@"
diff --git a/Build/source/texk/texlive/linked_scripts/pdfjam/pdf270 b/Build/source/texk/texlive/linked_scripts/pdfjam/pdf270
index bca6c57b02b..3b4abe15318 100755
--- a/Build/source/texk/texlive/linked_scripts/pdfjam/pdf270
+++ b/Build/source/texk/texlive/linked_scripts/pdfjam/pdf270
@@ -4,8 +4,8 @@
##
## Author David Firth (http://go.warwick.ac.uk/dfirth)
##
-## This is a simple wrapper for pdfjam, version 2.01
-##
-pdfjam --suffix rotated270 --angle 270 --fitpaper true --rotateoversize true "$@"
+## This is a simple wrapper for pdfjam, version 2.08
+##.
+exec pdfjam --suffix rotated270 --angle 270 --fitpaper true --rotateoversize true "$@"
diff --git a/Build/source/texk/texlive/linked_scripts/pdfjam/pdf90 b/Build/source/texk/texlive/linked_scripts/pdfjam/pdf90
index ac38aa490f3..dde5df820bb 100755
--- a/Build/source/texk/texlive/linked_scripts/pdfjam/pdf90
+++ b/Build/source/texk/texlive/linked_scripts/pdfjam/pdf90
@@ -4,8 +4,8 @@
##
## Author David Firth (http://go.warwick.ac.uk/dfirth)
##
-## This is a simple wrapper for pdfjam, version 2.01
+## This is a simple wrapper for pdfjam, version 2.08
##
-pdfjam --suffix rotated90 --angle 90 --fitpaper true --rotateoversize true "$@"
+exec pdfjam --suffix rotated90 --angle 90 --fitpaper true --rotateoversize true "$@"
diff --git a/Build/source/texk/texlive/linked_scripts/pdfjam/pdfbook b/Build/source/texk/texlive/linked_scripts/pdfjam/pdfbook
index 8d032d3a351..c6490b3881f 100755
--- a/Build/source/texk/texlive/linked_scripts/pdfjam/pdfbook
+++ b/Build/source/texk/texlive/linked_scripts/pdfjam/pdfbook
@@ -1,19 +1,55 @@
-#!/bin/sh
+#!/bin/sh
##
## pdfbook: Rearrange pages of one or more PDF files into 2-up signatures
##
-## Author David Firth (http://go.warwick.ac.uk/dfirth)
+## Author David Firth (http://go.warwick.ac.uk/dfirth), with help
+## from Marco Pessotto
##
-## This is a simple wrapper for pdfjam, version 2.01
+## This is a simple wrapper for pdfjam, version 2.08
##
-case "${1}" in
- --right-edge-binding)
+case $1 in
+ --short-edge)
+ shortedge=true ;
shift ;
- pdfjam --suffix book --signature* 4 --landscape "$@" ;;
- *)
- pdfjam --suffix book --signature 4 --landscape "$@" ;;
+ ;;
+ *)
+ ;;
esac
-
+for arg
+do
+ case $arg in
+ --signature*)
+ ## catches both --signature and --signature*
+ signature=true ; break
+ ;;
+ *) ;;
+ esac
+done
+##
+## If $signature is empty, we need to use a default:
+##
+if test -z "$signature" ; then
+ signature="--signature 4"
+else
+ signature=""
+fi
+##
+## Make the call to pdfjam:
+##
+if test -z "$shortedge"
+then
+ exec pdfjam --booklet true --landscape --suffix book $signature "$@"
+else
+ (kpsewhich everyshi.sty >/dev/null) || {
+ echo "the 'everyshi' package is not installed."; exit 1
+ }
+ exec pdfjam --booklet true --landscape --suffix book $signature \
+ --preamble '\usepackage{everyshi}
+\makeatletter
+\EveryShipout{\ifodd\c@page\pdfpageattr{/Rotate 180}\fi}
+\makeatother
+' "$@"
+fi
diff --git a/Build/source/texk/texlive/linked_scripts/pdfjam/pdfflip b/Build/source/texk/texlive/linked_scripts/pdfjam/pdfflip
index 125c4adea9c..3088e56a3e9 100755
--- a/Build/source/texk/texlive/linked_scripts/pdfjam/pdfflip
+++ b/Build/source/texk/texlive/linked_scripts/pdfjam/pdfflip
@@ -4,6 +4,6 @@
##
## Author David Firth (http://go.warwick.ac.uk/dfirth)
##
-## This is a simple wrapper for pdfjam, version 2.01
+## This is a simple wrapper for pdfjam, version 2.08
##
-pdfjam --suffix flipped --reflect true --fitpaper true "$@"
+exec pdfjam --suffix flipped --reflect true --fitpaper true "$@"
diff --git a/Build/source/texk/texlive/linked_scripts/pdfjam/pdfjam b/Build/source/texk/texlive/linked_scripts/pdfjam/pdfjam
index 52641909681..e12b466b43b 100755
--- a/Build/source/texk/texlive/linked_scripts/pdfjam/pdfjam
+++ b/Build/source/texk/texlive/linked_scripts/pdfjam/pdfjam
@@ -1,5 +1,5 @@
-#! /bin/sh
-version=2.01
+#!/bin/sh
+version=2.08
#########################################################################
## ##
## pdfjam: A shell-script interface to the "pdfpages" LaTeX package ##
@@ -18,10 +18,11 @@ version=2.01
## do not accept that, then you must not use it. ##
## ##
## The path searched for site-wide configuration files can be set ##
-## through the following variable: ##
+## by editing the following variable: ##
## ##
configpath='/etc:/usr/share/etc:/usr/local/share:/usr/local/etc' ##
## ##
+## Nothing else in this file should need to be changed. ##
## ##
#########################################################################
##
@@ -35,11 +36,16 @@ version=2.01
## --- or whether all configuration files should be ignored.
##
verbose=true
-for arg in "$@"; do
- case "$arg" in
+for arg
+do
+ case $arg in
--quiet | -q | --configpath)
verbose=false ;
;;
+ --version | -V)
+ echo "$version"
+ exit 0 ;
+ ;;
--batch)
batch=true ;
;;
@@ -56,7 +62,7 @@ done
##
## Check to see whether this is a "secondary" call to pdfjam:
##
-if test "$PDFJAM_CALL_NUMBER" = "" ## not a secondary call
+if test -z "$PDFJAM_CALL_NUMBER" ## not a secondary call
then
PDFJAM_CALL_NUMBER=0
fi
@@ -81,8 +87,6 @@ newline='
## Define a function to escape tricky characters in file names etc:
##
escape_chars () {
- ## use 'printf "%q" if available
-# (printf "%q" "${1}") ||
(printf "%s" "${1}" | sed 's/[^a-zA-Z0-9._/\-]/\\&/g')
}
##
@@ -105,15 +109,15 @@ prattle () { ## second argument here is non-null for continuation lines
lineCounter=`expr $lineCounter + 1` ;
if test $lineCounter -eq 1 && test ! -n "${2}" ;
then
- if test -e "$PDFJAM_MESSAGES_FILE"
- then printf "$prefix1$indent %b\n" "$line" 1>/dev/null >> \
+ if test -w "$PDFJAM_MESSAGES_FILE"
+ then printf "$prefix1$indent %s\n" "$line" 1>/dev/null >> \
"$PDFJAM_MESSAGES_FILE"
else messages="$messages$prefix1$indent $line$newline"
## msg file not made yet
fi
else
- if test -e "$PDFJAM_MESSAGES_FILE"
- then printf "$prefix2$indent %b\n" "$line" 1>/dev/null >> \
+ if test -w "$PDFJAM_MESSAGES_FILE"
+ then printf "$prefix2$indent %s\n" "$line" 1>/dev/null >> \
"$PDFJAM_MESSAGES_FILE"
else messages="$messages$prefix2$indent $line$newline"
## msg file not made yet
@@ -140,9 +144,9 @@ prattle "This is pdfjam version ${version}."
## /usr/share/etc/pdfjam.conf
## /usr/local/share/pdfjam.conf
## /usr/local/etc/pdfjam.conf
-## ~/.pdfjam.conf
+## $HOME/.pdfjam.conf
## (And they are read in that order; if a setting is made more than
-## once, the last one prevails.)
+## once, the last instance prevails.)
##
## An example configuration file can be found at
## http://go.warwick.ac.uk/pdfjam
@@ -183,10 +187,12 @@ checkfiles='false' ## Don't use the Unix 'file -Lb' utility to
## identify PDF files from their contents;
## rely on the .pdf or .PDF extension instead.
##
-suffix='pdfjam' ## Default filename suffix to be used when
+suffix='pdfjam' ## Default filename suffix to be used when
## --outfile is either (a) a directory, or (b)
## not specified in a --batch call.
##
+preamble='' ## Default LaTeX preamble string.
+##
paper='a4paper' ## Default paper size is ISO A4.
##
## END OF SETTINGS MADE DIRECTLY WITHIN THE SCRIPT
@@ -215,7 +221,7 @@ then
IFS="$OIFS"
PDFJAM_CONFIG=`printf "%s" "$PDFJAM_CONFIG" | sed 's/^/ /'`
if test "$batch" = true ; then export PDFJAM_CONFIG ; fi
- if test "$PDFJAM_CONFIG" = ""
+ if test -z "$PDFJAM_CONFIG"
then
prattle "(none found)" 1
else
@@ -245,12 +251,12 @@ which, see http://www.ctan.org/tex-archive/macros/latex/contrib/pdfpages).
Usage: pdfjam [OPTIONS] [--] [FILE1 [SEL1]] [FILE2 [SEL2]]...
where
-* 'FILE1' etc. are PDF files. For input from /dev/stdin, use the special name
- '/dev/stdin' in place of any of FILE1, FILE2, etc: this can be mixed with
- 'real' files as needed, to allow PDF data to be input through a pipe (note
- that if /dev/stdin is connected to tty, an error results). If 'FILE1' is
- absent, pdfjam will use '/dev/stdin' (and will use '-' for the page
- selection -- see next item).
+* 'FILE1' etc. are PDF files (JPG and PNG files are also allowed). For
+ input from /dev/stdin, use the special name '/dev/stdin' in place of any
+ of FILE1, FILE2, etc: this can be mixed with 'real' files as needed, to
+ allow input through a pipe (note that if /dev/stdin is connected to tty,
+ an error results). If 'FILE1' is absent, pdfjam will use '/dev/stdin'
+ (and will use '-' for the page selection -- see next item).
* 'SEL1' is a page selection for FILE1, etc.
To select all pages (the default) use '-'. See the pdfpages manual for
more details. An example:
@@ -273,6 +279,9 @@ where
--configpath
Output the 'configpath' variable and exit immediately; no
processing of PDF files.
+ --version (or -V)
+ Output the version number of pdfjam and exit immediately; no
+ processing of PDF files.
--quiet (or -q)
Suppress verbose commentary on progress.
--batch
@@ -319,8 +328,10 @@ where
line. An example:
pdfjam --nup 2x2 myfile.pdf -o myfile-4up.pdf \\
--preamble '\usepackage{fancyhdr} \pagestyle{fancy}'
- The --preamble option can be used, for example, to load LaTeX
- packages and/or to set global options.
+ The '--preamble' option can be used, for example, to load
+ LaTeX packages and/or to set global options. If '--preamble'
+ is used more than once in the call, the supplied preamble
+ strings are simply concatenated.
--keepinfo
--no-keepinfo
Preserve (or not) Title, Author, Subject and Keywords
@@ -392,9 +403,10 @@ where
* '--' can be used to signal that there are no more options to come.
Defaults for the options '--suffix', '--keepinfo', '--paper', '--outfile',
-'--landscape', '--twoside', '--tidy' and '--checkfiles' can be set in
-site-wide or user-specific configuration files. The path that is searched
-for site-wide configuration files (named pdfjam.conf) at this installation is
+'--landscape', '--twoside', '--tidy', '--checkfiles' and '--preamble' can be
+set in site-wide or user-specific configuration files. The path that is
+searched for site-wide configuration files (named pdfjam.conf) at this
+installation is
$configpath
This configuration path can be changed by editing the pdfjam script if
necessary. Any user-specific configuration should be put in a file named
@@ -422,7 +434,7 @@ E_CONFIG=78 # configuration error
## Define a function to print an error message and exit:
##
error_exit () {
- if (test -e "$PDFJAM_MESSAGES_FILE")
+ if (test -r "$PDFJAM_MESSAGES_FILE")
then cat "$PDFJAM_MESSAGES_FILE" >&2
else printf "$messages" 1>&2
fi
@@ -451,8 +463,9 @@ optionsFinished=""
##
## First note any '--checkfiles' or '--no-checkfiles' option
##
-for arg in "$@"; do
- case "$arg" in
+for arg
+do
+ case $arg in
--checkfiles)
checkfiles=true ;
callOptions="$callOptions --checkfiles" ;
@@ -467,7 +480,7 @@ while test -n "${1}${2}"; do
argUnmatched=""
if test "$optionsFinished" != true
then
- case "${1}" in
+ case ${1} in
--) ## signals end of command-line options
optionsFinished=true ;
shift ;
@@ -485,7 +498,7 @@ while test -n "${1}${2}"; do
fileSpec=`printf "%s" "$fileSpec" | sed 's/|awaited/|-/g'`
pageSpecAwaited=false
fi
- case "${1}" in
+ case ${1} in
--batch)
batch=true ;
;;
@@ -575,11 +588,11 @@ while test -n "${1}${2}"; do
--paper)
paper="${2}"
callOptions="$callOptions ${1} ${2}" ;
- shift;;
+ shift ;;
--pagecolor)
pagecolor="${2}" ;
callOptions="$callOptions ${1} ${2}" ;
- shift;;
+ shift ;;
--a4paper | --a5paper | --b5paper | --letterpaper | \
--executivepaper | --legalpaper)
## standard LaTeX paper sizes
@@ -589,7 +602,7 @@ while test -n "${1}${2}"; do
--a0paper | --a1paper | --a2paper | --a3paper | \
--a6paper | --b0paper | --b1paper | --b2paper | \
--b3paper | --b4paper | --b6paper)
- ## the 'geonetry' package is needed
+ ## the 'geometry' package is needed
if test "$geometry" != false ;
then
paper=`printf "%s" "${1}" | sed 's/^--//'` ;
@@ -598,7 +611,7 @@ while test -n "${1}${2}"; do
fi ;
;;
--papersize)
- ## the 'geonetry' package is needed
+ ## the 'geometry' package is needed
if test "$geometry" != false ;
then
papersize="papersize=${2}" ;
@@ -623,8 +636,7 @@ while test -n "${1}${2}"; do
callOptions="$callOptions --no-twoside" ;
;;
--preamble)
- preamble="${2}" ;
- callOptions="$callOptions ${1} '$preamble'" ;
+ preamble="$preamble${2}" ;
shift ;;
--*)
## options for \includepdfmerge
@@ -642,9 +654,9 @@ while test -n "${1}${2}"; do
;;
esac
fi
- if test "$optionsFinished" = true || test "$argUnmatched" = true
+ if test "$optionsFinished" = true || test "$argUnmatched" = true
then
- case "${1}" in
+ case ${1} in
"" | /dev/stdin)
fileSpec="${fileSpec}${newline}/dev/stdin|awaited"
pageSpecAwaited=true
@@ -655,22 +667,25 @@ while test -n "${1}${2}"; do
sed 's/|awaited/|-/g'`
pageSpecAwaited=false
else
- error_exit "no PDF file found at ${1}" \
+ error_exit "no PDF/JPG/PNG file found at ${1}" \
$E_NOINPUT
fi ;;
- *) ## All other args should be PDF source files and page
- ## selections; if not, we'll quit
+ *) ## All other args should be PDF (or JPG/PNG)
+ ## source files and page selections; if not, we'll quit
if test "$checkfiles" = true ; ## not always available
then
case `file -Lb "${1}"` in
- "PDF document"*) ## it's a PDF file as expected
+ "PDF document"*|"JPEG image data"*|"PNG image"*)
+ ## it's a PDF file (or JPG/PNG) as expected
fileSpec="$fileSpec${newline}"${1}"|awaited"
pageSpecAwaited=true
;;
*)
- case "${1}" in
- *.[pP][dD][fF]) ## should be PDF file, but isn't
- error_exit "no PDF file found at ${1}" \
+ case ${1} in
+ *.[pP][dD][fF] | *.[jJ][pP][eE][gG] | \
+ *.[jJ][pP][gG] | *.[pP][nN][gG])
+ ## should be PDF/JPG/PNG file, but isn't
+ error_exit "no PDF/JPG/PNG file found at ${1}" \
$E_NOINPUT
;;
*) ## if page spec needed, assume this is it;
@@ -680,7 +695,7 @@ while test -n "${1}${2}"; do
sed "s/|awaited/|$1/g"`
pageSpecAwaited=false
else
- error_exit "no PDF file found at ${1}" \
+ error_exit "no PDF/JPG/PNG file found at ${1}" \
$E_NOINPUT
fi
;;
@@ -688,8 +703,10 @@ while test -n "${1}${2}"; do
;;
esac
else ## no checking of file contents; rely on .pdf extension
- case "${1}" in
- *.[pP][dD][fF]) ## assume it's a PDF file
+ case ${1} in
+ *.[pP][dD][fF] | *.[jJ][pP][eE][gG] \
+ | *.[jJ][pP][gG] | *.[pP][nN][gG])
+ ## assume it's a PDF/JPG/PNG file
test -f "${1}" || error_exit \
"${1} not found" $E_NOINPUT
fileSpec="$fileSpec"$newline${1}"|"awaited
@@ -702,7 +719,7 @@ while test -n "${1}${2}"; do
sed "s/|awaited/|$1/g"`
pageSpecAwaited=false
else
- error_exit "no PDF file found at ${1}" \
+ error_exit "no PDF/JPG/PNG file found at ${1}" \
$E_NOINPUT
fi
;;
@@ -722,11 +739,11 @@ fileSpec=`printf "%s" "$fileSpec" | sed '/^$/d; s/^ //; s/|awaited$/|-/'`
if test $PDFJAM_CALL_NUMBER -eq 0 && test "$inputFromStdin" != true
then
## the special argument '/dev/stdin' was not used
- if test "$fileSpec" = "" ; then
+ if test -z "$fileSpec" ; then
## no argument specifying a PDF source was given
inputFromStdin=true
fileSpec="/dev/stdin|-"
- prattle "No pdf source specified: input is from stdin."
+ prattle "No PDF/JPG/PNG source specified: input is from stdin."
fi
fi
##
@@ -734,19 +751,22 @@ fi
##
miscOptions=`printf "%s" "$miscOptions" | sed 's/^,//'`
##
+if test -n "$preamble"
+then callOptions="$callOptions --preamble '$preamble'"
+fi
## Delete leading space from $callOptions:
##
callOptions=`printf "%s" "$callOptions" | sed 's/^ //'`
##
## Set up a document options variable:
##
-case "$landscape" in
+case $landscape in
true)
orientation=landscape ;;
*)
orientation="" ;;
esac
-case "$twoside" in
+case $twoside in
true)
twoside=twoside ;;
*)
@@ -755,11 +775,11 @@ esac
if test "$geometry" != false
then
## we haven't already found that geometry.sty is missing
- case "$paper" in
+ case $paper in
a0paper | a1paper | a2paper | a3paper | \
a6paper | b0paper | b1paper | b2paper | \
b3paper | b4paper | b6paper)
- ## the 'geonetry' package is needed
+ ## the 'geometry' package is needed
geometry=true ;
;;
*)
@@ -780,7 +800,7 @@ documentOptions=`printf "%s" "$documentOptions" | sed 's/^,//' | sed 's/,$//'`
if test $PDFJAM_CALL_NUMBER -eq 0 ## not a secondary call
then
## Check whether there's a pdflatex, if "$pdflatex" is still unset:
- case "$pdflatex" in
+ case $pdflatex in
"not found")
error_exit "can't find pdflatex!" $E_UNAVAILABLE
;;
@@ -853,7 +873,7 @@ then
else
if test "$keepinfo" = true
then
- case "$pdfinfo" in
+ case $pdfinfo in
"not found")
if test $PDFJAM_CALL_NUMBER -eq 0
then
@@ -891,6 +911,7 @@ fi
## Use mktemp if possible; otherwise fall back on mkdir,
## with random name to make file collisions less likely.
##
+original_umask=`umask`
umask 177
if test $PDFJAM_CALL_NUMBER = 0 ## don't repeat this work for secondary calls
then
@@ -925,8 +946,9 @@ then
messages="" ## we won't be using this variable again!
else
PDFJAM_TEMP_DIR="$PDFJAM_TEMP_DIR"/"file$PDFJAM_CALL_NUMBER"
- umask 077 && mkdir "$PDFJAM_TEMP_DIR"
+ (umask 077 && mkdir "$PDFJAM_TEMP_DIR")
fi
+umask $original_umask
##
## TEMPORARY DIRECTORY ALL DONE
##
@@ -939,7 +961,7 @@ fi
##
if test "$batch" = true ; then
if test "$fileSpec" = "" ; then
- error_exit "--batch was used, but no PDF source file(s) specified" \
+ error_exit "--batch was used, but no PDF/JPG/PNG source file(s) specified" \
$E_USAGE
fi
if test "$inputFromStdin" = true ; then
@@ -1012,7 +1034,7 @@ counter=0
##
stdinUnread=true
IFS="$newline"
-for k in $fileSpec
+for k in ${fileSpec}
do
counter=`expr $counter + 1`
sourcePath=`printf "%s" "$k" | sed 's/|[^|]*$//'`
@@ -1028,14 +1050,14 @@ do
then
error_exit "invalid page spec $pageSpec" $E_USAGE
fi
- case "$sourcePath" in
+ case $sourcePath in
/dev/stdin)
uniqueName="$PDFJAM_TEMP_DIR"/stdin.pdf
if test "$stdinUnread" = true
then
if tty -s ; then
error_exit \
- "tty is connected to connected to stdin, no PDF file found" \
+ "tty is connected to connected to stdin, no PDF/JPG/PNG file found" \
$E_NOINPUT
fi
cat > "$uniqueName"
@@ -1044,7 +1066,7 @@ do
;;
*)
pdfName=`basename "$sourcePath"`
- sourceDir=`dirname "$sourcePath"`
+ sourceDir=`dirname "$sourcePath"` ## zsh on Mac OS 10.5 chokes here
cd "$sourceDir" || exit 1 ## just to get the full path
sourceDir=`pwd`
cd "$pwd" || exit 1
@@ -1069,13 +1091,17 @@ then
prattle "Calling ${pdfinfo}..." ;
PDFinfo=`pdfinfo "$uniqueName"`;
pdftitl=`printf "%s" "$PDFinfo" | \
- grep -e '^Title:'| sed s/^Title:\\\s\*//`;
+ grep -e '^Title:'| sed s/^Title:\\\s\*// | \
+ sed -e 's/[#$%^&_{}~]/\\\&/g'`;
pdfauth=`printf "%s" "$PDFinfo" | \
- grep -e '^Author:'| sed s/^Author:\\\s\*//`;
+ grep -e '^Author:'| sed s/^Author:\\\s\*// | \
+ sed -e 's/[#$%^&_{}~]/\\\&/g'`;
pdfsubj=`printf "%s" "$PDFinfo" | \
- grep -e '^Subject:'| sed s/^Subject:\\\s\*//`;
+ grep -e '^Subject:'| sed s/^Subject:\\\s\*// | \
+ sed -e 's/[#$%^&_{}~]/\\\&/g'`;
pdfkeyw=`printf "%s" "$PDFinfo" | \
- grep -e '^Keywords:'| sed s/^Keywords:\\\s\*//`;
+ grep -e '^Keywords:'| sed s/^Keywords:\\\s\*// | \
+ sed -e 's/[#$%^&_{}~]/\\\&/g'`;
fi
if test -n "$pdfTitle" ; then
pdftitl="$pdfTitle"
@@ -1102,11 +1128,12 @@ tempFile="$PDFJAM_TEMP_DIR"/temp.tex
\documentclass[$documentOptions]{article}
\usepackage{color} \definecolor{bgclr}{RGB}{$pagecolor} \pagecolor{bgclr}
\usepackage[$papersize]{geometry}
+\usepackage[utf8]{inputenc}
\usepackage{hyperref}
-\hypersetup{pdftitle=$pdftitl}
-\hypersetup{pdfauthor=$pdfauth}
-\hypersetup{pdfsubject=$pdfsubj}
-\hypersetup{pdfkeywords=$pdfkeyw}
+\hypersetup{pdftitle={$pdftitl}}
+\hypersetup{pdfauthor={$pdfauth}}
+\hypersetup{pdfsubject={$pdfsubj}}
+\hypersetup{pdfkeywords={$pdfkeyw}}
\usepackage{pdfpages}
$preamble
\begin{document}
@@ -1120,13 +1147,13 @@ if test "$hyperref" = false; then ## we don't need hyperref
sed '/\\\hypersetup.*/d' > "${texFile}"
rm $tempFile
fi
-if test ! -n "$geometry" ; then geometry=false ; fi
+if test -z "$geometry" ; then geometry=false ; fi
if test "$geometry" = false; then ## geometry package is not to be used
cp $texFile $tempFile
cat $tempFile | sed '/\\\usepackage.*{geometry}/d' > $texFile
rm $tempFile
fi
-if test "$pagecolor" = ""; then ## color package is not needed
+if test -z "$pagecolor"; then ## color package is not needed
cp $texFile $tempFile
cat $tempFile | sed '/\\\usepackage.*{color}/d' > $texFile
rm $tempFile
@@ -1136,7 +1163,7 @@ fi
##
#########################################################################
##
-## RUN PDFLATEX AND COPY THE RESULTING PDF FILE TO STDOUT
+## RUN PDFLATEX AND COPY THE RESULTING PDF FILE
##
prattle "Calling ${pdflatex}..." ;
cd "$PDFJAM_TEMP_DIR" || exit 1
@@ -1147,11 +1174,10 @@ If '--no-tidy' was used, you can examine the
log file at
$fileName.log
to try to diagnose the problem."
-if ! ("$pdflatex" "$texFile" > "$msgFile")
-then
+$pdflatex $texFile > $msgFile || {
prattle "$failureText"
- error_exit "Output file not written" $E_SOFTWARE
-fi
+ error_exit "Output file not written" $E_SOFTWARE
+}
cd "$pwd" || exit 1
if test -f "$fileName".pdf ## if LaTeX didn't choke
then
@@ -1172,7 +1198,7 @@ then
separator="-"
if test "$pageSpec" != "-"
then
- separator="-$pageSpec"
+ separator=-"$pageSpec"-
fi
outFile=`printf "%s" "$outFile" | sed 's/\/$//'`
## (delete any trailing slash)
@@ -1188,8 +1214,9 @@ if test -f "$outFile" && test ! -w "$outFile"
then
error_exit "no write permission at ${outFile}" $E_CANTCREATE
fi
+#fileSize=`wc -c < "$fileName.pdf" | sed 's/^\ *//'`
(cat "$fileName".pdf > "$outFile" 2>/dev/null) &&
- prattle "Finished. Output was to ${outFile}." ||
+ prattle "Finished. Output was to '${outFile}'." ||
error_exit "cannot write output at ${outFile}" $E_CANTCREATE
if (test "$PDFJAM_CALL_NUMBER" = "0") && ## not a secondary call
(test $verbose = true)
diff --git a/Build/source/texk/texlive/linked_scripts/pdfjam/pdfjam-pocketmod b/Build/source/texk/texlive/linked_scripts/pdfjam/pdfjam-pocketmod
index 0bebe86961c..72efd0e9db9 100755
--- a/Build/source/texk/texlive/linked_scripts/pdfjam/pdfjam-pocketmod
+++ b/Build/source/texk/texlive/linked_scripts/pdfjam/pdfjam-pocketmod
@@ -6,15 +6,16 @@
##
## Author David Firth (http://go.warwick.ac.uk/dfirth)
##
-## This is a simple wrapper for (three runs of) pdfjam, version 2.01
+## This is a simple wrapper for (three runs of) pdfjam, version 2.08
##
##
## It's hard (?) to set up this particular script to read from /dev/stdin,
## so we'll just insist that the first argument is a file:
##
E_USAGE=64 ## for a command line usage error
-for arg in "$@"; do
- case "$arg" in
+for arg
+do
+ case $arg in
--batch)
printf "pdfjam-pocketmod ERROR: the --batch option is not allowed\n" 1>&2 ;
exit "$E_USAGE" ;;
@@ -22,6 +23,8 @@ for arg in "$@"; do
n='--no-tidy' ;;
--quiet | -q)
q='-q' ;;
+ --vanilla)
+ v='--vanilla' ;;
--checkfiles)
c='--checkfiles' ;;
*)
@@ -43,12 +46,12 @@ fi
## That's all the argument checking!
##
pageSpec="1-8" ## the default page spec
-case "${1}" in
+case ${1} in
--* | "") ## no page spec was given
;;
*) ## a page spec was given, so use it
pageSpec="$1" ;
shift ;;
esac
-pdfjam $n $q $c -o /dev/stdout "$sourceFile" "$pageSpec" | pdfjam --angle 180 $n $q -o /dev/stdout /dev/stdin '1,8,7,6' | pdfjam --nup 4x2 --landscape --frame true "$sourceFile" '2-5' /dev/stdin "$@"
+pdfjam $n $q $c $v -o /dev/stdout "$sourceFile" "$pageSpec" | pdfjam --angle 180 $n $q $v -o /dev/stdout /dev/stdin '1,8,7,6' | pdfjam --nup 4x2 --landscape --frame true "$sourceFile" '2-5' /dev/stdin "$@"
diff --git a/Build/source/texk/texlive/linked_scripts/pdfjam/pdfjam-slides3up b/Build/source/texk/texlive/linked_scripts/pdfjam/pdfjam-slides3up
index 3bd461c8883..e41e50a7b96 100755
--- a/Build/source/texk/texlive/linked_scripts/pdfjam/pdfjam-slides3up
+++ b/Build/source/texk/texlive/linked_scripts/pdfjam/pdfjam-slides3up
@@ -5,13 +5,13 @@
##
## Author David Firth (http://go.warwick.ac.uk/dfirth)
##
-## This is a simple wrapper for pdfjam, version 2.01
+## This is a simple wrapper for pdfjam, version 2.08
##
footskip=3.1cm ## default setting
pagecommand='{\thispagestyle{empty}}' ## default setting
-case "${1}" in
+case ${1} in
--pagenumbering)
- case "${2}" in
+ case ${2} in
false)
continue ;;
true)
@@ -20,10 +20,10 @@ case "${1}" in
pagecommand='{\thispagestyle{plain}}' ;
footskip="$2" ;;
esac ;
- shift 2 ;;
+ shift; shift ;;
*)
continue ;;
esac
-pdfjam --suffix 3up --nup 1x3 --frame true --noautoscale false \
+exec pdfjam --suffix 3up --nup 1x3 --frame true --noautoscale false \
--delta "0cm 0.2cm" --scale 0.87 --offset "-3.8cm 0cm" \
--preamble "\footskip $footskip" --pagecommand "$pagecommand" "$@"
diff --git a/Build/source/texk/texlive/linked_scripts/pdfjam/pdfjam-slides6up b/Build/source/texk/texlive/linked_scripts/pdfjam/pdfjam-slides6up
index 47fa2ea1ec8..bb65d013379 100755
--- a/Build/source/texk/texlive/linked_scripts/pdfjam/pdfjam-slides6up
+++ b/Build/source/texk/texlive/linked_scripts/pdfjam/pdfjam-slides6up
@@ -5,13 +5,13 @@
##
## Author David Firth (http://go.warwick.ac.uk/dfirth)
##
-## This is a simple wrapper for pdfjam, version 2.01
+## This is a simple wrapper for pdfjam, version 2.08
##
footskip=2.7cm ## default setting
pagecommand='{\thispagestyle{empty}}' ## default setting
-case "${1}" in
+case ${1} in
--pagenumbering)
- case "${2}" in
+ case ${2} in
false)
continue ;;
true)
@@ -20,11 +20,11 @@ case "${1}" in
pagecommand='{\thispagestyle{plain}}' ;
footskip="$2" ;;
esac ;
- shift 2 ;;
+ shift; shift ;;
*)
continue ;;
esac
-pdfjam --suffix 6up --nup 2x3 --frame true --noautoscale false \
+exec pdfjam --suffix 6up --nup 2x3 --frame true --noautoscale false \
--delta "0.2cm 0.3cm" --scale 0.95 --preamble "\footskip $footskip" \
--pagecommand "$pagecommand" "$@"
diff --git a/Build/source/texk/texlive/linked_scripts/pdfjam/pdfjoin b/Build/source/texk/texlive/linked_scripts/pdfjam/pdfjoin
index 02c397fc31d..923c50d6b6c 100755
--- a/Build/source/texk/texlive/linked_scripts/pdfjam/pdfjoin
+++ b/Build/source/texk/texlive/linked_scripts/pdfjam/pdfjoin
@@ -4,11 +4,12 @@
##
## Author David Firth (http://go.warwick.ac.uk/dfirth)
##
-## This is a simple wrapper for pdfjam, version 2.01
+## This is a simple wrapper for pdfjam, version 2.08
##
E_USAGE=64 ## for a command line usage error
-for arg in "$@"; do
- case "$arg" in
+for arg
+do
+ case $arg in
--batch)
printf "pdfjoin ERROR: the --batch option is not allowed\n" 1>&2;
exit "$E_USAGE" ;;
@@ -20,6 +21,6 @@ for arg in "$@"; do
*) continue ;;
esac
done
-pdfjam --fitpaper true --rotateoversize true --suffix joined "$@"
+exec pdfjam --fitpaper true --rotateoversize true --suffix joined "$@"
diff --git a/Build/source/texk/texlive/linked_scripts/pdfjam/pdfnup b/Build/source/texk/texlive/linked_scripts/pdfjam/pdfnup
index a4744a112fd..9b2a5f4fdac 100755
--- a/Build/source/texk/texlive/linked_scripts/pdfjam/pdfnup
+++ b/Build/source/texk/texlive/linked_scripts/pdfjam/pdfnup
@@ -4,11 +4,12 @@
##
## Author David Firth (http://go.warwick.ac.uk/dfirth)
##
-## This is a simple wrapper for pdfjam, version 2.01
+## This is a simple wrapper for pdfjam, version 2.08
##
E_USAGE=64 ## for a command line usage error
-for arg in "$@"; do
- case "$arg" in
+for arg
+do
+ case $arg in
--orient) ## this was allowed in previous versions of pdfnup
printf "pdfnup ERROR: the --orient option is not allowed,\n" 1>&2;
printf " use --landscape or --no-landscape to specify\n" 1>&2;
@@ -23,6 +24,6 @@ for arg in "$@"; do
*) continue ;;
esac
done
-pdfjam --suffix nup --nup 2x1 --landscape "$@"
+exec pdfjam --suffix nup --nup 2x1 --landscape "$@"
diff --git a/Build/source/texk/texlive/linked_scripts/pdfjam/pdfpun b/Build/source/texk/texlive/linked_scripts/pdfjam/pdfpun
index eee08a05d33..a49396d950e 100755
--- a/Build/source/texk/texlive/linked_scripts/pdfjam/pdfpun
+++ b/Build/source/texk/texlive/linked_scripts/pdfjam/pdfpun
@@ -5,13 +5,14 @@
##
## Author David Firth (http://go.warwick.ac.uk/dfirth)
##
-## This is a simple wrapper for (three runs of) pdfjam, version 2.01
+## This is a simple wrapper for (three runs of) pdfjam, version 2.08
##
##
E_USAGE=64 ## for a command line usage error
##
-for arg in "$@"; do
- case "$arg" in
+for arg
+do
+ case $arg in
--batch)
printf "pdfpun ERROR: the --batch option is not allowed\n" 1>&2;
exit "$E_USAGE" ;;
@@ -29,7 +30,7 @@ shift ;
##
## Some (very) minimal checking of the first argument:
##
-if test ! -e "$sourceFile" ;
+if test ! -f "$sourceFile" ;
then
printf "pdfpun ERROR: first argument must be a PDF file\n" ;
exit $E_USAGE ;
@@ -45,21 +46,21 @@ fi
## That's all the argument checking!
##
pageSpec="-" ## the default
-case "${1}" in
+case ${1} in
--* | "")
;;
*) ## a page spec was given
pageSpec="$1" ;
shift ;;
esac
-case "${1}" in
+case ${1} in
--outfile)
outFile="$2" ;
- shift 2 ;;
+ shift; shift ;;
*)
;;
esac
-if test "$outFile" = "" ;
+if test -z "$outFile" ;
then
printf "pdfpun ERROR: no output file specified\n" 1>&2 ;
exit "$E_USAGE" ;
diff --git a/Build/source/texk/texlive/linked_scripts/perltex/perltex.pl b/Build/source/texk/texlive/linked_scripts/perltex/perltex.pl
index e69de29bb2d..1c17aa73101 100755
--- a/Build/source/texk/texlive/linked_scripts/perltex/perltex.pl
+++ b/Build/source/texk/texlive/linked_scripts/perltex/perltex.pl
@@ -0,0 +1,503 @@
+#! /usr/bin/env perl
+
+###########################################################
+# Prepare a LaTeX run for two-way communication with Perl #
+# By Scott Pakin <scott+pt@pakin.org> #
+###########################################################
+
+#-------------------------------------------------------------------
+# This is file `perltex.pl',
+# generated with the docstrip utility.
+#
+# The original source files were:
+#
+# perltex.dtx (with options: `perltex')
+#
+# This is a generated file.
+#
+# Copyright (C) 2010 Scott Pakin <scott+pt@pakin.org>
+#
+# This file may be distributed and/or modified under the conditions
+# of the LaTeX Project Public License, either version 1.3c of this
+# license or (at your option) any later version. The latest
+# version of this license is in:
+#
+# http://www.latex-project.org/lppl.txt
+#
+# and version 1.3c or later is part of all distributions of LaTeX
+# version 2006/05/20 or later.
+#-------------------------------------------------------------------
+
+sub top_level_eval ($)
+{
+ return eval $_[0];
+}
+use Safe;
+use Opcode;
+use Getopt::Long;
+use Pod::Usage;
+use File::Basename;
+use Fcntl;
+use POSIX;
+use File::Spec;
+use IO::Handle;
+use warnings;
+use strict;
+my $latexprog;
+my $runsafely = 1;
+my @permittedops;
+my $usepipe = 1;
+my $progname = basename $0;
+my $jobname = "texput";
+my $toperl;
+my $fromperl;
+my $toflag;
+my $fromflag;
+my $doneflag;
+my $logfile;
+my $pipe;
+my @latexcmdline;
+my $styfile;
+my @macroexpansions;
+my $sandbox = new Safe;
+my $sandbox_eval;
+my $latexpid;
+my $pipestring = "\%\%\%\%\% Generated by $progname\n\\endinput\n";
+$latexprog = $ENV{"PERLTEX"} || "latex";
+Getopt::Long::Configure("require_order", "pass_through");
+GetOptions("help" => sub {pod2usage(-verbose => 1)},
+ "latex=s" => \$latexprog,
+ "safe!" => \$runsafely,
+ "pipe!" => \$usepipe,
+ "synctext=s" => \$pipestring,
+ "makesty" => sub {$styfile = "noperltex.sty"},
+ "permit=s" => \@permittedops) || pod2usage(2);
+@latexcmdline = @ARGV;
+my $firstcmd = 0;
+for ($firstcmd=0; $firstcmd<=$#latexcmdline; $firstcmd++) {
+ my $option = $latexcmdline[$firstcmd];
+ next if substr($option, 0, 1) eq "-";
+ if (substr ($option, 0, 1) ne "\\") {
+ $jobname = basename $option, ".tex" ;
+ $latexcmdline[$firstcmd] = "\\input $option";
+ }
+ last;
+}
+push @latexcmdline, "" if $#latexcmdline==-1;
+my $separator = "";
+foreach (1 .. 20) {
+ $separator .= chr(ord("A") + rand(26));
+}
+$toperl = $jobname . ".topl";
+$fromperl = $jobname . ".frpl";
+$toflag = $jobname . ".tfpl";
+$fromflag = $jobname . ".ffpl";
+$doneflag = $jobname . ".dfpl";
+$logfile = $jobname . ".lgpl";
+$pipe = $jobname . ".pipe";
+$latexcmdline[$firstcmd] =
+ sprintf '\makeatletter' . '\def%s{%s}' x 7 . '\makeatother%s',
+ '\plmac@tag', $separator,
+ '\plmac@tofile', $toperl,
+ '\plmac@fromfile', $fromperl,
+ '\plmac@toflag', $toflag,
+ '\plmac@fromflag', $fromflag,
+ '\plmac@doneflag', $doneflag,
+ '\plmac@pipe', $pipe,
+ $latexcmdline[$firstcmd];
+$toperl = File::Spec->rel2abs($toperl);
+$fromperl = File::Spec->rel2abs($fromperl);
+$toflag = File::Spec->rel2abs($toflag);
+$fromflag = File::Spec->rel2abs($fromflag);
+$doneflag = File::Spec->rel2abs($doneflag);
+$logfile = File::Spec->rel2abs($logfile);
+$pipe = File::Spec->rel2abs($pipe);
+$SIG{"ALRM"} = sub {
+ undef $latexpid;
+ exit 0;
+};
+$SIG{"PIPE"} = "IGNORE";
+sub delete_files (@)
+{
+ foreach my $filename (@_) {
+ unlink $filename;
+ while (-e $filename) {
+ unlink $filename;
+ sleep 0;
+ }
+ }
+}
+sub awaitexists ($)
+{
+ while (!-e $_[0]) {
+ sleep 0;
+ if (waitpid($latexpid, &WNOHANG)==-1) {
+ delete_files($toperl, $fromperl, $toflag,
+ $fromflag, $doneflag, $pipe);
+ undef $latexpid;
+ exit 0;
+ }
+ }
+}
+delete_files($toperl, $fromperl, $toflag, $fromflag, $doneflag, $pipe);
+open (LOGFILE, ">$logfile") || die "open(\"$logfile\"): $!\n";
+autoflush LOGFILE 1;
+if (defined $styfile) {
+ open (STYFILE, ">$styfile") || die "open(\"$styfile\"): $!\n";
+}
+if (!$usepipe || !eval {mkfifo($pipe, 0600)}) {
+ sysopen PIPE, $pipe, O_WRONLY|O_CREAT, 0755;
+ autoflush PIPE 1;
+ print PIPE $pipestring;
+ close PIPE;
+ $usepipe = 0;
+}
+defined ($latexpid = fork) || die "fork: $!\n";
+unshift @latexcmdline, $latexprog;
+if (!$latexpid) {
+ exec {$latexcmdline[0]} @latexcmdline;
+ die "exec('@latexcmdline'): $!\n";
+}
+if ($runsafely) {
+ @permittedops=(":browse") if $#permittedops==-1;
+ $sandbox->permit_only (@permittedops);
+ $sandbox_eval = sub {$sandbox->reval($_[0])};
+}
+else {
+ $sandbox_eval = \&top_level_eval;
+}
+while (1) {
+ awaitexists($toflag);
+ my $entirefile;
+ {
+ local $/ = undef;
+ open (TOPERL, "<$toperl") || die "open($toperl): $!\n";
+ $entirefile = <TOPERL>;
+ close TOPERL;
+ }
+ $entirefile =~ s/\r//g;
+ my ($optag, $macroname, @otherstuff) =
+ map {chomp; $_} split "$separator\n", $entirefile;
+ $macroname =~ s/^[^A-Za-z]+//;
+ $macroname =~ s/\W/_/g;
+ $macroname = "latex_" . $macroname;
+ if ($optag eq "USE") {
+ foreach (@otherstuff) {
+ s/\\/\\\\/g;
+ s/\'/\\\'/g;
+ $_ = "'$_'";
+ }
+ }
+ my $perlcode;
+ if ($optag eq "DEF") {
+ $perlcode =
+ sprintf "sub %s {%s}\n",
+ $macroname, $otherstuff[0];
+ }
+ elsif ($optag eq "USE") {
+ $perlcode = sprintf "%s (%s);\n", $macroname, join(", ", @otherstuff);
+ }
+ elsif ($optag eq "RUN") {
+ $perlcode = $otherstuff[0];
+ }
+ else {
+ die "${progname}: Internal error -- unexpected operation tag \"$optag\"\n";
+ }
+ print LOGFILE "#" x 31, " PERL CODE ", "#" x 32, "\n";
+ print LOGFILE $perlcode, "\n";
+ undef $_;
+ my $result;
+ {
+ my $warningmsg;
+ local $SIG{__WARN__} =
+ sub {chomp ($warningmsg=$_[0]); return 0};
+ $result = $sandbox_eval->($perlcode);
+ if (defined $warningmsg) {
+ $warningmsg =~ s/at \(eval \d+\) line \d+\W+//;
+ print LOGFILE "# ===> $warningmsg\n\n";
+ }
+ }
+ $result = "" if !$result || $optag eq "RUN";
+ if ($@) {
+ my $msg = $@;
+ $msg =~ s/at \(eval \d+\) line \d+\W+//;
+ $msg =~ s/\n/\\MessageBreak\n/g;
+ $msg =~ s/\s+/ /;
+ $result = "\\PackageError{perltex}{$msg}";
+ my @helpstring;
+ if ($msg =~ /\btrapped by\b/) {
+ @helpstring =
+ ("The preceding error message comes from Perl. Apparently,",
+ "the Perl code you tried to execute attempted to perform an",
+ "`unsafe' operation. If you trust the Perl code (e.g., if",
+ "you wrote it) then you can invoke perltex with the --nosafe",
+ "option to allow arbitrary Perl code to execute.",
+ "Alternatively, you can selectively enable Perl features",
+ "using perltex's --permit option. Don't do this if you don't",
+ "trust the Perl code, however; malicious Perl code can do a",
+ "world of harm to your computer system.");
+ }
+ else {
+ @helpstring =
+ ("The preceding error message comes from Perl. Apparently,",
+ "there's a bug in your Perl code. You'll need to sort that",
+ "out in your document and re-run perltex.");
+ }
+ my $helpstring = join ("\\MessageBreak\n", @helpstring);
+ $helpstring =~ s/\. /.\\space\\space /g;
+ $result .= "{$helpstring}";
+ }
+ push @macroexpansions, $result if defined $styfile && $optag eq "USE";
+ print LOGFILE "%" x 30, " LATEX RESULT ", "%" x 30, "\n";
+ print LOGFILE $result, "\n\n";
+ $result .= '\endinput';
+ open (FROMPERL, ">$fromperl") || die "open($fromperl): $!\n";
+ syswrite FROMPERL, $result;
+ close FROMPERL;
+ delete_files($toflag, $toperl, $doneflag);
+ open (FROMFLAG, ">$fromflag") || die "open($fromflag): $!\n";
+ close FROMFLAG;
+ if (open (PIPE, ">$pipe")) {
+ autoflush PIPE 1;
+ print PIPE $pipestring;
+ close PIPE;
+ }
+ awaitexists($toperl);
+ delete_files($fromflag);
+ open (DONEFLAG, ">$doneflag") || die "open($doneflag): $!\n";
+ close DONEFLAG;
+ alarm 1;
+ if (open (PIPE, ">$pipe")) {
+ autoflush PIPE 1;
+ print PIPE $pipestring;
+ close PIPE;
+ }
+ alarm 0;
+}
+END {
+ close LOGFILE;
+ if (defined $latexpid) {
+ kill (9, $latexpid);
+ exit 1;
+ }
+
+ if (defined $styfile) {
+ print STYFILE <<"STYFILEHEADER1";
+\\NeedsTeXFormat{LaTeX2e}[1999/12/01]
+\\ProvidesPackage{noperltex}
+ [2007/09/29 v1.4 Perl-free version of PerlTeX specific to $jobname.tex]
+STYFILEHEADER1
+ ;
+ print STYFILE <<'STYFILEHEADER2';
+\RequirePackage{filecontents}
+
+\let\noperltex@PackageError=\PackageError
+\renewcommand{\PackageError}[3]{}
+\RequirePackage{perltex}
+\let\PackageError=\noperltex@PackageError
+
+\newcount\plmac@macro@invocation@num
+\gdef\plmac@show@placeholder#1#2\@empty{%
+ \ifx#1U\relax
+ \endgroup
+ \advance\plmac@macro@invocation@num by 1\relax
+ \global\plmac@macro@invocation@num=\plmac@macro@invocation@num
+ \input{noperltex-\the\plmac@macro@invocation@num.tex}%
+ \else
+ \endgroup
+ \fi
+}
+STYFILEHEADER2
+ ;
+ foreach my $e (0 .. $#macroexpansions) {
+ print STYFILE "\n";
+ printf STYFILE "%% Invocation #%d\n", 1+$e;
+ printf STYFILE "\\begin{filecontents}{noperltex-%d.tex}\n", 1+$e;
+ print STYFILE $macroexpansions[$e], "\\endinput\n";
+ print STYFILE "\\end{filecontents}\n";
+ }
+ print STYFILE "\\endinput\n";
+ close STYFILE;
+ }
+
+ exit 0;
+}
+
+__END__
+
+=head1 NAME
+
+perltex - enable LaTeX macros to be defined in terms of Perl code
+
+=head1 SYNOPSIS
+
+perltex
+[B<--help>]
+[B<--latex>=I<program>]
+[B<-->[B<no>]B<safe>]
+[B<--permit>=I<feature>]
+[B<--makesty>]
+[I<latex options>]
+
+=head1 DESCRIPTION
+
+LaTeX -- through the underlying TeX typesetting system -- produces
+beautifully typeset documents but has a macro language that is
+difficult to program. In particular, support for complex string
+manipulation is largely lacking. Perl is a popular general-purpose
+programming language whose forte is string manipulation. However, it
+has no typesetting capabilities whatsoever.
+
+Clearly, Perl's programmability could complement LaTeX's typesetting
+strengths. B<perltex> is the tool that enables a symbiosis between
+the two systems. All a user needs to do is compile a LaTeX document
+using B<perltex> instead of B<latex>. (B<perltex> is actually a
+wrapper for B<latex>, so no B<latex> functionality is lost.) If the
+document includes a C<\usepackage{perltex}> in its preamble, then
+C<\perlnewcommand> and C<\perlrenewcommand> macros will be made
+available. These behave just like LaTeX's C<\newcommand> and
+C<\renewcommand> except that the macro body contains Perl code instead
+of LaTeX code.
+
+=head1 OPTIONS
+
+B<perltex> accepts the following command-line options:
+
+=over 4
+
+=item B<--help>
+
+Display basic usage information.
+
+=item B<--latex>=I<program>
+
+Specify a program to use instead of B<latex>. For example,
+C<--latex=pdflatex> would typeset the given document using
+B<pdflatex> instead of ordinary B<latex>.
+
+=item B<-->[B<no>]B<safe>
+
+Enable or disable sandboxing. With the default of B<--safe>,
+B<perltex> executes the code from a C<\perlnewcommand> or
+C<\perlrenewcommand> macro within a protected environment that
+prohibits ``unsafe'' operations such as accessing files or executing
+external programs. Specifying B<--nosafe> gives the LaTeX document
+I<carte blanche> to execute any arbitrary Perl code, including that
+which can harm the user's files. See L<Safe> for more information.
+
+=item B<--permit>=I<feature>
+
+Permit particular Perl operations to be performed. The B<--permit>
+option, which can be specified more than once on the command line,
+enables finer-grained control over the B<perltex> sandbox. See
+L<Opcode> for more information.
+
+=item B<--makesty>
+
+Generate a LaTeX style file called F<noperltex.sty>. Replacing the
+document's C<\usepackage{perltex}> line with C<\usepackage{noperltex}>
+produces the same output but does not require PerlTeX, making the
+document suitable for distribution to people who do not have PerlTeX
+installed. The disadvantage is that F<noperltex.sty> is specific to
+the document that produced it. Any changes to the document's PerlTeX
+macro definitions or macro invocations necessitates rerunning
+B<perltex> with the B<--makesty> option.
+
+=back
+
+These options are then followed by whatever options are normally
+passed to B<latex> (or whatever program was specified with
+C<--latex>), including, for instance, the name of the F<.tex> file to
+compile.
+
+=head1 EXAMPLES
+
+In its simplest form, B<perltex> is run just like B<latex>:
+
+ perltex myfile.tex
+
+To use B<pdflatex> instead of regular B<latex>, use the B<--latex>
+option:
+
+ perltex --latex=pdflatex myfile.tex
+
+If LaTeX gives a ``C<trapped by operation mask>'' error and you trust
+the F<.tex> file you're trying to compile not to execute malicious
+Perl code (e.g., because you wrote it yourself), you can disable
+B<perltex>'s safety mechansisms with B<--nosafe>:
+
+ perltex --nosafe myfile.tex
+
+The following command gives documents only B<perltex>'s default
+permissions (C<:browse>) plus the ability to open files and invoke the
+C<time> command:
+
+ perltex --permit=:browse --permit=:filesys_open
+ --permit=time myfile.tex
+
+=head1 ENVIRONMENT
+
+B<perltex> honors the following environment variables:
+
+=over 4
+
+=item PERLTEX
+
+Specify the filename of the LaTeX compiler. The LaTeX compiler
+defaults to ``C<latex>''. The C<PERLTEX> environment variable
+overrides this default, and the B<--latex> command-line option (see
+L</OPTIONS>) overrides that.
+
+=back
+
+=head1 FILES
+
+While compiling F<jobname.tex>, B<perltex> makes use of the following
+files:
+
+=over 4
+
+=item F<jobname.lgpl>
+
+log file written by Perl; helpful for debugging Perl macros
+
+=item F<jobname.topl>
+
+information sent from LaTeX to Perl
+
+=item F<jobname.frpl>
+
+information sent from Perl to LaTeX
+
+=item F<jobname.tfpl>
+
+``flag'' file whose existence indicates that F<jobname.topl> contains
+valid data
+
+=item F<jobname.ffpl>
+
+``flag'' file whose existence indicates that F<jobname.frpl> contains
+valid data
+
+=item F<jobname.dfpl>
+
+``flag'' file whose existence indicates that F<jobname.ffpl> has been
+deleted
+
+=item F<noperltex-#.tex>
+
+file generated by F<noperltex.sty> for each PerlTeX macro invocation
+
+=back
+
+=head1 NOTES
+
+B<perltex>'s sandbox defaults to what L<Opcode> calls ``C<:browse>''.
+
+=head1 SEE ALSO
+
+latex(1), pdflatex(1), perl(1), Safe(3pm), Opcode(3pm)
+
+=head1 AUTHOR
+
+Scott Pakin, I<scott+pt@pakin.org>
diff --git a/Build/source/texk/texlive/linked_scripts/pkfix-helper/pkfix-helper b/Build/source/texk/texlive/linked_scripts/pkfix-helper/pkfix-helper
index e69de29bb2d..26d56ede05a 100755
--- a/Build/source/texk/texlive/linked_scripts/pkfix-helper/pkfix-helper
+++ b/Build/source/texk/texlive/linked_scripts/pkfix-helper/pkfix-helper
@@ -0,0 +1,1362 @@
+#! /usr/bin/env perl
+
+#############################################
+# Help pkfix decipher fonts in a PostScript #
+# file produced by an ancient dvips #
+# #
+# By Scott Pakin <scott+pkfh@pakin.org> #
+#############################################
+
+use File::Spec;
+use File::Temp qw (tempfile);
+use File::Basename;
+use Getopt::Long;
+use Pod::Usage;
+use warnings;
+use strict;
+
+# Define some global variables.
+my $progname = basename $0; # Name of this program
+our $VERSION = "1.3"; # Version number of this program
+my %name2chars; # Map from a font name to a character list
+my $GS = $ENV{"GS"} || "gs"; # Name of the Ghostscript interpreter
+my $TFTOPL = $ENV{"TFTOPL"} || "tftopl"; # Name of the TFM to PL converter
+my $dpi = 300; # Number of dots per inch used to generate bitmapped characters
+my @tfmlist; # List of TFM files to use
+my %fontmatch; # Map from a font name to its best match
+my $xinc = 36; # Width of font name in PostScript points
+my $yinc = 24; # Height of font in PostScript points
+my $init_yinc = 36; # Space after title
+my %tfmfontwidth; # Map from font name to character number to character width
+my %tfm2size; # Map from font name to design size
+my %tfmmissing; # Set of TFM files we looked for but didn't find
+my ($dvips_xscale, $dvips_yscale); # Scaling factors from Dvips's PostScript CTM
+my $discard_output = $^O eq "MSWin32" ? "> NUL:" : "> /dev/null 2>&1"; # Command suffix to discard all output
+
+# Define the global variables that the user can modify from the command line.
+my $wanthelp = 0; # 1=user wants to see the program's documentation
+my $wantversion = 0; # 1=user wants to see the program's version number
+my $verbose = 1; # 0=quiet; 1=verbose output; 2=more verbose output
+my @forced_fonts; # Font mappings the user asserts are correct
+my @exclude_res; # Regexps for fonts to exclude
+my @extra_tfms; # Extra TFM files to use
+my %retained_t3s; # Type 3 fonts to retain as is
+my $sample_file_ps; # Name of a PostScript file of font samples to write
+my $sample_file_tex; # Name of a TeX file of font samples to write
+my $single_font_use = 0; # 1=one use per font; 0=allow repetitions
+my $samples_per_page = 25; # Number of font samples to print per page
+my $tfm_cache_file; # Name of a file in which to cache font metrics
+
+###########################################################################
+
+# Read %tfm2size, %tfmfontwidth, and %tfmmissing from a file named by
+# $tfm_cache_file.
+sub read_tfm_cache_file
+{
+ open(CACHEFILE, "<", $tfm_cache_file) || do {
+ print STDERR "Ignoring TFM cache file $tfm_cache_file ($!).\n" if $verbose;
+ return;
+ };
+ print STDERR "Reading TFM data from $tfm_cache_file ... " if $verbose;
+ my $numlines = 0;
+ while (my $oneline = <CACHEFILE>) {
+ chomp $oneline;
+ my @fields = split " ", $oneline;
+ die "${progname}: Internal error" if $#fields == -1;
+ my $tfm = shift @fields;
+ if ($#fields == -1) {
+ # No metrics -- TFM file must not have been found.
+ $tfmmissing{$tfm} = 1;
+ }
+ else {
+ # Parse and store the TFM data.
+ $tfm2size{$tfm} = shift @fields;
+ my %widths = @fields;
+ $tfmfontwidth{$tfm} = \%widths;
+ }
+ $numlines++;
+ }
+ close CACHEFILE;
+ print STDERR "done ($numlines entries).\n" if $verbose;
+}
+
+
+# Write %tfm2size, %tfmfontwidth, and %tfmmissing to a file named by
+# $tfm_cache_file.
+sub write_tfm_cache_file
+{
+ print STDERR "Writing TFM data to $tfm_cache_file ... " if $verbose;
+ open(CACHEFILE, ">", $tfm_cache_file) || die "${progname}: Failed to create $tfm_cache_file ($!)\n";
+ while (my ($tfm, $size) = each %tfm2size) {
+ my @widths = %{$tfmfontwidth{$tfm}};
+ print CACHEFILE "$tfm $size @widths\n";
+ }
+ foreach my $tfm (keys %tfmmissing) {
+ print CACHEFILE "$tfm\n";
+ }
+ close CACHEFILE;
+ print STDERR "done.\n" if $verbose;
+}
+
+
+# Given the base name of a .tfm file, process the file and return the
+# font's design size and a mapping from character number to character
+# width.
+sub tfm2widths ($)
+{
+ my $tfmname = $_[0]; # Name of the TFM file
+ my $designsize; # Design size of the font
+ my %num2width; # Resultant mapping
+ my $plname; # Name of PL file; some tftopl programs can't write to stdout.
+ my $plfile; # Filehandle corresponding to $plname
+
+ # First see if the information is already cached.
+ if (defined $tfm2size{$tfmname}) {
+ print STDERR " Processing $tfmname ... cached.\n" if $verbose >= 2;
+ return [$tfm2size{$tfmname}, %{$tfmfontwidth{$tfmname}}];
+ }
+ if (defined $tfmmissing{$tfmname}) {
+ print STDERR " Processing $tfmname ... cached as not found.\n" if $verbose >= 2;
+ return [$designsize, %num2width];
+ }
+
+ # The information is not cached -- read it from a file.
+ ($plfile, $plname) = tempfile (DIR => File::Spec->tmpdir(), SUFFIX => ".pl");
+ close $plfile;
+ if (!system "$TFTOPL $tfmname $plname $discard_output") {
+ print STDERR " Processing $tfmname ... " if $verbose >= 2;
+ open (PLFILE, "<$plname") || die "${progname}: Unable to open $tfmname ($!)\n";
+ my $charnum; # Current character number
+ while (my $oneline = <PLFILE>) {
+ # Store the current character width.
+ $oneline =~ /\(CHARWD R (\S+)\)/ && do {
+ $num2width{$charnum} = $1 * $designsize;
+ next;
+ };
+
+ # Store the current character number.
+ $oneline =~ /\(CHARACTER (\S) (\S+)/ && do {
+ if ($1 eq "O") {
+ # Octal character number
+ $charnum = oct $2;
+ }
+ elsif ($1 eq "C") {
+ # ASCII character number
+ $charnum = ord $2;
+ }
+ else {
+ die "${progname}: Unknown TFM character type \"$1\"\n";
+ }
+ next;
+ };
+
+ # Store the font design size.
+ $oneline =~ /\(DESIGNSIZE R (\S+)\)/ && do {
+ $designsize = $1 + 0.0;
+ next;
+ };
+ }
+ close PLFILE;
+ print STDERR "done.\n" if $verbose >= 2;
+ }
+ else {
+ print STDERR " Discarding $tfmname (not found).\n" if $verbose >= 3;
+ }
+ unlink $plname;
+ return [$designsize, %num2width];
+}
+
+
+# Given two character maps, one for a document font and one for a TFM
+# file, return the optimal scale factor for the TFM file to best match
+# the document font.
+sub optimal_scale ($$)
+{
+ my %docmap = %{$_[0]}; # Map for a document font
+ my %tfmmap = %{$_[1]}; # Map for a TFM font
+ my $doc_dot_tfm = 0.0; # Sum of $docmap{$c}*$tfmmap{$c} for all $c
+ my $tfm_dot_tfm = 0.0; # Sum of $tfmmap{$c}**2 for all $c
+
+ while (my ($char, $docwidth) = each %docmap) {
+ my $tfmwidth = $tfmmap{$char};
+ return undef if !defined $tfmwidth; # Match is impossible.
+ $doc_dot_tfm += $docwidth * $tfmwidth;
+ $tfm_dot_tfm += $tfmwidth**2;
+ }
+ return 1.0 if $tfm_dot_tfm == 0.0; # TFM characters all have zero width.
+ return $doc_dot_tfm / $tfm_dot_tfm;
+}
+
+
+# Compare two character maps and return their mismatch (smaller is
+# better). The third, optional, argument is a scale factor for the
+# TFM file.
+sub compare_maps ($$;$)
+{
+ my %docmap = %{$_[0]}; # Map for a document font
+ my %tfmmap = %{$_[1]}; # Map for a TFM font
+ my $scale = $_[2] || 1.0; # Scale for each TFM width
+ my $sqdiff = 0; # Sum of squares of differences
+
+ while (my ($char, $docwidth) = each %docmap) {
+ my $tfmwidth = $tfmmap{$char};
+ return 10**9 if !defined $tfmwidth; # Match is impossible.
+ $sqdiff += ($docwidth - $tfmwidth*$scale) ** 2;
+ }
+ return $sqdiff;
+}
+
+
+# Given a Type 3 font definition, surround it with DVIPSBitmapFont comments.
+sub write_comments ($$)
+{
+ my ($fontname, $fontdef) = @_;
+ return $fontdef if !defined $fontmatch{$fontname}; # Font should not be substituted
+ my $tfm = $fontmatch{$fontname}->{"tfm"};
+ my $scale = $fontmatch{$fontname}->{"scale"};
+ my $origsize = $tfm2size{$tfm};
+ my $begincomment = sprintf '%%DVIPSBitmapFont: %s %s %.5g %d',
+ $fontname, $tfm, $origsize*$scale, 1+$#{$name2chars{$fontname}};
+ my $endcomment = '%EndDVIPSBitmapFont';
+ return "\n" . $begincomment . "\n" . $fontdef . "\n" . $endcomment . "\n";
+}
+
+
+# Escape an array of characters for PostScript's benefit.
+sub psify (@)
+{
+ my @ps_chars;
+ foreach my $onechar (@_) {
+ my $charnum = ord $onechar;
+ if ($onechar =~ /[\\()]/) {
+ push @ps_chars, "\\" . $onechar;
+ }
+ elsif ($charnum >= 32 && $charnum <= 126) {
+ push @ps_chars, $onechar;
+ }
+ else {
+ push @ps_chars, sprintf "\\%03o", $charnum;
+ }
+ }
+ return @ps_chars;
+}
+
+
+# Escape an array of characters for TeX's benefit.
+sub texify (@)
+{
+ my @texchars;
+ foreach my $onechar (@_) {
+ if ($onechar =~ m|[\000-\037\\{}\177-\377]|) {
+ push @texchars, sprintf '\char"%02X{}', ord $onechar;
+ }
+ else {
+ push @texchars, $onechar;
+ }
+ }
+ return join "", @texchars;
+}
+
+
+# Parse a font specification into a hash of information.
+sub parse_font_spec ($)
+{
+ my $spec = $_[0];
+ my $parse_error = "${progname}: Unable to parse font specification \"$spec\"\n";
+
+ if ($spec =~ /^\s*([-\w]+)(.*)$/o) {
+ my $tfm = $1; # Name of tfm file (e.g., "cmr10")
+ my $scale_info = $2; # Concatenation of scale type and scale factor
+ my $scale_type; # "X"=multiply, "@"=assign
+ my $scale_amount; # How much to scale the TFM file, "*"=automatic
+
+ if ($scale_info =~ /^\s*$/o) {
+ # Empty
+ $scale_type = 'X';
+ $scale_amount = 1.0;
+ }
+ elsif ($scale_info =~ /^\s*\@\s*([\d.]+)\s*X\s*$/io) {
+ # "@ <number> X"
+ $scale_type = 'X';
+ $scale_amount = $1 + 0.0;
+ }
+ elsif ($scale_info =~ /^\s*\@\s*([\d.]+)\s*(pt|bp)\s*$/io) {
+ # "@ <number> pt" or "@ <number> bp"
+ $scale_type = '@';
+ $scale_amount = $1;
+ $scale_amount *= 72.0/72.27 if $2 eq "bp"; # Convert to TeX points
+ }
+ elsif ($scale_info =~ /^\s*\@\s*\*\s*$/o) {
+ # "@ *"
+ $scale_type = 'X';
+ $scale_amount = "*"
+ }
+ else {
+ die $parse_error;
+ }
+ return {"tfm" => $tfm,
+ "scale_type" => $scale_type,
+ "scale" => $scale_amount};
+ }
+ die $parse_error;
+}
+
+
+# Return all of the unique items in a given list.
+sub unique_items (@)
+{
+ my %item_hash;
+ foreach my $item (@_) {
+ $item_hash{$item} = 1;
+ }
+ return keys %item_hash;
+}
+
+###########################################################################
+
+# Parse the command line.
+Getopt::Long::Configure ("bundling");
+GetOptions ("h|help" => \$wanthelp,
+ "v|verbose+" => \$verbose,
+ "V|version" => \$wantversion,
+ "q|quiet" => sub {$verbose = 0},
+ "f|force=s" => \@forced_fonts,
+ "i|include=s" => \@extra_tfms,
+ "x|exclude=s" => \@exclude_res,
+ "k|keep=s" => sub {$retained_t3s{$_[1]} = 1},
+ "t|tex=s" => \$sample_file_tex,
+ "p|ps=s" => \$sample_file_ps,
+ "s|spp=i" => \$samples_per_page,
+ "C|cache=s" => \$tfm_cache_file,
+ "1|no-repeats" => \$single_font_use) || pod2usage(2);
+if ($wantversion) {
+ print "pkfix-helper $VERSION\n";
+ exit 0;
+}
+if ($wanthelp) {
+ pod2usage (-verbose => $verbose,
+ -exitval => "NOEXIT");
+ print "Report bugs to scott+pkfh\@pakin.org.\n" if $verbose == 1;
+ exit 0;
+}
+my $infilename = $#ARGV>=0 ? $ARGV[0] : "-";
+my $outfilename = $#ARGV>=1 ? $ARGV[1] : "-";
+die "${progname}: Samples per page must be at least 1 ($samples_per_page was specified)\n" if $samples_per_page < 1;
+
+# Convert any user-specified TFMs to the appropriate internal format.
+foreach my $tfm (@extra_tfms) {
+ my $font_spec = parse_font_spec $tfm;
+ push @tfmlist, $font_spec;
+}
+
+# Parse the list of forced font mappings.
+foreach my $mapstr (@forced_fonts) {
+ $mapstr =~ /^(\w+)\s*=\s*(.*)$/ || die "${progname}: Unable to parse font specification \"$mapstr\"\n";
+ $fontmatch{$1} = parse_font_spec $2;
+}
+
+# Construct a list of (possibly nonexistent) TFM files to try. These
+# should be in order of decreasing likelihood. Each entry in the list
+# is of the form {full name, scale factor}.
+@exclude_res = ('^\s*$') if $#exclude_res == -1;
+foreach my $size_scale ([10, 1.0],
+ [12, 1.0],
+ [10, 1.1],
+ [17, 1.0],
+ [ 9, 1.0],
+ [ 8, 1.0],
+ [ 7, 1.0],
+ [ 6, 1.0],
+ [ 5, 1.0],
+ [10, 1.2],
+ # The following are common cmbx12 section headings.
+ [12, 14.40/12],
+ [12, 17.28/12],
+ [12, 20.74/12],
+ [12, 24.88/12]) {
+ my ($pointsize, $scale) = @$size_scale;
+ FONTLOOP:
+ foreach my $basefont (qw (cmr cmb cmtt cmbsy cmbx cmbxsl cmbxti
+ cmcsc cmex cmitt cmmi cmmib cmsl cmsltt
+ cmss cmssbx cmssi cmsy cmti lasy lasyb
+ msam msbm cmssdc cmtcsc)) {
+ my $friendly_name = sprintf "%s%d \@ %.5gX", $basefont, $pointsize, $scale;
+ foreach my $regexp (@exclude_res) {
+ next FONTLOOP if $friendly_name =~ $regexp;
+ }
+ push @tfmlist, {"tfm" => $basefont . $pointsize,
+ "scale_type" => "X",
+ "scale" => $scale};
+ }
+}
+
+# Read the entire input file.
+$| = 1;
+if ($verbose) {
+ printf STDERR "Reading %s ... ", $infilename eq "-" ? "standard input" : $infilename;
+}
+my $entirefile;
+{
+ local $/ = undef;
+ open (INFILE, "<$infilename") || die "open(\"$infilename\"): $!\n";
+ binmode INFILE;
+ $entirefile = <INFILE>;
+ close INFILE;
+}
+print STDERR "done.\n" if $verbose;
+
+# Construct a mapping from each document font name to a list of valid
+# characters in that font.
+while ($entirefile =~ m|/(\S+)\s+\d+\s+\d+\s+df(.*?>[^<>]*?[DI])\s+E|gs) {
+ my $fontname = $1; # Name of current font (e.g., "Fa")
+ my $fontbody = $2; # List of character definitions as hexadecimal strings
+ my $charnum = 0; # Current character number
+ my @charlist = (); # List of valid characters in PostScript-friendly format
+
+ while ($fontbody =~ /<[0-9A-F\s]+>(.*?[DI])/gs) {
+ # Put the current character number in $charnum then append the
+ # corresponding character to @charlist.
+ my @chardef = split " ", $1;
+ if ($chardef[$#chardef] eq "I") {
+ $charnum++;
+ }
+ else {
+ $charnum = $chardef[$#chardef-1];
+ }
+ push @charlist, chr $charnum;
+ }
+ $name2chars{$fontname} = \@charlist;
+}
+my @sortedfontnames = sort {$#{$name2chars{$b}} <=> $#{$name2chars{$a}}
+ || $a cmp $b} keys %name2chars;
+if ($verbose) {
+ printf STDERR "Number of Type 3 fonts encountered: %d\n", 1+$#sortedfontnames;
+ if ($verbose >= 2) {
+ foreach my $fontname (@sortedfontnames) {
+ printf STDERR " %s -- %3d character(s)\n", $fontname, 1+$#{$name2chars{$fontname}};
+ }
+ }
+}
+die "${progname}: No Type 3 fonts were encountered in the input file\n" if $#sortedfontnames==-1;
+
+# Determine the number of dots per inch used to generate the bitmaps.
+if ($entirefile =~ /dpi=(\d+)/i
+ || $entirefile =~ /Resolution (\d+)dpi/i) {
+ $dpi = $1 + 0;
+ printf STDERR "Bitmapped fonts are typeset at $dpi DPI.\n" if $verbose;
+}
+else {
+ warn "${progname}: Could not determine the target printer resolution; assuming $dpi DPI\n";
+}
+
+# Insert some helper code after the first ProcSet.
+my $output_width_ps = $entirefile; # PostScript code to output character widths
+my $showfontnamecode = <<"SHOWFONTNAME";
+\%\%BeginProcSet: $progname.pro
+TeXDict begin
+
+% char0 char1 PRINT-WIDTH -
+%
+% Output the name of the current font (font-name-string), its character
+% number (char0), and the character's width in PostScript points.
+
+/print-width {
+ pop
+ (FONT: ) print font-name-string print
+ ( CHAR: ) print 8 string cvs print
+ ( XPOS: ) print currentpoint pop 80 string cvs print
+ (\\n) print
+} bind def
+
+% font-name sample-string PRINT-CHAR-WIDTHS -
+%
+% Store the name of font-name in the string font-name-string. Then, select
+% font-name and, for each character of test-string, call print-width
+% to output its width.
+
+/print-char-widths {
+ /sample-string exch def
+ /font-name exch def
+ font-name 8 string cvs /font-name-string exch def
+ font-name cvx exec {print-width} sample-string kshow
+} bind def
+
+end
+\%\%EndProcSet
+SHOWFONTNAME
+ ;#'
+if ($output_width_ps !~ s/\%\%EndProcSet/$&\n$showfontnamecode/s) {
+ print STDERR 'No %%EndProcSet comment was found. We have to guess where to inject PostScript code.', "\n" if $verbose >= 3;
+ die "${progname}: Unable to inject prologue code\n" if $output_width_ps !~ s/TeXDict begin\s+\d+\s+\d+\s+bop/\n$showfontnamecode\n$&/s;
+}
+
+# Define some code to display the width of every valid character in
+# every bitmapped font. Fonts are displayed in decreasing order of
+# the number of characters used.
+my $displaycode = "\%\%Page: 1 1\nTeXDict begin\n1 0 bop\n";
+foreach my $fontnum (0 .. $#sortedfontnames) {
+ # Duplicate the last character of the sample string so kshow can
+ # process the final character delta.
+ my $fontname = $sortedfontnames[$fontnum];
+ my @charlist = psify @{$name2chars{$fontname}};
+ my $samplestring = join("", @charlist) . $charlist[$#charlist];
+
+ # Typeset the string starting at horizontal offset 0.
+ $displaycode .= sprintf "0 0 moveto\n";
+ $displaycode .= "/$fontname ($samplestring) print-char-widths\n";
+}
+
+# Dvips scales the page. Determine the scaling it uses.
+$displaycode .= <<'ENDDISPLAYCODE';
+(CURRENTMATRIX: ) print
+matrix currentmatrix ==
+(\n) print
+eop
+end
+ENDDISPLAYCODE
+ ;
+
+# Replace the bulk of the PostScript file with the display code.
+if ($output_width_ps !~ s/\%\%Page:.*(\%\%Trailer)/$displaycode$1/s) {
+ print STDERR 'No %%Page and/or %%Trailer comments were found. We have to guess where to inject PostScript code.', "\n" if $verbose >= 3;
+ die "${progname}: Unable to inject display code\n" if $output_width_ps !~ s/TeXDict begin\s+\d+\s+\d+\s+bop.*eop\s+end/\n$displaycode\n/s;
+}
+
+# Output the modified PostScript code to a temporary file, run
+# Ghostscript on the temporary file, and process Ghostscript's output.
+my ($psfile, $psfilename) = tempfile ("pkfix-helper-XXXXXX",
+ DIR => File::Spec->tmpdir(),
+ SUFFIX => ".ps");
+binmode $psfile;
+print $psfile $output_width_ps;
+close $psfile;
+undef $output_width_ps;
+my %fontwidth; # Map from font name to character number to character width
+my @previnfo = ("", 0.0); # Previous font name and final character position
+my $gscmd = "$GS -q -dNOPAUSE -dBATCH -dNODISPLAY $psfilename";
+print STDERR "Finding character widths ... " if $verbose >= 1;
+print STDERR "\n Invoking: $gscmd\n" if $verbose >= 2;
+print STDERR "done.\n" if $verbose >= 1;
+open (GSCMD, "$gscmd|") || die "${progname}: failed to fork ($!)\n";
+while (my $oneline = <GSCMD>) {
+ if ($oneline =~ /FONT: (\S+)\s*CHAR: (\d+)\s*XPOS: (\S+)/o) {
+ my ($fontname, $charnum, $xpos) = ($1, $2, $3);
+ my $width = $xpos + 0.0;
+ $width -= $previnfo[1] if $fontname eq $previnfo[0];
+ $fontwidth{$fontname}->{$charnum} = $width * 72.27 / $dpi;
+ @previnfo = ($fontname, $xpos);
+ }
+ elsif ($oneline =~ /CURRENTMATRIX: \[\s*([-\d.]+)\s+[-\d.]+\s+[-\d.]+\s+([-\d.]+)\s+[-\d.]+\s+[-\d.]+\s*\]/o) {
+ $dvips_xscale = $1 * 1.0;
+ $dvips_yscale = $2 * -1.0;
+ }
+}
+close GSCMD || die "${progname}: failed to run $GS ($!)\n";
+unlink $psfilename;
+die "${progname}: No character-width information was found\n" if !%fontwidth;
+
+# Read TFM font metrics from a cache file if specified.
+read_tfm_cache_file() if defined $tfm_cache_file;
+
+# Read each TFM file and store its design size and character widths.
+print STDERR "Reading TFM files ... " if $verbose;
+print STDERR "\n" if $verbose >= 2;
+foreach my $tfm (sort {$a cmp $b}
+ unique_items map {$_->{"tfm"}} (@tfmlist, values %fontmatch)) {
+ my ($designsize, %num2widths) = @{tfm2widths $tfm};
+ if (%num2widths) {
+ $tfmfontwidth{$tfm} = \%num2widths;
+ $tfm2size{$tfm} = $designsize * 1.0;
+ }
+ else {
+ $tfmmissing{$tfm} = 1;
+ }
+}
+
+# Remove nonexistent fonts from @tfmlist and replace all absolute
+# ("@") scaling with relative ("X") scaling.
+my @goodtfmlist;
+foreach my $tfminfo (@tfmlist) {
+ my $tfm = $tfminfo->{"tfm"};
+ next if !defined ($tfmfontwidth{$tfm});
+ $tfminfo->{"designsize"} = $tfm2size{$tfm};
+ if ($tfminfo->{"scale_type"} eq "@") {
+ # Convert absolute to relative sizes.
+ $tfminfo->{"scale_type"} = "X";
+ $tfminfo->{"scale"} /= $tfminfo->{"designsize"};
+ }
+ push @goodtfmlist, $tfminfo;
+}
+@tfmlist = @goodtfmlist;
+undef @goodtfmlist;
+
+# Do the same for all user-specified font mappings.
+while (my ($fontname, $tfminfo) = each %fontmatch) {
+ my $tfm = $tfminfo->{"tfm"};
+ if (!defined ($tfmfontwidth{$tfm})) {
+ print STDERR "failed.\n" if $verbose;
+ die "${progname}: Unable to process user-specified TFM file \"$tfm\"\n";
+ }
+ $tfminfo->{"designsize"} = $tfm2size{$tfm};
+ if ($tfminfo->{"scale_type"} eq "@") {
+ # Convert absolute to relative sizes.
+ $tfminfo->{"scale_type"} = "X";
+ $tfminfo->{"scale"} /= $tfminfo->{"designsize"};
+ }
+}
+
+# Report the number of fonts in our repertoire.
+my $numtfms = keys %tfm2size;
+my $numfonts = 1 + $#tfmlist;
+print STDERR "done ($numtfms TFMs in $numfonts scaling variations).\n" if $verbose;
+die "${progname}: No TFM files were processed successfully\n" if !$numtfms;
+
+# Write the TFM font metrics to a a cache file if specified.
+write_tfm_cache_file() if defined $tfm_cache_file;
+
+# Compare every document font (ordered by decreasing number of
+# characters utilized) to every TFM file (in increasing order of
+# obscurity).
+print STDERR "Matching fonts:\n" if $verbose;
+foreach my $fontname (@sortedfontnames) {
+ my @besttfms; # Best matching TFM file(s), sizes, and scales
+ my $bestmatch = 10**9; # Best matching value
+
+ # Don't substitute the font if we were told not to.
+ if (defined $retained_t3s{$fontname}) {
+ print STDERR " Retaining $fontname as a bitmapped font.\n" if $verbose == 1;
+ next;
+ }
+
+ # Determine the list of eligible fonts to compare against.
+ my @eligible_tfms;
+ foreach my $tfminfo ($fontmatch{$fontname} || @tfmlist) {
+ if ($tfminfo->{"scale"} eq "*") {
+ # Replace "*" with the best scaling factor we can find.
+ my $newscale = optimal_scale $fontwidth{$fontname}, $tfmfontwidth{$tfminfo->{"tfm"}};
+ if (defined $newscale) {
+ # Replace the "*" with the optimal multiplier.
+ my %newtfminfo = %$tfminfo;
+ $newtfminfo{"scale"} = $newscale;
+ push @eligible_tfms, \%newtfminfo;
+ }
+ else {
+ # Fonts are incomparable.
+ my $tfm = $tfminfo->{"tfm"};
+ print STDERR " Not scaling $tfm; ${fontname}'s character set is not a subset of ${tfm}'s.\n" if $verbose >= 2;
+ }
+ }
+ else {
+ # The desired scaling factor is specified explicitly.
+ push @eligible_tfms, $tfminfo;
+ }
+ }
+ die "${progname}: No fonts are eligible to match $fontname\n" if !@eligible_tfms;
+
+ # Try each TFM file in increasing order of obscurity.
+ print STDERR " Processing $fontname ... " if $verbose == 1;
+ foreach my $tfminfo (@eligible_tfms) {
+ my $tfm = $tfminfo->{"tfm"};
+ my $scale = $tfminfo->{"scale"};
+ printf STDERR " Comparing %s and %s \@ %.5gX ... ", $fontname, $tfm, $scale if $verbose >= 2;
+ my $match = compare_maps $fontwidth{$fontname}, $tfmfontwidth{$tfm}, $scale;
+ if ($bestmatch > $match) {
+ # We found a closer match than what we had before.
+ $bestmatch = $match;
+ @besttfms = ($tfminfo);
+ }
+ elsif ($bestmatch == $match) {
+ # We found an equal match to what we had before.
+ push @besttfms, $tfminfo;
+ }
+ printf STDERR "done (mismatch=%.5f).\n", $match if $verbose >= 2;
+ }
+
+ # Select the first of the best matches.
+ $fontmatch{$fontname} = $besttfms[0];
+ my $besttfminfo = $fontmatch{$fontname};
+ my $besttfm = $besttfminfo->{"tfm"};
+ my $bestscale = $besttfminfo->{"scale"};
+ if ($verbose >= 2) {
+ if ($#besttfms == 0) {
+ # Single winner
+ printf STDERR " Best match for %s is %s \@ %.5gX with mismatch=%.5f.\n\n",
+ $fontname, $besttfm, $bestscale, $bestmatch;
+ }
+ else {
+ # Tie for first place
+ printf STDERR " Best match for %s is %s \@ %.5gX (tied among %s) with mismatch=%.5f.\n\n",
+ $fontname, $besttfm, $bestscale,
+ join("/", map {sprintf "%s\@%.5gX", $_->{"tfm"}, $_->{"scale"}} @besttfms),
+ $bestmatch;
+ }
+ }
+ elsif ($verbose == 1) {
+ printf STDERR "done (%s \@ %.5gX, mismatch=%.5f).\n", $besttfm, $bestscale, $bestmatch;
+ }
+ warn "${progname}: Best match for $fontname is rather poor\n" if $bestmatch>=1.0;
+
+ # Optionally remove the font from @goodtfmlist.
+ if ($single_font_use) {
+ @tfmlist = grep {$_->{"tfm"} ne $besttfm
+ || $_->{"scale"} != $bestscale} @tfmlist;
+ }
+}
+
+# Insert %DVIPSBitmapFont comments around every Type 3 font definition.
+my $commented_ps = $entirefile;
+my $infilename_ps = $infilename; # PostScript version of $infilename
+$infilename_ps =~ s/[()\\]/\\$1/g;
+$commented_ps =~ s|^\%(End)?DVIPSBitmapFont.*$||gm; # Remove existing comments (if any)
+$commented_ps =~ s|/(\S+)\s+\d+\s+\d+\s+df.*?>[^<]*?[DI]\s+E|write_comments($1, $&)|gse;
+if ($commented_ps !~ /\%\%EndProlog/) {
+ # pkfix fails silently if it doesn't see an %%EndProlog.
+ print STDERR "No %%EndProlog comment was found. Adding one.\n" if $verbose >= 3;
+ $commented_ps =~ s|TeXDict begin\s+\d+\s+\d+\s+bop|\%\%EndProlog\n$&|s;
+}
+
+# Help pkfix handle ancient versions of dvips by artificially making
+# dvips look newer.
+$commented_ps =~ s|(\%\%Creator: dvips\S*) \S+|$1 5.62|; # pkfix rejects dvips <= 5.58
+if ($commented_ps =~ s|(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+\@start|$1 $2 $3 $4 $5 ($infilename_ps) \@start|gx) {
+ # pkfix expects *six* arguments to @start, not five as in old
+ # versions of dvips.
+ $commented_ps =~ s|/\@start\s*\{|$& pop |;
+}
+if ($commented_ps !~ /^%DVIPSParameters:.*dpi=([\dx]+)/) {
+ # Tell pkfix what resolution to use.
+ my $dvips_params = "\%DVIPSParameters: dpi=$dpi";
+ if ($commented_ps !~ s|^\%\%EndComments.*$|$&\n$dvips_params|m) {
+ # Comments must have been stripped.
+ $commented_ps =~ s|\n|\n$dvips_params\n|;
+ }
+}
+
+# Write the modified PostScript code to the specified file.
+open (OUTFILE, ">$outfilename") || die "${progname}: Unable to open $outfilename ($!)\n";
+print OUTFILE $commented_ps;
+close OUTFILE;
+undef $commented_ps;
+
+# If the user requested a PostScript font sample, produce that.
+if (defined $sample_file_ps) {
+ # Insert some helper code at an appropriate place in the file.
+ my $sample_ps = $entirefile;
+ my $showfontnamecode = <<"SHOWFONTNAME";
+\%\%BeginProcSet: $progname.pro
+TeXDict begin
+
+% font-name sample-string PRINT-FONT-SAMPLE -
+%
+% Store the name of font-name in the string font-name-string. Then,
+% output font-name-string in Times Bold in case the user wants to view
+% the font samples. Next, select font-name and output the sample
+% string. Finally, move the cursor to the next line in preparation for
+% the next invocation of print-font-sample.
+/print-font-sample {
+ /sample-string exch def
+ /font-name exch def
+ font-name 8 string cvs /font-name-string exch def
+ gsave
+ /Times-Bold 12 selectfont
+ font-name-string show (:) show
+ grestore
+ gsave
+ 36 0 rmoveto
+ font-name cvx exec
+ currentfont bitmap-font-transform makefont setfont
+ sample-string show
+ grestore
+ 0 -24 rmoveto
+} def
+
+\% Define a transformation matrix for dvips bitmapped fonts. We _could_
+\% do this dynamically but there seems to be a bug in GhostView (v3.6.1)
+\% or GhostScript (ESP v7.07.1) that causes the page layout to change
+\% with rescaling. To avoid problems we simply hardwire the scaling
+\% factor.
+/bitmap-font-transform [$dvips_xscale 0.0 0.0 $dvips_yscale 0 0] def
+
+end
+\%\%EndProcSet
+SHOWFONTNAME
+ ;
+ if ($sample_ps !~ s/\%\%EndProcSet/$&\n$showfontnamecode/) {
+ print STDERR 'No %%EndProcSet comment was found. We have to guess where to inject PostScript code.', "\n" if $verbose >= 3;
+ die "${progname}: Unable to inject prologue code\n" if $sample_ps !~ s/TeXDict begin\s+\d+\s+\d+\s+bop/\n$showfontnamecode\n$&/s;
+ }
+
+ # Generate code to output a sample of each font in turn.
+ my $displaycode = <<"PAGEHEADER";
+\%\%Page: 1 1
+TeXDict begin
+1 0 bop
+
+\% Display a page title.
+0 0 moveto
+initmatrix
+gsave
+ /Helvetica 18 selectfont
+ (Fonts used by $infilename_ps) show
+grestore
+0 -$init_yinc rmoveto
+
+\% Display samples of each document font in decreasing order of the number
+\% of characters utilized from the font.
+PAGEHEADER
+;
+ my $pageno = 1;
+ foreach my $fontnum (0 .. $#sortedfontnames) {
+ my $fontname = $sortedfontnames[$fontnum];
+ my $samplestring = join("", psify @{$name2chars{$fontname}});
+ $displaycode .= "/$fontname ($samplestring) print-font-sample\n";
+ if ($fontnum % $samples_per_page == $samples_per_page-1
+ && $fontnum != $#sortedfontnames) {
+ # Insert a page break after every $samples_per_page font samples.
+ $pageno++;
+ $displaycode .= <<"PAGETRANSITION";
+eop
+end
+\%\%Page: $pageno $pageno
+TeXDict begin
+$pageno @{[$pageno-1]} bop
+0 0 moveto
+initmatrix
+PAGETRANSITION
+;
+ }
+ }
+ $displaycode .= "eop\nend\n";
+ if ($sample_ps !~ s/\%\%Page:.*(\%\%Trailer)/$displaycode$1/s) {
+ print STDERR 'No %%Page and/or %%Trailer comments were found. We have to guess where to inject PostScript code.', "\n" if $verbose >= 3;
+ die "${progname}: Unable to inject display code\n" if $sample_ps !~ s/TeXDict begin\s+\d+\s+\d+\s+bop.*eop\s+end/\n$displaycode\n/s;
+ }
+
+ # Write the PostScript file.
+ open (SAMPLE_PS, ">$sample_file_ps") || die "${progname}: Unable to open $sample_file_ps ($!)\n";
+ binmode SAMPLE_PS;
+ print SAMPLE_PS $sample_ps;
+ close SAMPLE_PS;
+ undef $sample_ps;
+}
+
+# If the user requested a TeX font sample, produce that.
+if (defined $sample_file_tex) {
+ my $oneline; # One line to write to the TeX file.
+
+ open (SAMPLE_TEX, ">$sample_file_tex") || die "${progname}: Unable to open $sample_file_tex ($!)\n";
+ select (SAMPLE_TEX);
+ $| = 1;
+ select (STDOUT);
+ format SAMPLE_TEX =
+% ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ~~
+$oneline
+.
+ $oneline = <<"TEX_HEADER";
+This file was generated by $0. DO NOT EDIT. Edit $progname instead.
+Note that this is a Plain TeX document. Compile it with tex, *not*
+latex.
+TEX_HEADER
+ ;
+ write SAMPLE_TEX;
+ print SAMPLE_TEX <<'TEX_BOILERPLATE';
+
+% Make printable all special characters between
+% \makeprintable...\endmakeprintable except for "\", "{", and "}".
+\def\makeprintable{%
+ \begingroup
+ \def\do##1{\catcode`##1=12}%
+ \dospecials
+ \catcode`\\=0\relax
+ \catcode`\{=1\relax
+ \catcode`\}=2\relax
+}
+\let\endmakeprintable=\endgroup
+
+% Put a piece of text at specific PostScript coordinates.
+\newdimen\xofs
+\newdimen\yofs
+\def\put(#1,#2)#{%
+ \leavevmode
+ \begingroup
+ \makeprintable
+ \xofs=#1bp
+ \yofs=#2bp
+ \afterassignment\puthelper
+ \toks0=%
+}
+\def\puthelper{%
+ \lower\yofs
+ \rlap{\hskip\xofs\the\toks0}%
+ \endmakeprintable
+ \endgroup
+}
+
+% We use Times Bold at 12 point for font names.
+\font\timesbXII=ptmb at 12bp
+
+% Don't add extra space to paragraphs.
+\parindent=0pt
+\parskip=0pt
+
+% Output the document's title.
+\font\somefont=phvr at 18bp
+TEX_BOILERPLATE
+ ;
+ # Output the document's title.
+ my $infilename_tex = texify split "", $infilename;
+ print SAMPLE_TEX '\put(0, 0){\somefont New fonts to use for ', "$infilename_tex}\n";
+
+ # Output samples of each font in turn.
+ print SAMPLE_TEX "\n\% Output font samples.\n";
+ my $firstfontnum = 0;
+ foreach my $fontnum (0 .. $#sortedfontnames) {
+ # Output a single font sample.
+ my $fontname = $sortedfontnames[$fontnum];
+ my $samplestring = texify @{$name2chars{$fontname}};
+ my $yoffset = ($fontnum-$firstfontnum)*$yinc + $init_yinc;
+ printf SAMPLE_TEX "\\put(0, %d){\\timesbXII %s:}%%\n", $yoffset, $fontname;
+ my $tfm = $fontmatch{$fontname}->{"tfm"};
+ my $scale = $fontmatch{$fontname}->{"scale"};
+ my $size = $tfm2size{$tfm};
+ printf SAMPLE_TEX "\\font\\somefont=%s%s\\somefont\n",
+ $tfm, $scale==1.0 ? "" : sprintf(" at %.5gbp", $scale*$size);
+ printf SAMPLE_TEX "\\put(%d, %d){%s}%%\n", $xinc, $yoffset, $samplestring;
+
+ if ($fontnum % $samples_per_page == $samples_per_page-1) {
+ # Insert a page break after every $samples_per_page font samples.
+ print SAMPLE_TEX "\\vskip 0pt plus 1fill\\eject\n";
+ $firstfontnum = $fontnum;
+ }
+ }
+
+ # Complete the TeX file.
+ print SAMPLE_TEX "\n\\bye\n";
+ close SAMPLE_TEX;
+}
+
+###########################################################################
+
+__END__
+
+=head1 NAME
+
+pkfix-helper - preprocess dvips-produced PostScript documents before passing them to pkfix
+
+
+=head1 SYNOPSIS
+
+pkfix-helper
+[B<--help>]
+[B<--verbose>]
+[B<--force>=I<name>=I<fontspec>]
+[B<--ps>=I<filename.ps>]
+[B<--tex>=I<filename.tex>]
+[B<--cache>=I<filename>]
+[B<--include>=I<fontspec>]
+[B<--exclude>=I<regexp>]
+[B<--keep>=I<fontspec>]
+[B<--quiet>]
+[B<--no-repeats>]
+[B<--spp>=I<number>]
+[I<input.ps> [I<output.ps>]]
+
+
+=head1 DESCRIPTION
+
+=head2 Motivation
+
+PostScript documents created with old versions of B<dvips> almost
+invariably utilize bitmapped (PostScript S<Type 3>) fonts. The
+problem with bitmapped fonts is that they target a specific device
+resolution; a PostScript file produced using S<300 DPI> fonts will
+look grainy on a S<600 DPI> printer. Even worse, I<all> bitmapped
+fonts look grainy when zoomed in on screen. The solution is to use
+vector (PostScript S<Type 1>) fonts, which are resolution-independent
+and appear crisp at any size or scale.
+
+While it is no longer difficult to configure B<dvips> to use vector
+fonts, it is not always possible to rerun B<dvips> on an old F<.dvi>
+file. The F<.dvi> file and document source may have been lost; or,
+the source may no longer compile because packages it depends upon may
+no longer be available.
+
+Heiko Oberdiek's B<pkfix> script replaces bitmapped fonts in
+B<dvips>-produced PostScript files with the corresponding vector
+fonts. It works by parsing the PostScript comments with which
+B<dvips> surrounds bitmapped-font definitions. For example, a font
+definition beginning with the comment C<%DVIPSBitmapFont: Fi cmss10 11
+28> and ending with a matching C<%EndDVIPSBitmapFont> is known to
+define font C<Fi> as C<cmss10> (Computer Modern Sans Serif at a design
+size of S<10 points>) scaled to S<C<11> points>. Only the C<28>
+characters actually used by the document are defined. B<pkfix> then
+replaces the font definition with one that defines C<Fi> using the
+same set of characters but taken from the F<cmss10.pfb> vector font
+file.
+
+Unfortunately, B<pkfix> works only with versions of B<dvips> newer
+than v5.58 S<(ca. 1996)>. Naturally, the older a PostScript document,
+the less likely its sources still exist and can still be recompiled.
+Older versions of B<dvips> lack C<%DVIPSBitmapFont> comments and
+various other PostScript comments on which B<pkfix> relies. Without
+PostScript comments to guide it, B<pkfix> is unable to determine which
+vector fonts correspond with which bitmapped fonts.
+
+=head2 Overview
+
+The B<pkfix-helper> script is a preprocessor for B<pkfix> that
+attempts to determine the association between each document-font name
+S<(e.g., C<Fi>)> in a PostScript file and the original font S<(e.g.,
+C<cmss10>)> and fonts size (e.g., S<C<11> points>). It then
+fabricates the PostScript comments that B<pkfix> expects to see so
+that B<pkfix> can do its job.
+
+B<pkfix-helper> works by comparing every document font against every
+F<.tfm> font file it knows about (assuming that each such font has a
+corresponding F<.pfb> vector version) and selecting the best matching
+F<.tfm> file for every document font. B<pkfix-helper> has access only
+to the widths of characters and only to those characters actually used
+in the document. Also, the program recognizes only a limited set of
+the most popular F<.tfm> files and scaling factors. Consequently, the
+comparison is imperfect and B<pkfix-helper> may attribute an incorrect
+font to a given name. Fonts comprising only one or two characters
+actually used in a document are particularly problematic for
+B<pkfix-helper> because many fonts may be near-enough matches to fool
+the problem.
+
+B<pkfix-helper> is designed so that a user can guide the
+font-selection process by manually designating matching fonts. With a
+modicum of diligence and patience a user can correct any mismatched
+fonts and help the program provide proper input to B<pkfix>.
+
+
+=head1 OPTIONS
+
+B<pkfix-helper> accepts on the command line the filename of a
+PostScript document to process (with the default being the standard
+input device) and the filename of a modified PostScript document to
+create (with the default being the standard output device). The
+program also accepts the following command-line options:
+
+=head2 Frequently Used Options
+
+=over 4
+
+=item B<-h>, B<--help>
+
+Display usage information and exit. The B<--verbose> and B<--quiet>
+options can be used to increase and decrease the amount of information
+presented.
+
+=item B<-v>, B<--verbose>
+
+Increase the amount of status information that B<pkfix-helper>
+displays as it runs. Additional instances of B<--verbose> on the
+command line further increase the program's verbosity. By default,
+only major operations are displayed. A single B<--verbose>
+additionally displays information about individual font comparisons.
+A second B<--verbose> additionally displays details about some of the
+program's internal operations.
+
+=item B<-f> I<name>=I<fontspec>, B<--force>=I<name>=I<fontspec>
+
+Force B<pkfix-helper> to associate a specific font with a given font
+name appearing the document. I<name> is a two-character B<dvips> font
+name such as C<Fa>. I<fontspec> is a font specification such as
+S<C<cmsy10 @ 1.1X>>. The B<--force> option can be specified
+repeatedly on the command line.
+
+=item B<-p> I<filename.ps>, B<--ps>=I<filename.ps>
+
+Create a PostScript file called I<filename.ps> that shows the B<dvips>
+name and a font sample of every font used by the input document.
+
+=item B<-t> I<filename.tex>, B<--tex>=I<filename.tex>
+
+Create a Plain TeX file called I<filename.tex> that shows the B<dvips>
+name and a font sample of every font that B<pkfix-helper> used in the
+output document.
+
+=back
+
+=head2 Infrequently Used Options
+
+=over 4
+
+=item B<-C> I<filename>, B<--cache>=I<filename>
+
+Speed up TFM file processing by caching character metrics into file
+I<filename>. On some systems it takes a long time to read a TFM file,
+spawn F<tftopl> to convert it to PL format, and extract from the PL
+data the metrics for each character. The first time B<--cache> is
+specified, B<pkfix-helper> proceeds as normal then writes all of the
+extracted character metrics to I<filename>. On subsequent runs in
+which B<--cache>=I<filename> is specified, B<pkfix-helper> reads the
+previously extracted metrics from I<filename>, going through the
+F<tftopl>-based process only for TFM files that were not previously
+encountered.
+
+=item B<-i> I<fontspec>, B<--include>=I<fontspec>
+
+Add I<fontspec> to the list of font specifications against which
+B<pkfix-helper> compares I<every> document font. (In contrast,
+B<--force> designates a font specification to use only for a
+I<specific> document font.) The B<--include> option can be specified
+repeatedly on the command line.
+
+=item B<-x> I<regexp>, B<--exclude>=I<regexp>
+
+Remove all font specifications matching regular expression I<regexp>
+from B<pkfix-helper>'s list of known fonts. The B<--exclude> option
+can be specified repeatedly on the command line.
+
+=item B<-k> I<fontspec>, B<--keep>=I<fontspec>
+
+Do not substitute a vector font for bitmapped font I<fontspec> (C<Fa>,
+C<Fb>, etc.). This is useful when converting documents that use
+obscure bitmapped fonts for which there is no vector equivalent. For
+example, it was somewhat common in the past to include graphics such
+as university or corporate logos into a document by converting the
+bitmapped image into a single-character font and using that font in
+LaTeX. B<--keep> prevents such fonts from being replaced. The
+B<--keep> option can be specified repeatedly on the command line.
+
+=item B<-q>, B<--quiet>
+
+Instruct B<pkfix-helper> to produce no output during its run except
+for fatal error messages.
+
+=item B<-1>, B<--no-repeats>
+
+Prevent B<pkfix-helper> from associating the same I<fontspec> with
+more than one B<dvips> font name.
+
+=item B<-s>, B<--spp>
+
+Specify the number of font samples per page to print to the files
+indicated using the B<--ps> and B<--tex> options. The default
+S<value, 25,> should work well in most circumstances.
+
+=back
+
+
+=head1 DIAGNOSTICS
+
+=over 4
+
+=item C<Best match for I<name> is rather poor>
+
+The best font B<pkfix-helper> found for B<dvips> font name I<name> has
+a mismatch value greater than or equal S<to 1.0>. (The mismatch value
+is the sum of the squares of the difference between the character
+widths of a document font and a potential replacement font.) Use the
+B<--force> option to designate an alternative replacement font or
+scaling amount.
+
+=back
+
+
+=head1 EXAMPLES
+
+For the purpose of the following examples, assume that F<oldfile.ps>
+is the name of a PostScript file produced by an old version of
+B<dvips> and utilizing at least one bitmapped font. It's always worth
+verifying that B<pkfix> can't convert the file on its own:
+
+ $ pkfix oldfile.ps newfile.ps
+ PKFIX 1.3, 2005/02/25 - Copyright (c) 2001, 2005 by Heiko Oberdiek.
+ ==> no fonts converted
+
+(Alternatively B<pkfix> may issue an error message such as C<!!!
+Error: Parse error (@start parameters)!>.) Only when B<pkfix> can't
+replace bitmapped fonts with vector fonts is B<pkfix-helper> needed.
+In its simplest form, B<pkfix-helper> takes the name of an input file
+(F<oldfile.ps> in this example) and the name of an output file
+(F<pkfix-oldfile.ps>), which will have the same contents as the input
+file but serve as suitable input for B<pkfix>:
+
+ $ pkfix-helper oldfile.ps pkfix-oldfile.ps
+ Reading oldfile.ps ... done.
+ Number of Type 3 fonts encountered: 10
+ Bitmapped fonts are typeset at 600 DPI.
+ Finding character widths ... done.
+ Reading TFM files ... done (103 TFMs in 193 scaling variations).
+ Matching fonts:
+ Processing Fi ... done (cmr10 @ 1X, mismatch=0.11683).
+ Processing Fa ... done (cmti10 @ 1X, mismatch=0.08892).
+ Processing Fb ... done (cmr8 @ 1X, mismatch=0.07133).
+ Processing Ff ... done (cmbx12 @ 1.2X, mismatch=0.02948).
+ Processing Fh ... done (cmtt10 @ 1X, mismatch=0.06895).
+ Processing Fd ... done (cmmi10 @ 1X, mismatch=0.03966).
+ Processing Fj ... done (cmbx12 @ 1X, mismatch=0.03972).
+ Processing Fe ... done (cmbx10 @ 1X, mismatch=0.00762).
+ Processing Fg ... done (cmsy10 @ 1X, mismatch=0.00875).
+ Processing Fc ... done (cmr6 @ 1X, mismatch=0.00284).
+
+ $ pkfix pkfix-oldfile.ps newfile.ps
+ PKFIX 1.3, 2005/02/25 - Copyright (c) 2001, 2005 by Heiko Oberdiek.
+ *** Font conversion: `cmti10' -> `CMTI10'.
+ *** Font conversion: `cmr8' -> `CMR8'.
+ *** Font conversion: `cmr6' -> `CMR6'.
+ *** Font conversion: `cmmi10' -> `CMMI10'.
+ *** Font conversion: `cmbx10' -> `CMBX10'.
+ *** Font conversion: `cmbx12' -> `CMBX12'.
+ *** Font conversion: `cmsy10' -> `CMSY10'.
+ *** Font conversion: `cmtt10' -> `CMTT10'.
+ *** Font conversion: `cmr10' -> `CMR10'.
+ *** Font conversion: `cmbx12' -> `CMBX12'.
+ *** Merging font `CMBX12' (2).
+ ==> 10 converted fonts.
+ ==> 1 merged font.
+
+Although B<pkfix-helper> tries to automate as much as possible the
+font-detection process, some fonts will invariably be incorrectly
+identified. The program outputs a warning message if it I<knows> a
+match is bad but the lack of a warning message does not necessarily
+indicate that B<pkfix-helper> did a good job. It is therefore
+strongly recommended that the user produce "before" and "after" font
+sheets:
+
+ $ pkfix-helper -q oldfile.ps pkfix-oldfile.ps \
+ --ps=oldfonts.ps --tex=newfonts.tex
+
+ $ tex newfonts.tex
+ This is TeX, Version 3.14159 (Web2C 7.4.5)
+ (./newfonts.tex [1] )
+ Output written on newfonts.dvi (1 page, 1292 bytes).
+ Transcript written on newfonts.log.
+
+ $ dvips newfonts.dvi -o newfonts.ps
+ This is dvips(k) 5.92b Copyright 2002 Radical Eye Software (www.radicaleye.com)
+ ' TeX output 2006.06.11:1636' -> newfonts.ps
+ <texc.pro><8r.enc><texps.pro>. <cmr6.pfb><cmsy10.pfb><cmbx10.pfb><cmbx12.pfb>
+ <cmmi10.pfb><cmtt10.pfb><cmr8.pfb><cmti10.pfb><cmr10.pfb>[1]
+
+After running the preceding commands, F<oldfonts.ps> shows samples of
+the fonts in F<oldfile.ps> and F<newfonts.ps> shows samples of the
+replacement fonts that B<pkfix-helper> used to produce
+F<pkfix-oldfile.ps>. Print F<oldfonts.ps> and F<newfonts.ps> and
+compare them carefully for incorrect fonts and sizes.
+
+Suppose that the choice of C<cmbx12 @ 1.2X> for font C<Ff> looks
+wrong; say the characters look taller in F<oldfonts.ps> than in
+F<newfonts.ps>. This is where the trial-and-error stage begins.
+Let's hypothesize that C<cmb12> is a better match than C<cmbx12> but
+we don't know how much to scale the font. Fortunately,
+B<pkfix-helper> allows C<*> to be used as a scaling factor to tell the
+program to automatically detect an optimal scaling factor, even if
+doing so means choosing a highly nonstandard font size:
+
+ $ pkfix-helper oldfile.ps pkfix-oldfile.ps --force="Ff=cmb12 @ *"
+ Reading oldfile.ps ... done.
+ Number of Type 3 fonts encountered: 10
+ Bitmapped fonts are typeset at 600 DPI.
+ Finding character widths ... done.
+ Reading TFM files ... failed.
+ pkfix-helper: Unable to process user-specified TFM file "cmb12"
+
+Oops, it looks like we don't have a F<cmb12.tfm> file on our system.
+Let's try scaling up F<cmb10.tfm> instead:
+
+ $ pkfix-helper oldfile.ps pkfix-oldfile.ps --force="Ff=cmb10 @ *"
+ Reading oldfile.ps ... done.
+ Number of Type 3 fonts encountered: 10
+ Bitmapped fonts are typeset at 600 DPI.
+ Finding character widths ... done.
+ Reading TFM files ... done (103 TFMs in 193 scaling variations).
+ Matching fonts:
+ Processing Fi ... done (cmr10 @ 1X, mismatch=0.11683).
+ Processing Fa ... done (cmti10 @ 1X, mismatch=0.08892).
+ Processing Fb ... done (cmr8 @ 1X, mismatch=0.07133).
+ Processing Ff ... done (cmb10 @ 1.5708X, mismatch=0.00035).
+ Processing Fh ... done (cmtt10 @ 1X, mismatch=0.06895).
+ Processing Fd ... done (cmmi10 @ 1X, mismatch=0.03966).
+ Processing Fj ... done (cmbx12 @ 1X, mismatch=0.03972).
+ Processing Fe ... done (cmbx10 @ 1X, mismatch=0.00762).
+ Processing Fg ... done (cmsy10 @ 1X, mismatch=0.00875).
+ Processing Fc ... done (cmr6 @ 1X, mismatch=0.00284).
+
+The match has definitely improved, although S<15.708 pt.> is certainly
+an odd size for a font. Then again, many documents I<do> use
+nonstandard sizes so this may in fact be correct. The best way to
+verify is once again to produce, print, and compare a pair of font
+samples and iterate until all of the fonts look correct. Use one
+instance of B<--force> for each font you want to alter.
+
+
+=head1 ENVIRONMENT
+
+B<pkfix-helper> honors the following environment variables:
+
+=over 8
+
+=item GS
+
+The name of the Ghostscript interpreter (default: F<gs>)
+
+=item TFTOPL
+
+The name of a utility for converting F<.tfm> files to F<.pl> files
+(default: F<tftopl>)
+
+=back
+
+
+=head1 BUGS
+
+Even when B<pkfix-helper> finds a perfect match S<(i.e., the> correct
+font in the correct size) the mismatch value is still typically
+nonzero. The same error is probably what causes B<pkfix-helper>
+sometimes to consider the wrong font as being a better match than the
+correct font. Suggestions for fixing these bugs are welcome.
+
+
+=head1 RESTRICTIONS
+
+B<pkfix-helper> works only with PostScript files produced by B<dvips>,
+not with arbitrary PostScript files. The program has not been tested
+with output from versions of B<dvips> older than v5.490 S<(ca. 1992)>;
+output from older versions may or may not work. Only bitmapped fonts
+loaded by B<dvips> can be analyzed, not bitmapped fonts loaded by
+embedded graphics.
+
+B<pkfix-helper> works by comparing character widths, not the actual
+glyphs. Consequently, it is misled by sets of fonts with similar
+character widths (at least for those characters used by a given
+document). As an extreme example, all Computer Modern Teletype fonts
+of a given design size (e.g., C<cmtt10>, C<cmsltt10>, and C<cmitt10>)
+use exactly the same widths for all characters. Human assistance is
+generally needed to guide B<pkfix-helper>'s font-matching procedures.
+
+
+=head1 NOTES
+
+Files produced using the B<--tex> option are Plain TeX files and
+therefore must be compiled with F<tex> (or a variation such as
+F<pdftex>, F<luatex>, etc.), I<not> with F<latex>.
+
+
+=head1 SEE ALSO
+
+pkfix(1), dvips(1), tex(1), gs(1)
+
+PostScript Language Reference, Third Edition.
+Published by Addison-Wesley, ISBN 0-201-37922-8,
+L<http://www.adobe.com/products/postscript/pdfs/PLRM.pdf>.
+
+
+=head1 AUTHOR
+
+Scott Pakin, I<scott+pkfh@pakin.org>
+
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright (C) 2010, Scott Pakin
+
+This file may be distributed and/or modified under the conditions of
+the LaTeX Project Public License, either version 1.3c of this license
+or (at your option) any later version. The latest version of this
+license is in L<http://www.latex-project.org/lppl.txt> and version
+1.3c or later is part of all distributions of LaTeX version 2006/05/20
+or later.
diff --git a/Build/source/texk/texlive/linked_scripts/ppower4/pdfthumb.tlu b/Build/source/texk/texlive/linked_scripts/ppower4/pdfthumb.tlu
index 17061cdbb1e..f5a5aef9114 100755
--- a/Build/source/texk/texlive/linked_scripts/ppower4/pdfthumb.tlu
+++ b/Build/source/texk/texlive/linked_scripts/ppower4/pdfthumb.tlu
@@ -1,6 +1,6 @@
#!/usr/bin/env texlua
--*-Lua-*-
--- $Id: pdfthumb.texlua 8373 2008-05-28 02:00:59Z vvv $
+-- $Id$
-- Copyright (C) 2008 Reinhard Kotucha.
-- You may freely use, modify and/or distribute this file.
@@ -20,7 +20,7 @@ function fixwin(args_unix)
end
end
-kpse.set_program_name('pdfthumb.texlua', 'pdfthumb')
+kpse.set_program_name(arg[-1], 'pdfthumb')
jarfile=kpse.find_file('pp4p.jar', 'texmfscripts')
@@ -41,4 +41,3 @@ os.exit(ret)
ret=os.spawn(command)
os.exit(ret)
-ft \ No newline at end of file
diff --git a/Build/source/texk/texlive/linked_scripts/ppower4/ppower4.tlu b/Build/source/texk/texlive/linked_scripts/ppower4/ppower4.tlu
index a9ed72204e8..27c03b22849 100755
--- a/Build/source/texk/texlive/linked_scripts/ppower4/ppower4.tlu
+++ b/Build/source/texk/texlive/linked_scripts/ppower4/ppower4.tlu
@@ -1,6 +1,6 @@
#!/usr/bin/env texlua
--*-Lua-*-
--- $Id: ppower4.texlua 6969 2008-03-14 22:17:00Z reinhardk $
+-- $Id$
-- Copyright (C) 2008 Reinhard Kotucha.
-- You may freely use, modify and/or distribute this file.
@@ -20,7 +20,7 @@ function fixwin(args_unix)
end
end
-kpse.set_program_name('ppower4.texlua', 'ppower4')
+kpse.set_program_name(arg[-1], 'ppower4')
jarfile=kpse.find_file('pp4p.jar', 'texmfscripts')
diff --git a/Build/source/texk/texlive/linked_scripts/pst2pdf/pst2pdf b/Build/source/texk/texlive/linked_scripts/pst2pdf/pst2pdf
index 238757135d0..6b6ab985429 100755
--- a/Build/source/texk/texlive/linked_scripts/pst2pdf/pst2pdf
+++ b/Build/source/texk/texlive/linked_scripts/pst2pdf/pst2pdf
@@ -3,9 +3,10 @@ eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}' && eval 'exec perl -S $0 $
use strict; # to be sure, that all is safe ... :-)
-# v. 0.10 simplify the use of PSTricks with pdf
-# 2010-01-04 (c) Herbert Voss <hvoss@tug.org>
+# v. 0.11 simplify the use of PSTricks with pdf
+# 2011-01-18 (c) Herbert Voss <hvoss@tug.org>
# $Id: pst2pdf.pl 239 2010-01-01 17:23:13Z herbert $
+# Thanks to Pablo Gonzales Luengo
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -31,7 +32,9 @@ use Getopt::Long; # read parameter
#----------------------- User part begin ------------------------
my $imageDir = "images"; # where to save the images
my @imageType = ("eps","pdf"); # all image types
-push (@imageType, "png") if $^O ne 'MSWin32';
+my $machine = "Linux";
+if ($^O eq 'MSWin32') { $machine = "Windows"; }
+else { push (@imageType, "png"); }
my $Iext = ".pdf"; # leave empty, if not a special one
my $tempDir = "."; # temporary directory
my $verbose = 1; # 0 or 1, logfile
@@ -39,6 +42,8 @@ my $clear = 0; # 0 or 1, clears all temporary files
my $DPI = 75; # very low value for the png's
my $Iscale = 1; # for \includegraphics
my $noImages = 0; # 1->create no images
+my $runBibTeX = 0; # 1->runs bibtex
+my $runBiber = 0; # 1->runs biber and sets $runBibTeX=0
#----------------------- User part end ---------------------------
@ARGV == 0 && die "file name expected!\n";
@@ -57,6 +62,8 @@ my $result = GetOptions ("DPI=i" => \$DPI, # numeric
"Iext=s" => \$Iext, # string
"clear" => \$clear, # flag
"noImages" => \$noImages, # flag
+ "runBibTeX" => \$runBibTeX, # flag
+ "runBiber" => \$runBiber, # flag
"verbose" => \$verbose); # flag
LOG ("Parameters:");
@@ -68,6 +75,14 @@ LOG ("==> tempDir = $tempDir");
LOG ("==> verbose = $verbose");
LOG ("==> clear = $clear");
LOG ("==> noImages = $noImages");
+LOG ("==> runBibTeX= $runBibTeX");
+LOG ("==> runBiber = $runBiber");
+
+if ($runBibTeX && $runBiber) {
+ LOG ("!!! you cannot run BibTeX and Biber at the same document ...");
+ LOG ("!!! Assuming to run Biber");
+ $runBibTeX = 0;
+}
my $imgNo = 0; # internal image counter
my $pstExaLoaded = 0; # Document loads pst-exa
@@ -228,6 +243,11 @@ sub runTeX {
system("convert $imageDir/-1.ppm $imageDir/$filename-$imgNo.png");
system("rm $imageDir/-1.ppm");
}
+ if ($Itype eq "jpg") {
+ system("pdftoppm -f 1 -l 1 -r $DPI $imageDir/$filename-$imgNo.pdf $imageDir/");
+ system("convert $imageDir/-1.ppm $imageDir/$filename-$imgNo.jpg");
+ system("rm $imageDir/-1.ppm");
+ }
if ($Itype eq "eps") { system("pdftops -f 1 -l 1 -eps $imageDir/$filename-$imgNo.pdf $imageDir/$filename-$imgNo.eps"); }
}
$imgNo++;
@@ -251,7 +271,7 @@ sub runFile {
my @PS = @$aref;
open (FILEp, "<$tempDir/$filename.preamble") or die "cannot open $tempDir/$filename.preamble!";
open (FILEsub, ">$tempDir/$filename-tmp.tex") or die "cannot open $tempDir/$filename-tmp.tex!";
-# print FILEsub "\\RequirePackage{listings}\n";
+ #print FILEsub "\\RequirePackage{listings}\n";
while (<FILEp>) { print FILEsub $_; }
print FILEsub "\\pagestyle{empty}\n";
print FILEsub "\\newenvironment{postscript}{}{}\n";
@@ -271,11 +291,11 @@ sub runpdfTeX() {
open (PDF, ">$tempDir/$pdfname-pdf.tex") or die "cannot open $tempDir/$pdfname-pdf.tex!";
open (FILE, "<$name.tex") or die "cannot open $name!";
print PDF "\\RequirePackage{graphicx}\n";
- if ($pstExaLoaded < 1) {
- print PDF "\\let\\IncludeGraphics\\includegraphics\n"; }
+ #if ($pstExaLoaded < 1) {
+ print PDF "\\let\\IncludeGraphics\\includegraphics\n"; #}
# print PDF "\\setkeys{Gin}{scale=0.25}\n";
# print PDF "\\providecommand\\psset[1]{}\n";
-# print PDF "\\graphicspath{{$imageDir/}}\n";
+ print PDF "\\graphicspath{{$imageDir/}}\n"; #uncomment
my $ignore = 0;
my $IMGno = 0;
my $depth = -1;
@@ -295,7 +315,7 @@ sub runpdfTeX() {
$type = 2;
$ignore = 1;
if ($iPS > 1) { print PDF substr($_,0,--$iPS); } # add preceeding text
- print PDF "\\IncludeGraphics[scale=$Iscale]{$imageDir/$name-tmp-$IMGno$Iext}";
+ print PDF "\\IncludeGraphics[scale=$Iscale]{$pdfname-tmp-$IMGno$Iext}"; #change name x pdfname
$IMGno++;
} # postscript env
if ( $type < 2 ) {
@@ -309,8 +329,8 @@ sub runpdfTeX() {
LOG("Increase depth: $depth");
if ( $depth == 0 ) {
if ($iPST > 1) { print PDF substr($_,0,--$iPST); }# add preceeding text
- print PDF "\\IncludeGraphics[scale=$Iscale]{$imageDir/$name-tmp-$IMGno$Iext}";
-# print PDF "\\IncludeGraphics{$name-tmp-$IMGno$Iext}"; # use \graphicspath
+ # print PDF "\\IncludeGraphics[scale=$Iscale]{$imageDir/$pdfname-tmp-$IMGno$Iext}"; #change name x pdfname
+ print PDF "\\IncludeGraphics[scale=$Iscale]{$pdfname-tmp-$IMGno$Iext}"; # use \graphicspath
$IMGno++;
LOG("Increase Image counter: $IMGno");
}
@@ -345,10 +365,12 @@ sub runpdfTeX() {
} # while (<FILE>)
close (FILE);
close (PDF);
- system("pdflatex $tempDir/$pdfname-pdf");
- system("makeindex $tempDir/$pdfname-pdf.idx");
- system("bibtex $tempDir/$pdfname-pdf");
+ my $runAgain = 0;
system("pdflatex $tempDir/$pdfname-pdf");
+ if (-e "$tempDir/$pdfname-pdf.idx") { system("makeindex $tempDir/$pdfname-pdf.idx"); $runAgain++; }
+ if ($runBibTeX && -e "$tempDir/$pdfname-pdf.aux") { system("bibtex $tempDir/$pdfname-pdf"); $runAgain++; }
+ if ($runBiber && -e "$tempDir/$pdfname-pdf.bcf") { system("biber $tempDir/$pdfname-pdf"); $runAgain++; }
+ if ($runAgain) { system("pdflatex $tempDir/$pdfname-pdf"); }
if ( $clear ) {
unlink "$tempDir/$pdfname-pdf.log";
unlink "$tempDir/$pdfname-pdf.aux";
@@ -374,7 +396,7 @@ TODO
=head1 DESCRIPTION
-runs latex and pdflatex on the TeX
+runs latex and pdflatex on the TeX source file
=head1 OPTIONS
@@ -396,6 +418,10 @@ runs latex and pdflatex on the TeX
=item --noImages - create no images, build only pdf
+=item --runBibTeX - run bibtex on the aux file, if exists
+
+=item --runBiber - run biber on the bcf file, if exists
+
=back
=head1 AUTHORS
@@ -404,7 +430,7 @@ Herbert Voss <hvoss@tug.org>
=head1 COPYRIGHT
-Copyright (c) 2007-2009 Herbert Voss <hvoss@tug.org>
+Copyright (c) 2007-2011 Herbert Voss <hvoss@tug.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/Build/source/texk/texlive/linked_scripts/purifyeps/purifyeps b/Build/source/texk/texlive/linked_scripts/purifyeps/purifyeps
index d1916891e3c..ff686227f01 100755
--- a/Build/source/texk/texlive/linked_scripts/purifyeps/purifyeps
+++ b/Build/source/texk/texlive/linked_scripts/purifyeps/purifyeps
@@ -7,7 +7,7 @@
# By Scott Pakin <scott+peps@pakin.org> #
################################################################
-our $VERSION = "1.0a";
+our $VERSION = "1.1";
require 5.006_001;
use English;
use Getopt::Long;
@@ -15,6 +15,7 @@ use FindBin qw($Script);
use File::Basename;
use File::Temp qw(tempfile);
use File::Spec;
+use File::Which;
use Pod::Usage;
use Pod::Man;
use Cwd qw(cwd chdir);
@@ -28,15 +29,18 @@ my %TeX_to_PS; # Hash table representing the contents of the above
my $infile = "-"; # Input file (default: stdin)
my $outfile = "-"; # Output file (default: stdout)
my $tempbase; # Base name of temporary files
+my @fontmappath = ( # Where to look for $fontmap
+ "/usr/share/pstoedit/",
+ "/usr/local/share/pstoedit/");
########################################################################
# At the end of the program's execution, delete all of our temporary files.
END {
if (defined $tempbase) {
- foreach my $tempfile ($tempbase, <$tempbase.*>) {
- unlink $tempfile;
- }
+ foreach my $tempfile ($tempbase, <$tempbase.*>) {
+ unlink $tempfile;
+ }
}
}
@@ -55,11 +59,11 @@ sub extractName (@)
# Extract the first word from the token list.
$firstWord = shift @tokens;
if (substr ($firstWord, 0, 1) eq '"') {
- while (substr ($firstWord, length($firstWord)-1, 1) ne '"') {
- return (undef, @tokens) if $#tokens==-1;
- $firstWord .= " " . shift @tokens;
- }
- $firstWord = substr ($firstWord, 1, length($firstWord)-2);
+ while (substr ($firstWord, length($firstWord)-1, 1) ne '"') {
+ return (undef, @tokens) if $#tokens==-1;
+ $firstWord .= " " . shift @tokens;
+ }
+ $firstWord = substr ($firstWord, 1, length($firstWord)-2);
}
# Return the word plus the remaining tokens.
@@ -72,7 +76,7 @@ sub executeCommand ($)
{
my $command = $_[0];
if (system "$command 1>&2") {
- die "${Script}: The following command failed with exit code @{[int($?/256)]}:\n $command\n";
+ die "${Script}: The following command failed with exit code @{[int($?/256)]}:\n $command\n";
}
}
@@ -86,8 +90,8 @@ sub createManPage ($$$)
# Produce a (*roff) man page.
my $parser = Pod::Man->new (center => "",
- release => "v$VERSION",
- section => $section);
+ release => "v$VERSION",
+ section => $section);
$parser->parse_from_file ($PROGRAM_NAME, $manfile);
# Touch up the man page, and use groff to write
@@ -98,9 +102,9 @@ sub createManPage ($$$)
close MANFILE;
open (MANFILE, "|groff -man > $manfile") or die "${Script}: $! ($manfile)\n";
foreach (@manlines) {
- s/LaTeX/L\\h'-0.36m'\\v'-0.15'\\s-2A\\s+2\\v'0.15'\\h'-0.15m'TeX/g;
- s/TeX/T\\h'-0.1667m'\\v'0.20'E\\v'-0.20'\\h'-0.125m'X/g;
- s/\\\*\(--/--/g;
+ s/LaTeX/L\\h'-0.36m'\\v'-0.15'\\s-2A\\s+2\\v'0.15'\\h'-0.15m'TeX/g;
+ s/TeX/T\\h'-0.1667m'\\v'0.20'E\\v'-0.20'\\h'-0.125m'X/g;
+ s/\\\*\(--/--/g;
print MANFILE $_;
}
close MANFILE;
@@ -111,11 +115,20 @@ sub createManPage ($$$)
# Initialize our base temporary filename.
(undef, $tempbase) = tempfile ($Script . "-XXXXXXXX", DIR => File::Spec->tmpdir());
+# Try to determine the full filename of the font-map file.
+foreach my $dir (@fontmappath) {
+ my $newfontmap = File::Spec->catfile($dir, $fontmap);
+ if (-e $newfontmap) {
+ $fontmap = $newfontmap;
+ last;
+ }
+}
+
# Process the command line.
my ($wanthelp, $wantman, $wantps, $wantversion);
my $versionmsg = "purifyeps version $VERSION
-Copyright (C) 2009 Scott Pakin
+Copyright (C) 2010 Scott Pakin
This program may be distributed and/or modified under the conditions
of the LaTeX Project Public License, either version 1.3c of this
@@ -130,18 +143,18 @@ version 2006/05/20 or later.
";
my $man_section = 1; # Section number of man page
GetOptions ("fontmap=s" => \$fontmap,
- "make-man:s" => \$wantman,
- "make-ps-man:s" => \$wantps,
- "section=s" => \$man_section,
- "V|version" => \$wantversion,
- "help" => \$wanthelp) || pod2usage(2);
+ "make-man:s" => \$wantman,
+ "make-ps-man:s" => \$wantps,
+ "section=s" => \$man_section,
+ "V|version" => \$wantversion,
+ "help" => \$wanthelp) || pod2usage(2);
do {print $versionmsg; exit 1} if $wantversion;
pod2usage(1) if $wanthelp;
if (defined $wantman) {
# Create a troff manual page.
my $filename = ($wantman eq "" ?
- basename ($PROGRAM_NAME, ".pl") . ".$man_section" :
- $wantman);
+ basename ($PROGRAM_NAME, ".pl") . ".$man_section" :
+ $wantman);
createManPage ($filename, 0, $man_section);
print STDERR "Wrote $filename\n";
exit 0;
@@ -149,8 +162,8 @@ if (defined $wantman) {
if (defined $wantps) {
# Create a PostScript manual page.
my $filename = ($wantps eq "" ?
- basename ($PROGRAM_NAME, ".pl") . ".ps" :
- $wantps);
+ basename ($PROGRAM_NAME, ".pl") . ".ps" :
+ $wantps);
createManPage ($filename, 1, $man_section);
print STDERR "Wrote $filename\n";
exit 0;
@@ -159,46 +172,51 @@ $infile = shift @ARGV if $#ARGV!=-1;
$outfile = shift @ARGV if $#ARGV!=-1;
pod2usage(2) if $#ARGV!=-1; # Too many arguments.
+# Ensure that pstoedit is installed. Give a helpful error message if it isn't.
+if (!defined which("pstoedit")) {
+ die "${Script}: pstoedit must be installed and must appear in the executable search path\n";
+}
+
# Read the font map file into a hash table.
-open (FONTMAP, "<$fontmap") || die "${Script}: $! ($fontmap)\n";
+open (FONTMAP, "<$fontmap") || die "${Script}: $! ($fontmap); specify an alternative with --fontmap\n";
FONTMAP_LINE:
while (<FONTMAP>) {
- # Clean up the line.
- chomp;
- s/\%.*//;
- my $origLine = $_;
- next if /^\s*$/;
- my @tokens = split " ";
-
- # Extract the PostScript name, which may be quoted and contain spaces.
- my $PSname;
- ($PSname, @tokens) = extractName @tokens;
- if (!defined $PSname) {
- warn "${fontmap}:$NR: warning: incorrect format -- ignoring line\n";
- next FONTMAP_LINE;
- }
-
- # Extract the TeX name, which may also be quoted and contain spaces.
- my $TeXname;
- ($TeXname, @tokens) = extractName @tokens;
- if (!defined $TeXname) {
- warn "${fontmap}:$NR: warning: incorrect format -- ignoring line\n";
- next FONTMAP_LINE;
- }
-
- # That should be the end of the line.
- if ($#tokens != -1) {
- warn "${fontmap}:$NR: warning: incorrect format -- ignoring line\n";
- next FONTMAP_LINE;
- }
-
- # Store the mapping in a hash table.
- # HEURISTIC: If the mapping is not unique, map the TeX
+ # Clean up the line.
+ chomp;
+ s/\%.*//;
+ my $origLine = $_;
+ next if /^\s*$/;
+ my @tokens = split " ";
+
+ # Extract the PostScript name, which may be quoted and contain spaces.
+ my $PSname;
+ ($PSname, @tokens) = extractName @tokens;
+ if (!defined $PSname) {
+ warn "${fontmap}:$NR: warning: incorrect format -- ignoring line\n";
+ next FONTMAP_LINE;
+ }
+
+ # Extract the TeX name, which may also be quoted and contain spaces.
+ my $TeXname;
+ ($TeXname, @tokens) = extractName @tokens;
+ if (!defined $TeXname) {
+ warn "${fontmap}:$NR: warning: incorrect format -- ignoring line\n";
+ next FONTMAP_LINE;
+ }
+
+ # That should be the end of the line.
+ if ($#tokens != -1) {
+ warn "${fontmap}:$NR: warning: incorrect format -- ignoring line\n";
+ next FONTMAP_LINE;
+ }
+
+ # Store the mapping in a hash table.
+ # HEURISTIC: If the mapping is not unique, map the TeX
# name to the *shortest* PostScript name.
- if (!defined $TeX_to_PS{$TeXname} ||
- length $PSname < length $TeX_to_PS{$TeXname}) {
- $TeX_to_PS{$TeXname} = $PSname;
- }
+ if (!defined $TeX_to_PS{$TeXname} ||
+ length $PSname < length $TeX_to_PS{$TeXname}) {
+ $TeX_to_PS{$TeXname} = $PSname;
+ }
}
close FONTMAP;
@@ -222,33 +240,33 @@ while (<MPSFILE>) {
chomp;
my @tokens = split " ";
if ($#tokens == -1) {
- push @purified_eps, "$_\n";
- next;
+ push @purified_eps, "$_\n";
+ next;
}
# Convert the list of document fonts.
if ($tokens[0] eq "%%DocumentFonts:") {
- my $outputLine = shift @tokens;
- foreach my $TeXname (@tokens) {
- my $PSname = $TeX_to_PS{$TeXname};
- if (!defined $PSname) {
- warn "${outfile_clean}:$NR: warning: TeX font \"$TeXname\" does not appear in $fontmap\n";
- $outputLine .= " $TeXname";
- }
- else {
- $outputLine .= " $PSname";
- }
- }
- push @purified_eps, "$outputLine\n";
- next;
+ my $outputLine = shift @tokens;
+ foreach my $TeXname (@tokens) {
+ my $PSname = $TeX_to_PS{$TeXname};
+ if (!defined $PSname) {
+ warn "${outfile_clean}:$NR: warning: TeX font \"$TeXname\" does not appear in $fontmap\n";
+ $outputLine .= " $TeXname";
+ }
+ else {
+ $outputLine .= " $PSname";
+ }
+ }
+ push @purified_eps, "$outputLine\n";
+ next;
}
# Convert the font name definitions.
- if ($#tokens == 2 &&
- $tokens[0] eq $tokens[1] &&
- $tokens[2] eq "def") {
- push @purified_eps, sprintf " %s /%s def\n", $tokens[0], $TeX_to_PS{substr($tokens[1],1)};
- next;
+ if ($#tokens == 2 &&
+ $tokens[0] eq $tokens[1] &&
+ $tokens[2] eq "def") {
+ push @purified_eps, sprintf " %s /%s def\n", $tokens[0], $TeX_to_PS{substr($tokens[1],1)};
+ next;
}
# By default, output the line as is.
@@ -289,10 +307,10 @@ purifyeps
[I<.eps input file> [I<.eps output file>]]
purifyeps
-B<--make-man> [=I<filename>] [B<--section>=I<section>]
+B<--make-man>[=I<filename>] [B<--section>=I<section>]
purifyeps
-B<--make-ps-man> [=I<filename>] [B<--section>=I<section>]
+B<--make-ps-man>[=I<filename>] [B<--section>=I<section>]
=head1 DESCRIPTION
@@ -497,7 +515,7 @@ preamble tells B<pdflatex> that all F<.eps> files are in MPS format:
\ifpdf
\DeclareGraphicsRule{.eps}{mps}{*}{}
\makeatletter
- \g@addto@macro\Gin@extensions{,.eps}
+ \g@addto@macro\Gin@extensions{,.eps}
\makeatother
\fi
diff --git a/Build/source/texk/texlive/linked_scripts/sty2dtx/sty2dtx.pl b/Build/source/texk/texlive/linked_scripts/sty2dtx/sty2dtx.pl
index e69de29bb2d..5c0730ca5bb 100644..100755
--- a/Build/source/texk/texlive/linked_scripts/sty2dtx/sty2dtx.pl
+++ b/Build/source/texk/texlive/linked_scripts/sty2dtx/sty2dtx.pl
@@ -0,0 +1,800 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+################################################################################
+# $Id: sty2dtx.pl 2125 2011-02-02 15:25:40Z martin $
+################################################################################
+my $COPYRIGHT = << 'EOT';
+ Copyright (c) 2010-2011 Martin Scharrer <martin@scharrer-online.de>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+EOT
+################################################################################
+my $DESCRIPTION = << 'EOT';
+ Converts a .sty file (LaTeX package) to .dtx format (documented LaTeX source),
+ by surrounding macro definitions with 'macro' and 'macrocode' environments.
+ The macro name is automatically inserted as an argument to the 'macro'
+ environemnt.
+ Code lines outside macro definitions are wrapped only in 'macrocode'
+ environments. Empty lines are removed.
+ The script is not thought to be fool proof and 100% accurate but rather
+ as a good start to convert undocumented style files to .dtx files.
+
+ Usage:
+ perl sty2dtx.pl infile [infile ...] outfile
+ or
+ perl sty2dtx.pl < file.sty > file.dtx
+
+
+ The following macro definitions are detected when they are at the start of a
+ line (can be prefixed by \global, \long, \protected and/or \outer):
+ \def \edef \gdef \xdef
+ \newcommand{\name} \newcommand*{\name}
+ \newcommand\name \newcommand*\name
+ \renewcommand{\name} \renewcommand*{\name}
+ \renewcommand\name \renewcommand*\name
+ \providecommand{\name} \providecommand*{\name}
+ \providecommand\name \providecommand*\name
+ \@namedef{\name} \@namedef\name
+
+ The following environment definitions are detected when they are at the start
+ of a line:
+ \newenvironment{name} \renewenvironemnt{name} \provideenvironment{name}
+
+ The macro and environment definition must either end at the same line or with
+ a '}' on its own on a line.
+
+EOT
+################################################################################
+my $VERSION = "v2.1 " . substr( '$Date: 2011-02-02 15:25:40 +0000 (Wed, 02 Feb 2011) $', 7, 10 );
+$VERSION =~ tr/-/\//;
+my $TITLE = << "EOT";
+ sty2dtx -- Converts a LaTeX .sty file to a documented .dtx file
+ Version: $VERSION
+EOT
+
+sub usage {
+ print << "EOT";
+sty2dtx.pl [<options>] [--<VAR>=<VALUE> ...] [--] [<infile> ...] [<outfile>]
+Version: $VERSION
+EOT
+ print << 'EOT';
+Files:
+ * can be '-' for STDIN or STDOUT, which is the default if no files are given
+ * multiple input files are merged to one output file
+
+Variables:
+ can be defined using --<VAR>=<VALUE> or --<VAR> <VALUE> and will be used for
+ substitutions in the template file.
+ Common variables:
+ author, email, maintainer, year (for copyright),
+ version, date, description (of package/class),
+ type (either 'package' default or 'class'),
+ filebase (automatically set from output or input file name),
+
+Options:
+ -h : Print this help text
+ -H : Print extended help
+ -V : Print version and copyright
+ -v : Be verbose
+ -o <output> : Use given file as output
+ -O : Overwrite already existing output file(s)
+ -B : Use basename of single input file for output file
+ -I : Also create .ins (install) file
+ -c : Only use code section (like v1.0)
+ -i <ins file> : Create .ins file with given name
+ -t <template> : Use this file as template instead of the default one
+ -T <template> : Use this file as template for the .ins file
+ -e <file> : Export default .dtx template to file and exit
+ -E <file> : Export default .ins template to file and exit
+ -D : Use current date as file date
+ -F <file> : Read more options and variables from file.
+ Should contain one option or variable per line only.
+
+Examples:
+ Produce 'file.dtx' from 'file.sty':
+ sty2dtx.pl < file.sty > file.dtx
+ or
+ sty2dtx.pl file.sty file.dtx
+ or
+ sty2dtx.pl -B file.sty
+
+ Produce 'file.dtx' and 'file.ins' from 'file.sty':
+ sty2dtx.pl -I file.sty file.dtx
+ or
+ sty2dtx.pl file.sty -i file.sty file.dtx
+ or
+ sty2dtx.pl -IB file.sty
+
+ Set custom variable values:
+ sty2dtx.pl --author Me --email me@there.com mypkg.sty mypkg.dtx
+
+ Produce DTX file for a class:
+ sty2dtx.pl --type class mycls.sty mycls.dtx
+
+EOT
+ exit(0);
+}
+
+my $ERROR = "sty2dtx: Error:";
+
+# Used as format string of printf so that the '%' must be doubled:
+my $macrostart = <<'EOT';
+%%
+%% \begin{macro}{\%s}
+%s%% \begin{macrocode}
+EOT
+
+my $environmentstart = <<'EOT';
+%%
+%% \begin{environment}{%s}
+%s%% \begin{macrocode}
+EOT
+
+my $macrodescription = <<'EOT';
+%%
+%% \DescribeMacro{\%s}
+%%
+EOT
+
+my $envdescription = <<'EOT';
+%%
+%% \DescribeEnv{%s}
+%%
+EOT
+
+# Printed normally:
+my $macrostop = <<'EOT';
+% \end{macrocode}
+% \end{macro}
+%
+EOT
+
+my $environmentstop = <<'EOT';
+% \end{macrocode}
+% \end{environment}
+%
+EOT
+
+my $macrocodestart = <<'EOT';
+% \begin{macrocode}
+EOT
+
+my $macrocodestop = <<'EOT';
+% \end{macrocode}
+EOT
+
+my $USAGE = ''; # Store macro names for usage section
+my $IMPL = ''; # Store implementation section
+
+my $mode = 0;
+# 0 = outside of macro or macrocode environments
+# 1 = inside 'macrocode' environment
+# 2 = inside 'macro' environment
+# 3 = inside 'environment' environment
+
+# RegExs for macro names and defintion:
+my $rmacroname = qr/[a-zA-Z\@:]+/; # Add ':' for LaTeX3 style macros
+my $rusermacro = qr/[a-zA-Z]+/; # Macros intended for users
+my $rmacrodef = qr/
+ ^ # Begin of line (no whitespaces!)
+ (
+ (?:(?:\\global|\\long|\\protected|\\outer)\s*)* # Prefixes (maybe with whitespace between them)
+ )
+ \\(
+ [gex]?def \s* \\ # TeX definitions
+ | (?:new|renew|provide)command\s* \*? \s* {? \s* \\ # LaTeX definitions
+ | \@namedef{? # Definition by name only
+ )
+ ($rmacroname) # Macro name without backslash
+ \s* }? # Potential closing brace
+ (.*) # Rest of line
+ /xms;
+
+my $renvdef = qr/
+ ^ # Begin of line (no whitespaces!)
+ \\(
+ (?:new|renew|provide)environment\s* { \s* # LaTeX definitions
+ )
+ ($rmacroname) # Environment names follow same rules as macro names
+ \s* } # closing brace
+ (.*) # Rest of line
+ /xms;
+
+my $comments = '';
+
+# Print end of environment, if one is open
+sub close_environment {
+ if ( $mode == 1 ) {
+ $IMPL .= $macrocodestop;
+ }
+ elsif ( $mode == 2 ) {
+ # Happens only if closing brace is not on a line by its own.
+ $IMPL .= $macrostop;
+ }
+ elsif ( $mode == 3 ) {
+ $IMPL .= $environmentstop;
+ }
+}
+
+my ( $mday, $mon, $year ) = ( localtime(time) )[ 3 .. 5 ];
+$mon = sprintf( "%02d", $mon + 1 );
+$year += 1900;
+
+my @files;
+my $outfile = '';
+my $verbose = 0;
+my $codeonly = 0;
+my $install = 0;
+my $usebase = 0;
+my $overwrite = 0;
+my $installfile;
+my $templfile;
+my $installtempl;
+my $checksum = 0;
+
+# Holds the variables for the templates, is initiated with default values:
+my %vars = (
+ type => 'package',
+ class => 'ltxdoc',
+ year => "$year",
+);
+
+# Handle options
+sub option {
+ my $opt = shift;
+ if ( $opt eq 'h' ) {
+ usage();
+ }
+ elsif ( $opt eq 'H' ) {
+ print $TITLE;
+ print "\n";
+ print $DESCRIPTION;
+ exit(0);
+ }
+ elsif ( $opt eq 'c' ) {
+ $codeonly = 1;
+ }
+ elsif ( $opt eq 'B' ) {
+ $usebase = 1;
+ }
+ elsif ( $opt eq 't' ) {
+ close(DATA);
+ $templfile = shift @ARGV;
+ open( DATA, '<', $templfile )
+ or die "$ERROR Couldn't open template file '$templfile'\n";
+ }
+ elsif ( $opt eq 'e' ) {
+ my $templ = shift @ARGV;
+ if ( $templ ne '-' ) {
+ open( STDOUT, '>', $templ )
+ or die "$ERROR Couldn't open new template file '$templ'\n";
+ }
+ while (<DATA>) {
+ last if /^__INS__$/;
+ print;
+ }
+ print STDERR
+ "Exported default template for .dtx files to file '$templ'\n"
+ if $verbose;
+ exit(0);
+ }
+ elsif ( $opt eq 'E' ) {
+ my $templ = shift @ARGV;
+ if ( $templ ne '-' ) {
+ open( STDOUT, '>', $templ )
+ or die "$ERROR Couldn't open new template file '$templ'\n";
+ }
+ while (<DATA>) {
+ last if /^__INS__$/;
+ }
+ while (<DATA>) {
+ print;
+ }
+ print STDERR
+ "Exported default template for .ins files to file '$templ'\n"
+ if $verbose;
+ exit(0);
+ }
+ elsif ( $opt eq 'v' ) {
+ $verbose++;
+ }
+ elsif ( $opt eq 'I' ) {
+ $install = 1;
+ }
+ elsif ( $opt eq 'i' ) {
+ $installfile = shift @ARGV;
+ $install = 1;
+ }
+ elsif ( $opt eq 'T' ) {
+ $installtempl = shift @ARGV;
+ }
+ elsif ( $opt eq 'V' ) {
+ print $TITLE;
+ print "\n";
+ print $COPYRIGHT;
+ exit(0);
+ }
+ elsif ( $opt eq 'F' ) {
+ my $optfile = shift @ARGV;
+
+ # Read more options and variables from file
+ open( my $OPT, '<', $optfile )
+ or die("Couldn't open options file '$optfile'!\n");
+ while ( my $line = <$OPT> ) {
+ chomp $line;
+
+ # Skip comment lines
+ next if $line =~ /^\s*[#%]/;
+
+ # Split variable lines without equal sign into name and value
+ if ( substr( $line, 0, 2 ) eq '--' and index( $line, '=' ) == -1 ) {
+ my ( $var, $val ) = split( /\s+/, $line, 2 );
+ $val =~ s/^["']|["']$//g;
+ unshift @ARGV, $var, $val;
+ }
+ else {
+ unshift @ARGV, $line;
+ }
+ }
+ close($OPT);
+ }
+ elsif ( $opt eq 'D' ) {
+ $vars{date} = "$year/$mon/$mday";
+ }
+ elsif ( $opt eq 'o' ) {
+ $outfile = shift @ARGV;
+ }
+ elsif ( $opt eq 'O' ) {
+ $overwrite = 1;
+ }
+ else {
+ print STDERR "sty2dtx: unknown option '-$opt'!\n";
+ exit(2);
+ }
+}
+
+# Count number of backslashes in code for file checksum
+sub addtochecksum {
+ my $line = shift;
+ $checksum += $line =~ tr{\\}{\\};
+}
+
+################################################################################
+# Parse arguments
+while (@ARGV) {
+ my $arg = shift;
+
+ # '--' Marks rest of arguments as files
+ if ( $arg eq '--' ) {
+ push @files, @ARGV;
+ last;
+ }
+
+ # Options and variables
+ elsif ( $arg =~ /^(-+)(.+)$/ ) {
+ my $dashes = $1;
+ my $name = $2;
+
+ # Single dash => option
+ if ( length($dashes) == 1 ) {
+ foreach my $opt ( split //, $name ) {
+ option($opt);
+ }
+ }
+ # Douple Dash => Variable
+ # Form "--var=value"
+ elsif ($name =~ /^([^=]+)=(.*)$/) {
+ $vars{lc($1)} = $2;
+ }
+ # Form "--var value"
+ else {
+ $vars{ lc($name) } = shift;
+ }
+ }
+ # Files
+ else {
+ push @files, $arg;
+ }
+}
+
+
+# Last (but not only) argument is output file, except if it is '-' (=STDOUT)
+if ( $outfile || @files > 1 ) {
+ $outfile = pop @files unless $outfile;
+ $vars{filebase} = substr( $outfile, 0, rindex( $outfile, '.' ) )
+ if not exists $vars{filebase};
+}
+elsif ( @files == 1 ) {
+ my $infile = $files[0];
+ $vars{filebase} = substr( $infile, 0, rindex( $infile, '.' ) )
+ if not exists $vars{filebase};
+ if ($usebase) {
+ $outfile = $vars{filebase} . '.dtx';
+ }
+}
+if ( $outfile && $outfile ne '-' ) {
+ if ( !$overwrite && -e $outfile && $outfile ne '/dev/null' ) {
+ die( "$ERROR output file '$outfile' does already exists!"
+ . " Use the -O option to overwrite.\n" );
+ }
+ open( OUTPUT, '>', $outfile )
+ or die("$ERROR Could not open output file '$outfile'!");
+ select OUTPUT;
+}
+
+
+################################################################################
+# Read input files
+@ARGV = @files;
+while (<>) {
+ # Test for macro definition command
+ if (/$rmacrodef/) {
+ my $pre = $1 || ""; # before command
+ my $cmd = $2; # definition command
+ my $name = $3; # macro name
+ my $rest = $4; # rest of line
+
+ # Add to usage section if it is a user level macro
+ if ( $name =~ /^$rusermacro$/i ) {
+ $USAGE .= sprintf( $macrodescription, $name );
+ }
+
+ close_environment();
+
+ # Print 'macro' environment with current line.
+ $IMPL .= sprintf( $macrostart, $name, $comments );
+ addtochecksum($_);
+ $IMPL .= $_;
+ $comments = '';
+
+ # Inside macro mode
+ $mode = 2;
+
+ # Test for one line definitions.
+ # $pre is tested to handle '{\somecatcodechange\gdef\name{short}}' lines
+ my $prenrest = $pre . $rest;
+ if ( $prenrest =~ tr/{/{/ == $prenrest =~ tr/}/}/ ) {
+ $IMPL .= $macrostop;
+ # Outside mode
+ $mode = 0;
+ }
+ }
+ # Test for environment definition command
+ elsif (/$renvdef/) {
+ my $cmd = $1; # definition command
+ my $name = $2; # macro name
+ my $rest = $3; # rest of line
+
+ # Add to usage section if it is a user level environment
+ # Can use the same RegEx as for macro names
+ if ( $name =~ /^$rusermacro$/i ) {
+ $USAGE .= sprintf( $envdescription, $name );
+ }
+
+ close_environment();
+
+ # Print 'environment' environment with current line.
+ $IMPL .= sprintf( $environmentstart, $name, $comments );
+ addtochecksum($_);
+ $IMPL .= $_;
+ $comments = '';
+
+ # Inside environment mode
+ $mode = 3;
+
+ # Test for one line definitions.
+ my $nopen = ( $rest =~ tr/{/{/ );
+ if ( $nopen >= 2 && $nopen == ( $rest =~ tr/}/}/ ) ) {
+ $IMPL .= $environmentstop;
+ # Outside mode
+ $mode = 0;
+ }
+ }
+ # Collect comment lines, might be inserted as macro or environment description
+ # Real comments are either: 1) starting with a '%' at SOL or 2) are followed
+ # by at least one whitespace. This exclude (most) commented out code.
+ elsif (/^%|^\s*%\s/) {
+ $_ =~ s/^\s*//;
+ $comments .= $_;
+ if ( $mode == 1 ) {
+ $IMPL .= $macrocodestop;
+ $mode = 0;
+ }
+ }
+ # Remove empty lines (mostly between macros)
+ elsif (/^$/) {
+ if ($comments) {
+ # Flush collected outside comments
+ $IMPL .= $comments . "%\n";
+ $comments = '';
+ }
+ }
+ else {
+ addtochecksum($_);
+ # If inside an environment
+ if ($mode) {
+ if ($comments) {
+ $IMPL .= $macrocodestop . $comments . $macrocodestart;
+ $comments = '';
+ }
+ $IMPL .= $_;
+ # A single '}' on a line ends a 'macro' or 'environment' environment
+ if ( $mode > 1 && /^\}\s*$/ ) {
+ $IMPL .= ( $mode == 2 ) ? $macrostop : $environmentstop;
+ $mode = 0;
+ }
+ }
+ else {
+ # Start macrocode environment
+ $IMPL .= $comments . $macrocodestart . $_;
+ $mode = 1;
+ $comments = '';
+ }
+ }
+}
+
+close_environment();
+
+################################################################################
+# Set extra/auto variables
+$vars{IMPLEMENTATION} = $IMPL;
+$vars{USAGE} = $USAGE;
+$vars{type} = "\L$vars{type}";
+$vars{Type} = "\L\u$vars{type}";
+$vars{extension} = $vars{type} eq 'class' ? 'cls' : 'sty';
+$vars{checksum} = $checksum if not exists $vars{checksum}; # Allow user to overwrite
+$vars{maintainer} = $vars{author}
+ if not exists $vars{maintainer} and exists $vars{author};
+
+################################################################################
+# Write DTX file
+if ($codeonly) {
+ print $IMPL;
+ if ($verbose) {
+ print STDERR "Generated DTX file";
+ print STDERR " '$outfile'" if $outfile and $outfile ne '-';
+ print STDERR " (code only).\n";
+ }
+}
+else {
+ while (<DATA>) {
+ last if /^__INS__$/;
+ # Substitute template variables
+ s/<\+([^+]+)\+>\n?/exists $vars{$1} ? $vars{$1} : "<+$1+>"/eg;
+ print;
+ }
+
+ if ($verbose) {
+ print STDERR "Generated DTX file";
+ print STDERR " '$outfile'" if $outfile and $outfile ne '-';
+ print STDERR " using template '$templfile'" if $templfile;
+ print STDERR ".\n";
+ }
+}
+
+################################################################################
+# Write INS file if requested
+exit(0) unless $install;
+
+if ( ( !$outfile || $outfile eq '-' ) && !$installfile ) {
+ print STDERR
+ "Warning: Did not generate requested .ins file because main file\n";
+ print STDERR " was written to STDOUT and no -i option was given.\n";
+ exit(1);
+}
+
+if ($installtempl) {
+ open( DATA, '<', $installtempl )
+ or die "$ERROR Could't open template '$installtempl' for .ins file.";
+}
+elsif ($codeonly) {
+ # If DATA template was not used for main file go forward to correct position
+ while (<DATA>) {
+ last if /^__INS__$/;
+ }
+}
+
+$installfile = $vars{filebase} . '.ins' unless defined $installfile;
+if ( !$overwrite && -e $installfile && $installfile ne '/dev/null' ) {
+ die( "$ERROR Output file '$installfile' does already exists!"
+ . " Use the -O option to overwrite.\n" );
+}
+open( INS, '>', $installfile )
+ or die "$ERROR Could't open new .ins file '$installfile'.";
+
+while (<DATA>) {
+ # Substitute template variables
+ s/<\+([^+]+)\+>\n?/exists $vars{$1} ? $vars{$1} : "<+$1+>"/eg;
+ print INS $_;
+}
+
+if ($verbose) {
+ print STDERR "Generated INS file '$installfile'";
+ print STDERR " using template '$installtempl'" if $installtempl;
+ print STDERR ".\n";
+}
+################################################################################
+# The templates for the DTX file and INS file
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+# Adepted from the skeleton file provided by the 'dtxtut' (DTX tuturial).
+# The '<+var+>' format was choosen because it is used by the latex suite for Vim.
+# Therfore all variables which are not expanded are easily accessible to the
+# user using a certain feature in the latex suite.
+#
+# Perl modules like 'Template' were not used to support minimal Perl installation
+# for typical LaTeX installations.
+#
+__DATA__
+% \iffalse meta-comment
+%
+% Copyright (C) <+year+> by <+author+> <<+email+>>
+% ---------------------------------------------------------------------------
+% This work may be distributed and/or modified under the
+% conditions of the LaTeX Project Public License, either version 1.3
+% of this license or (at your option) any later version.
+% The latest version of this license is in
+% http://www.latex-project.org/lppl.txt
+% and version 1.3 or later is part of all distributions of LaTeX
+% version 2005/12/01 or later.
+%
+% This work has the LPPL maintenance status `maintained'.
+%
+% The Current Maintainer of this work is <+maintainer+>.
+%
+% This work consists of the files <+filebase+>.dtx and <+filebase+>.ins
+% and the derived filebase <+filebase+>.<+extension+>.
+%
+% \fi
+%
+% \iffalse
+%<*driver>
+\ProvidesFile{<+filebase+>.dtx}
+%</driver>
+%<<+type+>>\NeedsTeXFormat{LaTeX2e}[1999/12/01]
+%<<+type+>>\Provides<+Type+>{<+filebase+>}
+%<*<+type+>>
+ [<+date+> <+version+> <+description+>]
+%</<+type+>>
+%
+%<*driver>
+\documentclass{ltxdoc}
+\usepackage{<+filebase+>}[<+date+>]
+\EnableCrossrefs
+\CodelineIndex
+\RecordChanges
+\begin{document}
+ \DocInput{<+filebase+>.dtx}
+ \PrintChanges
+ \PrintIndex
+\end{document}
+%</driver>
+% \fi
+%
+% \CheckSum{<+checksum+>}
+%
+% \CharacterTable
+% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z
+% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z
+% Digits \0\1\2\3\4\5\6\7\8\9
+% Exclamation \! Double quote \" Hash (number) \#
+% Dollar \$ Percent \% Ampersand \&
+% Acute accent \' Left paren \( Right paren \)
+% Asterisk \* Plus \+ Comma \,
+% Minus \- Point \. Solidus \/
+% Colon \: Semicolon \; Less than \<
+% Equals \= Greater than \> Question mark \?
+% Commercial at \@ Left bracket \[ Backslash \\
+% Right bracket \] Circumflex \^ Underscore \_
+% Grave accent \` Left brace \{ Vertical bar \|
+% Right brace \} Tilde \~}
+%
+%
+% \changes{<+version+>}{<+date+>}{Converted to DTX file}
+%
+% \DoNotIndex{\newcommand,\newenvironment}
+%
+% \providecommand*{\url}{\texttt}
+% \GetFileInfo{<+filebase+>.dtx}
+% \title{The \textsf{<+filebase+>} package}
+% \author{<+author+> \\ \url{<+email+>}}
+% \date{\fileversion~from \filedate}
+%
+% \maketitle
+%
+% \section{Introduction}
+%
+% Put text here.
+%
+% \section{Usage}
+%
+% Put text here.
+%
+<+USAGE+>
+%
+% \StopEventually{}
+%
+% \section{Implementation}
+%
+% \iffalse
+%<*<+type+>>
+% \fi
+%
+<+IMPLEMENTATION+>
+%
+% \iffalse
+%</<+type+>>
+% \fi
+%
+% \Finale
+\endinput
+__INS__
+%% Copyright (C) <+year+> by <+author+> <<+email+>>
+%% --------------------------------------------------------------------------
+%% This work may be distributed and/or modified under the
+%% conditions of the LaTeX Project Public License, either version 1.3
+%% of this license or (at your option) any later version.
+%% The latest version of this license is in
+%% http://www.latex-project.org/lppl.txt
+%% and version 1.3 or later is part of all distributions of LaTeX
+%% version 2005/12/01 or later.
+%%
+%% This work has the LPPL maintenance status `maintained'.
+%%
+%% The Current Maintainer of this work is <+maintainer+>.
+%%
+%% This work consists of the files <+filebase+>.dtx and <+filebase+>.ins
+%% and the derived filebase <+filebase+>.<+extension+>.
+%%
+
+\input docstrip.tex
+\keepsilent
+
+\usedir{tex/latex/<+filebase+>}
+
+\preamble
+
+This is a generated file.
+
+Copyright (C) <+year+> by <+author+> <<+email+>>
+--------------------------------------------------------------------------
+This work may be distributed and/or modified under the
+conditions of the LaTeX Project Public License, either version 1.3
+of this license or (at your option) any later version.
+The latest version of this license is in
+ http://www.latex-project.org/lppl.txt
+and version 1.3 or later is part of all distributions of LaTeX
+version 2005/12/01 or later.
+
+\endpreamble
+
+\generate{\file{<+filebase+>.<+extension+>}{\from{<+filebase+>.dtx}{<+type+>}}}
+
+\obeyspaces
+\Msg{*************************************************************}
+\Msg{* *}
+\Msg{* To finish the installation you have to move the following *}
+\Msg{* file into a directory searched by TeX: *}
+\Msg{* *}
+\Msg{* <+filebase+>.<+extension+> *}
+\Msg{* *}
+\Msg{* To produce the documentation run the file <+filebase+>.dtx *}
+\Msg{* through LaTeX. *}
+\Msg{* *}
+\Msg{* Happy TeXing! *}
+\Msg{* *}
+\Msg{*************************************************************}
+
+\endbatchfile
diff --git a/Build/source/texk/texlive/linked_scripts/svn-multi/svn-multi.pl b/Build/source/texk/texlive/linked_scripts/svn-multi/svn-multi.pl
index b98168f7dcc..4dcc32498eb 100755
--- a/Build/source/texk/texlive/linked_scripts/svn-multi/svn-multi.pl
+++ b/Build/source/texk/texlive/linked_scripts/svn-multi/svn-multi.pl
@@ -6,9 +6,9 @@
#
# Copyright (C) 2006-2009 Martin Scharrer
# E-mail: martin@scharrer-online.de
-# WWW: http://www.scharrer-online.de/latex/svn-multi/
+# WWW: http://latex.scharrer-online.de/svn-multi/
#
-# $Id: svn-multi-pl.dtx 692 2009-03-27 21:38:45Z martin $
+# $Id: svn-multi-pl.dtx 1873 2010-07-26 15:31:45Z martin $
#
# This program works only in combination with the LaTeX package 'svn-multi' and
# generates .svx files with '\svnidlong' macros holding Subversion keywords for
@@ -57,7 +57,7 @@ use warnings;
use File::Basename;
my $VERSION = "0.2";
my ($REV,$DATE) =
- (split ' ','$Id: svn-multi-pl.dtx 692 2009-03-27 21:38:45Z martin $')[2,3];
+ (split ' ','$Id: svn-multi-pl.dtx 1873 2010-07-26 15:31:45Z martin $')[2,3];
my $dollar = '$';
my @PATH;
@@ -133,8 +133,8 @@ my $resvnexternal = qr/
$ # end of line
/x;
-if (-e "$jobname.svn" and open( my $svnfh, '<', "$jobname.svn")) {
- print STDOUT "Reading '$jobname.svn'.\n";
+if (-e "$jobname.aux" and open( my $svnfh, '<', "$jobname.aux")) {
+ print STDOUT "Reading '$jobname.aux'.\n";
while (<$svnfh>) {
chomp;
if (/$resvnexternalpath/) {
@@ -149,7 +149,7 @@ if (-e "$jobname.svn" and open( my $svnfh, '<', "$jobname.svn")) {
close ($svnfh);
}
else {
- warn "No .svn file found for '$jobname'!\n";
+ warn "No .aux file found for '$jobname'!\n";
}
# Add TEXINPUTS to path
@@ -329,7 +329,7 @@ Usage:
Description:
This LaTeX helper script collects Subversion keywords from non-(La)TeX files
and provides it to the 'svn-multi' package using '.svx' files. It will first
- scan the file '<jobname>.svn' for files declared by the '\svnextern' macro but
+ scan the file '<jobname>.aux' for files declared by the '\svnextern' macro but
also allows to provide additional files including the corresponding groups. The
keywords for the additional files will be written in the file '<jobname>.svx'.
diff --git a/Build/source/texk/texlive/linked_scripts/tetex/texdoctk.pl b/Build/source/texk/texlive/linked_scripts/tetex/texdoctk.pl
index 7d0b2c8c292..36497183e3b 100755
--- a/Build/source/texk/texlive/linked_scripts/tetex/texdoctk.pl
+++ b/Build/source/texk/texlive/linked_scripts/tetex/texdoctk.pl
@@ -14,6 +14,10 @@ use Getopt::Long;
Getopt::Long::config('bundling');
use File::Basename;
+BEGIN {
+ Win32::SetChildShowWindow(0) if defined &Win32::SetChildShowWindow;
+}
+
my $IsWin32 = ($^O =~ /MSWin32/i);
if ($IsWin32) {
diff --git a/Build/source/texk/texlive/linked_scripts/texlive/tlmgr.pl b/Build/source/texk/texlive/linked_scripts/texlive/tlmgr.pl
index e69de29bb2d..783b7e970c6 100755
--- a/Build/source/texk/texlive/linked_scripts/texlive/tlmgr.pl
+++ b/Build/source/texk/texlive/linked_scripts/texlive/tlmgr.pl
@@ -0,0 +1,6373 @@
+#!/usr/bin/env perl
+# $Id$
+#
+# Copyright 2008, 2009, 2010, 2011 Norbert Preining
+# This file is licensed under the GNU General Public License version 2
+# or any later version.
+
+my $svnrev = '$Revision$';
+my $datrev = '$Date$';
+my $tlmgrrevision;
+if ($svnrev =~ m/: ([0-9]+) /) {
+ $tlmgrrevision = $1;
+} else {
+ $tlmgrrevision = "unknown";
+}
+$datrev =~ s/^.*Date: //;
+$datrev =~ s/ \(.*$//;
+$tlmgrrevision .= " ($datrev)";
+
+our $Master;
+our $ismain;
+our $loadmediasrcerror;
+our $packagelogfile;
+our $packagelogged;
+our $tlmgr_config_file;
+
+BEGIN {
+ $^W = 1;
+ $ismain = (__FILE__ eq $0);
+ # WARNING
+ # The only use anticipated for tlmgr.pl as library for the 2009 release
+ # is the Windows w32client prototype script.
+ # Unix-specific problems with use as library will probably go undetected.
+
+ # make subprograms (including kpsewhich) have the right path:
+ my ($bindir, $kpsewhichname);
+ if ($^O =~ /^MSWin/i) {
+ # on win32 $0 and __FILE__ point directly to tlmgr.pl; they can be relative
+ $Master = __FILE__;
+ $Master =~ s!\\!/!g;
+ $Master =~ s![^/]*$!../../..!
+ unless ($Master =~ s!/texmf/scripts/texlive/tlmgr\.pl$!!i);
+ $bindir = "$Master/bin/win32";
+ $kpsewhichname = "kpsewhich.exe";
+ # path already set by wrapper batchfile
+ } else {
+ $Master = __FILE__;
+ $Master =~ s,/*[^/]*$,,;
+ if ($ismain) {
+ $bindir = $Master;
+ $Master = "$Master/../..";
+ } else {
+ # for the time being, this code will not be used or tested
+ $Master = "$Master/../../..";
+ # no code yet for $bindir; would have to detect platform
+ }
+ # make subprograms (including kpsewhich) have the right path:
+ $ENV{"PATH"} = "$bindir:$ENV{PATH}";
+ $kpsewhichname = "kpsewhich";
+ }
+ if (-r "$bindir/$kpsewhichname") {
+ # if not in bootstrapping mode => kpsewhich exists, so use it to get $Master
+ chomp($Master = `kpsewhich -var-value=SELFAUTOPARENT`);
+ }
+ $::installerdir = $Master;
+
+ #
+ # make Perl find our packages first:
+ unshift (@INC, "$Master/tlpkg");
+ unshift (@INC, "$Master/texmf/scripts/texlive");
+}
+
+use Cwd qw/abs_path/;
+use Digest::MD5;
+use Pod::Usage;
+use Getopt::Long qw(:config no_autoabbrev permute);
+use strict;
+
+use TeXLive::TLConfig;
+use TeXLive::TLMedia;
+use TeXLive::TLPDB;
+use TeXLive::TLPOBJ;
+use TeXLive::TLUtils;
+use TeXLive::TLWinGoo;
+use TeXLive::TLDownload;
+use TeXLive::TLConfFile;
+TeXLive::TLUtils->import(qw(member info give_ctan_mirror win32 dirname
+ mkdirhier copy log debug tlcmp));
+use TeXLive::TLPaper;
+
+binmode(STDOUT, ":utf8");
+binmode(STDERR, ":utf8");
+
+our %config; # hash of config settings from config file
+our $tlmediasrc; # media from which we install/update
+our $tlmediatlpdb;
+our $location; # location from which the new packages come
+our $localtlmedia; # local installation which we are munging
+our $localtlpdb; # local installation which we are munging
+
+# flags for machine-readable form
+our $FLAG_REMOVE = "d";
+our $FLAG_FORCIBLE_REMOVED = "f";
+our $FLAG_UPDATE = "u";
+our $FLAG_REVERSED_UPDATE = "r";
+our $FLAG_AUTOINSTALL = "a";
+our $FLAG_INSTALL = "i";
+our $FLAG_REINSTALL = "I";
+
+# option variables
+$::gui_mode = 0;
+$::machinereadable = 0;
+
+our %opts;
+
+main() if $ismain;
+
+sub main {
+ my %options; # TL options from local tlpdb
+
+ my %globaloptions = (
+ "gui" => 1,
+ "gui-lang" => "=s",
+ "debug-translation" => 1,
+ "location|repository|repo" => "=s",
+ "machine-readable" => 1,
+ "package-logfile" => "=s",
+ "persistent-downloads" => "!",
+ "no-execute-actions" => 1,
+ "pause" => 1,
+ "print-platform|print-arch" => 1,
+ "version" => 1,
+ "help|h|?" => 1);
+
+ my %actionoptions = (
+ "backup" => { "backupdir" => "=s",
+ "clean" => ":-99",
+ "all" => 1,
+ "dry-run|n" => 1 },
+ "check" => { "use-svn" => 1 },
+ "generate" => { "localcfg" => "=s",
+ "dest" => "=s",
+ "rebuild-sys" => 1 },
+ "gui" => { "load" => 1 },
+ "install" => { "no-depends" => 1,
+ "no-depends-at-all" => 1,
+ "reinstall" => 1,
+ "force" => 1,
+ "dry-run|n" => 1 },
+ "list" => { "only-installed" => 1 },
+ "paper" => { "list" => 1 },
+ "path" => { "w32mode" => "=s" },
+ "platform" => { "dry-run|n" => 1 },
+ "postaction" => { "w32mode" => "=s",
+ "all" => 1,
+ "fileassocmode" => "=i" },
+ "recreate-tlpdb"=> { "platform|arch" => "=s" },
+ "remove" => { "no-depends" => 1,
+ "no-depends-at-all" => 1,
+ "force" => 1,
+ "dry-run|n" => 1 },
+ "restore" => { "backupdir" => "=s",
+ "dry-run|n" => 1,
+ "all" => 1,
+ "force" => 1 },
+ "search" => { "global" => 1,
+ "word" => 1,
+ "file" => 1,
+ "keyword" => 1,
+ "list" => 1,
+ "all" => 1,
+ "characterization" => 1,
+ "functionality" => 1,
+ "taxonomy" => 1 },
+ "show" => { "list" => 1,
+ "taxonomy" => 1,
+ "keyword" => 1,
+ "characterization" => 1,
+ "functionality" => 1 },
+ "uninstall" => { "force" => 1 },
+ "update" => { "no-depends" => 1,
+ "no-depends-at-all" => 1,
+ "all" => 1,
+ "self" => 1,
+ "list" => 1,
+ "no-auto-remove" => 1,
+ "no-auto-install" => 1,
+ "reinstall-forcibly-removed" => 1,
+ "force" => 1,
+ "backupdir" => "=s",
+ "backup" => 1,
+ "exclude" => "=s@",
+ "dry-run|n" => 1 },
+ );
+
+ my %optarg;
+ for my $k (keys %globaloptions) {
+ if ($globaloptions{$k} eq "1") {
+ $optarg{$k} = 1;
+ } else {
+ $optarg{"$k" . $globaloptions{$k}} = 1;
+ }
+ }
+ for my $v (values %actionoptions) {
+ for my $k (keys %$v) {
+ if ($v->{$k} eq "1") {
+ $optarg{$k} = 1;
+ } else {
+ $optarg{"$k" . $v->{$k}} = 1;
+ }
+ }
+ }
+
+ # save command line options for later restart, if necessary
+ @::SAVEDARGV = @ARGV;
+
+ TeXLive::TLUtils::process_logging_options();
+
+ GetOptions(\%opts, keys(%optarg)) or pod2usage(2);
+
+ $::debug_translation = 0;
+ $::debug_translation = 1 if $opts{"debug-translation"};
+
+ $::machinereadable = $opts{"machine-readable"}
+ if (defined($opts{"machine-readable"}));
+
+ my $action = shift @ARGV;
+ if (!defined($action)) {
+ if ($opts{"gui"}) { # -gui = gui
+ $action = "gui";
+ } elsif ($opts{"print-platform"}) { # -print-arch = print-arch
+ $action = "print-platform";
+ } else {
+ $action = "";
+ }
+ }
+
+ $action = "platform" if ($action eq "arch");
+
+ ddebug("action = $action\n");
+ for my $k (keys %opts) {
+ ddebug("$k => $opts{$k}\n");
+ }
+ ddebug("arguments: @ARGV\n") if @ARGV;
+
+ if ($opts{"version"} || (defined $action && $action eq "version")) {
+ info(give_version());
+ finish(0);
+ }
+
+ if (defined($action) && ($action =~ m/^help/i)) {
+ $opts{"help"} = 1;
+ }
+
+ if (defined($action) && ($action =~ m/^print-platform$/i)) {
+ print TeXLive::TLUtils::platform(), "\n";
+ exit 0;
+ }
+
+ if ((!defined($action) || !$action) && !$opts{"help"}) {
+ die "$0: missing action; try --help if you need it.\n";
+ }
+
+ if ($opts{"help"}) {
+ # perldoc does ASCII emphasis on the output, so it's nice to use it.
+ # But not all Unix platforms have it, and on Windows our Config.pm
+ # can apparently interfere, so always skip it there.
+ my @noperldoc = ();
+ if (win32() || ! TeXLive::TLUtils::which("perldoc")) {
+ @noperldoc = ("-noperldoc", "1");
+ }
+ # in some cases LESSPIPE of less breaks control characters
+ # and the output of pod2usage is broken.
+ # We add/set LESS=-R in the environment and unset LESSPIPE to be sure
+ if (defined($ENV{'LESS'})) {
+ $ENV{'LESS'} .= " -R";
+ } else {
+ $ENV{'LESS'} = "-R";
+ }
+ delete $ENV{'LESSPIPE'};
+ delete $ENV{'LESSOPEN'};
+ if ($action && ($action ne "help")) {
+ pod2usage(-exitstatus => 0, -verbose => 99,
+ -sections => "NAME|SYNOPSIS|ACTIONS/$action.*" , @noperldoc);
+ } else {
+ pod2usage(-exitstatus => 0, -verbose => 2, @noperldoc);
+ }
+ }
+
+ # unify arguments so that the $action contains paper in all cases
+ # and push the first arg back to @ARGV for action_paper processing
+ if ($action =~ /^(paper|xdvi|pdftex|dvips|dvipdfmx?|context)$/) {
+ unshift(@ARGV, $action);
+ $action = "paper";
+ }
+
+ # --machine-readable is only supported by update.
+ if ($::machinereadable &&
+ $action ne "update" && $action ne "install" && $action ne "option") {
+ tlwarn("tlmgr: --machine-readable output not supported for $action\n");
+ }
+
+ # check on supported arguments
+ #
+ my %suppargs;
+ %suppargs = %{$actionoptions{$action}}
+ if defined($actionoptions{$action});
+ my @notvalidargs;
+ for my $k (keys %opts) {
+ my @allargs = keys %suppargs;
+ push @allargs, keys %globaloptions;
+ my $found = 0;
+ for my $ok (@allargs) {
+ my @variants = split '\|', $ok;
+ if (TeXLive::TLUtils::member($k, @variants)) {
+ $found = 1;
+ last;
+ }
+ }
+ push @notvalidargs, $k if !$found;
+ }
+ if (@notvalidargs) {
+ my $msg =
+ "The following arguments are not supported for the action $action:\n";
+ for my $c (@notvalidargs) {
+ $msg .= " $c";
+ }
+ $msg .= "\n";
+ my @noperldoc = ();
+ if (win32() || ! TeXLive::TLUtils::which("perldoc")) {
+ @noperldoc = ("-noperldoc", "1");
+ }
+ pod2usage(-msg => $msg, -exitstatus => 1, -verbose => 1, @noperldoc);
+ }
+
+ # besides doing normal logging if -logfile is specified, we try to log
+ # package related actions (install, remove, update) to
+ # the package-log file TEXMFSYSVAR/web2c/tlmgr.log
+ $packagelogged = 0; # how many msgs we logged
+ chomp (my $texmfsysvar = `kpsewhich -var-value=TEXMFSYSVAR`);
+ $packagelogfile = $opts{"package-logfile"};
+ $packagelogfile ||= "$texmfsysvar/web2c/tlmgr.log";
+ #
+ # Try to open the packagelog file, but do NOT die when that does not work
+ if (!open(PACKAGELOG, ">>$packagelogfile")) {
+ debug("Cannot open package log file $packagelogfile for appending\n");
+ debug("Will not log package installation/removal/update for that run\n");
+ $packagelogfile = "";
+ }
+
+ $loadmediasrcerror = "Cannot load TeX Live database from ";
+
+ # load the config file and set the config options
+ # load it BEFORE starting downloads as we set persistent-downloads there!
+ load_config_file();
+
+ # set global variable if execute actions should be suppressed
+ $::no_execute_actions = 1 if (defined($opts{'no-execute-actions'}));
+
+
+ # if we are asked to use persistent connections try to start it here
+ {
+ my $do_persistent;
+ if (defined($opts{'persistent-downloads'})) {
+ # a command line argument for persistent-downloads has been given,
+ # either with --no-... or --... that overrides any other setting
+ $do_persistent = $opts{'persistent-downloads'};
+ } else {
+ # check if it is set in the config file
+ if (defined($config{'persistent-downloads'})) {
+ $do_persistent = $config{'persistent-downloads'};
+ }
+ }
+ # default method is doing persistent downloads:
+ if (!defined($do_persistent)) {
+ $do_persistent = 1;
+ }
+ debug("tlmgr:main: persistent download are $do_persistent\n");
+ TeXLive::TLUtils::setup_persistent_downloads()
+ if $do_persistent;
+ if (!defined($::tldownload_server)) {
+ debug("tlmgr:main: ::tldownload_server not defined\n");
+ } else {
+ debug("tlmgr:main: ::tldownload_server defined\n");
+ }
+ }
+
+ execute_action($action, @ARGV);
+
+ # end of main program.
+
+} # end main
+
+sub give_version {
+ if (!defined($::version_string)) {
+ $::version_string = "";
+ $::version_string .= "tlmgr revision $tlmgrrevision\n";
+ $::version_string .= "tlmgr using installation: $Master\n";
+ if (open (REL_TL, "$Master/release-texlive.txt")) {
+ # print first and last lines, which have the TL version info.
+ my @rel_tl = <REL_TL>;
+ $::version_string .= $rel_tl[0];
+ $::version_string .= $rel_tl[$#rel_tl];
+ close (REL_TL);
+ }
+ }
+ #
+ # add the list of revisions
+ if ($::opt_verbosity > 0) {
+ $::version_string .= "Revision of modules:";
+ $::version_string .= "\nTLConfig: " . TeXLive::TLConfig->module_revision();
+ $::version_string .= "\nTLUtils: " . TeXLive::TLUtils->module_revision();
+ $::version_string .= "\nTLPOBJ: " . TeXLive::TLPOBJ->module_revision();
+ $::version_string .= "\nTLPDB: " . TeXLive::TLPDB->module_revision();
+ $::version_string .= "\nTLMedia: " . TeXLive::TLMedia->module_revision();
+ $::version_string .= "\nTLPaper: " . TeXLive::TLPaper->module_revision();
+ $::version_string .= "\nTLWinGoo: " . TeXLive::TLWinGoo->module_revision();
+ $::version_string .= "\n";
+ }
+ return $::version_string;
+}
+
+
+sub execute_action {
+ my ($action, @argv) = @_;
+
+ # we have to set @ARGV to the @argv since many of the action_* subs
+ # use GetOption
+ @ARGV = @argv;
+
+ # actions which shouldn't have any lasting effects, such as search or
+ # list, end by calling finish(0), which skips postinstall actions.
+ if ($action =~ m/^_include_tlpobj$/) {
+ # this is an internal function that should not be used outside
+ init_local_db();
+ for my $f (@ARGV) {
+ my $tlpobj = TeXLive::TLPOBJ->new;
+ $tlpobj->from_file($f);
+ # we now have to check whether that is a .doc or .src package, so shipping
+ # src or doc files from a different package.
+ # We should have that package already installed ...
+ my $pkg = $tlpobj->name;
+ if ($pkg =~ m/^(.*)\.(source|doc)$/) {
+ # got a .src or .doc package
+ my $type = $2;
+ my $mothership = $1;
+ my $mothertlp = $localtlpdb->get_package($mothership);
+ if (!defined($mothertlp)) {
+ tlwarn("We are trying to add ${type} files to a nonexistent package $mothership!\n");
+ tlwarn("Trying to continue!\n");
+ # the best we can do is rename that package to $mothername and add it!
+ $tlpobj->name($mothership);
+ # add the src/docfiles tlpobj under the mothership name
+ $localtlpdb->add_tlpobj($tlpobj);
+ } else {
+ if ($type eq "source") {
+ $mothertlp->srcfiles($tlpobj->srcfiles);
+ $mothertlp->srcsize($tlpobj->srcsize);
+ } else {
+ # must be "doc"
+ $mothertlp->docfiles($tlpobj->docfiles);
+ $mothertlp->docsize($tlpobj->docsize);
+ }
+ # that make sure that the original entry is overwritten
+ $localtlpdb->add_tlpobj($mothertlp);
+ }
+ } else {
+ # completely normal package, just add it
+ $localtlpdb->add_tlpobj($tlpobj);
+ }
+ $localtlpdb->save;
+ }
+ finish(0);
+ } elsif ($action =~ m/^get-mirror$/i) {
+ my $loc = give_ctan_mirror();
+ print "$loc\n";
+ finish(0);
+ } elsif ($action =~ m/^generate$/i) {
+ action_generate();
+ } elsif ($action =~ m/^gui$/i) {
+ action_gui();
+ } elsif ($action =~ m/^platform$/i) {
+ action_platform();
+ } elsif ($action =~ m/^option$/i) {
+ action_option();
+ finish(0);
+ } elsif ($action =~ m/^list$/i) {
+ action_list();
+ finish(0);
+ } elsif ($action =~ m/^check$/i) {
+ action_check();
+ } elsif ($action =~ m/^install$/i) {
+ action_install();
+ } elsif ($action =~ m/^update$/i) {
+ action_update();
+ } elsif ($action =~ m/^backup$/i) {
+ action_backup();
+ } elsif ($action =~ m/^restore$/i) {
+ action_restore();
+ } elsif ($action =~ m/^path$/i) {
+ action_path();
+ } elsif ($action =~ m/^search$/i) {
+ action_search();
+ finish(0);
+ } elsif ($action =~ m/^postaction$/i) {
+ action_postaction();
+ finish(0);
+ } elsif ($action =~ m/^conf$/i) {
+ action_conf();
+ finish(0);
+ } elsif ($action =~ m/^show$/i) {
+ action_show();
+ finish(0);
+ } elsif ($action =~ m/^remove$/i) {
+ action_remove();
+ } elsif ($action =~ /^paper$/) {
+ action_paper();
+ } elsif ($action =~ m/^uninstall$/i) {
+ action_uninstall();
+ finish(0);
+ } elsif ($action =~ m/^recreate-tlpdb$/i) {
+ action_recreate_tlpdb();
+ finish(0);
+ } else {
+ die "$0: unknown action: $action; try --help if you need it.\n";
+ }
+
+ # close the special log file
+ if ($packagelogfile && !$::gui_mode) {
+ info("tlmgr: package log updated at $packagelogfile\n") if $packagelogged;
+ close(PACKAGELOG);
+ }
+
+ # run external programs.
+ my $error_count = &handle_execute_actions();
+
+ # done, just in case there are 256 errors.
+ # we return here, because finish might return if we are running
+ # in the GUI, and we want to pass the error count on to the GUI
+ return finish($error_count ? 1 : 0);
+}
+
+
+
+# run CMD with notice to the user and if exit status is nonzero, complain.
+# return exit status.
+#
+sub do_cmd_and_check
+{
+ my $cmd = shift;
+ # we output the pre-running notice on a separate line so that
+ # tlmgr front ends (MacOSX's TeX Live Utility) can read it
+ # and show it to the user before the possibly long delay.
+ info("running $cmd ...\n");
+ my ($out, $ret) = TeXLive::TLUtils::run_cmd("$cmd 2>&1");
+ if ($ret == 0) {
+ info("done running $cmd.\n");
+ log("--output of $cmd:\n$out\n--end of output of $cmd.");
+ } else {
+ info("\n");
+ tlwarn("$cmd failed (status $ret), output:\n$out\n");
+ }
+ return $ret;
+}
+
+# run external programs (mktexlsr, updmap-sys, etc.) as specified by the
+# keys in the RET hash. We return the number of unsuccessful runs, zero
+# if all ok.
+#
+# If the "map" key is specified, the value may be a reference to a list
+# of map command strings to pass to updmap, e.g., "enable Map=ascii.map".
+#
+sub handle_execute_actions
+{
+ my $errors = 0;
+
+ if ($::files_changed) {
+ $errors += do_cmd_and_check("mktexlsr");
+ $::files_changed = 0;
+ }
+
+ #
+ # maps handling
+ # if enabled and disabled -> do nothing
+ # if only enabled -> enable it
+ # if only disabled -> disable it
+ {
+ my %do_enable;
+ my %do_disable;
+ my $run_full = 0;
+ for my $m (keys %{$::execute_actions{'enable'}{'maps'}}) {
+ $run_full = 1;
+ $do_enable{$m} = 1;
+ }
+ for my $m (keys %{$::execute_actions{'disable'}{'maps'}}) {
+ $run_full = 1;
+ if (defined($do_enable{$m})) {
+ # we are upgrading because it is disabled and enabled, so
+ # delete the entry in do_enable, it is already
+ delete $do_enable{$m};
+ } else {
+ # not new installed, so simply removed
+ $do_disable{$m} = 1;
+ }
+ }
+ for my $m (keys %do_disable) {
+ $errors += do_cmd_and_check("updmap-sys --nomkmap --nohash --disable $m");
+ }
+ for my $m (keys %do_enable) {
+ my $str = "updmap-sys --nomkmap --nohash --enable " .
+ $::execute_actions{'enable'}{'maps'}{$m} . "=$m";
+ $errors += do_cmd_and_check($str);
+ }
+ $errors += do_cmd_and_check("updmap-sys") if $run_full;
+ }
+
+
+ chomp(my $TEXMFSYSVAR = `kpsewhich -var-value=TEXMFSYSVAR`);
+ chomp(my $TEXMFLOCAL = `kpsewhich -var-value=TEXMFLOCAL`);
+
+
+ # format-regenerate is used when the paper size changes. In that
+ # case, if option("create_formats") is set, we simply want to generate
+ #
+ {
+ my %done_formats;
+ my %updated_engines;
+ my %format_to_engine;
+ my %do_enable;
+ my $do_full = 0;
+ for my $m (keys %{$::execute_actions{'enable'}{'formats'}}) {
+ $do_full = 1;
+ $do_enable{$m} = 1;
+ # here we check whether an engine is updated
+ my %foo = %{$::execute_actions{'enable'}{'formats'}{$m}};
+ if (!defined($foo{'name'}) || !defined($foo{'engine'})) {
+ tlwarn("tlmgr: Very strange error, please report ", %foo);
+ } else {
+ $format_to_engine{$m} = $foo{'engine'};
+ if ($foo{'name'} eq $foo{'engine'}) {
+ $updated_engines{$m} = 1;
+ }
+ }
+ }
+ for my $m (keys %{$::execute_actions{'disable'}{'formats'}}) {
+ $do_full = 1;
+ }
+ my $opt_fmt = $localtlpdb->option("create_formats");
+ if ($do_full) {
+ info("regenerating fmtutil.cnf in $TEXMFSYSVAR\n");
+ TeXLive::TLUtils::create_fmtutil($localtlpdb,
+ "$TEXMFSYSVAR/web2c/fmtutil.cnf",
+ "$TEXMFLOCAL/web2c/fmtutil-local.cnf");
+ }
+ if ($opt_fmt && !$::regenerate_all_formats) {
+ # first regenerate all formats --byengine
+ for my $e (keys %updated_engines) {
+ log ("updating formats based on $e\n");
+ $errors += do_cmd_and_check("fmtutil-sys --no-error-if-no-format --byengine $e");
+ }
+ # now rebuild all other formats
+ for my $f (keys %do_enable) {
+ next if defined($updated_engines{$format_to_engine{$f}});
+ # ignore disabled formats
+ next if !$::execute_actions{'enable'}{'formats'}{$f}{'mode'};
+ log ("(re)creating format dump $f\n");
+ $errors += do_cmd_and_check("fmtutil-sys --byfmt $f");
+ $done_formats{$f} = 1;
+ }
+ }
+ }
+
+ #
+ # hyphenation patterns
+ # if something has changed do the whole stuff ...
+ {
+ my $do_something = 0;
+ for my $m (keys %{$::execute_actions{'enable'}{'hyphens'}}) {
+ $do_something = 1;
+ last;
+ }
+ for my $m (keys %{$::execute_actions{'disable'}{'hyphens'}}) {
+ $do_something = 1;
+ last;
+ }
+
+ if ($do_something) {
+ for my $ext ("dat", "def", "dat.lua") {
+ my $lang = "language.$ext";
+ info("regenerating $lang\n");
+
+ my $arg1 = "$TEXMFSYSVAR/tex/generic/config/language.$ext";
+ my $arg2 = "$TEXMFLOCAL/tex/generic/config/language-local.$ext";
+ if ($ext eq "dat") {
+ TeXLive::TLUtils::create_language_dat($localtlpdb, $arg1, $arg2);
+ } elsif ($ext eq "def") {
+ TeXLive::TLUtils::create_language_def($localtlpdb, $arg1, $arg2);
+ } else {
+ TeXLive::TLUtils::create_language_lua($localtlpdb, $arg1, $arg2);
+ }
+
+ if (! TeXLive::TLUtils::win32()) {
+ # Use full path for external command, except on Windows.
+ $lang = "$TEXMFSYSVAR/tex/generic/config/$lang";
+ }
+ if ($localtlpdb->option("create_formats")
+ && !$::regenerate_all_formats) {
+ $errors += do_cmd_and_check("fmtutil-sys --byhyphen $lang");
+ }
+ }
+ }
+ }
+
+ #
+ if ($::regenerate_all_formats) {
+ info("Regenerating all formats, this may take some time ...");
+ $errors += do_cmd_and_check("fmtutil-sys --all");
+ info("done\n");
+ $::regenerate_all_formats = 0;
+ }
+
+ # undefine the global var, otherwise in GUI mode the actions are accumulating
+ undef %::execute_actions;
+
+ return $errors;
+}
+
+
+# REMOVE
+#
+# tlmgr remove foo bar baz
+# will remove the packages foo bar baz itself
+# and will remove all .ARCH dependencies, too
+# and if some of them are collections it will also remove the
+# depending packages which are NOT Collections|Schemes.
+# if some of them are referenced somewhere they will not be removed
+# unless --force given
+#
+# tlmgr remove --no-depends foo bar baz
+# will remove the packages foo bar baz itself without any dependencies
+# but it will still remove all .ARCH dependency
+# if some of them are referenced somewhere they will not be removed
+# unless --force given
+#
+# tlmgr remove --no-depends-at-all foo bar baz
+# will absolutely only install foo bar baz not even taking .ARCH into
+# account
+#
+sub action_remove {
+ # we do the following:
+ # - (not implemented) order collections such that those depending on
+ # other collections are first removed, and then those which only
+ # depend on packages. Otherwise
+ # remove collection-latex collection-latexrecommended
+ # will not succeed
+ # - first loop over all cmd line args and consider only the collections
+ # - for each to be removed collection:
+ # . check that no other collections/scheme asks for that collection
+ # . remove the collection
+ # . remove all dependencies
+ # - for each normal package not already removed (via the above)
+ # . check that no collection/scheme still depends on this package
+ # . remove the package
+ #
+ $opts{"no-depends"} = 1 if $opts{"no-depends-at-all"};
+ my %already_removed;
+ my @more_removal;
+ init_local_db();
+ return if !check_on_writable();
+ info("remove: dry run, no changes will be made\n") if $opts{"dry-run"};
+ my @packs = @ARGV;
+ #
+ # we have to be carefull not to remove too many packages. The idea is
+ # as follows:
+ # - let A be the set of all packages to be removed from the cmd line
+ # - let A* be the set of A with all dependencies expanded
+ # - let B be the set of all packages
+ # - let C = B \ A*, ie the set of all packages without those packages
+ # in the set of A*
+ # - let C* be the set of C with all dependencies expanded
+ # - let D = A* \ C*, ie the set of all packages to be removed (A*)
+ # without all the package that are still needed (C*)
+ # - remove all package in D
+ # - for any package in A (not in A*, in A, ie on the cmd line) that is
+ # also in C* (so a package that was asked for to be removed on the
+ # cmd line, but it isn't because someone else asks for it), warn the
+ # user that it is still needed
+ #
+ # remove all .ARCH dependencies, too, unless $opts{"no-depends-at-all"}
+ @packs = $localtlpdb->expand_dependencies("-only-arch", $localtlpdb, @packs) unless $opts{"no-depends-at-all"};
+ # remove deps unless $opts{"no-depends"}
+ @packs = $localtlpdb->expand_dependencies("-no-collections", $localtlpdb, @packs) unless $opts{"no-depends"};
+ my %allpacks;
+ for my $p ($localtlpdb->list_packages) { $allpacks{$p} = 1; }
+ for my $p (@packs) { delete($allpacks{$p}); }
+ my @neededpacks = $localtlpdb->expand_dependencies($localtlpdb, keys %allpacks);
+ my %packs;
+ my %origpacks;
+ my @origpacks = $localtlpdb->expand_dependencies("-only-arch", $localtlpdb, @ARGV) unless $opts{"no-depends-at-all"};
+ for my $p (@origpacks) { $origpacks{$p} = 1; }
+ for my $p (@packs) { $packs{$p} = 1; }
+ for my $p (@neededpacks) {
+ if (defined($origpacks{$p})) {
+ # that package was asked for to be removed on the cmd line
+ my @needed = $localtlpdb->needed_by($p);
+ if ($opts{"force"}) {
+ info("tlmgr: $p is needed by " . join(" ", @needed) . "\n");
+ info("tlmgr: removing it anyway, due to --force\n");
+ } else {
+ delete($packs{$p});
+ tlwarn("tlmgr: not removing $p, needed by " .
+ join(" ", @needed) . "\n");
+ }
+ } else {
+ delete($packs{$p});
+ }
+ }
+ @packs = keys %packs;
+ foreach my $pkg (sort @packs) {
+ my $tlp = $localtlpdb->get_package($pkg);
+ next if defined($already_removed{$pkg});
+ if (!defined($tlp)) {
+ info("$pkg: package not present, cannot remove\n");
+ } else {
+ # in the first round we only remove collections, nothing else
+ # but removing collections will remove all dependencies, too
+ # save the information of which packages have already been removed
+ # into %already_removed.
+ if ($tlp->category eq "Collection") {
+ my $foo = 0;
+ info ("remove $pkg\n");
+ if (!$opts{"dry-run"}) {
+ $foo = $localtlmedia->remove_package($pkg);
+ logpackage("remove: $pkg");
+ }
+ if ($foo) {
+ # removal was successful, so the return is at least 0x0001 mktexlsr
+ # remove dependencies, too
+ $already_removed{$pkg} = 1;
+ }
+ } else {
+ # save all the other packages into the @more_removal list to
+ # be removed at the second state. Note that if a package has
+ # already been removed due to a removal of a collection
+ # it will be marked as such in %already_removed and not tried again
+ push @more_removal, $pkg;
+ }
+ }
+ }
+ foreach my $pkg (sort @more_removal) {
+ if (!defined($already_removed{$pkg})) {
+ info ("remove $pkg\n");
+ if (!$opts{"dry-run"}) {
+ if ($localtlmedia->remove_package($pkg)) {
+ # removal was successful
+ logpackage("remove: $pkg");
+ $already_removed{$pkg} = 1;
+ }
+ }
+ }
+ }
+ if ($opts{"dry-run"}) {
+ # stop here, don't do any postinstall actions
+ return;
+ } else {
+ $localtlpdb->save;
+ my @foo = sort keys %already_removed;
+ if (@foo) {
+ info("tlmgr: actually removed these packages: @foo\n");
+ } else {
+ info("tlmgr: no packages removed.\n");
+ }
+ }
+}
+
+
+# PAPER
+# ARGV can look like:
+# paper a4
+# paper letter
+# [xdvi|...|context] paper [help|papersize|--list]
+#
+sub action_paper {
+ init_local_db();
+ chomp(my $texmfsysconfig = `kpsewhich -var-value=TEXMFSYSCONFIG`);
+ $ENV{"TEXMFCONFIG"} = $texmfsysconfig;
+
+ my $action = shift @ARGV;
+ if ($action =~ m/^paper$/i) { # generic paper
+ my $newpaper = shift @ARGV;
+ if ($opts{"list"}) { # tlmgr paper --list => complain.
+ tlwarn("tlmgr: ignoring paper setting to $newpaper with --list\n")
+ if $newpaper; # complain if they tried to set, too.
+ tlwarn("tlmgr: please specify a program before paper --list, ",
+ "as in: tlmgr pdftex paper --list\n");
+
+ } elsif (!defined($newpaper)) { # tlmgr paper => show all current sizes.
+ TeXLive::TLPaper::paper_all($texmfsysconfig,undef);
+
+ } elsif ($newpaper !~ /^(a4|letter)$/) { # tlmgr paper junk => complain.
+ $newpaper = "the empty string" if !defined($newpaper);
+ tlwarn("tlmgr: expected `a4' or `letter' after paper, not $newpaper\n");
+
+ } else { # tlmgr paper {a4|letter} => do it.
+ return if !check_on_writable();
+ TeXLive::TLPaper::paper_all($texmfsysconfig,$newpaper);
+ }
+
+ } else { # program-specific paper
+ my $prog = $action; # first argument is the program to change
+ my $arg = shift @ARGV; # get "paper" argument
+ if (!defined($arg) || $arg ne "paper") {
+ $arg = "the empty string." if ! $arg;
+ tlwarn("tlmgr: expected `paper' after $prog, not $arg\n");
+ return;
+ }
+ # the do_paper progs check for the argument --list, so if given
+ # restore it to the cmd line.
+ if (@ARGV) {
+ return if !check_on_writable();
+ }
+ unshift(@ARGV, "--list") if $opts{"list"};
+ TeXLive::TLPaper::do_paper($prog,$texmfsysconfig,@ARGV);
+ }
+}
+
+
+# SHOW
+#
+sub action_show {
+ init_local_db();
+ my $taxonomy;
+ if ($opts{"taxonomy"} || $opts{"characterization"} || $opts{"functionality"}
+ || $opts{"keyword"}) {
+ $taxonomy = load_taxonomy_datafile();
+ if (!defined($taxonomy)) {
+ tlwarn("tlmgr: Cannot load taxonomy file, showing taxonomies not supported.\n");
+ }
+ }
+ foreach my $pkg (@ARGV) {
+ my $tlpdb = $localtlpdb;
+ my $tlp = $localtlpdb->get_package($pkg);
+ my $installed = 0;
+ if (!$tlp) {
+ if (!$tlmediatlpdb) {
+ init_tlmedia();
+ }
+ $tlp = $tlmediatlpdb->get_package($pkg);
+ $tlpdb = $tlmediatlpdb;
+ } else {
+ $installed = 1;
+ }
+ if ($tlp) {
+ my @colls;
+ if ($tlp->category ne "Collection" && $tlp->category ne "Scheme") {
+ @colls = $localtlpdb->needed_by($pkg);
+ if (!@colls) {
+ # not referenced in the local tlpdb, so try the remote here, too
+ if (!$tlmediatlpdb) {
+ init_tlmedia();
+ }
+ @colls = $tlmediatlpdb->needed_by($pkg);
+ }
+ }
+ # some packages might depend on other packages, so do not
+ # include arbitrary package in the list of collections, but
+ # only collectons:
+ @colls = grep {m;^collection-;} @colls;
+ print "package: ", $tlp->name, "\n";
+ print "category: ", $tlp->category, "\n";
+ print "shortdesc: ", $tlp->shortdesc, "\n" if ($tlp->shortdesc);
+ print "longdesc: ", $tlp->longdesc, "\n" if ($tlp->longdesc);
+ print "installed: ", ($installed ? "Yes" : "No"), "\n";
+ print "revision: ", $tlp->revision, "\n" if ($installed);
+ print "cat-version: ", $tlp->cataloguedata->{'version'}, "\n"
+ if $tlp->cataloguedata->{'version'};
+ print "cat-date: ", $tlp->cataloguedata->{'date'}, "\n"
+ if $tlp->cataloguedata->{'date'};
+ print "cat-license: ", $tlp->cataloguedata->{'license'}, "\n"
+ if $tlp->cataloguedata->{'license'};
+ print "collection: ", @colls, "\n" if (@colls);
+ if ($opts{"keyword"} || $opts{"taxonomy"}) {
+ print "keywords: ";
+ if (defined($taxonomy->{'by-package'}{'keyword'}{$pkg})) {
+ print join(', ',@{$taxonomy->{'by-package'}{'keyword'}{$pkg}}), "\n";
+ } else {
+ print "(none found)\n";
+ }
+ }
+ if ($opts{"functionality"} || $opts{"taxonomy"}) {
+ print "function: ";
+ if (defined($taxonomy->{'by-package'}{'functionality'}{$pkg})) {
+ print join(', ',@{$taxonomy->{'by-package'}{'functionality'}{$pkg}}), "\n";
+ } else {
+ print "(none found)\n";
+ }
+ }
+ if ($opts{"characterization"} || $opts{"taxonomy"}) {
+ print "primary characterization: ";
+ if (defined($taxonomy->{'by-package'}{'primary'}{$pkg})) {
+ print $taxonomy->{'by-package'}{'primary'}{$pkg}, "\n";
+ } else {
+ print "(none found)\n";
+ }
+ print "secondary characterization: ";
+ if (defined($taxonomy->{'by-package'}{'secondary'}{$pkg})) {
+ print $taxonomy->{'by-package'}{'secondary'}{$pkg}, "\n";
+ } else {
+ print "(none found)\n";
+ }
+ }
+ if ($opts{"list"}) {
+ if ($tlp->category eq "Collection" || $tlp->category eq "Scheme") {
+ # in the case of collections of schemes we list the deps
+ my @deps = $tlp->depends;
+ if (@deps) {
+ print "depends:\n";
+ for my $d (@deps) {
+ print "\t$d\n";
+ }
+ }
+ }
+ print "Included files, by type:\n";
+ # if the package has a .ARCH dependency we also list the files for
+ # those packages
+ my @todo = $tlpdb->expand_dependencies("-only-arch", $tlpdb, ($pkg));
+ for my $d (sort @todo) {
+ my $foo = $tlpdb->get_package($d);
+ if (!$foo) {
+ tlwarn ("\nShould not happen, no dependent package $d");
+ next;
+ }
+ if ($d ne $pkg) {
+ print "depending package $d:\n";
+ }
+ if ($foo->runfiles) {
+ print "run files:\n";
+ for my $f (sort $foo->runfiles) { print " $f\n"; }
+ }
+ if ($foo->srcfiles) {
+ print "source files:\n";
+ for my $f (sort $foo->srcfiles) { print " $f\n"; }
+ }
+ if ($foo->docfiles) {
+ print "doc files:\n";
+ for my $f (sort $foo->docfiles) {
+ print " $f";
+ my $dfd = $foo->docfiledata;
+ if (defined($dfd->{$f})) {
+ for my $k (keys %{$dfd->{$f}}) {
+ print " $k=\"", $dfd->{$f}->{$k}, '"';
+ }
+ }
+ print "\n";
+ }
+ }
+ # in case we have them
+ if ($foo->allbinfiles) {
+ print "bin files (all platforms):\n";
+ for my $f (sort $foo->allbinfiles) { print " $f\n"; }
+ }
+ }
+ }
+ print "\n";
+ } else {
+ printf STDERR "tlmgr: cannot find $pkg\n";
+ }
+ }
+ return;
+}
+
+# PATH
+#
+sub action_path {
+ my $what = shift @ARGV;
+ if (!defined($what) || ($what !~ m/^(add|remove)$/i)) {
+ tlwarn("tlmgr: action path needs one argument, either add or remove\n");
+ return;
+ }
+ init_local_db();
+ my $winadminmode = 0;
+ if (win32()) {
+ #
+ # for w32 we do system wide vs user setting detection as follows:
+ # - if --w32mode is NOT given,
+ # - if admin
+ # --> honor opt_w32_multi_user setting in tlpdb
+ # - if not admin
+ # - if opt_w32_multi_user == NO
+ # --> do user path adjustment
+ # - if opt_w32_multi_user == YES
+ # --> do nothing, warn that the setting is on, suggest --w32mode user
+ # - if --w32mode admin
+ # - if admin
+ # --> ignore opt_w32_multi_user and do system path adjustment
+ # - if non-admin
+ # --> do nothing but warn that user does not have privileges
+ # - if --w32mode user
+ # - if admin
+ # --> ignore opt_w32_multi_user and do user path adjustment
+ # - if non-admin
+ # --> ignore opt_w32_multi_user and do user path adjustment
+ if (!$opts{"w32mode"}) {
+ $winadminmode = $localtlmedia->tlpdb->option("w32_multi_user");
+ if (!TeXLive::TLWinGoo::admin()) {
+ if ($winadminmode) {
+ tlwarn("The TLPDB specifies system wide path adjustments\nbut you don't have admin privileges.\nFor user path adjustment please use\n\t--w32mode user\n");
+ # and do nothing
+ return;
+ }
+ }
+ } else {
+ # we are in the block where a --w32mode argument is given
+ # we reverse the tests:
+ if (TeXLive::TLWinGoo::admin()) {
+ # in admin mode we simply use what is given on the cmd line
+ if ($opts{"w32mode"} eq "user") {
+ $winadminmode = 0;
+ } elsif ($opts{"w32mode"} eq "admin") {
+ $winadminmode = 1;
+ } else {
+ tlwarn("Unknown --w32admin mode: $opts{w32mode}, should be 'admin' or 'user'\n");
+ return;
+ }
+ } else {
+ # we are non-admin
+ if ($opts{"w32mode"} eq "user") {
+ $winadminmode = 0;
+ } elsif ($opts{"w32mode"} eq "admin") {
+ tlwarn("You don't have the privileges to work in --w32mode admin\n");
+ return;
+ } else {
+ tlwarn("Unknown --w32admin mode: $opts{w32mode}, should be 'admin' or 'user'\n");
+ return;
+ }
+ }
+ }
+ }
+ if ($what =~ m/^add$/i) {
+ if (win32()) {
+ TeXLive::TLUtils::w32_add_to_path(
+ $localtlmedia->location . "/bin/win32",
+ $winadminmode);
+ TeXLive::TLWinGoo::broadcast_env();
+ } else {
+ TeXLive::TLUtils::add_symlinks($localtlmedia->tlpdb->root,
+ $localtlmedia->platform(),
+ $localtlmedia->tlpdb->option("sys_bin"),
+ $localtlmedia->tlpdb->option("sys_man"),
+ $localtlmedia->tlpdb->option("sys_info"));
+ }
+ } elsif ($what =~ m/^remove$/i) {
+ if (win32()) {
+ TeXLive::TLUtils::w32_remove_from_path(
+ $localtlmedia->location . "/bin/win32",
+ $winadminmode);
+ TeXLive::TLWinGoo::broadcast_env();
+ } else {
+ # remove symlinks
+ TeXLive::TLUtils::remove_symlinks($localtlmedia->tlpdb->root,
+ $localtlmedia->platform(),
+ $localtlmedia->tlpdb->option("sys_bin"),
+ $localtlmedia->tlpdb->option("sys_man"),
+ $localtlmedia->tlpdb->option("sys_info"));
+ }
+ } else {
+ # that should not happen
+ tlwarn("\nShould not happen, action_path what=$what");
+ exit 1;
+ }
+ return;
+}
+
+
+# taxonomy subroutines
+#
+sub load_taxonomy_datafile {
+ init_local_db();
+ my $taxonomy;
+ my $fpath = $localtlpdb->root
+ . "/texmf/scripts/texlive/var/texcatalogue.keywords";
+ if (! -r $fpath) {
+ tlwarn("tlmgr: taxonomy file $fpath not readable: $!\n");
+ return;
+ }
+ if (!open (TAXF, $fpath)) {
+ tlwarn("tlmgr: taxonomy file $fpath cannot be opened: $!\n");
+ return;
+ }
+ # suck in the whole file contents
+ my @foo = <TAXF>;
+ close(TAXF);
+ my $foo = "@foo";
+ no strict "vars";
+ # the no strict "vars" is *ABSOLUTELY* necessary otherwise the file is
+ # not evaluated, no idea why!
+ eval "$foo";
+ use strict "vars";
+ return $taxonomy;
+}
+
+sub walk_level_tree {
+ my $cp = shift;
+ my $prestring = shift;
+ my $print_packages = shift;
+ if (defined($cp->{'_packages_'})) {
+ print "$prestring\n";
+ if ($print_packages) {
+ my @pkgs = sort @{$cp->{'_packages_'}};
+ print "\t@pkgs\n";
+ }
+ }
+ for my $cz (keys %$cp) {
+ if ($cz ne '_packages_') {
+ my $nextstring = "$prestring > $cz";
+ my $np = $cp->{$cz};
+ &walk_level_tree($np,$nextstring);
+ }
+ }
+}
+
+# SEARCH
+#
+sub action_search {
+ my $r = shift @ARGV;
+ my $ret = "";
+ my $tlpdb;
+ my $taxonomy;
+ #
+ if (!$opts{"list"} && (!defined($r) || !$r)) {
+
+ tlwarn("tlmgr: nothing to search for.\n");
+ return;
+ }
+ if ($opts{"extended"}) {
+ tlwarn("tlmgr: sorry, extended searching not implemented by now.\n");
+ return;
+ }
+ # check the arguments
+ my $search_type_nr = 0;
+ $search_type_nr++ if $opts{"file"};
+ $search_type_nr++ if $opts{"taxonomy"};
+ $search_type_nr++ if $opts{"characterization"};
+ $search_type_nr++ if $opts{"functionality"};
+ $search_type_nr++ if $opts{"keyword"};
+ $search_type_nr++ if $opts{"all"};
+ if ($search_type_nr > 1) {
+ tlwarn("tlmgr: please specify only one thing to search for!\n");
+ return;
+ }
+ init_local_db();
+ if ($opts{"global"}) {
+ init_tlmedia();
+ $tlpdb = $tlmediasrc->tlpdb;
+ } else {
+ $tlpdb = $localtlpdb;
+ }
+ my $search_characterization =
+ $opts{"characterization"} || $opts{"taxonomy"} || $opts{"all"};
+ my $search_functionality =
+ $opts{"functionality"} || $opts{"taxonomy"} || $opts{"all"};
+ my $search_keyword =
+ $opts{"keyword"} || $opts{"taxonomy"} || $opts{"all"};
+ my $search_tlpdb =
+ $opts{"all"} ||
+ !($opts{"taxonomy"} || $opts{"characterization"} ||
+ $opts{"functionality"} || $opts{"keyword"});
+ if ($opts{"all"} || $opts{"taxonomy"} || $opts{"characterization"}
+ || $opts{"functionality"} || $opts{"keyword"}) {
+ $taxonomy = load_taxonomy_datafile();
+ if (!defined($taxonomy)) {
+ tlwarn("tlmgr: Cannot load taxonomy file;",
+ " searching/listing for taxonomies not supported.\n");
+ return;
+ }
+ if ($opts{"list"}) {
+ if ($search_keyword) {
+ print "\f Keywords:\n";
+ for (sort keys %{$taxonomy->{'by-taxonomy'}{'keyword'}}) {
+ print "\t$_\n";
+ }
+ print "\n";
+ }
+ if ($search_functionality) {
+ print "\f Functionalities:\n";
+ &walk_level_tree($taxonomy->{'by-taxonomy'}{'functionality'}, "", 0);
+ print "\n";
+ }
+ if ($search_characterization) {
+ # Assume all possible characterizations occur under the primary ones!
+ print "\f Characterizations:\n";
+ &walk_level_tree($taxonomy->{'by-taxonomy'}{'primary'}, "", 0);
+ print "\n";
+ }
+ return;
+ }
+ }
+ foreach my $pkg ($tlpdb->list_packages) {
+ my $tlp = $tlpdb->get_package($pkg);
+ if ($opts{"file"}) {
+ my @files = $tlp->all_files;
+ if ($tlp->relocated) {
+ for (@files) { s:^$RelocPrefix/:$RelocTree/:; }
+ }
+ my @ret = grep(m;$r;, @files);
+ if (@ret) {
+ print "$pkg:\n";
+ foreach (@ret) {
+ print "\t$_\n";
+ }
+ }
+ } else {
+ next if ($pkg =~ m/\./);
+ # the other searching is done together
+ my $t = "";
+ if ($search_keyword) {
+ $t = $t . join('\n', @{$taxonomy->{'by-package'}{'keyword'}{$pkg}})
+ if (defined($taxonomy->{'by-package'}{'keyword'}{$pkg}));
+ }
+ if ($search_functionality) {
+ $t = $t.join('\n', @{$taxonomy->{'by-package'}{'functionality'}{$pkg}})
+ if (defined($taxonomy->{'by-package'}{'functionality'}{$pkg}));
+ }
+ if ($search_characterization) {
+ $t = "$t$taxonomy->{'by-package'}{'primary'}{$pkg}\n"
+ if (defined($taxonomy->{'by-package'}{'primary'}{$pkg}));
+ $t = "$t$taxonomy->{'by-package'}{'secondary'}{$pkg}\n"
+ if (defined($taxonomy->{'by-package'}{'secondary'}{$pkg}));
+ }
+ if ($search_tlpdb) {
+ $t .= "$pkg\n";
+ $t = "$t" . $tlp->shortdesc . "\n"
+ if (defined($tlp->shortdesc));
+ $t = "$t" . $tlp->longdesc . "\n"
+ if (defined($tlp->longdesc));
+ }
+ my $shortdesc = $tlp->shortdesc;
+ $shortdesc |= "";
+ my $pat = $r;
+ if ($opts{"word"}) {
+ $pat = '\W' . $r . '\W';
+ }
+ if ($t =~ m/$pat/i) {
+ $ret .= " $pkg - $shortdesc\n";
+ }
+ }
+ }
+ print $ret;
+ return;
+}
+
+
+# RESTORE
+#
+# read the directory and check what files/package/rev are available
+# for restore
+sub get_available_backups {
+ my $bd = shift;
+ my $do_stat = shift;
+ # initialize the hash(packages) of hash(revisions)
+ my %backups;
+ opendir (DIR, $bd) || die "opendir($bd) failed: $!";
+ my @dirents = readdir (DIR);
+ closedir (DIR) || warn "closedir($bd) failed: $!";
+ #
+ # see below for explanation, this has effects only on W32
+ my $oldwsloppy = ${^WIN32_SLOPPY_STAT};
+ ${^WIN32_SLOPPY_STAT} = 1;
+ #
+ for my $dirent (@dirents) {
+ next if (-d $dirent);
+ next if ($dirent !~ m/^(.*)\.r([0-9]+)\.tar\.xz$/);
+ if (!$do_stat) {
+ $backups{$1}->{$2} = 1;
+ next;
+ }
+ my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
+ $atime,$mtime,$ctime,$blksize,$blocks) = stat("$bd/$dirent");
+ # times: as we want to be portable we try the following times:
+ # - first choice is ctime which hopefully works nicely
+ # - on UFS (OSX) ctime is not supported, so use mtime
+ # furthermore, if we are on W32 we want to be fast and make only
+ # a sloppy stat
+ # for more on that please see man perlport
+ my $usedt = $ctime;
+ if (!$usedt) {
+ # can happen on
+ $usedt = $mtime;
+ }
+ if (!$usedt) {
+ # stat failed, set key to -1 as a sign that there is a backup
+ # but we cannot stat it
+ $backups{$1}->{$2} = -1;
+ } else {
+ $backups{$1}->{$2} = $usedt;
+ }
+ }
+ # reset the original value of the win32 sloppy mode for stating files
+ ${^WIN32_SLOPPY_STAT} = $oldwsloppy;
+ return %backups;
+}
+
+sub restore_one_package {
+ my ($pkg, $rev, $bd) = @_;
+ # first remove the package, then reinstall it
+ # this way we get rid of useless files
+ my $restore_file = "$bd/${pkg}.r${rev}.tar.xz";
+ if (! -r $restore_file) {
+ tlwarn("Cannot read $restore_file, no action taken\n");
+ return;
+ }
+ $localtlmedia->remove_package($pkg);
+ TeXLive::TLMedia->_install_package($restore_file , 0, [] ,$localtlpdb);
+ logpackage("restore: $pkg ($rev)");
+ # now we have to read the .tlpobj file and add it to the DB
+ my $tlpobj = TeXLive::TLPOBJ->new;
+ $tlpobj->from_file($localtlpdb->root . "/tlpkg/tlpobj/$pkg.tlpobj");
+ $localtlpdb->add_tlpobj($tlpobj);
+ TeXLive::TLUtils::announce_execute_actions("enable",
+ $localtlpdb->get_package($pkg));
+ $localtlpdb->save;
+}
+
+sub check_backupdir_selection {
+ my $warntext = "";
+ if ($opts{"backupdir"}) {
+ my $ob = abs_path($opts{"backupdir"});
+ $ob && ($opts{"backupdir"} = $ob);
+ if (! -d $opts{"backupdir"}) {
+ $warntext .= "tlmgr: backupdir argument\n";
+ $warntext .= " $opts{'backupdir'}\n";
+ $warntext .= "is not a directory.\n";
+ return (0, $warntext);
+ }
+ } else {
+ # no argument, check for presence in TLPDB
+ init_local_db(1);
+ $opts{"backupdir"} = norm_tlpdb_path($localtlpdb->option("backupdir"));
+ if (!$opts{"backupdir"}) {
+ return (0, "tlmgr: No way to determine backupdir.\n");
+ }
+ # we are still here, there is something set in tlpdb
+ my $ob = abs_path($opts{"backupdir"});
+ $ob && ($opts{"backupdir"} = $ob);
+ if (! -d $opts{"backupdir"}) {
+ $warntext = "tlmgr: backupdir as set in tlpdb\n";
+ $warntext .= " $opts{'backupdir'}\n";
+ $warntext .= "is not a directory.\n";
+ return (0, $warntext);
+ }
+ }
+ return 1;
+}
+
+sub action_restore {
+ # tlmgr restore [--backupdir dir] --all
+ # restores of all packages found in backupdir the latest version
+ # tlmgr restore --backupdir dir
+ # lists all packages with all revisions
+ # tlmgr restore --backupdir dir pkg
+ # lists all revisions of pkg
+ # tlmgr restore --backupdir dir pkg rev
+ # restores pkg to revision rev
+ # check the backup dir argument
+
+ {
+ my ($a, $b) = check_backupdir_selection();
+ if (!$a) {
+ # in all these cases we want to terminate in the non-gui mode
+ tlwarn($b);
+ tlwarn("Exiting.\n");
+ exit 1;
+ }
+ }
+ info("restore: dry run, no changes will be made\n") if $opts{"dry"};
+
+ # initialize the hash(packages) of hash(revisions), do stat files! (the 1)
+ my %backups = get_available_backups($opts{"backupdir"}, 1);
+ my ($pkg, $rev) = @ARGV;
+ if (defined($pkg) && $opts{"all"}) {
+ tlwarn("Do you want to restore all packages or only $pkg?\n");
+ tlwarn("Terminating.\n");
+ exit 1;
+ }
+ if ($opts{"all"}) {
+ init_local_db(1);
+ return if !check_on_writable();
+ if (!$opts{"force"}) {
+ print "Do you really want to restore all packages to the latest revision found in\n\t$opts{'backupdir'}\n===> (y/N): ";
+ my $yesno = <STDIN>;
+ if ($yesno !~ m/^y(es)?$/i) {
+ print "Ok, cancelling the restore!\n";
+ finish(0);
+ }
+ }
+ for my $p (sort keys %backups) {
+ my @tmp = sort {$b <=> $a} (keys %{$backups{$p}});
+ my $rev = $tmp[0];
+ print "Restoring $p, $rev from $opts{'backupdir'}/${p}.r${rev}.tar.xz\n";
+ if (!$opts{"dry"}) {
+ # first remove the package, then reinstall it
+ # this way we get rid of useless files
+ restore_one_package($p, $rev, $opts{"backupdir"});
+ }
+ }
+ # localtlpdb already saved, so we are finished
+ return;
+ }
+ #
+ # intermediate sub
+ sub report_backup_revdate {
+ my $p = shift;
+ my %revs = @_;
+ my @rs = sort {$b <=> $a} (keys %revs);
+ for my $rs (@rs) {
+ my $dstr;
+ if ($revs{$rs} == -1) {
+ $dstr = "unknown";
+ } else {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
+ localtime($revs{$rs});
+ # localtime returns dates starting from 1900, and the month is 0..11
+ $dstr = sprintf "%04d-%02d-%02d %02d:%02d",
+ $year+1900, $mon+1, $mday, $hour, $min;
+ }
+ print "$rs ($dstr) ";
+ }
+ }
+ # end sub
+ if (!defined($pkg)) {
+ if (keys %backups) {
+ print "Available backups:\n";
+ foreach my $p (sort keys %backups) {
+ print "$p: ";
+ report_backup_revdate($p, %{$backups{$p}});
+ print "\n";
+ }
+ } else {
+ print "No backups available in $opts{'backupdir'}\n";
+ }
+ finish(0);
+ }
+ if (!defined($rev)) {
+ print "Available backups for $pkg: ";
+ report_backup_revdate($pkg, %{$backups{$pkg}});
+ print "\n";
+ finish(0);
+ }
+ # we did arrive here, so we try to restore ...
+ if (defined($backups{$pkg}->{$rev})) {
+ return if !check_on_writable();
+ if (!$opts{"force"}) {
+ print "Do you really want to restore $pkg to revision $rev (y/N): ";
+ my $yesno = <STDIN>;
+ if ($yesno !~ m/^y(es)?$/i) {
+ print "Ok, cancelling the restore!\n";
+ finish(0);
+ }
+ }
+ print "Restoring $pkg, $rev from $opts{'backupdir'}/${pkg}.r${rev}.tar.xz\n";
+ if (!$opts{"dry"}) {
+ init_local_db(1);
+ # first remove the package, then reinstall it
+ # this way we get rid of useless files
+ restore_one_package($pkg, $rev, $opts{"backupdir"});
+ }
+ } else {
+ print "revision $rev for $pkg is not present in $opts{'backupdir'}\n";
+ }
+}
+
+sub action_backup {
+ init_local_db(1);
+ # --clean argument
+ # can be either -1 ... don't clean
+ # 0 ... remove all backups
+ # N ... keep only N backups
+ # that parallels the value of autoclean in the configuration
+ # we have to be careful, because if simply --clean is given, we should
+ # check for the value saved in the tlpdb, and if that is not present
+ # do nothing.
+ # We have set clean to clean:-99 which makes -99 the default value
+ # if only --clean is given without any argument
+ # !defined($opts{"clean"}) -> no --clean given
+ # $opts{"clean"} = -99 -> --clean without argument given, check tlpdb
+ # $opts{"clean"} = -1, 0, N -> --clean=N given, check argument
+ #
+ my $clean_mode = 0;
+ $clean_mode = 1 if defined($opts{"clean"});
+ if ($clean_mode) {
+ if ($opts{"clean"} == -99) {
+ # we need to check the tlpdb
+ my $tlpdb_option = $localtlpdb->option("autobackup");
+ if (!defined($tlpdb_option)) {
+ tlwarn ("--clean given without an argument, but no default clean\n");
+ tlwarn ("mode specified in the tlpdb, terminating.\n");
+ exit 1;
+ }
+ $opts{"clean"} = $tlpdb_option;
+ }
+ # now $opts{"clean"} is something, but maybe not a number, check for
+ # validity
+ if ($opts{"clean"} =~ m/^(-1|[0-9]+)$/) {
+ # get rid of leading zeros etc etc
+ $opts{"clean"} = $opts{"clean"} + 0;
+ } else {
+ tlwarn ("clean mode as specified on the command line or as given by default\n");
+ tlwarn ("must be an integer larger or equal than -1, terminating.\n");
+ exit 1;
+ }
+ }
+ # check the backup dir argument
+ {
+ my ($a, $b) = check_backupdir_selection();
+ if (!$a) {
+ # in all these cases we want to terminate in the non-gui mode
+ tlwarn($b);
+ tlwarn("Exiting.\n");
+ exit 1;
+ }
+ }
+
+ # if we do --clean --all we also want to remove packages that
+ # are not present anymore in the tlpdb, so use the readdir mode
+ # to determine backups
+ if ($opts{"all"} && $clean_mode) {
+ # initialize the hash(packages) of hash(revisions)
+ # no need to stat the files
+ my %backups = get_available_backups($opts{"backupdir"}, 0);
+ init_local_db(1);
+ for my $p (sort keys %backups) {
+ clear_old_backups ($p, $opts{"backupdir"}, $opts{"clean"}, $opts{"dry-run"});
+ }
+ return 1;
+ }
+
+ # in case we are not cleaning or cleaning only specific packages
+ # use the one-by-one mode
+ my @todo;
+ if ($opts{"all"}) {
+ @todo = $localtlpdb->list_packages;
+ } else {
+ @todo = @ARGV;
+ @todo = $localtlpdb->expand_dependencies("-only-arch", $localtlpdb, @todo);
+ }
+ if (!@todo) {
+ printf "tlmgr backup takes either a list of packages or --all\n";
+ }
+ foreach my $pkg (@todo) {
+ if ($clean_mode) {
+ clear_old_backups ($pkg, $opts{"backupdir"}, $opts{"clean"}, $opts{"dry-run"});
+ } else {
+ my $tlp = $localtlpdb->get_package($pkg);
+ info("saving current status of $pkg to $opts{'backupdir'}/${pkg}.r" .
+ $tlp->revision . ".tar.xz\n");
+ if (!$opts{"dry-run"}) {
+ $tlp->make_container("xz", $localtlpdb->root,
+ $opts{"backupdir"}, "${pkg}.r" . $tlp->revision);
+ }
+ }
+ }
+}
+
+# =====================================================================
+# INFRASTRUCTURE UPDATE ON WINDOWS
+# =====================================================================
+# Infrastructure files cannot be updated directly from the
+# tlmgr.pl script due to file locking problem on Windows - files that
+# are in use (either open or executing) cannot be removed or replaced.
+# For that reason the update process is performed by a batch script
+# outside of tlmgr.pl.
+# There are three pieces involved in the update: tlmgr.bat
+# launcher, write_w32_updater subroutine below and a batch
+# updater script. Their roles are as follows:
+# * tlmgr.bat is a watchdog, it launches tlmgr.pl and watches for
+# the updater script that is to be executed. If the updater script
+# exists before tlmgr.pl is launched, it will be removed or
+# tlmgr.bat will abort if it fails to do so. This means that the
+# updater script has to be created by the current invocation of
+# tlmgr.pl. Futhermore, the updater script is renamed from
+# updater-w32 to updater-w32.bat just before it is run, and thus
+# it can be executed only once.
+# * write_w32_updater subroutine in tlmgr.pl prepares the update
+# and writes the updater script. Packages in .xz archives are
+# dowloaded/copied and uncompressed to .tar files. Also .tar
+# backups of the current packages are made. If everything is
+# successful, the update script is created from the template.
+# Otherwise the update is aborted.
+# * updater-w32[.bat] batch script, triggers and executes the actual
+# update. It first restarts itself in a separate instance of cmd.exe
+# (and in a new console window in gui mode) and runs the update
+# from there. The update is run with echo on and all output is
+# logged to a file (or stderr in verbose mode). After successful
+# infrastructure update, tlmgr is optionally restarted if update
+# of other packages is asked for.
+# The infrastructure update itself proceeds as follows:
+# (1) untar all package archives
+# (2) include .tlpobj files into tlpdb
+# (3) print update info to console
+# Any error during (1) or (2) triggers the rollback sequence:
+# (1) print failed update info to console
+# (2) untar all package backups
+# (3) include .tlpobj files (from backup) into tlpdb
+# (4) print restore info to console
+# Any error during (2) or (3) and we go into panic state. At this
+# point there is no guarantee that the installation is still working.
+# There is not much we can do but to print failed restore info and
+# give instructions to download and run 'update-tlmgr-latest.exe'
+# to repair the installation.
+# =====================================================================
+#
+sub write_w32_updater {
+ my ($restart_tlmgr, $ref_files_to_be_removed, @w32_updated) = @_;
+ my @infra_files_to_be_removed = @$ref_files_to_be_removed;
+ # TODO do something with these files TODO
+ my $media = $tlmediasrc->media;
+ my $mediatlpdb = $tlmediasrc->tlpdb;
+ # we have to download/copy also the src/doc files if necessary!
+ my $container_src_split = $mediatlpdb->config_src_container;
+ my $container_doc_split = $mediatlpdb->config_doc_container;
+ # get options about src/doc splitting from $totlpdb
+ # TT: should we use local options to decide about install of doc & src?
+ my $opt_src = $localtlpdb->option("install_srcfiles");
+ my $opt_doc = $localtlpdb->option("install_docfiles");
+ my $root = $localtlpdb->root;
+ my $temp = "$root/temp";
+ my $repo = $mediatlpdb->root . "/$Archive";
+ TeXLive::TLUtils::mkdirhier($temp);
+ tlwarn("Backup option not implemented for infrastructure update.\n") if ($opts{"backup"});
+ if ($media eq 'DVD') {
+ tlwarn("Creating updater from DVD currently not implemented!\n");
+ tlwarn("But it should not be necessary!\n");
+ return 1; # abort
+ }
+ my (@upd_tar, @upd_tlpobj, @upd_info, @rst_tar, @rst_tlpobj, @rst_info);
+ foreach my $pkg (@w32_updated) {
+ my $mediatlp = $mediatlpdb->get_package($pkg);
+ my $localtlp = $localtlpdb->get_package($pkg);
+ my $oldrev = $localtlp->revision;
+ my $newrev = $mediatlp->revision;
+ # we do install documenation files for category Documentation even if
+ # option("install_docfiles") is false
+ my $opt_real_doc = ($mediatlp->category =~ m/documentation/i) ? 1 : $opt_doc;
+ my @pkg_parts = ($pkg);
+ push(@pkg_parts, "$pkg.source") if ($container_src_split && $opt_src && $mediatlp->srcfiles);
+ push(@pkg_parts, "$pkg.doc") if ($container_doc_split && $opt_real_doc && $mediatlp->docfiles);
+ foreach my $pkg_part (@pkg_parts) {
+ push (@upd_tar, "$pkg_part.tar");
+ push (@upd_tlpobj, "tlpkg\\tlpobj\\$pkg_part.tlpobj");
+ }
+ push (@upd_info, "$pkg ^($oldrev -^> $newrev^)");
+ push (@rst_tar, "__BACKUP_$pkg.r$oldrev.tar");
+ push (@rst_tlpobj, "tlpkg\\tlpobj\\$pkg.tlpobj");
+ push (@rst_info, "$pkg ^($oldrev^)");
+ next if ($opts{"dry-run"});
+ # create backup; make_container expects file name in a format: some-name.r[0-9]+
+ my ($size, $md5, $fullname) = $localtlp->make_container("tar", $root, $temp, "__BACKUP_$pkg.r$oldrev");
+ if ($size <= 0) {
+ tlwarn("Creation of backup container of $pkg failed.\n");
+ return 1; # backup failed? abort
+ }
+ foreach my $pkg_part (@pkg_parts) {
+ if ($media eq 'CD') {
+ copy("$repo/$pkg_part.tar.xz", "$temp");
+ } else { # net
+ TeXLive::TLUtils::download_file("$repo/$pkg_part.tar.xz", "$temp/$pkg_part.tar.xz");
+ }
+ # now we should have the file present
+ if (!-r "$temp/$pkg_part.tar.xz") {
+ tlwarn("Couldn't get $pkg_part.tar.xz, that is bad\n");
+ return 1; # abort
+ }
+ # unpack xz archive
+ my $sysret = system("$::progs{'xzdec'} < \"$temp/$pkg_part.tar.xz\" > \"$temp/$pkg_part.tar\"");
+ if ($sysret) {
+ tlwarn("Couldn't unpack $pkg_part.tar.xz\n");
+ return 1; # unpack failed? abort
+ }
+ unlink("$temp/$pkg_part.tar.xz"); # we don't need that archive anymore
+ }
+ }
+
+ # prepare updater script
+ my $respawn_cmd = "cmd.exe /e:on/v:off/d/c";
+ $respawn_cmd = "start /wait $respawn_cmd" if ($::gui_mode);
+ my $gui_pause = ($::gui_mode ? "pause" : "rem");
+ my $upd_log = ($::opt_verbosity ? "STDERR" : '"%~dp0update-self.log"');
+ my $std_handles_redir = ($::opt_verbosity ? "1^>^&2" : "2^>$upd_log 1^>^&2");
+ my $pkg_log = ($packagelogfile ? "\"$packagelogfile\"" : "nul");
+ my $post_update_msg = "You may now close this window.";
+ my $rerun_tlmgr = "rem";
+ if ($restart_tlmgr) {
+ $post_update_msg = "About to restart tlmgr to complete update ...";
+ # quote all arguments for tlmgr restart in case of spaces
+ $rerun_tlmgr = join (" ", map ("\"$_\"", @::SAVEDARGV) );
+ $rerun_tlmgr = "if not errorlevel 1 tlmgr.bat $rerun_tlmgr";
+ }
+ my $batch_script = <<"EOF";
+:: This file is part of an automated update process of
+:: infrastructure files and should not be run standalone.
+:: For more details about the update process see comments
+:: in tlmgr.pl (subroutine write_w32_updater).
+
+ if [%1]==[:doit] goto :doit
+ if not exist "%~dp0tar.exe" goto :notar
+ $respawn_cmd call "%~f0" :doit $std_handles_redir
+ $rerun_tlmgr
+ goto :eof
+
+:notar
+ echo %~nx0: cannot run without "%~dp0tar.exe"
+ findstr "^::" <"%~f0"
+ exit /b 1
+
+:doit
+ set prompt=TL\$G
+ title TeX Live Manager $TeXLive::TLConfig::ReleaseYear Update
+ set PERL5LIB=$root/tlpkg/tlperl/lib
+ >con echo DO NOT CLOSE THIS WINDOW!
+ >con echo TeX Live infrastructure update in progress ...
+ >con echo Detailed command logging to $upd_log
+ chdir /d "%~dp0.."
+ if not errorlevel 1 goto :update
+ >con echo Could not change working directory to "%~dp0.."
+ >con echo Aborting infrastructure update, no changes have been made.
+ >con $gui_pause
+ exit /b 1
+
+:update
+ for %%I in (@upd_tar) do (
+ temp\\tar.exe -xf temp\\%%I
+ if errorlevel 1 goto :rollback
+ )
+ tlpkg\\tlperl\\bin\\perl.exe .\\texmf\\scripts\\texlive\\tlmgr.pl _include_tlpobj @upd_tlpobj
+ if errorlevel 1 goto :rollback
+ >>$pkg_log echo [%date% %time%] self update: @upd_info
+ >con echo self update: @upd_info
+ del "%~dp0*.tar" "%~dp0tar.exe"
+ >con echo Infrastructure update finished successfully.
+ >con echo $post_update_msg
+ >con $gui_pause
+ exit /b 0
+
+:rollback
+ >>$pkg_log echo [%date% %time%] failed self update: @upd_info
+ >con echo failed self update: @upd_info
+ >con echo Rolling back to previous version ...
+ for %%I in (@rst_tar) do (
+ temp\\tar.exe -xf temp\\%%I
+ if errorlevel 1 goto :panic
+ )
+ tlpkg\\tlperl\\bin\\perl.exe .\\texmf\\scripts\\texlive\\tlmgr.pl _include_tlpobj @rst_tlpobj
+ if errorlevel 1 goto :panic
+ >>$pkg_log echo [%date% %time%] self restore: @rst_info
+ >con echo self restore: @rst_info
+ >con echo Infrastructure update failed. Previous version has been restored.
+ >con $gui_pause
+ exit /b 1
+
+:panic
+ >>$pkg_log echo [%date% %time%] failed self restore: @rst_info
+ >con echo failed self restore: @rst_info
+ >con echo FATAL ERROR:
+ >con echo Infrastructure update failed and backup recovery failed too.
+ >con echo To repair your TeX Live installation download and run:
+ >con echo $TeXLive::TLConfig::TeXLiveURL/update-tlmgr-latest.exe
+ >con $gui_pause
+ exit /b 666
+EOF
+
+ ddebug("\n:: UPDATER BATCH SCRIPT ::\n$batch_script\n:: END OF FILE ::\n");
+ if ($opts{"dry-run"}) {
+ my $upd_info = "self update: @upd_info";
+ $upd_info =~ s/\^//g;
+ info($upd_info);
+ } else {
+ copy("$root/tlpkg/installer/tar.exe", "$temp");
+ # make sure copied tar is working
+ if (system("\"$temp/tar.exe\" --version >nul")) {
+ tlwarn("Could not copy tar.exe, that is bad.\n");
+ return 1; # abort
+ }
+ open UPDATER, ">$temp/updater-w32" or die "Cannot create updater script: $!";
+ print UPDATER $batch_script;
+ close UPDATER;
+ }
+ return 0;
+}
+
+
+# UPDATE
+
+# compute the list of auto-install, auto-remove, forcibly-removed
+# packages from the list of packages to be installed
+# the list of packages passed in is already expanded
+sub auto_remove_install_force_packages {
+ my @todo = @_;
+ my %removals_full;
+ my %forcermpkgs_full;
+ my %newpkgs_full;
+ my %new_pkgs_due_forcerm_coll;
+ # check for new/removed/forcibly removed packages.
+ # we start from the list of installed collections in the local tlpdb
+ # which are also present in the remote database
+ # and expand this list once with expand_dependencies in the local tlpdb
+ # and once in the tlmedia tlpdb. Then we compare the lists
+ # let A = set of local expansions
+ # B = set of remote expansions
+ # then we should(?) have
+ # B \ A set of new packages
+ # A \ B set of packages removed on the server
+ # A \cup B set of packages which should be checked for forcible removal
+ #
+ my @all_schmscolls = ();
+ for my $p ($localtlpdb->schemes) {
+ push (@all_schmscolls, $p) if defined($tlmediatlpdb->get_package($p));
+ }
+ for my $p ($localtlpdb->collections) {
+ push (@all_schmscolls, $p) if defined($tlmediatlpdb->get_package($p));
+ }
+ my @localexpansion_full =
+ $localtlpdb->expand_dependencies($localtlpdb, @all_schmscolls);
+ my @remoteexpansion_full =
+ $tlmediatlpdb->expand_dependencies($localtlpdb, @all_schmscolls);
+
+ # compute new/remove/forcerm based on the full expansions
+ for my $p (@remoteexpansion_full) {
+ $newpkgs_full{$p} = 1;
+ }
+ for my $p (@localexpansion_full) {
+ delete($newpkgs_full{$p});
+ $removals_full{$p} = 1;
+ }
+ for my $p (@remoteexpansion_full) {
+ delete($removals_full{$p});
+ }
+ # in a first round we check only for forcibly removed collections
+ # this is necessary to NOT declare a package that is contained
+ # in a forcibly removed collections as auto-install since it appears
+ # in the @remoteexpansion_full, but not in @localexpansion_full.
+ for my $p (@localexpansion_full) {
+ # intersection, don't check A\B and B\A
+ next if $newpkgs_full{$p};
+ next if $removals_full{$p};
+ next if ($tlmediatlpdb->get_package($p)->category ne "Collection");
+ my $tlp = $localtlpdb->get_package($p);
+ if (!defined($tlp)) {
+ if ($opts{"reinstall-forcibly-removed"}) {
+ $newpkgs_full{$p} = 1;
+ } else {
+ $forcermpkgs_full{$p} = 1;
+ }
+ }
+ }
+ # now we have in %forcermpkgs_full only collections that have been
+ # forcibly removed. Again, expand those against the remote tlpdb
+ # and remove the expanded packages from the list of localexpansion.
+ my @pkgs_from_forcerm_colls =
+ $tlmediatlpdb->expand_dependencies($localtlpdb, keys %forcermpkgs_full);
+ #
+ # the package in @pkgs_from_forcerm_colls would be auto-installed, so
+ # check for that:
+ for my $p (keys %newpkgs_full) {
+ if (member($p, @pkgs_from_forcerm_colls)) {
+ delete $newpkgs_full{$p};
+ $new_pkgs_due_forcerm_coll{$p} = 1;
+ }
+ }
+ #
+ # now create the final list of forcerm packages by checking against
+ # all packages
+ for my $p (@localexpansion_full) {
+ # intersection, don't check A\B and B\A
+ next if $newpkgs_full{$p};
+ next if $removals_full{$p};
+ my $tlp = $localtlpdb->get_package($p);
+ if (!defined($tlp)) {
+ if ($opts{"reinstall-forcibly-removed"}) {
+ $newpkgs_full{$p} = 1;
+ } else {
+ $forcermpkgs_full{$p} = 1;
+ }
+ }
+ }
+ #
+ # for some packages (texworks, psview, ...) we only have w32 packages
+ # in the repository, but it is possible that alternative repositories
+ # ship binaries for some platforms (like texworks for linux on tlcontrib)
+ # currently updating from tlnet will remove these alternative .ARCH
+ # packages because they are not listed anywhere locally, so they
+ # are considered as disappearing.
+ # We remove here packages PKG.ARCH if the main package PKG is found
+ # here and is *not* disappearing, from the removal hash
+ for my $p (keys %removals_full) {
+ if ($p =~ m/^([^.]*)\./) {
+ my $mpkg = $1;
+ if (!defined($removals_full{$mpkg})) {
+ delete($removals_full{$p});
+ }
+ }
+ }
+ #
+ # now take only the subset of packages that is in @todo
+ # note that @todo is already expanded in action_updated according
+ # to the --no-depends and --no-depends-at-all options
+ #
+ my %removals;
+ my %forcermpkgs;
+ my %newpkgs;
+ for my $p (@todo) {
+ $removals{$p} = 1 if defined($removals_full{$p});
+ $forcermpkgs{$p} = 1 if defined($forcermpkgs_full{$p});
+ $newpkgs{$p} = 1 if defined($newpkgs_full{$p});
+ }
+ debug ("tlmgr: new pkgs: " . join("\n\t",keys %newpkgs) . "\n");
+ debug ("tlmgr: deleted : " . join("\n\t",keys %removals) . "\n");
+ debug ("tlmgr: forced : " . join("\n\t",keys %forcermpkgs) . "\n");
+
+ return (\%removals, \%newpkgs, \%forcermpkgs, \%new_pkgs_due_forcerm_coll);
+}
+
+# tlmgr update foo
+# if foo is of type Package|Documentation it will update only foo
+# and the respective .ARCH dependencies
+# if foo is of type Collection|Scheme it will update itself AND
+# will check all depending packs of type NOT(COllection|Scheme)
+# for necessary updates
+#
+# tlmgr update --no-depends foo
+# as above, but will not check for depends of Collections/Schemes
+# but it will still update .ARCH deps
+#
+# tlmgr update --no-depends-at-all foo
+# will absolutely only update foo not even taking .ARCH into account
+#
+# TLMedia->install_package INSTALLS ONLY ONE PACKAGE, no deps whatsoever
+# anymore. That has all to be done by hand.
+#
+sub machine_line {
+ my ($flag1) = @_;
+ my $ret = 0;
+ if ($flag1 eq "-ret") {
+ $ret = 1;
+ shift;
+ }
+ my ($pkg, $flag, $lrev, $rrev, @args) = @_;
+ $lrev ||= "-";
+ $rrev ||= "-";
+ $flag ||= "?";
+ my $str = "$pkg\t$flag\t$lrev\t$rrev\t";
+ $str .= join("\t", @args) if (@args);
+ $str .= "\n";
+ return($str) if $ret;
+ print $str;
+}
+
+sub action_update {
+ init_local_db(1);
+ $opts{"no-depends"} = 1 if $opts{"no-depends-at-all"};
+
+ # make a quick check on command line arguments to avoid loading
+ # the remote db uselessly.
+ # we require:
+ # if no --list is given: either --self or --all or <pkgs>
+ # if --list is given: nothing
+ # other options just change the behaviour
+ if (!($opts{"list"} || @ARGV || $opts{"all"} || $opts{"self"})) {
+ tlwarn("tlmgr update: please specify a list of packages, --all, --self, or --list.\n");
+ return;
+ }
+
+ init_tlmedia();
+ info("update: dry run, no changes will be made\n") if $opts{"dry-run"};
+
+ my @excluded_pkgs = ();
+ if ($opts{"exclude"}) {
+ @excluded_pkgs = @{$opts{"exclude"}};
+ }
+
+ if (!$opts{"list"}) {
+ return if !check_on_writable();
+ }
+
+ # check for updates to tlmgr and die unless either --force or --list or --self
+ # is given
+ my @critical = check_for_critical_updates($localtlpdb, $tlmediatlpdb);
+ my $dry_run_cont = $opts{"dry-run"} && ($opts{"dry-run"} < 0);
+ if ( !$dry_run_cont && !$opts{"self"} && @critical) {
+ critical_updates_warning();
+ if ($opts{"force"}) {
+ tlwarn("$0: Continuing due to --force.\n");
+ } elsif ($opts{"list"}) {
+ # do not warn here
+ } else {
+ if ($::gui_mode) {
+ # return here and don't do any updates
+ return;
+ } else {
+ die "$0: Exiting, please read above warning.\n";
+ }
+ }
+ }
+
+ my $autobackup = 0;
+ # check for the tlpdb option autobackup, and if present and true (!= 0)
+ # assume we are doing backups
+ if (!$opts{"backup"}) {
+ $autobackup = $localtlpdb->option("autobackup");
+ if ($autobackup) {
+ # check the format, we currently allow only natural numbers, and -1
+ if ($autobackup eq "-1") {
+ debug ("Automatic backups activated, keeping all backups.\n");
+ $opts{"backup"} = 1;
+ } elsif ($autobackup eq "0") {
+ debug ("Automatic backups disabled.\n");
+ } elsif ($autobackup =~ m/^[0-9]+$/) {
+ debug ("Automatic backups activated, keeping $autobackup backups.\n");
+ $opts{"backup"} = 1;
+ } else {
+ tlwarn ("Option autobackup can only be an integer >= -1.\n");
+ tlwarn ("Disabling auto backups.\n");
+ $localtlpdb->option("autobackup", 0);
+ $autobackup = 0;
+ }
+ }
+ }
+
+ # cmd line --backup, we check for --backupdir, and if that is not given
+ # we try to get the default from the tlpdb. If that doesn't work, exit.
+ if ($opts{"backup"}) {
+ my ($a, $b) = check_backupdir_selection();
+ if (!$a) {
+ # in all these cases we want to terminate in the non-gui mode
+ tlwarn($b);
+ tlwarn("Exiting.\n");
+ exit 1;
+ }
+ }
+
+ # finally, if we have --backupdir, but no --backup, just enable it
+ $opts{"backup"} = 1 if $opts{"backupdir"};
+
+ info("tlmgr: saving backups to $opts{'backupdir'}\n")
+ if $opts{"backup"} && !$::machinereadable;
+
+ # these two variables are used throughout this function
+ my $root = $localtlpdb->root;
+ my $temp = "$root/temp";
+
+ # remove old _BACKUP packages that have piled up in temp
+ # they can be recognized by their name starting with __BACKUP_
+ for my $f (<$temp/__BACKUP_*>) {
+ unlink($f) unless $opts{"dry-run"};
+ }
+
+
+ my @todo;
+ if ($opts{"list"}) {
+ if ($opts{"all"}) {
+ @todo = $localtlpdb->list_packages;
+ } elsif ($opts{"self"}) {
+ @todo = @critical;
+ } else {
+ if (@ARGV) {
+ @todo = @ARGV;
+ } else {
+ @todo = $localtlpdb->list_packages;
+ }
+ }
+ } elsif ($opts{"self"} && @critical) {
+ @todo = @critical;
+ } elsif ($opts{"all"}) {
+ @todo = $localtlpdb->list_packages;
+ } else {
+ @todo = @ARGV;
+ }
+ # don't do anything if we have been invoced in a strange way
+ if (!@todo) {
+ if ($opts{"self"}) {
+ info("tlmgr: no updates for tlmgr present.\n");
+ } else {
+ tlwarn("tlmgr update: please specify a list of packages, --all, or --self.\n");
+ }
+ }
+
+ if (!($opts{"self"} && @critical) || ($opts{"self"} && $opts{"list"})) {
+ # update all .ARCH dependencies, too, unless $opts{"no-depends-at-all"}:
+ @todo = $tlmediatlpdb->expand_dependencies("-only-arch", $localtlpdb, @todo)
+ unless $opts{"no-depends-at-all"};
+ #
+ # update general deps unless $opts{"no-depends"}:
+ @todo = $tlmediatlpdb->expand_dependencies("-no-collections",$localtlpdb,@todo)
+ unless $opts{"no-depends"};
+ #
+ # filter out critical packages
+ @todo = grep (!m/$CriticalPackagesRegexp/, @todo)
+ unless $opts{"list"};
+ }
+
+ my ($remref, $newref, $forref, $new_due_to_forcerm_coll_ref) =
+ auto_remove_install_force_packages(@todo);
+ my %removals = %$remref;
+ my %forcermpkgs = %$forref;
+ my %newpkgs = %$newref;
+ my %new_due_to_forcerm_coll = %$new_due_to_forcerm_coll_ref;
+
+ # check that the --exclude options do not conflict with the
+ # options --no-auto-remove, --no-auto-install, --reinstall-forcibly-removed
+ my @option_conflict_lines = ();
+ my $in_conflict = 0;
+ if (!$opts{"no-auto-remove"}) {
+ for my $pkg (keys %removals) {
+ for my $ep (@excluded_pkgs) {
+ if ($pkg eq $ep || $pkg =~ m/^$ep\./) {
+ push @option_conflict_lines, "$pkg: excluded but scheduled for auto-removal\n";
+ $in_conflict = 1;
+ last; # of the --exclude for loop
+ }
+ }
+ }
+ }
+ if (!$opts{"no-auto-install"}) {
+ for my $pkg (keys %newpkgs) {
+ for my $ep (@excluded_pkgs) {
+ if ($pkg eq $ep || $pkg =~ m/^$ep\./) {
+ push @option_conflict_lines, "$pkg: excluded but scheduled for auto-install\n";
+ $in_conflict = 1;
+ last; # of the --exclude for loop
+ }
+ }
+ }
+ }
+ if ($opts{"reinstall-forcibly-removed"}) {
+ for my $pkg (keys %forcermpkgs) {
+ for my $ep (@excluded_pkgs) {
+ if ($pkg eq $ep || $pkg =~ m/^$ep\./) {
+ push @option_conflict_lines, "$pkg: excluded but scheduled for reinstall\n";
+ $in_conflict = 1;
+ last; # of the --exclude for loop
+ }
+ }
+ }
+ }
+ if ($in_conflict) {
+ tlwarn("Conflicts have been found:\n");
+ for (@option_conflict_lines) { tlwarn(" $_"); }
+ tlwarn("Please resolve these conflicts!\n");
+ exit 1;
+ }
+
+ #
+ # we first collect the list of packages to be actually updated or installed
+ my %updated;
+ my @new;
+ my @addlines;
+
+ TODO: foreach my $pkg (sort @todo) {
+ next if ($pkg =~ m/^00texlive/);
+ for my $ep (@excluded_pkgs) {
+ if ($pkg eq $ep || $pkg =~ m/^$ep\./) {
+ info("Skipping excluded package $pkg\n");
+ next TODO;
+ }
+ }
+ my $tlp = $localtlpdb->get_package($pkg);
+ if (!defined($tlp)) {
+ # if the user has forcibly removed (say) bin-makeindex, then the
+ # loop above has no way to add bin-makeindex.ARCH into the
+ # %forcermpkgs hash, but the .ARCH will still be in the dependency
+ # expansion. So try both with and without the .ARCH extension.
+ (my $pkg_noarch = $pkg) =~ s/\.[^.]*$//;
+ my $forcerm_coll = $forcermpkgs{$pkg} || $forcermpkgs{$pkg_noarch};
+
+ # similarly for new packages. If latexmk is new, latexmk.ARCH
+ # will be in the dependency expansion, and we want it.
+ my $newpkg_coll = $newpkgs{$pkg} || $newpkgs{$pkg_noarch};
+ if ($forcerm_coll) {
+ if ($::machinereadable) {
+ # TODO should we add a revision number
+ push @addlines,
+ machine_line("-ret", $pkg, $FLAG_FORCIBLE_REMOVED, "-", "-", "-");
+ } else {
+ info("skipping forcibly removed package $pkg\n");
+ }
+ next;
+ } elsif ($newpkg_coll) {
+ # do nothing here, it will be reported below.
+ } elsif (defined($removals{$pkg})) {
+ # skipping this package, it has been removed due to server removal
+ # and has already been removed
+ next;
+ } elsif (defined($new_due_to_forcerm_coll{$pkg})) {
+ debug("tlmgr: $pkg seems to be contained in a forcibly removed" .
+ " collection, not auto-installing it!\n");
+ next;
+ } else {
+ tlwarn("\ntlmgr: $pkg mentioned, but neither new nor forcibly removed\n");
+ next;
+ }
+ # install new packages
+ my $mediatlp = $tlmediatlpdb->get_package($pkg);
+ if (!defined($mediatlp)) {
+ tlwarn("\nShould not happen: $pkg not found in $location");
+ next;
+ }
+ my $mediarev = $mediatlp->revision;
+ push @new, $pkg;
+ next;
+ }
+ my $rev = $tlp->revision;
+ my $mediatlp = $tlmediatlpdb->get_package($pkg);
+ if (!defined($mediatlp)) {
+ debug("$pkg cannot be found in $location\n");
+ next;
+ }
+ my $mediarev = $mediatlp->revision;
+ if ($rev < $mediarev) {
+ $updated{$pkg} = 0; # will be changed to one on successful update
+ } elsif ($rev > $mediarev) {
+ if ($::machinereadable) {
+ push @addlines,
+ machine_line("-ret", $pkg, $FLAG_REVERSED_UPDATE, $rev, $mediarev, "-");
+ } else {
+ info("$pkg: local: $rev, source: $mediarev (reverse, keep)\n");
+ }
+ }
+ }
+ my @updated = sort keys %updated;
+ for my $i (sort @new) {
+ debug("$i new package\n");
+ }
+ for my $i (@updated) {
+ debug("$i upd package\n");
+ }
+
+ # number calculation
+ # without w32 special packages, those are dealt with in the updater batch
+ # script
+ my $totalnr = $#updated + 1;
+ my @alltodo = @updated;
+ my $nrupdated = 0;
+ my $currnr = 1;
+
+ # we have to remove all the stuff before we install other packages
+ # to support moving of files from one package to another.
+ # remove the packages that have disappeared:
+ # we add that only to the list of total packages do be worked on
+ # when --all is given, because we remove packages only on --all
+ if (!$opts{"no-auto-remove"}) {
+ my @foo = keys %removals;
+ $totalnr += $#foo + 1;
+ }
+ if (!$opts{"no-auto-install"}) {
+ $totalnr += $#new + 1;
+ push @alltodo, @new;
+ }
+
+ # sizes_of_packages returns the sizes of *all* packages if nothing
+ # is passed over, so if @new and @updated both are empty we will
+ # get something wrong back, namely the total size of all packages
+ my %sizes;
+ if (@alltodo) {
+ %sizes = %{$tlmediatlpdb->sizes_of_packages(
+ $localtlpdb->option("install_srcfiles"),
+ $localtlpdb->option("install_docfiles"), @alltodo)};
+ } else {
+ $sizes{'__TOTAL__'} = 0;
+ }
+
+ print "total-bytes\t$sizes{'__TOTAL__'}\n" if $::machinereadable;
+ print "end-of-header\n" if $::machinereadable;
+
+ # print deferred machine-readable lines after the header
+ for (@addlines) { print; }
+
+ #
+ # compute the list of moved files from %removals, @new, @updated
+ #
+ my %do_warn_on_move;
+ {
+ # keep all these vars local to this block
+ my @removals = keys %removals;
+ my %old_files_to_pkgs;
+ my %new_files_to_pkgs;
+ # first save for each file in the currently installed packages
+ # to be updated the packages it is contained it (might be more!)
+ #
+ for my $p (@updated, @removals) {
+ my $pkg = $localtlpdb->get_package($p);
+ tlwarn("Should not happen: $p not found in local tlpdb\n") if (!$pkg);
+ next;
+ for my $f ($pkg->all_files) {
+ push @{$old_files_to_pkgs{$f}}, $p;
+ }
+ }
+ for my $p (@updated, @new) {
+ my $pkg = $tlmediatlpdb->get_package($p);
+ tlwarn("Should not happen: $p not found in $location\n") if (!$pkg);
+ next;
+ for my $f ($pkg->all_files) {
+ if ($pkg->relocated) {
+ $f =~ s:^$RelocPrefix/:$RelocTree/:;
+ }
+ push @{$new_files_to_pkgs{$f}}, $p;
+ }
+ }
+ #
+ # the idea of supressing warnings is simply that if a file is present
+ # in more than one package either in the beginning or after a full
+ # update then this should give a warning. In all other cases
+ # the warning should be supressed.
+ for my $f (keys %old_files_to_pkgs) {
+ my @a = @{$old_files_to_pkgs{$f}};
+ $do_warn_on_move{$f} = 1 if ($#a > 0)
+ }
+ for my $f (keys %new_files_to_pkgs) {
+ my @a = @{$new_files_to_pkgs{$f}};
+ $do_warn_on_move{$f} = 1 if ($#a > 0)
+ }
+ }
+
+
+ #
+ # ORDER OF PACKAGE ACTIONS
+ # 1. removals
+ # 2. updates
+ # 3. auto-install
+ # that way if a file has been moved from one to another package it
+ # removing the old version after the new package has been installed
+ # will not give a warning about files being included somewhere else
+ #
+
+ #
+ # REMOVALS
+ #
+ for my $p (keys %removals) {
+ if ($opts{"no-auto-remove"}) {
+ info("not removing $p due to -no-auto-remove (removed on server)\n");
+ } else {
+ &ddebug("removing package $p\n");
+ my $pkg = $localtlmedia->tlpdb->get_package($p);
+ if (! $pkg) {
+ # This happened when a collection was removed by the user,
+ # and then renamed on the server, e.g., collection-langarab ->
+ # collection-langarabic; Luecking report 20 July 2009.
+ &ddebug(" get_package($p) failed, ignoring");
+ next;
+ }
+ my $rev = $pkg->revision;
+ if ($opts{"list"}) {
+ if ($::machinereadable) {
+ machine_line($p, $FLAG_REMOVE, $rev, "-", "-", "-");
+ } else {
+ info("$p: local: $rev, source: <absent> (auto-remove)\n");
+ }
+ $currnr++;
+ } else {
+ # new we are sure that:
+ # - $opts{"no-auto-remove"} is *not* set
+ # - $opts{"list"} is *not* set
+ # we have to check in addition that
+ # - $opts{"dry-run"} is not set
+ if ($::machinereadable) {
+ machine_line($p, $FLAG_REMOVE, $rev, "-", "-", "-");
+ } else {
+ info("[$currnr/$totalnr] auto-remove: $p\n");
+ }
+ if (!$opts{"dry-run"}) {
+ if ($opts{"backup"}) {
+ $pkg->make_container("xz", $root,
+ $opts{"backupdir"},
+ "${p}.r" . $pkg->revision,
+ $pkg->relocated);
+ if ($autobackup) {
+ # in case we do auto backups we remove older backups
+ clear_old_backups($p, $opts{"backupdir"}, $autobackup);
+ }
+ }
+ $localtlmedia->remove_package($p);
+ logpackage("remove: $p");
+ }
+ $currnr++;
+ }
+ }
+ }
+
+
+ my $starttime = time();
+ my $donesize = 0;
+ my $totalsize = $sizes{'__TOTAL__'};
+
+
+ #
+ # UPDATES AND NEW PACKAGES
+ #
+ # order:
+ # - update normal packages
+ # - install new normal packages
+ # - update collections
+ # - install new collections
+ # - update schemes
+ # - install new schemes (? will not happen?)
+ #
+ # this makes sure that only if all depending packages are installed
+ # the collection is updated, which in turn makes sure that
+ # if the installation of a new package does break it will not be
+ # counted as forcibly removed later on.
+ #
+ my @inst_packs;
+ my @inst_colls;
+ my @inst_schemes;
+ for my $pkg (@updated) {
+ # we do name checking here, not to load all tlpobj again and again
+ if ($pkg =~ m/^scheme-/) {
+ push @inst_schemes, $pkg;
+ } elsif ($pkg =~ m/^collection-/) {
+ push @inst_colls, $pkg;
+ } else {
+ push @inst_packs, $pkg;
+ }
+ }
+ @inst_packs = sort packagecmp @inst_packs;
+
+ my @new_packs;
+ my @new_colls;
+ my @new_schemes;
+ for my $pkg (sort @new) {
+ # we do name checking here, not to load all tlpobj again and again
+ if ($pkg =~ m/^scheme-/) {
+ push @new_schemes, $pkg;
+ } elsif ($pkg =~ m/^collection-/) {
+ push @new_colls, $pkg;
+ } else {
+ push @new_packs, $pkg;
+ }
+ }
+ @new_packs = sort packagecmp @new_packs;
+ my %is_new;
+ for my $pkg (@new_packs, @new_colls, @new_schemes) {
+ $is_new{$pkg} = 1;
+ }
+
+ #
+ # TODO idea
+ # currently this big loop contains a long if then for new packages
+ # and updated package. That *could* be merged into one so that
+ # some things like the logging has not been written two times.
+ # OTOH, the control flow in the "new package" part is much simpler
+ # and following it after the change would make it much harder
+ #
+ foreach my $pkg (@inst_packs, @new_packs, @inst_colls, @new_colls, @inst_schemes, @new_schemes) {
+
+ if (!$is_new{$pkg}) {
+ # skip this loop if infra update on w32
+ next if ($pkg =~ m/^00texlive/);
+ my $tlp = $localtlpdb->get_package($pkg);
+ # we checked already that this package is present!
+ # but our checks seem to be wrong, no idea why
+ # ahhh, it seems that it can happen due to a stupid incident, a bug
+ # on the server:
+ # - remove a package from a collection
+ # - at the same time increase its version number
+ # then what happens is:
+ # - first the package is removed (auto-remove!)
+ # - then it is tried to be updated here, which is not working!
+ # report that and ask for report
+ if (!defined($tlp)) {
+ tlwarn("tlmgr: inconsistency on the server:\n");
+ tlwarn("tlmgr: tlp for package $pkg cannot be found, please report.\n");
+ next;
+ }
+ my $unwind_package;
+ my $remove_unwind_container = 0;
+ my $rev = $tlp->revision;
+ my $mediatlp = $tlmediatlpdb->get_package($pkg);
+ if (!defined($mediatlp)) {
+ debug("$pkg cannot be found in $location\n");
+ next;
+ }
+ my $mediarev = $mediatlp->revision;
+ $nrupdated++;
+ if ($opts{"list"}) {
+ if ($::machinereadable) {
+ machine_line($pkg, $FLAG_UPDATE, $rev, $mediarev, $sizes{$pkg});
+ } else {
+ my $kb = int($sizes{$pkg} / 1024) + 1;
+ info("$pkg [${kb}k]: local: $rev, source: $mediarev (update)\n");
+ }
+ $updated{$pkg} = 1;
+ next;
+ } elsif (win32() && ($pkg =~ m/$CriticalPackagesRegexp/)) {
+ # we pretend that the update happened
+ # in order to calculate file changes properly
+ $updated{$pkg} = 1;
+ next;
+ }
+
+ # older tlmgr forgot to clear the relocated bit when saving a tlpobj
+ # into the local tlpdb, although the paths were rewritten.
+ # We have to clear this bit otherwise the make_container calls below
+ # for creating an unwind container will create some rubbish
+ # TODO for user mode we should NOT clear this bit!
+ if ($tlp->relocated) {
+ debug("tlmgr: warn, relocated bit set for $pkg, but that is wrong!\n");
+ $tlp->relocated(0);
+ }
+
+ if ($opts{"backup"} && !$opts{"dry-run"}) {
+ $tlp->make_container("xz", $root,
+ $opts{"backupdir"}, "${pkg}.r" . $tlp->revision,
+ $tlp->relocated);
+ $unwind_package =
+ "$opts{'backupdir'}/${pkg}.r" . $tlp->revision . ".tar.xz";
+
+ if ($autobackup) {
+ # in case we do auto backups we remove older backups
+ clear_old_backups($pkg, $opts{"backupdir"}, $autobackup);
+ }
+ }
+
+ my ($estrem, $esttot);
+ if (!$opts{"list"}) {
+ ($estrem, $esttot) = TeXLive::TLUtils::time_estimate($totalsize,
+ $donesize, $starttime);
+ }
+
+ if ($::machinereadable) {
+ machine_line($pkg, $FLAG_UPDATE, $rev, $mediarev, $sizes{$pkg}, $estrem, $esttot);
+ } else {
+ my $kb = int ($sizes{$pkg} / 1024) + 1;
+ info("[$currnr/$totalnr, $estrem/$esttot] update: $pkg [${kb}k] ($rev -> $mediarev)");
+ }
+ $donesize += $sizes{$pkg};
+ $currnr++;
+
+ if ($opts{"dry-run"}) {
+ info("\n") unless $::machinereadable;
+ $updated{$pkg} = 1;
+ next;
+ } else {
+ info(" ... ") unless $::machinereadable; # more to come
+ }
+
+ if (!$unwind_package) {
+ # no backup was made, so let us create a temporary .tar file
+ # of the package
+ my $tlp = $localtlpdb->get_package($pkg);
+ my ($s, $m, $fullname) = $tlp->make_container("tar", $root, $temp,
+ "__BACKUP_${pkg}.r" . $tlp->revision,
+ $tlp->relocated);
+ if ($s <= 0) {
+ tlwarn("\n$0: Creation of backup container of $pkg failed.\n");
+ tlwarn("Continuing to update other packages, please retry...\n");
+ # we should try to update other packages at least
+ next;
+ }
+ $remove_unwind_container = 1;
+ $unwind_package = "$fullname";
+ }
+ # first remove the package, then reinstall it
+ # this way we get rid of useless files
+ # force the deinstallation since we will reinstall it
+ #
+ # the remove_package should also remove empty dirs in case
+ # a dir is changed into a file
+ if ($pkg =~ m/$CriticalPackagesRegexp/) {
+ debug("Not removing critical package $pkg\n");
+ } else {
+ $localtlmedia->remove_package($pkg,
+ "remove-warn-files" => \%do_warn_on_move);
+ }
+ if ($tlmediasrc->install_package($pkg, $localtlpdb)) {
+ # installation succeeded because we got a reference
+ logpackage("update: $pkg ($rev -> $mediarev)");
+ unlink($unwind_package) if $remove_unwind_container;
+ # remember successful update
+ $updated{$pkg} = 1;
+ #
+ # if we updated a .ARCH package we have to announce the postactions
+ # of the parent package so that formats are rebuild
+ if ($pkg =~ m/^([^.]*)\./) {
+ my $parent = $1;
+ if (!TeXLive::TLUtils::member($parent, @inst_packs, @new_packs, @inst_colls, @new_colls, @inst_schemes, @new_schemes)) {
+ # ok, nothing happens with the parent package, so we have to
+ # find it and execute the postactions
+ my $parentobj = $localtlpdb->get_package($parent);
+ if (!defined($parentobj)) {
+ # well, in this case we might have hit a package that only
+ # has .ARCH package, like psv.win32, so do nothing
+ debug("tlmgr: .ARCH package without parent, not announcing postaction\n");
+ } else {
+ debug("tlmgr: announcing parent execute action for $pkg\n");
+ TeXLive::TLUtils::announce_execute_actions("enable", $parentobj);
+ }
+ }
+ }
+ } else {
+ # install_package returned a scalar, so error.
+ # now in fact we should do some cleanup, removing files and
+ # dirs from the new package before re-installing the old one.
+ # TODO
+ logpackage("failed update: $pkg ($rev -> $mediarev)");
+ tlwarn("\n\nInstallation of new version of $pkg did fail, trying to unwind.\n");
+ if (win32()) {
+ # w32 is notorious for not releasing a file immediately
+ # we experienced permission denied errors
+ my $newname = $unwind_package;
+ $newname =~ s/__BACKUP/___BACKUP/;
+ copy ("-f", $unwind_package, $newname);
+ # try to remove the file if has been created by us
+ unlink($unwind_package) if $remove_unwind_container;
+ # and make sure that the temporary file is removed in any case
+ $remove_unwind_container = 1;
+ $unwind_package = $newname;
+ }
+ my $instret = TeXLive::TLMedia->_install_package("$unwind_package", 0,
+ [], $localtlpdb);
+ if ($instret) {
+ # now we have to include the tlpobj
+ my $tlpobj = TeXLive::TLPOBJ->new;
+ $tlpobj->from_file($root . "/tlpkg/tlpobj/$pkg.tlpobj");
+ $localtlpdb->add_tlpobj($tlpobj);
+ $localtlpdb->save;
+ logpackage("restore: $pkg ($rev)");
+ tlwarn("Restoring old package state succeeded.\n");
+ } else {
+ logpackage("failed restore: $pkg ($rev)");
+ tlwarn("Restoring of old package did NOT succeed.\n");
+ tlwarn("Most likely repair: run tlmgr install $pkg and hope.\n");
+ }
+ unlink($unwind_package) if $remove_unwind_container;
+ }
+ info("done\n") unless $::machinereadable;
+ } else { # $is_new{$pkg} is true!!!
+ #
+ # NEW PACKAGES
+ #
+ if ($opts{"no-auto-install"}) {
+ info("not auto-installing $pkg due to -no-auto-install (new on server)\n")
+ unless $::machinereadable;
+ } else {
+ # install new packages
+ my $mediatlp = $tlmediatlpdb->get_package($pkg);
+ if (!defined($mediatlp)) {
+ tlwarn("\nShould not happen: $pkg not found in $location");
+ next;
+ }
+ my $mediarev = $mediatlp->revision;
+ my ($estrem, $esttot);
+ if (!$opts{"list"}) {
+ ($estrem, $esttot) = TeXLive::TLUtils::time_estimate($totalsize,
+ $donesize, $starttime);
+ }
+ if ($::machinereadable) {
+ my @maargs = ($pkg, $FLAG_AUTOINSTALL, "-", $mediatlp->revision, $sizes{$pkg});
+ if (!$opts{"list"}) {
+ push @maargs, $estrem, $esttot;
+ }
+ machine_line(@maargs);
+ } else {
+ my $kb = int($sizes{$pkg} / 1024) + 1;
+ if ($opts{"list"}) {
+ info("$pkg [${kb}k]: local: <absent>, source: " . $mediatlp->revision . " (auto-install)\n");
+ } else {
+ info("[$currnr/$totalnr, $estrem/$esttot] auto-install: $pkg [${kb}k]\n");
+ }
+ }
+ $currnr++;
+ $donesize += $sizes{$pkg};
+ next if ($opts{"dry-run"} || $opts{"list"});
+ if ($tlmediasrc->install_package($pkg, $localtlpdb)) {
+ # installation succeeded because we got a reference
+ logpackage("auto-install new: $pkg ($mediarev)");
+ $nrupdated++;
+ } else {
+ tlwarn("$0: couldn't install new package $pkg\n");
+ }
+ }
+ }
+ }
+
+
+ print "end-of-updates\n" if $::machinereadable;
+
+ #
+ # check that if updates to the critical packages are present all of
+ # them have been successfully updated
+ my $infra_update_done = 1;
+ my @infra_files_to_be_removed;
+ if ($opts{"list"}) {
+ $infra_update_done = 0;
+ } else {
+ for my $pkg (@critical) {
+ next unless (defined($updated{$pkg}));
+ $infra_update_done &&= $updated{$pkg};
+ my $oldtlp;
+ my $newtlp;
+ if ($updated{$pkg}) {
+ $oldtlp = $localtlpdb->get_package($pkg);
+ $newtlp = $tlmediatlpdb->get_package($pkg);
+ } else {
+ # update failed but we could introduce new files, that
+ # should be removed now as a part of restoring backup
+ $oldtlp = $tlmediatlpdb->get_package($pkg);
+ $newtlp = $localtlpdb->get_package($pkg);
+ }
+ die ("That shouldn't happen: $pkg not found in tlpdb") if !defined($newtlp);
+ die ("That shouldn't happen: $pkg not found in tlpdb") if !defined($oldtlp);
+ my @old_infra_files = $oldtlp->all_files;
+ my @new_infra_files = $newtlp->all_files;
+ my %del_files;
+ @del_files{@old_infra_files} = ();
+ delete @del_files{@new_infra_files};
+ for my $k (keys %del_files) {
+ my @found_pkgs = $localtlpdb->find_file($k);
+ if ($#found_pkgs >= 0) {
+ my $bad_file = 1;
+ if (win32()) {
+ # on win32 the packages have not been removed already,
+ # so we check that the only package listed in @found_pkgs
+ # is the one we are working on ($pkg)
+ if ($#found_pkgs == 0 && $found_pkgs[0] =~ m/^$pkg:/) {
+ # only one package has been returned and it
+ # matches the current package followed by a colon
+ # remember the TLPDB->find_file returns
+ # $pkg:$file
+ # in this case we can ignore it
+ $bad_file = 0;
+ }
+ }
+ if ($bad_file) {
+ tlwarn("The file $k has disappeared from the critical" .
+ "package $pkg but is still present in @found_pkgs\n");
+ } else {
+ push @infra_files_to_be_removed, $k;
+ }
+ } else {
+ push @infra_files_to_be_removed, $k;
+ }
+ }
+ }
+
+ if (!win32()) {
+ for my $f (@infra_files_to_be_removed) {
+ # TODO actually unlink the stuff
+ #unlink("$Master/$f");
+ debug("removing disappearing file $f\n");
+ }
+ }
+ } # end of if ($opts{"list"}) ... else part
+
+ # check if any additional updates are asked for
+ my $other_updates_asked_for = 0;
+ if ($opts{"all"}) {
+ $other_updates_asked_for = 1;
+ } else {
+ foreach my $p (@ARGV) {
+ if ($p !~ m/$CriticalPackagesRegexp/) {
+ $other_updates_asked_for = 1;
+ last;
+ }
+ }
+ }
+
+ my $restart_tlmgr = 0;
+ if ($opts{"self"} && @critical &&
+ $infra_update_done && $other_updates_asked_for) {
+ # weed out the --self argument from the saved arguments
+ @::SAVEDARGV = grep (!m/^-?-self$/, @::SAVEDARGV);
+ $restart_tlmgr = 1;
+ }
+
+ # infra update and tlmgr restart on win32 is done by the updater batch script
+ if (win32() && !$opts{"list"} && @critical) {
+ info("Preparing TeX Live infrastructure update...\n");
+ for my $f (@infra_files_to_be_removed) {
+ debug("file scheduled for removal $f\n");
+ }
+ my $ret = write_w32_updater($restart_tlmgr,
+ \@infra_files_to_be_removed, @critical);
+ if ($ret) {
+ tlwarn ("Aborting infrastructure update.\n");
+ $restart_tlmgr = 0 if ($opts{"dry-run"});
+ }
+ }
+
+ # only when we are not dry-running we restart the program
+ if (!win32() && $restart_tlmgr && !$opts{"dry-run"} && !$opts{"list"}) {
+ info ("Restarting tlmgr to complete update ...\n");
+ debug("restarting tlmgr @::SAVEDARGV\n");
+ exec("tlmgr", @::SAVEDARGV);
+ die "cannot restart tlmgr, please retry update";
+ }
+
+ # for --dry-run we cannot restart tlmgr (no way to fake successful infra update)
+ # instead we call action_update() again and signal this by $opts{"dry-run"} = -1
+ if ($opts{"dry-run"} && !$opts{"list"} && $restart_tlmgr) {
+ $opts{"self"} = 0;
+ $opts{"dry-run"} = -1;
+ $localtlmedia = undef;
+ $tlmediatlpdb = undef;
+ info ("Restarting tlmgr to complete update ...\n");
+ action_update();
+ return;
+ }
+
+ # if a real update from default disk location didn't find anything,
+ # warn if nothing is updated.
+ if (!(@new || @updated)) {
+ info("tlmgr: no updates available\n");
+ if ($tlmediasrc->media ne "NET"
+ && !$opts{"dry-run"}
+ && !$opts{"repository"}
+ ) {
+ tlwarn("\nYour installation is set up to look on the disk for updates.\n");
+ tlwarn("To install from the Internet for this one time only, run\n");
+ tlwarn(" tlmgr -repository $TeXLiveURL\n");
+ tlwarn("\nTo change the default for all future updates, run\n");
+ tlwarn(" tlmgr option repository $TeXLiveURL\n\n");
+ }
+ }
+}
+
+
+# INSTALL
+#
+# tlmgr install foo bar baz
+# will create the closure under dependencies of {foo,bar,baz}, i.e. all
+# dependencies recursively down to the last package, and install all
+# the packages that are necessary
+#
+# tlmgr install --no-depends foo bar baz
+# will *only* install these three packages (if they are not already installed
+# but it will STILL INSTALL foo.ARCH if they are necessary.
+#
+# tlmgr install --no-depends-at-all foo bar baz
+# will absolutely only install these three packages, and will not even
+# take .ARCH deps into account
+#
+# tlmgr install --reinstall ...
+# behaves exactely like without --reinstall BUT the following two
+# differences:
+# . dependencies are not expanded from collection to collection, so
+# if you reinstall a collection then all its dependencies of type
+# Package will be reinstalled, too, but not the dependencies on
+# other collection, because that would force the full reinstallation
+# of the whole installation
+# . it does not care for whether a package seems to be installed or
+# not (that is the --reinstall)
+#
+# TLMedia->install_package does ONLY INSTALL ONE PACKAGE, no deps whatsoever
+# anymore! That has all to be done by hand.
+#
+sub action_install {
+ init_local_db(1);
+ return if !check_on_writable();
+ # initialize the TLMedia from $location
+ $opts{"no-depends"} = 1 if $opts{"no-depends-at-all"};
+ init_tlmedia();
+
+ # check for updates to tlmgr itself, and die unless --force is given
+ if (check_for_critical_updates( $localtlpdb, $tlmediatlpdb)) {
+ critical_updates_warning();
+ if ($opts{"force"}) {
+ tlwarn("Continuing due to --force\n");
+ } else {
+ if ($::gui_mode) {
+ # return here and don't do any updates
+ return;
+ } else {
+ die "tlmgr: Not continuing, please see warning above!\n";
+ }
+ }
+ }
+
+ $opts{"no-depends"} = 1 if $opts{"no-depends-at-all"};
+ info("install: dry run, no changes will be made\n") if $opts{"dry-run"};
+
+ my @packs = @ARGV;
+ # first expand the .ARCH dependencies unless $opts{"no-depends-at-all"}
+ @packs = $tlmediatlpdb->expand_dependencies("-only-arch", $localtlpdb, @ARGV) unless $opts{"no-depends-at-all"};
+ # now expand all others unless $opts{"no-depends"}
+ # if $opts{"reinstall"} do not collection->collection dependencies
+ if ($opts{"reinstall"}) {
+ @packs = $tlmediatlpdb->expand_dependencies("-no-collections", $localtlpdb, @packs) unless $opts{"no-depends"};
+ } else {
+ @packs = $tlmediatlpdb->expand_dependencies($localtlpdb, @packs) unless $opts{"no-depends"};
+ }
+ #
+ # installation order of packages:
+ # first all normal packages, then collections, then schemes
+ # isn't already installed, but the collection already updated, it will
+ # be reported as forcibly removed.
+ my @inst_packs;
+ my @inst_colls;
+ my @inst_schemes;
+ for my $pkg (sort @packs) {
+ # we do name checking here, not to load all tlpobj again and again
+ if ($pkg =~ m/^scheme-/) {
+ push @inst_schemes, $pkg;
+ } elsif ($pkg =~ m/^collection-/) {
+ push @inst_colls, $pkg;
+ } else {
+ push @inst_packs, $pkg;
+ }
+ }
+ @inst_packs = sort packagecmp @inst_packs;
+
+ my $starttime = time();
+ # count packages
+ my $totalnr = 0;
+ my %revs;
+ my @todo;
+ for my $pkg (@inst_packs, @inst_colls, @inst_schemes) {
+ my $pkgrev = 0;
+ my $mediatlp = $tlmediatlpdb->get_package($pkg);
+ if (!defined($mediatlp)) {
+ tlwarn("package $pkg not present in package repository.\n");
+ next;
+ }
+ if (defined($localtlpdb->get_package($pkg))) {
+ if ($opts{"reinstall"}) {
+ $totalnr++;
+ $revs{$pkg} = $mediatlp->revision;
+ push @todo, $pkg;
+ } else {
+ debug("already installed: $pkg\n");
+ }
+ } else {
+ $totalnr++;
+ $revs{$pkg} = $mediatlp->revision;
+ push @todo, $pkg;
+ }
+ }
+ # return if there is nothing to install!
+ return if (!@todo);
+
+ my $currnr = 1;
+ my %sizes = %{$tlmediatlpdb->sizes_of_packages(
+ $localtlpdb->option("install_srcfiles"),
+ $localtlpdb->option("install_docfiles"), @todo)};
+ defined($sizes{'__TOTAL__'}) || ($sizes{'__TOTAL__'} = 0);
+ my $totalsize = $sizes{'__TOTAL__'};
+ my $donesize = 0;
+
+ print "total-bytes\t$sizes{'__TOTAL__'}\n" if $::machinereadable;
+ print "end-of-header\n" if $::machinereadable;
+
+ foreach my $pkg (@todo) {
+ my $flag = $FLAG_INSTALL;
+ my $re = "";
+ if (defined($localtlpdb->get_package($pkg))) {
+ if ($opts{"reinstall"}) {
+ $re = "re";
+ $flag = $FLAG_REINSTALL;
+ } else {
+ debug("already installed (but didn't we say that already?): $pkg\n");
+ next;
+ }
+ }
+ my ($estrem, $esttot) = TeXLive::TLUtils::time_estimate($totalsize,
+ $donesize, $starttime);
+ my $kb = int($sizes{$pkg} / 1024) + 1;
+ if ($::machinereadable) {
+ machine_line($pkg, $flag, "-", $revs{$pkg}, $sizes{$pkg}, $estrem, $esttot);
+ } else {
+ info("[$currnr/$totalnr, $estrem/$esttot] ${re}install: $pkg [${kb}k]\n");
+ }
+ if (!$opts{"dry-run"}) {
+ $tlmediasrc->install_package($pkg, $localtlpdb);
+ logpackage("${re}install: $pkg");
+ }
+ $donesize += $sizes{$pkg};
+ $currnr++;
+ }
+ print "end-of-updates\n" if $::machinereadable;
+ if ($opts{"dry-run"}) {
+ # stop here, don't do any postinstall actions
+ return(0);
+ }
+}
+
+sub action_list {
+ init_local_db();
+ # make sure that the @ARGV hash is not changed in case we switch to
+ # show mode
+ my ($what) = @ARGV;
+ if ($what) {
+ # if the argument to list is either 'collection' or 'scheme'
+ # we list them, otherwise we go direct into tlmgr show $pkg mode
+ if ($what !~ m/^(collection|scheme)/i) {
+ tlwarn("(switching to show mode)\n");
+ action_show();
+ return;
+ }
+ } else {
+ $what = "";
+ }
+ my $tlm;
+ if ($opts{"only-installed"}) {
+ $tlm = $localtlmedia;
+ } else {
+ init_tlmedia();
+ $tlm = $tlmediasrc;
+ }
+ my @whattolist;
+ if ($what =~ m/^collection/i) {
+ @whattolist = $tlm->tlpdb->collections;
+ } elsif ($what =~ m/^scheme/i) {
+ @whattolist = $tlm->tlpdb->schemes;
+ } else {
+ @whattolist = $tlm->tlpdb->list_packages;
+ }
+ foreach (@whattolist) {
+ next if ($_ =~ m/^00texlive/);
+ if (defined($localtlpdb->get_package($_))) {
+ print "i ";
+ } else {
+ print " ";
+ }
+ my $foo = $tlm->tlpdb->get_package($_)->shortdesc;
+ print "$_: ", defined($foo) ? $foo : "(shortdesc missing)" , "\n";
+ }
+ return;
+}
+
+
+# OPTION
+#
+sub action_option {
+ my $what = shift @ARGV;
+ $what = "show" unless defined($what);
+ init_local_db();
+ if ($what =~ m/^show$/i) {
+ for my $o (keys %{$localtlpdb->options}) {
+ # ignore some things which are win32 specific
+ next if ($o eq "desktop_integration" && !win32());
+ next if ($o eq "file_assocs" && !win32());
+ next if ($o eq "w32_multi_user" && !win32());
+ if (win32()) {
+ next if ($o =~ m/^sys_/);
+ }
+ if (defined $TLPDBOptions{$o}) {
+ if ($::machinereadable) {
+ print "$TLPDBOptions{$o}->[2]\t", $localtlpdb->option($o), "\n";
+ } else {
+ info("$TLPDBOptions{$o}->[3] ($TLPDBOptions{$o}->[2]): " .
+ $localtlpdb->option($o) . "\n");
+ }
+ } else {
+ tlwarn ("option $o not supported\n");
+ }
+ }
+ } elsif ($what =~ m/^showall$/i) {
+ my %loc = %{$localtlpdb->options};
+ for my $o (sort keys %TLPDBOptions) {
+ if ($::machinereadable) {
+ print "$TLPDBOptions{$o}->[2]\t",
+ (defined($loc{$o}) ? $loc{$o} : "(not set)"), "\n";
+ } else {
+ info("$TLPDBOptions{$o}->[3] ($TLPDBOptions{$o}->[2]): " .
+ (defined($loc{$o}) ? $loc{$o} : "(not set)") . "\n");
+ }
+ }
+ } else {
+ if ($what eq "location") {
+ # rewrite location -> repository
+ $what = "repository";
+ }
+ my $found = 0;
+ for my $opt (keys %TLPDBOptions) {
+ if ($what eq $TLPDBOptions{$opt}->[2]) {
+ $found = 1;
+ # the option argument matches the name
+ my $val = shift @ARGV;
+ if (defined($val)) {
+ return if !check_on_writable();
+ # set new value
+ # here we have to care for some special cases
+ if ($what eq $TLPDBOptions{"location"}->[2]) {
+ # support "ctan" on the cmd line
+ if ($val =~ m/^ctan$/i) {
+ $val = "$TeXLive::TLConfig::TeXLiveURL";
+ }
+ info("tlmgr: setting default package repository to $val\n");
+ $localtlpdb->option($opt, $val);
+ } elsif ($what eq $TLPDBOptions{"backupdir"}->[2]) {
+ info("tlmgr: setting option $what to $val.\n");
+ if (! -d $val) {
+ info("tlmgr: the directory $val does not exists, it has to be created\n");
+ info("tlmgr: before backups can be done automatically.\n");
+ }
+ $localtlpdb->option($opt, $val);
+ } elsif ($what eq $TLPDBOptions{"w32_multi_user"}->[2]) {
+ # when running w32 do not allow that a non-admin users sets
+ # this from false to true
+ my $do_it = 0;
+ if (win32()) {
+ if (admin()) {
+ $do_it = 1;
+ } else {
+ if ($val) {
+ # non admin and tries to set to true, warn
+ tlwarn("tlmgr: non-admin user cannot set $TLPDBOptions{'w32_multi_user'}->[2] option to true\n");
+ } else {
+ $do_it = 1;
+ }
+ }
+ } else {
+ $do_it = 1;
+ }
+ if ($do_it) {
+ if ($val) {
+ info("tlmgr: setting option $what to 1.\n");
+ $localtlpdb->option($opt, 1);
+ } else {
+ info("tlmgr: setting option $what to 0.\n");
+ $localtlpdb->option($opt, 0);
+ }
+ }
+ } else {
+ # default case, switch for different types
+ if ($TLPDBOptions{$opt}->[0] eq "b") {
+ if ($val) {
+ info("tlmgr: setting option $what to 1.\n");
+ $localtlpdb->option($opt, 1);
+ } else {
+ info("tlmgr: setting option $what to 0.\n");
+ $localtlpdb->option($opt, 0);
+ }
+ } elsif ($TLPDBOptions{$opt}->[0] eq "p") {
+ info("tlmgr: setting option $what to $val.\n");
+ $localtlpdb->option($opt, $val);
+ } elsif ($TLPDBOptions{$opt}->[0] eq "u") {
+ info("tlmgr: setting option $what to $val.\n");
+ $localtlpdb->option($opt, $val);
+ } elsif ($TLPDBOptions{$opt}->[0] =~ m/^n(:((-)?\d+)?..((-)?\d+)?)?$/) {
+ my $isgood = 1;
+ my $n = int($val);
+ my $low;
+ my $up;
+ if (defined($1)) {
+ # range given
+ if (defined($2)) {
+ # lower border
+ if ($2 > $n) {
+ tlwarn("value $n for $what out of range ($TLPDBOptions{$opt}->[0])\n");
+ $isgood = 0;
+ }
+ }
+ if (defined($4)) {
+ # upper border
+ if ($4 < $n) {
+ tlwarn("value $n for $what out of range ($TLPDBOptions{$opt}->[0])\n");
+ $isgood = 0;
+ }
+ }
+ }
+ if ($isgood) {
+ info("tlmgr: setting option $what to $n.\n");
+ $localtlpdb->option($opt, $n);
+ }
+ } else {
+ tlwarn ("Unknown type of option $opt: $TLPDBOptions{$opt}->[0]\n");
+ }
+ }
+ $localtlpdb->save;
+ # now also save the TLPOBJ of 00texlive.installation
+ my $tlpo = $localtlpdb->get_package("00texlive.installation");
+ if ($tlpo) {
+ if (open(TOFD, ">$Master/tlpkg/tlpobj/00texlive.installation.tlpobj")) {
+ $tlpo->writeout(\*TOFD);
+ close(TOFD);
+ } else {
+ tlwarn("Cannot save 00texlive.installation to $Master/tlpkg/tlpobj/00texlive.installation.tlpobj\n");
+ }
+ }
+ } else {
+ # show current value
+ if ($::machinereadable) {
+ print "$TLPDBOptions{$opt}->[2]\t", $localtlpdb->option($opt), "\n";
+ } else {
+ info ("$TLPDBOptions{$opt}->[3] ($TLPDBOptions{$opt}->[2]): " .
+ $localtlpdb->option($opt) . "\n");
+ }
+ }
+ last;
+ }
+ }
+ if (!$found) {
+ tlwarn("tlmgr: option $what not supported!\n");
+ }
+ }
+ return;
+}
+
+
+# ARCH
+#
+sub action_platform {
+ my @extra_w32_packs = qw/tlperl.win32 tlgs.win32 tlpsv.win32
+ collection-wintools
+ dviout.win32 wintools.win32/;
+ if ($^O=~/^MSWin(32|64)$/i) {
+ warn("action `platform' not supported on Windows\n");
+ return();
+ }
+ my $what = shift @ARGV;
+ init_local_db(1);
+ info("platform: dry run, no changes will be made\n") if $opts{"dry-run"};
+ $what || ($what = "list");
+ if ($what =~ m/^list$/i) {
+ # list the available platforms
+ # initialize the TLMedia from $location
+ init_tlmedia();
+ my $mediatlpdb = $tlmediasrc->tlpdb;
+ my @already_installed_arch = $localtlpdb->available_architectures;
+ print "Available platforms:\n";
+ foreach my $a ($mediatlpdb->available_architectures) {
+ if (member($a,@already_installed_arch)) {
+ print "(i) $a\n";
+ } else {
+ print " $a\n";
+ }
+ }
+ print "Already installed platforms are marked with (i)\n";
+ print "You can add new platforms with: tlmgr platform add ARCH1 ARCH2...\n";
+ finish(0);
+ } elsif ($what =~ m/^add$/i) {
+ return if !check_on_writable();
+ init_tlmedia();
+ my $mediatlpdb = $tlmediasrc->tlpdb;
+ my @already_installed_arch = $localtlpdb->available_architectures;
+ my @available_arch = $mediatlpdb->available_architectures;
+ my @todoarchs;
+ foreach my $a (@ARGV) {
+ if (TeXLive::TLUtils::member($a, @already_installed_arch)) {
+ print "Platform $a is already installed\n";
+ next;
+ }
+ if (!TeXLive::TLUtils::member($a, @available_arch)) {
+ print "Platform $a not available, use 'tlmgr platform list'!\n";
+ next;
+ }
+ push @todoarchs, $a;
+ }
+ foreach my $pkg ($localtlpdb->list_packages) {
+ next if ($pkg =~ m/^00texlive/);
+ my $tlp = $localtlpdb->get_package($pkg);
+ foreach my $dep ($tlp->depends) {
+ if ($dep =~ m/^(.*)\.ARCH$/) {
+ # we have to install something
+ foreach my $a (@todoarchs) {
+ if ($tlmediatlpdb->get_package("$pkg.$a")) {
+ info("install: $pkg.$a\n");
+ $tlmediasrc->install_package("$pkg.$a", $localtlpdb)
+ if (!$opts{"dry-run"});
+ }
+ }
+ }
+ }
+ }
+ if (TeXLive::TLUtils::member('win32', @todoarchs)) {
+ # install the necessary w32 stuff
+ for my $p (@extra_w32_packs) {
+ info("install: $p\n");
+ $tlmediasrc->install_package($p, $localtlpdb) if (!$opts{"dry-run"});
+ }
+ }
+ # update the option("available_architectures") list of installed archs
+ if (!$opts{"dry-run"}) {
+ my @larchs = $localtlpdb->setting("available_architectures");
+ push @larchs, @todoarchs;
+ $localtlpdb->setting("available_architectures",@larchs);
+ $localtlpdb->save;
+ }
+ } elsif ($what =~ m/^remove$/i) {
+ return if !check_on_writable();
+ my @already_installed_arch = $localtlpdb->available_architectures;
+ my @todoarchs;
+ my $currentarch = $localtlmedia->platform();
+ foreach my $a (@ARGV) {
+ if (!TeXLive::TLUtils::member($a, @already_installed_arch)) {
+ print "Platform $a not installed, use 'tlmgr platform list'!\n";
+ next;
+ }
+ if ($currentarch eq $a) {
+ info("You are running on platform $a, you cannot remove that one!\n");
+ next;
+ }
+ push @todoarchs, $a;
+ }
+ foreach my $pkg ($localtlpdb->list_packages) {
+ next if ($pkg =~ m/^00texlive/);
+ my $tlp = $localtlpdb->get_package($pkg);
+ if (!$tlp) {
+ # that is a package foobar.$a that has already been remove but
+ # is still in the list above, so ignore that
+ next;
+ }
+ foreach my $dep ($tlp->depends) {
+ if ($dep =~ m/^(.*)\.ARCH$/) {
+ # we have to install something
+ foreach my $a (@todoarchs) {
+ if ($localtlpdb->get_package("$pkg.$a")) {
+ info("remove: $pkg.$a\n");
+ $localtlmedia->remove_package("$pkg.$a") if (!$opts{"dry-run"});
+ }
+ }
+ }
+ }
+ }
+ if (TeXLive::TLUtils::member('win32', @todoarchs)) {
+ for my $p (@extra_w32_packs) {
+ info("remove: $p\n");
+ $localtlmedia->remove_package($p) if (!$opts{"dry-run"});
+ }
+ }
+ if (!$opts{"dry-run"}) {
+ # try to remove bin/$a dirs
+ for my $a (@todoarchs) {
+ if (!rmdir("$Master/bin/$a")) {
+ tlwarn("binary directory $Master/bin/$a not empty after removal of $a.\n");
+ }
+ }
+ # update the option("available_architectures") list of installed archs
+ my @larchs = $localtlpdb->setting("available_architectures");
+ my @newarchs;
+ for my $a (@larchs) {
+ push @newarchs, $a if !member($a, @todoarchs);
+ }
+ $localtlpdb->setting("available_architectures",@newarchs);
+ $localtlpdb->save;
+ }
+ } else {
+ die "Unknown option for platform: $what";
+ }
+}
+
+
+# GENERATE
+#
+sub action_generate {
+ my $what = shift @ARGV;
+ init_local_db();
+
+ # we create fmtutil.cnf, language.dat, language.def in TEXMFSYSVAR and
+ # updmap.cfg in TEXMFSYSCONFIG. The reason is that calls to
+ # updmap-sys (as is done by the tlmgr update call when packages with
+ # maps are installed) will create the updmap.cfg file in
+ # TEXMFSYSCONFIG. It would start from the version in TEXMFSYSVAR, but
+ # after that the TEXMFSYSCONFIG takes precedence and thus the
+ # mechanism explained in updmap.cfg header would not work. We don't
+ # want that. So just use TEXMFSYSCONFIG from the start.
+ #
+ chomp (my $TEXMFSYSVAR = `kpsewhich -var-value=TEXMFSYSVAR`);
+ chomp (my $TEXMFSYSCONFIG = `kpsewhich -var-value=TEXMFSYSCONFIG`);
+ chomp (my $TEXMFLOCAL = `kpsewhich -var-value=TEXMFLOCAL`);
+
+ # we do generate all config files, treat $opts{"dest"} as pattern
+ # and make it append the respective extensions
+ my $append_extension = (($opts{"dest"} && ($what eq "language")) ? 1 : 0);
+
+ if ($what =~ m/^language(\.dat|\.def|\.dat\.lua)?$/i) {
+ #
+ # if --rebuild-sys is given *and* --dest we warn that this might not
+ # work if the destination is not the default one
+ if ($opts{"rebuild-sys"} && $opts{"dest"}) {
+ tlwarn("tlmgr generate $what: warning: both --rebuild-sys and --dest\n",
+ "given; the call to fmtutil-sys can fail if the given\n",
+ "destination is different from the default.\n");
+ }
+ #
+ # we have to set TEXMFVAR, TEXMFCONFIG in the environment so that
+ # searching for language.(dat/def) does search in the right place
+ if ($what =~ m/^language(\.dat\.lua)?$/i) {
+ my $dest = $opts{"dest"} ||
+ "$TEXMFSYSVAR/tex/generic/config/language.dat.lua";
+ $dest .= ".dat.lua" if $append_extension;
+ my $localcfg = $opts{"localcfg"} ||
+ "$TEXMFLOCAL/tex/generic/config/language-local.dat.lua";
+ debug("$0: writing language.dat.lua data to $dest\n");
+ TeXLive::TLUtils::create_language_lua($localtlpdb, $dest, $localcfg);
+ if ($opts{"rebuild-sys"}) {
+ do_cmd_and_check("fmtutil-sys --byhyphen $dest");
+ } else {
+ info("To make the newly-generated language.dat take effect,"
+ . " run fmtutil-sys --byhyphen $dest.\n");
+ }
+ }
+ if ($what =~ m/^language(\.dat)?$/i) {
+ my $dest = $opts{"dest"} ||
+ "$TEXMFSYSVAR/tex/generic/config/language.dat";
+ $dest .= ".dat" if $append_extension;
+ my $localcfg = $opts{"localcfg"} ||
+ "$TEXMFLOCAL/tex/generic/config/language-local.dat";
+ debug ("$0: writing language.dat data to $dest\n");
+ TeXLive::TLUtils::create_language_dat($localtlpdb, $dest, $localcfg);
+ if ($opts{"rebuild-sys"}) {
+ do_cmd_and_check("fmtutil-sys --byhyphen $dest");
+ } else {
+ info("To make the newly-generated language.dat take effect,"
+ . " run fmtutil-sys --byhyphen $dest.\n");
+ }
+ }
+ if ($what =~ m/^language(\.def)?$/i) {
+ my $dest = $opts{"dest"} ||
+ "$TEXMFSYSVAR/tex/generic/config/language.def";
+ $dest .= ".def" if $append_extension;
+ my $localcfg = $opts{"localcfg"} ||
+ "$TEXMFLOCAL/tex/generic/config/language-local.def";
+ debug("$0: writing language.def data to $dest\n");
+ TeXLive::TLUtils::create_language_def($localtlpdb, $dest, $localcfg);
+ if ($opts{"rebuild-sys"}) {
+ do_cmd_and_check("fmtutil-sys --byhyphen $dest");
+ } else {
+ info("To make the newly-generated language.def take effect,"
+ . " run fmtutil-sys --byhyphen $dest.\n");
+ }
+ }
+
+ } elsif ($what =~ m/^fmtutil$/i) {
+ my $dest = $opts{"dest"} || "$TEXMFSYSVAR/web2c/fmtutil.cnf";
+ my $localcfg = $opts{"localcfg"} || "$TEXMFLOCAL/web2c/fmtutil-local.cnf";
+ debug("$0: writing new fmtutil.cnf to $dest\n");
+ TeXLive::TLUtils::create_fmtutil($localtlpdb, $dest, $localcfg);
+
+ if ($opts{"rebuild-sys"}) {
+ do_cmd_and_check("fmtutil-sys --all");
+ } else {
+ info("To make the newly-generated fmtutil.cnf take effect,"
+ . " run fmtutil-sys --all.\n");
+ }
+
+ } elsif ($what =~ m/^updmap$/i) {
+ my $dest = $opts{"dest"} || "$TEXMFSYSCONFIG/web2c/updmap.cfg";
+ my $localcfg = $opts{"localcfg"} || "$TEXMFLOCAL/web2c/updmap-local.cfg";
+ debug("$0: writing new updmap.cfg to $dest\n");
+ TeXLive::TLUtils::create_updmap($localtlpdb, $dest, $localcfg);
+
+ if ($opts{"rebuild-sys"}) {
+ do_cmd_and_check("updmap-sys");
+ } else {
+ info("To make the newly-generated updmap.cfg take effect,"
+ . " run updmap-sys.\n");
+ }
+
+ } else {
+ die "$0: Unknown option for generate: $what; try --help if you need it.\n";
+ }
+
+ return;
+}
+
+
+# GUI
+#
+sub action_gui {
+ eval { require Tk; };
+ if ($@) {
+ # that didn't work out, give some usefull error message and stop
+ my $tkmissing = 0;
+ if ($@ =~ /^Can\'t locate Tk\.pm/) {
+ $tkmissing = 1;
+ }
+ if ($tkmissing) {
+ if ($^O=~/^MSWin(32|64)$/i) {
+ # that should not happen, we are shipping Tk!!
+ require Win32;
+ my $msg = "Cannot load Tk, that should not happen as we ship it!\nHow did you start tlmgrgui??\n(Error message: $@)\n";
+ Win32::MsgBox($msg, 1|Win32::MB_ICONSTOP(), "Warning");
+ } else {
+ printf STDERR "
+Cannot load Tk, thus the GUI cannot be started!
+The Perl/Tk module is not shipped with the TeX Live installation.
+You have to install it to get tlmgr GUI running. See
+http://tug.org/texlive/distro.html for more details.
+
+";
+ }
+ } else {
+ printf STDERR "Problem loading Tk: $@\n";
+ }
+ exit 1;
+ }
+
+ # now check that we can actually create a top level window,
+ # on darwin the X server might not be started, or on unix we are working
+ # on a console, or whatever.
+ eval { my $foo = Tk::MainWindow->new; $foo->destroy; };
+ if ($@) {
+ printf STDERR "perl/Tk unusable, cannot create main windows.
+That could be a consequence of not having X Windows installed or started!
+Error message from creating MainWindow:
+ $@
+";
+ exit 1;
+ }
+
+ # be sure that sub actions do *not* finish
+ $::gui_mode = 1;
+ # also unset the $opts{"gui"} to make recursive calls to action_* not starting
+ # another GUI instance (or better trying to ...)
+ $opts{"gui"} = 0;
+
+ require("tlmgrgui.pl");
+ # should not be reached
+ exit(1);
+}
+
+
+# UNINSTALL
+#
+sub action_uninstall {
+ if (win32()) {
+ printf STDERR "Please use \"Add/Remove Programs\" from the Control Panel to removing TeX Live!\n";
+ finish(1);
+ }
+ return if !check_on_writable();
+ my $force = defined($opts{"force"}) ? $opts{"force"} : 0;
+ if (!$force) {
+ print("If you answer yes here the whole TeX Live installation will be removed!\n");
+ print "Remove TeX Live (y/N): ";
+ my $yesno = <STDIN>;
+ if ($yesno !~ m/^y(es)?$/i) {
+ print "Ok, cancelling the removal!\n";
+ finish(0);
+ }
+ }
+ print ("Ok, removing the whole installation:\n");
+ init_local_db();
+ TeXLive::TLUtils::remove_symlinks($localtlmedia->tlpdb->root,
+ $localtlmedia->platform(),
+ $localtlmedia->tlpdb->option("sys_bin"),
+ $localtlmedia->tlpdb->option("sys_man"),
+ $localtlmedia->tlpdb->option("sys_info"));
+ # now do remove the rest
+ system("rm", "-rf", "$Master/texmf-dist");
+ system("rm", "-rf", "$Master/texmf-doc");
+ system("rm", "-rf", "$Master/texmf-var");
+ system("rm", "-rf", "$Master/texmf");
+ system("rm", "-rf", "$Master/tlpkg");
+ system("rm", "-rf", "$Master/bin");
+ system("rm", "-rf", "$Master/readme-html.dir");
+ system("rm", "-rf", "$Master/readme-txt.dir");
+ for my $f (qw/doc.html index.html LICENSE.CTAN LICENSE.TL README
+ README.usergroups release-texlive.txt texmf.cnf/) {
+ system("rm", "-f", "$Master/$f");
+ }
+ if (-d "$Master/temp") {
+ system("rmdir", "--ignore-fail-on-non-empty", "$Master/temp");
+ }
+ unlink("$Master/install-tl.log");
+ # should we do that????
+ system("rm", "-rf", "$Master/texmf-config");
+ system("rmdir", "--ignore-fail-on-non-empty", "$Master");
+}
+
+
+# RECREATE-TLPDB
+#
+sub action_recreate_tlpdb {
+ return if !check_on_writable();
+ my $tlpdb = TeXLive::TLPDB->new;
+ $tlpdb->root($Master);
+ my $inst = TeXLive::TLPOBJ->new;
+ $inst->name("00texlive.installation");
+ $inst->category("TLCore");
+ my @deps;
+ # options are done further down with $tlpdb->reset_options()
+ #for my $k (keys %TeXLive::TLConfig::TLPDBOptions) {
+ # push @deps, "opt_$k:" . $TeXLive::TLConfig::TLPDBOptions{k}->[1];
+ #}
+ # find list of available archs
+ my @archs;
+ opendir (DIR, "$Master/bin") || die "opendir($Master/bin) failed: $!";
+ my @dirents = readdir (DIR);
+ closedir (DIR) || warn "closedir($Master/bin) failed: $!";
+ for my $dirent (@dirents) {
+ next if $dirent eq ".";
+ next if $dirent eq "..";
+ next unless -d "$Master/bin/$dirent";
+ if (-r "$Master/bin/$dirent/kpsewhich" || -r "$Master/bin/$dirent/kpsewhich.exe") {
+ push @archs, $dirent;
+ debug("Skipping directory $Master/bin/$dirent, no kpsewhich there\n");
+ }
+ }
+ push @deps, "setting_available_architectures:" . join(" ",@archs);
+ # we have to find out the default arch
+ # if there is only one dir in $Master/bin then we are settled,
+ # otherwise we expect the user to pass a correct arch string
+ if (!TeXLive::TLUtils::member(TeXLive::TLUtils::platform(), @archs)) {
+ # hmm that is bad, the platform as detected is not in the list
+ # of installed platforms, so the option --arch has to be given
+ # if only one is installed use that one
+ if ($#archs == 0) {
+ # only one arch available, fine, use it as default
+ push @deps, "setting_platform:$archs[0]";
+ } else {
+ if (defined($opts{"platform"})) {
+ if (member($opts{"platform"}, @archs)) {
+ push @deps, "setting_platform:" . $opts{"platform"};
+ } else {
+ tlwarn("The platform you passed in with --platform is not present in $Master/bin\n");
+ tlwarn("Please specify one of the available ones: @archs\n");
+ exit(1);
+ }
+ } else {
+ tlwarn("More than one platform available: @archs\n");
+ tlwarn("Please pass one as the default you are running on with --platform=...\n");
+ exit(1);
+ }
+ }
+ }
+ $inst->depends(@deps);
+ # now we have all the stuff for 00texlive.installation done
+ $tlpdb->add_tlpobj($inst);
+ # reset the options to default values
+ $tlpdb->add_default_options();
+ # check for location == _MASTER_
+ if ($tlpdb->option("location") eq "__MASTER__") {
+ $tlpdb->option("location", $TeXLive::TLConfig::TeXLiveURL);
+ }
+ # add the other stuff in $Master/tlpkg/tlpobj/*.tlpobj
+ # we can ignore *.{source,doc}.tlpobj because they are already
+ # included in the *.tlpobj parent one at install time
+ # (TODO: we should actually REMOVE the *.{source,doc}.tlpobj files
+ # at package install time)
+ opendir (DIR, "$Master/tlpkg/tlpobj") or die "opendir($Master/tlpkg/tlpobj) failed: $!";
+ my @tlps = readdir(DIR);
+ closedir (DIR) || warn "closedir($Master/tlpkg/tlpobj) failed: $!";
+ for my $t (@tlps) {
+ next if -d $t; # also does . and ..
+ next if ($t !~ m/\.tlpobj$/i);
+ # ignore .source and .doc tlpobjs
+ next if ($t =~ m/\.(source|doc)\.tlpobj$/i);
+ my $tlp = TeXLive::TLPOBJ->new;
+ $tlp->from_file("$Master/tlpkg/tlpobj/$t");
+ $tlpdb->add_tlpobj($tlp);
+ }
+ # writeout the re-created tlpdb to stdout
+ $tlpdb->writeout;
+ return;
+}
+
+# CHECK
+#
+sub init_tltree {
+ my $svn = shift;
+ my $Master = $localtlpdb->root;
+ my $tltree = TeXLive::TLTREE->new ("svnroot" => $Master);
+ if ($svn) {
+ debug("Initializine TLTREE from svn\n");
+ $tltree->init_from_svn;
+ } else {
+ debug("Initializine TLTREE from find\n");
+ $tltree->init_from_files;
+ }
+ return($tltree);
+}
+
+sub action_check {
+ my $svn = defined($opts{"use-svn"}) ? $opts{"use-svn"} : 0;
+ my $what = shift @ARGV;
+ $what || ($what = "all");
+ init_local_db();
+ my $ret = 0;
+ if ($what =~ m/^all/i) {
+ my $tltree = init_tltree($svn);
+ print "Running check files:\n";
+ $ret |= check_files($tltree);
+ print "Running check depends:\n";
+ $ret |= check_depends();
+ print "Running check executes:\n";
+ $ret |= check_executes();
+ print "Running check runfiles:\n";
+ $ret |= check_runfiles();
+ } elsif ($what =~ m/^files/i) {
+ my $tltree = init_tltree($svn);
+ $ret |= check_files($tltree);
+ } elsif ($what =~ m/^collections/i) {
+ tlwarn("the \"collections\" check is replaced by the \"depends\" check.\n");
+ $ret |= check_depends();
+ } elsif ($what =~ m/^depends/i) {
+ $ret |= check_depends();
+ } elsif ($what =~ m/^runfiles/i) {
+ $ret |= check_runfiles();
+ } elsif ($what =~ m/^executes/i) {
+ $ret |= check_executes();
+ } else {
+ print "No idea how to check that: $what\n";
+ }
+ finish($ret);
+}
+
+# check file coverage in both direction.
+#
+sub check_files {
+ my $tltree = shift;
+ my $ret = 0;
+ my %filetopacks;
+ my $Master = $localtlpdb->root;
+ debug("Collecting all files of all packages\n");
+ for my $p ($localtlpdb->list_packages()) {
+ # ignore files in the installer
+ next if ($p eq "00texlive.installer");
+ my $tlp = $localtlpdb->get_package($p);
+ my @files = $tlp->all_files;
+ if ($tlp->relocated) {
+ for (@files) { s:^$RelocPrefix/:$RelocTree/:; }
+ }
+ for my $f (@files) {
+ push @{$filetopacks{$f}}, $p;
+ }
+ }
+ my @multiple = ();
+ my @missing = ();
+ debug("Checking for occurrences and existence of all files\n");
+ for (keys %filetopacks) {
+ push @missing, $_ if (! -r "$Master/$_");
+ my @foo = @{$filetopacks{$_}};
+ if ($#foo < 0) {
+ warn "that shouldn't happen: $_\n";
+ } elsif ($#foo > 0) {
+ push @multiple, $_;
+ }
+ }
+ if ($#multiple >= 0) {
+ $ret = 1;
+ print "\f Multiple included files (relative to $Master):\n";
+ for (sort @multiple) {
+ my @foo = @{$filetopacks{$_}};
+ print " $_ (@foo)\n";
+ }
+ print "\n";
+ }
+ if ($#missing >= 0) {
+ $ret = 1;
+ print "\f Files mentioned in tlpdb but missing (relative to $Master):\n";
+ for my $m (@missing) {
+ print "\t$m\n";
+ }
+ print "\n";
+ }
+
+ # if we are on W32, return (no find). We need -use-svn only for
+ # checking the live repository on tug, which is not w32.
+ my $arch = $localtlmedia->platform();
+ return $ret if $arch eq "win32";
+
+ # check that all files in the trees are covered.
+ #
+ my @IgnorePatterns = qw!
+ support/ source/ setuptl/
+ texmf-dist/ls-R$ texmf-doc/ls-R$ texmf/ls-R$
+ tlpkg/tlpsrc tlpkg/bin tlpkg/lib/ tlpkg/libexec tlpkg/tests/ tlpkg/etc
+ tlpkg/texlive.tlpdb
+ tlpkg/tlpobj
+ texmf-var/
+ texmf-config/
+ texmf.cnf
+ install-tl.log
+ tlpkg/texlive.profile
+ tlpkg/installer
+ !;
+ my %tltreefiles = %{$tltree->{'_allfiles'}};
+ my @tlpdbfiles = keys %filetopacks;
+ my @nohit;
+ for my $f (keys %tltreefiles) {
+ # if it is mentioned in the tlpdb or is ignored it is considered
+ # as covered, thus, otherwise we push it onto the nothit list
+ if (!defined($filetopacks{$f})) {
+ my $ignored = 0;
+ for my $p (@IgnorePatterns) {
+ if ($f =~ m/^$p/) {
+ $ignored = 1;
+ last;
+ }
+ }
+ if (!$ignored) {
+ push @nohit, $f;
+ }
+ }
+ }
+ if (@nohit) {
+ $ret = 1;
+ print "\f Files present but not covered (relative to $Master):\n";
+ for my $f (sort @nohit) {
+ print " $f\n";
+ }
+ print "\n";
+ }
+ return($ret);
+}
+
+# check runfiles
+#
+# Check there are no duplicated runtimes files
+sub check_runfiles {
+ my $Master = $localtlpdb->root;
+ # build a list of all runtime files associated to 'normal' packages
+ #
+ (my $non_normal = `ls $Master/bin`) =~ s/\n/\$|/g; # binaries
+ $non_normal .= '^0+texlive|^bin-|^collection-|^scheme-|^texlive-|^texworks';
+ my @runtime_files = ();
+ #
+ foreach my $tlpn ($localtlpdb->list_packages) {
+ next if ($tlpn =~ /$non_normal/);
+ #
+ my $tlp = $localtlpdb->get_package($tlpn);
+ my @files = $tlp->runfiles;
+ if ($tlp->relocated) {
+ for (@files) {
+ s!^$TeXLive::TLConfig::RelocPrefix/!$TeXLive::TLConfig::RelocTree/!;
+ }
+ }
+ # special case for koma-script where doc/src files are in runfiles section
+ if ($tlpn eq "koma-script") {
+ @files = grep {!m;^texmf-dist/source/latex/koma-script/;} @files;
+ @files = grep {!m;^texmf-dist/doc/latex/koma-script/;} @files;
+ }
+ push @runtime_files, @files;
+ }
+
+ # build the duplicates list
+ #
+ my @duplicates = (""); # just to use $duplicates[-1] freely
+ my $prev = "";
+ foreach my $f (sort map { TeXLive::TLUtils::basename($_) } @runtime_files) {
+ push (@duplicates, $f) if (($f eq $prev) and not ($f eq $duplicates[-1]));
+ $prev = $f;
+ }
+ shift @duplicates; # get rid of the fake 1st value
+
+ # @duplicates = ('8r-base.map', 'aer.sty', 'lm-ec.map'); # for debugging
+
+ # check if duplicates are different files
+ #
+ foreach my $f (@duplicates) {
+ # assume tex4ht, xdy, afm stuff is ok, and don't worry about
+ # Changes, README et al. Other per-format versions.
+ next if $f =~ /\.(afm|cfg|4hf|htf|xdy)$/;
+ next if $f =~ /^(Changes|README|language\.dat|(czech|slovak)\.sty)$/;
+ next if $f =~ /^(libertine\.sty|m-tex4ht\.tex|metatex\.tex)$/;
+ next if $f =~ /^(kinsoku\.tex|luatools\.lua|cid2code\.txt|etex\.src)$/;
+ next if $f =~ /^(ps2mfbas\.mf|pstricks\.con|tex4ht\.env)$/;
+ next if $f =~ /^(texutil\.rb|tlmgrgui\.pl)$/;
+ #
+ my @copies = grep (/\/$f$/, @runtime_files);
+ # map files can be duplicated as long as copies don't concern the same engine
+ if ($f =~ /\.map$/) {
+ my $need_check = 0;
+ my $prev_dir = "";
+ my @cop = @copies; # don't break the outside list
+ map { s#^texmf-dist/fonts/map/(.*?)/.*#$1# } @cop;
+ foreach my $dir (sort @cop ) {
+ last if ($need_check = ($dir eq $prev_dir));
+ $prev_dir = $dir;
+ }
+ next unless $need_check;
+ }
+ # if all copies are identical, ok, else, complain
+ my $diff = 0;
+ for (my $i = 1; $i < scalar(@copies); $i++) {
+ if ($diff = tlcmp("$Master/$copies[$i-1]", "$Master/$copies[$i]")) {
+ print "# $f\ndiff $Master/$copies[$i-1] $Master/$copies[$i]\n";
+ last;
+ }
+ }
+ print join ("\n", @copies), "\n" if ($diff and (scalar(@copies) > 2));
+ }
+}
+
+# check executes
+#
+sub check_executes {
+ my $Master = $localtlpdb->root;
+ my (%maps,%langcodes,%fmtlines);
+ for my $pkg ($localtlpdb->list_packages) {
+ for my $e ($localtlpdb->get_package($pkg)->executes) {
+ if ($e =~ m/add(Mixed)?Map\s+(.*)$/) {
+ my $foo = $2;
+ chomp($foo);
+ push @{$maps{$foo}}, $pkg;
+ } elsif ($e =~ m/AddFormat\s+(.*)$/) {
+ my $foo = $1;
+ chomp($foo);
+ push @{$fmtlines{$foo}}, $pkg;
+ } elsif ($e =~ m/AddHyphen\s+.*\s+file=(\S+)(\s*$|\s+.*)/) {
+ my $foo = $1;
+ chomp($foo);
+ push @{$langcodes{$foo}}, $pkg;
+ } else {
+ warn "$pkg: unmatched execute: $e\n";
+ }
+ }
+ }
+ my %badmaps;
+ foreach my $mf (keys %maps) {
+ my @pkgsfound = @{$maps{$mf}};
+ if ($#pkgsfound > 0) {
+ tlwarn ("map file $mf is referenced in the executes of @pkgsfound\n");
+ } else {
+ # less then 1 occurrences is not possible, so we have only one
+ # package that contains the reference to that map file
+ my $pkgfoundexecute = $pkgsfound[0];
+ my @found = $localtlpdb->find_file($mf);
+ if ($#found < 0) {
+ $badmaps{$mf} = $maps{$mf};
+ } elsif ($#found > 0) {
+ # we want to check for multiple inclusions
+ my %mapfn;
+ foreach my $foo (@found) {
+ $foo =~ m/^(.*):(.*)$/;
+ push @{$mapfn{$2}}, $1;
+ }
+ foreach my $k (keys %mapfn) {
+ my @bla = @{$mapfn{$k}};
+ if ($#bla > 0) {
+ tlwarn ("map file $mf occurs multiple times (in pkg @bla)!\n");
+ }
+ }
+ } else {
+ # only one occurrence found, we check that the map is also contained
+ # in the right package!
+ my ($pkgcontained) = ( $found[0] =~ m/^(.*):.*$/ );
+ if ($pkgcontained ne $pkgfoundexecute) {
+ tlwarn("map file $mf: execute in $pkgfoundexecute, map file in $pkgcontained\n");
+ }
+ }
+ }
+ }
+ if (keys %badmaps) {
+ print "mentioned map file not occuring in any package:\n";
+ foreach my $mf (keys %badmaps) {
+ print "\t$mf (execute in @{$badmaps{$mf}})\n";
+ }
+ }
+ my %badhyphcodes;
+ my %problemhyphen;
+ foreach my $lc (keys %langcodes) {
+ next if ($lc eq "zerohyph.tex");
+ my @found = $localtlpdb->find_file("texmf-dist/tex/generic/hyph-utf8/loadhyph/$lc");
+ if ($#found < 0) {
+ # try again this time search all packages
+ my @found = $localtlpdb->find_file("$lc");
+ if ($#found < 0) {
+ $badhyphcodes{$lc} = $langcodes{$lc};
+ } else {
+ $problemhyphen{$lc} = [ @found ];
+ }
+ }
+ }
+ if (keys %badhyphcodes) {
+ print "\f mentioned hyphen loaders without file:\n";
+ foreach my $mf (keys %badhyphcodes) {
+ print "\t$mf (execute in @{$badhyphcodes{$mf}})\n";
+ }
+ }
+ # disable the echoing of problematic hyphens
+ #if (keys %problemhyphen) {
+ # print "hyphen files with possible problematic location:\n";
+ # foreach my $mf (keys %problemhyphen) {
+ # print "\t$mf (@{$problemhyphen{$mf}})\n";
+ # }
+ #}
+ #
+ # what should be checked for the executes? we could check
+ # - the existence of the engine in bin/i386-linux or all $arch
+ # - the existence of the format name link/bat
+ # - parse the options parameter and check for the inifile
+ # - rework the format definition that we have inifile=pdflatex.ini
+ # isn't the * unnecessary?
+ my %missingbins;
+ my %missingengines;
+ my %missinginis;
+ for (keys %fmtlines) {
+ my %r = TeXLive::TLUtils::parse_AddFormat_line("$_");
+ if (defined($r{"error"})) {
+ die "$r{'error'}, parsing $_, package(s) @{$fmtlines{$_}}";
+ }
+ my $opt = $r{"options"};
+ my $engine = $r{"engine"};
+ my $name = $r{"name"};
+ my $mode = $r{"mode"};
+ # special case for cont-en ...
+ next if ($name eq "cont-en");
+ # we check that the name exist in bin/$arch
+ for my $a ($localtlpdb->available_architectures) {
+ my $f = "$Master/bin/$a/$name";
+ if (!check_file($a, $f)) {
+ push @{$missingbins{$_}}, "bin/$a/$name" if $mode;
+ }
+ if (!check_file($a, "$Master/bin/$a/$engine")) {
+ push @{$missingengines{$_}}, "bin/$a/$engine" if $mode;
+ }
+ }
+ # check for the existence of the .ini file
+ # by using the last word in the options value
+ my $inifile = $opt;
+ # $inifile now contains "bla bla bla *file.ini"
+ # strip initial and trailing "
+ $inifile =~ s/^"(.*)"$/$1/;
+ # remove everything before the last space
+ $inifile =~ s/^.* ([^ ]*)$/$1/;
+ # remove the optional leading *
+ $inifile =~ s/^\*//;
+ my @found = $localtlpdb->find_file("$inifile");
+ if ($#found < 0) {
+ $missinginis{$_} = "$inifile";
+ }
+ }
+ if (keys %missinginis) {
+ print "\f mentioned ini files that cannot be found:\n";
+ for my $i (keys %missinginis) {
+ print "\t $missinginis{$i} (execute: $i)\n";
+ }
+ }
+ if (keys %missingengines) {
+ print "\f mentioned engine files that cannot be found:\n";
+ for my $i (keys %missingengines) {
+ print "\t @{$missingengines{$i}}\n";
+ }
+ }
+ if (keys %missingbins) {
+ print "\f mentioned bin files that cannot be found:\n";
+ for my $i (keys %missingbins) {
+ print "\t @{$missingbins{$i}}\n";
+ }
+ }
+}
+
+sub check_file {
+ my ($a, $f) = @_;
+ if (-r $f) {
+ return 1;
+ } else {
+ # not -r, so check for the extensions .bat and .exe on w32 and cygwin
+ if (($a eq "win32") || ($a eq "i386-cygwin")) {
+ if (-r "$f.exe" || -r "$f.bat") {
+ return 1;
+ }
+ }
+ return 0;
+ }
+}
+
+# check depends
+#
+sub check_depends {
+ my $ret = 0;
+ my $Master = $localtlpdb->root;
+ my %presentpkg;
+ for my $pkg ($localtlpdb->list_packages) {
+ $presentpkg{$pkg} = 1;
+ }
+ # list of collections.
+ my @colls = $localtlpdb->collections;
+ my @coll_deps = $localtlpdb->expand_dependencies("-no-collections",$localtlpdb,@colls);
+ my %coll_deps;
+ @coll_deps{@coll_deps} = (); # initialize hash with keys from list
+
+ my (%wrong_dep, @no_dep);
+ for my $pkg ($localtlpdb->list_packages) {
+ # do not check any package starting with 00texlive.
+ next if $pkg =~ m/^00texlive/;
+
+ # For each package, check that it is a dependency of some collection.
+ # Whatever is left in %coll_deps after this loop will be the problem
+ if (! exists $coll_deps{$pkg}) {
+ # Except that schemes and our special .win32 packages are ok.
+ push (@no_dep, $pkg) unless $pkg =~/^scheme-|\.win32$/;
+ }
+
+ # For each dependency, check that we have a package.
+ for my $d ($localtlpdb->get_package($pkg)->depends) {
+ next if ($d =~ m/\.ARCH$/);
+ if (!defined($presentpkg{$d})) {
+ push (@{$wrong_dep{$d}}, $pkg);
+ }
+ }
+ }
+
+ if (keys %wrong_dep) {
+ $ret++;
+ print "\f DEPENDS WITHOUT PACKAGES:\n";
+ for my $d (keys %wrong_dep) {
+ print "$d in: @{$wrong_dep{$d}}\n";
+ }
+ }
+
+ if (@no_dep) {
+ $ret++;
+ print "\f PACKAGES NOT IN ANY COLLECTION: @no_dep\n";
+ }
+
+ return $ret;
+}
+
+# POSTACTION
+# explictly run the various post actions, e.g.,
+# on a client system or overriding global settings.
+#
+# tlmgr postaction [--w32mode=user|admin] [--fileassocmode=1|2] [--all]
+# [install|remove] [shortcut|fileassoc|script] [<package> <package> ...]
+
+sub action_postaction {
+ my $how = shift @ARGV;
+ if (!defined($how) || ($how !~ m/^(install|remove)$/i)) {
+ tlwarn("action postaction needs at least two arguments, first being either 'install' or 'remove'\n");
+ return;
+ }
+ my $type = shift @ARGV;
+ my $badtype = 0;
+ if (!defined($type)) {
+ $badtype = 1;
+ } elsif ($type !~ m/^(shortcut|fileassoc|script)$/i) {
+ $badtype = 1;
+ }
+ if ($badtype) {
+ tlwarn("action postaction needs as second argument one from 'shortcut', 'fileassoc', 'script'\n");
+ return;
+ }
+ if (win32()) {
+ if ($opts{"w32mode"}) {
+ if ($opts{"w32mode"} eq "user") {
+ if (TeXLive::TLWinGoo::admin()) {
+ debug("Switching to user mode on user request\n");
+ TeXLive::TLWinGoo::non_admin();
+ }
+ # in user mode we also switch TEXMFSYSVAR to TEXMFVAR since
+ # xetex.pl, but maybe others are writing to TEXMFSYSVAR
+ chomp($ENV{"TEXMFSYSVAR"} = `kpsewhich -var-value TEXMFVAR`);
+ } elsif ($opts{"w32mode"} eq "admin") {
+ if (!TeXLive::TLWinGoo::admin()) {
+ tlwarn("You don't have the permissions for --w32mode=admin\n");
+ return;
+ }
+ } else {
+ tlwarn("action postaction --w32mode can only be 'admin' or 'user'\n");
+ return;
+ }
+ }
+ }
+ my @todo;
+ if ($opts{"all"}) {
+ init_local_db();
+ @todo = $localtlpdb->list_packages;
+ } else {
+ if ($#ARGV < 0) {
+ tlwarn("action postaction: need either --all or a list of packages\n");
+ return;
+ }
+ init_local_db();
+ @todo = @ARGV;
+ @todo = $localtlpdb->expand_dependencies("-only-arch", $localtlpdb, @todo);
+ }
+ if ($type =~ m/^shortcut$/i) {
+ if (!win32()) {
+ tlwarn("action postaction shortcut only works on windows.\n");
+ return;
+ }
+ for my $p (@todo) {
+ my $tlp = $localtlpdb->get_package($p);
+ if (!defined($tlp)) {
+ tlwarn("$p is not installed, ignoring it.\n");
+ } else {
+ # run all shortcut actions, desktop and menu integration
+ TeXLive::TLUtils::do_postaction($how, $tlp, 0, 1, 1, 0);
+ }
+ }
+ } elsif ($type =~ m/^fileassoc$/i) {
+ if (!win32()) {
+ tlwarn("action postaction fileassoc only works on windows.\n");
+ return;
+ }
+ my $fa = $localtlpdb->option("file_assocs");
+ if ($opts{"fileassocmode"}) {
+ if ($opts{"fileassocmode"} < 1 || $opts{"fileassocmode"} > 2) {
+ tlwarn("action postaction: value of --fileassocmode can only be 1 or 2\n");
+ return;
+ }
+ $fa = $opts{"fileassocmode"};
+ }
+ for my $p (@todo) {
+ my $tlp = $localtlpdb->get_package($p);
+ if (!defined($tlp)) {
+ tlwarn("$p is not installed, ignoring it.\n");
+ } else {
+ TeXLive::TLUtils::do_postaction($how, $tlp, $fa, 0, 0, 0);
+ }
+ }
+ } elsif ($type =~ m/^script$/i) {
+ for my $p (@todo) {
+ my $tlp = $localtlpdb->get_package($p);
+ if (!defined($tlp)) {
+ tlwarn("$p is not installed, ignoring it.\n");
+ } else {
+ TeXLive::TLUtils::do_postaction($how, $tlp, 0, 0, 0, 1);
+ }
+ }
+ } else {
+ tlwarn("action postaction needs one of 'shortcut', 'fileassoc', 'script'\n");
+ return;
+ }
+}
+
+# CONF
+# tries to mimic texconfig conf but can also set values for both tlmgr
+# and texmf conf files.
+#
+sub action_conf {
+ my $arg = shift @ARGV;
+ if (!defined($arg)) {
+ texconfig_conf_mimic();
+ return;
+ }
+ if ($arg eq "tlmgr" || $arg eq "texmf") {
+ my ($fn,$cf);
+ if ($arg eq "tlmgr") {
+ chomp (my $TEXMFCONFIG = `kpsewhich -var-value=TEXMFCONFIG`);
+ $fn = "$TEXMFCONFIG/tlmgr/config";
+ $cf = TeXLive::TLConfFile->new($fn, "#", "=");
+ } else {
+ $fn = "$Master/texmf.cnf";
+ $cf = TeXLive::TLConfFile->new($fn, "[%#]", "=");
+ }
+ my ($key,$val) = @ARGV;
+ if (!defined($key)) {
+ # show all settings
+ if ($cf) {
+ info("$arg configuration values (from $fn):\n");
+ for my $k ($cf->keys) {
+ info("$k = " . $cf->value($k) . "\n");
+ }
+ } else {
+ info("$arg config file $fn not present\n");
+ }
+ } else {
+ if (!defined($val)) {
+ if (defined($cf->value($key))) {
+ info("$arg $key value: " . $cf->value($key) . " ($fn)\n");
+ } else {
+ info("$key not defined in $arg config file ($fn)\n");
+ if ($arg eq "texmf") {
+ # not in user-specific file, show anything kpsewhich gives us.
+ chomp (my $defval = `kpsewhich -var-value $key`);
+ if ($? != 0) {
+ info("$arg $key default value is unknown");
+ } else {
+ info("$arg $key default value: $defval");
+ }
+ info(" (kpsewhich -var-value)\n");
+ }
+ }
+ } else {
+ info("setting $arg $key to $val (in $fn)\n");
+ $cf->value($key, $val);
+ }
+ }
+ if ($cf->is_changed) {
+ $cf->save;
+ }
+ } else {
+ warn "tlmgr: unknown conf arg: $arg (try tlmgr or texmf)\n";
+ }
+}
+
+# output various values in same form as texconfig conf.
+sub texconfig_conf_mimic {
+ my $PATH = $ENV{'PATH'};
+ info("=========================== version information ==========================\n");
+ info(give_version());
+ info("==================== executables found by searching PATH =================\n");
+ info("PATH: $PATH\n");
+ for my $cmd (qw/kpsewhich updmap fmtutil tlmgr tex pdftex mktexpk
+ dvips dvipdfmx/) {
+ info("$cmd: " . TeXLive::TLUtils::which($cmd) . "\n");
+ }
+ info("=========================== active config files ==========================\n");
+ for my $m (qw/texmf.cnf updmap.cfg fmtutil.cnf config.ps mktex.cnf
+ pdftexconfig.tex/) {
+ info("$m: " . `kpsewhich $m`);
+ }
+
+ #tlwarn("missing finding of XDvi, config!\n");
+
+ info("============================= font map files =============================\n");
+ for my $m (qw/psfonts.map pdftex.map ps2pk.map dvipdfm.map/) {
+ info("$m: " . `kpsewhich $m`);
+ }
+
+ info("=========================== kpathsea variables ===========================\n");
+ for my $v (qw/TEXMFMAIN TEXMFDIST TEXMFLOCAL TEXMFSYSVAR TEXMFSYSCONFIG TEXMFVAR TEXMFCONFIG TEXMFHOME VARTEXFONTS TEXMF SYSTEXMF TEXMFDBS WEB2C TEXPSHEADERS TEXCONFIG ENCFONTS TEXFONTMAPS/) {
+ info("$v=" . `kpsewhich -var-value=$v`);
+ }
+
+ info("==== kpathsea variables from environment only (ok if no output here) ====\n");
+ my @envVars = qw/
+ AFMFONTS BIBINPUTS BSTINPUTS CMAPFONTS CWEBINPUTS ENCFONTS GFFONTS
+ GLYPHFONTS INDEXSTYLE LIGFONTS MFBASES MFINPUTS MFPOOL MFTINPUTS
+ MISCFONTS MPINPUTS MPMEMS MPPOOL MPSUPPORT OCPINPUTS OFMFONTS
+ OPENTYPEFONTS OPLFONTS OTPINPUTS OVFFONTS OVPFONTS PDFTEXCONFIG PKFONTS
+ PSHEADERS SFDFONTS T1FONTS T1INPUTS T42FONTS TEXBIB TEXCONFIG TEXDOCS
+ TEXFONTMAPS TEXFONTS TEXFORMATS TEXINDEXSTYLE TEXINPUTS TEXMFCNF
+ TEXMFDBS TEXMFINI TEXMFSCRIPTS TEXPICTS TEXPKS TEXPOOL TEXPSHEADERS
+ TEXSOURCES TFMFONTS TRFONTS TTFONTS VFFONTS WEB2C WEBINPUTS
+ /;
+ for my $v (@envVars) {
+ if (defined($ENV{$v})) {
+ info("$v=$ENV{$v}\n");
+ }
+ }
+}
+
+
+# Subroutines galore.
+#
+# set global $location variable.
+# if we cannot read tlpdb, die if arg SHOULD_I_DIE is true.
+#
+# if an argument is given and is true init_local_db will die if
+# setting up of programs failed.
+#
+sub init_local_db {
+ my ($should_i_die) = @_;
+ # if the localtlpdb is already defined do simply return here already
+ # to make sure that the settings in the local tlpdb do not overwrite
+ # stuff changed via the GUI
+ return if defined $localtlmedia;
+ return if defined $localtlpdb;
+ $localtlmedia = TeXLive::TLMedia->new ( $Master );
+ die("cannot setup TLMedia in $Master") unless (defined($localtlmedia));
+ $localtlpdb = $localtlmedia->tlpdb;
+ die("cannot find tlpdb in $Master") unless (defined($localtlpdb));
+ # setup the programs, for w32 we need the shipped wget/xz etc, so we
+ # pass the location of these files to setup_programs.
+ if (!setup_programs("$Master/tlpkg/installer", $localtlmedia->platform)) {
+ tlwarn("Couldn't set up the necessary programs.\nInstallation of packages is not supported.\nPlease report to texlive\@tug.org.\n");
+ if (defined($should_i_die) && $should_i_die) {
+ finish(1);
+ } else {
+ tlwarn("Continuing anyway ...\n");
+ }
+ }
+ # let cmd line options override the settings in localtlpdb
+ my $loc = norm_tlpdb_path($localtlpdb->option("location"));
+ if (defined($loc)) {
+ $location = $loc;
+ }
+ if (defined($opts{"location"})) {
+ $location = $opts{"location"};
+ }
+ if (!defined($location)) {
+ die("No installation source found, nor in the texlive.tlpdb nor on the cmd line.\nPlease specify one!");
+ }
+ if ($location =~ m/^ctan$/i) {
+ $location = "$TeXLive::TLConfig::TeXLiveURL";
+ }
+ # we normalize the path only if it is
+ # - neither a URL starting with http or ftp
+ # - if we are on windows it does not start with Drive:[\/]
+ if (! ( $location =~ m!^(http|ftp)://!i ||
+ (win32() && (!(-e $location) || ($location =~ m!^.:[\\/]!) ) ) ) ) {
+ # seems to be a local path, try to normalize it
+ my $testloc = abs_path($location);
+ # however, if we were given a url, that will get "normalized" to the
+ # empty string, it not being a path. Restore the original value if so.
+ $location = $testloc if $testloc;
+ }
+}
+
+
+# initialize the global $tlmediasrc object, or die.
+# uses the global $location.
+#
+sub init_tlmedia
+{
+ if (defined($tlmediatlpdb) && ($tlmediatlpdb->root eq $location)) {
+ # nothing to be done
+ return;
+ }
+
+ # choose a mirror if we are asked.
+ if ($location =~ m/^ctan$/i) {
+ $location = give_ctan_mirror();
+ } elsif ($location =~ m,^$TeXLiveServerURL,) {
+ my $mirrorbase = TeXLive::TLUtils::give_ctan_mirror_base();
+ $location =~ s,^$TeXLiveServerURL,$mirrorbase,;
+ }
+
+ # this "location-url" line should not be changed since GUI programs
+ # depend on it:
+ print "location-url\t$location\n" if $::machinereadable;
+ info("tlmgr: package repository $location\n");
+
+ # if we talk about a net location try to download the hash of the tlpdb
+ # - if that is possible, check for the locally saved file and if the hash
+ # agrees load the local copy if present instead of the remote one,
+ # if the hashes disagree, load the remote tlpdb
+ # - if that does not work assume we are offline or target not reachable,
+ # so warn the user and use saved, but note that installation will
+ # not work
+
+ my $local_copy_tlpdb_used = 0;
+ if ($location =~ m;^(http|ftp)://;) {
+ # first check that the saved tlpdb is present at all
+ my $loc_digest = Digest::MD5::md5_hex($location);
+ my $loc_copy_of_remote_tlpdb =
+ "$Master/$InfraLocation/texlive.tlpdb.$loc_digest";
+ ddebug("loc_digest = $loc_digest\n");
+ ddebug("loc_copy = $loc_copy_of_remote_tlpdb\n");
+ if (-r $loc_copy_of_remote_tlpdb) {
+ ddebug("loc copy found!\n");
+ # we found the tlpdb matching the current location
+ # check for the remote hash
+ my $path = "$location/$InfraLocation/$DatabaseName.md5";
+ ddebug("remote path of digest = $path\n");
+ my $fh = TeXLive::TLUtils::download_file($path, "|");
+ my $rem_digest;
+ if (read ($fh, $rem_digest, 32) != 32) {
+ info(<<END_NO_INTERNET);
+Unable to download the remote TeX Live database,
+but found a local copy so using that.
+
+You may want to try specifying an explicit or different CTAN mirror;
+see the information and examples for the -repository option at
+http://tug.org/texlive/doc/install-tl.html
+(or in the output of install-tl --help).
+
+END_NO_INTERNET
+ # above text duplicated in install-tl
+
+ $tlmediasrc = TeXLive::TLMedia->new(-location => $location,
+ -tlpdbfile => $loc_copy_of_remote_tlpdb);
+ $local_copy_tlpdb_used = 1;
+ } else {
+ ddebug("found remote digest: $rem_digest\n");
+ my $rem_copy_digest = TeXLive::TLUtils::tlmd5($loc_copy_of_remote_tlpdb);
+ ddebug("rem_copy_digest = $rem_copy_digest\n");
+ if ($rem_copy_digest eq $rem_digest) {
+ debug("md5 of local copy identical with remote hash\n");
+ $tlmediasrc = TeXLive::TLMedia->new(-location => $location,
+ -tlpdbfile => $loc_copy_of_remote_tlpdb);
+ $local_copy_tlpdb_used = 1;
+ }
+ }
+ }
+ }
+ if (!$local_copy_tlpdb_used) {
+ # $tlmediasrc is a global variable
+ $tlmediasrc = TeXLive::TLMedia->new(-location => $location);
+ }
+ die($loadmediasrcerror . $location) unless defined($tlmediasrc);
+ $tlmediatlpdb = $tlmediasrc->tlpdb;
+ # we allow a range of years to be specified by the remote tlpdb
+ # for which it might work.
+ # the lower limit is TLPDB->config_release
+ # the upper limit is TLPDB->config_maxrelease
+ # if the later is not present only the year in config_release is accepted
+ # checks are done on the first 4 digits only
+ # Why only the first four places: some optional network distributions
+ # might use
+ # release/2009-foobar
+ # If it should work for 2009 and 2010, please use
+ # release/2009-foobar
+ # maxrelease/2010-foobar
+ my $texlive_release = $tlmediatlpdb->config_release;
+ my $texlive_maxrelease = $tlmediatlpdb->config_maxrelease;
+ if (!defined($texlive_release)) {
+ tldie "The installation repository does not specify a release year for which it was prepared, bailing out.\n";
+ }
+ # still here, so we have $texlive_release defined
+ my $texlive_release_year = $texlive_release;
+ $texlive_release_year =~ s/^(....).*$/$1/;
+ if ($texlive_release_year !~ m/^[1-9][0-9][0-9][0-9]$/) {
+ tldie "The installation repository does not specify a release year: $texlive_release, bailing out.\n";
+ }
+ # so $texlive_release_year is numeric, good
+ if (defined($texlive_maxrelease)) {
+ # we specify a range of years!
+ my $texlive_maxrelease_year = $texlive_maxrelease;
+ $texlive_maxrelease_year =~ s/^(....).*$/$1/;
+ if ($texlive_maxrelease_year !~ m/^[1-9][0-9][0-9][0-9]$/) {
+ tldie "The installation repository does not specify a valid maximal release year: $texlive_maxrelease, bailing out.\n";
+ }
+ # ok, all numeric and fine, check for range
+ if ($TeXLive::TLConfig::ReleaseYear < $texlive_release_year
+ || $TeXLive::TLConfig::ReleaseYear > $texlive_maxrelease_year) {
+ tldie <<END_BADRANGE
+$0: The TeX Live versions supported by the repository
+ ($texlive_release_year--$texlive_maxrelease_year)
+do not include the version of the local installation
+ ($TeXLive::TLConfig::ReleaseYear). Goodbye.
+END_BADRANGE
+ }
+ } else {
+ # $texlive_maxrelease not defined, so only one year is valid
+ if ($texlive_release_year != $TeXLive::TLConfig::ReleaseYear) {
+ tldie <<END_BADYEAR
+$0: The TeX Live versions of the local installation
+and the repository being accessed are not compatible:
+ local: $TeXLive::TLConfig::ReleaseYear
+ repository: $texlive_release_year
+(Perhaps you need to use a different CTAN mirror?) Goodbye.
+END_BADYEAR
+ }
+ }
+
+ # check for being frozen
+ if ($tlmediatlpdb->option("frozen")) {
+ my $frozen_msg = <<FROZEN;
+TeX Live $TeXLive::TLConfig::ReleaseYear is frozen forever and will no
+longer be updated. This happens in preparation for a new release.
+
+If you're interested in helping to pretest the new release (when
+pretests are available), please read http://tug.org/texlive/pretest.html.
+Otherwise, just wait, and the new release will be ready in due time.
+FROZEN
+ # don't die here, we want to allow updates even if tlnet is frozen!
+ tlwarn($frozen_msg);
+ }
+
+ # save remote database if it is a net location
+ # make sure that the writeout of the tlpdb is done in UNIX mode
+ # since otherwise the sha256 will change.
+ if (!$local_copy_tlpdb_used && $location =~ m;^(http|ftp)://;) {
+ my $loc_digest = Digest::MD5::md5_hex($location);
+ my $loc_copy_of_remote_tlpdb =
+ "$Master/$InfraLocation/texlive.tlpdb.$loc_digest";
+ my $tlfh;
+ if (!open($tlfh, ">:unix", $loc_copy_of_remote_tlpdb)) {
+ # that should be only a debug statement, since a user without
+ # write permission might have done a tlmgr search --global or
+ # similar
+ &debug("Cannot save remote TeX Live database to $loc_copy_of_remote_tlpdb: $!\n");
+ } else {
+ &debug("writing out tlpdb to $loc_copy_of_remote_tlpdb\n");
+ $tlmediatlpdb->writeout($tlfh);
+ close($tlfh);
+ }
+ }
+}
+
+
+
+# finish handles the -pause option (wait for input from stdin),
+# and then exits unless the global $::gui_mode is set, in which case we
+# merely return.
+#
+sub finish
+{
+ my ($ret) = @_;
+
+ if ($ret > 0) {
+ print "tlmgr: exiting unsuccessfully (status $ret).\n";
+ }
+
+ if ($opts{"pause"}) {
+ print "Press Enter to exit the program.\n";
+ <STDIN>;
+ }
+
+ if ($::gui_mode) {
+ return($ret);
+ } else {
+ exit($ret);
+ }
+}
+
+#
+# config file handling
+# config files are located in TEXMFCONFIG/tlmgr/config thus specific
+# for each user
+#
+# format:
+# key=value
+#
+# allowed keys at the moment
+# gui-expertmode = 0|1
+#
+sub load_config_file
+{
+ #
+ # first set default values
+ # the default for gui-expertmode is 1 since that is what we
+ # have shipped till now
+ $config{"gui-expertmode"} = 1;
+
+ chomp (my $TEXMFCONFIG = `kpsewhich -var-value=TEXMFCONFIG`);
+ my $fn = "$TEXMFCONFIG/tlmgr/config";
+ $tlmgr_config_file = TeXLive::TLConfFile->new($fn, "#", "=");
+ if ($tlmgr_config_file->key_present("gui_expertmode")) {
+ $tlmgr_config_file->rename_key("gui_expertmode", "gui-expertmode");
+ }
+ for my $key ($tlmgr_config_file->keys) {
+ my $val = $tlmgr_config_file->value($key);
+ if ($key eq "gui-expertmode") {
+ if ($val eq "0") {
+ $config{"gui-expertmode"} = 0;
+ } elsif ($val eq "1") {
+ $config{"gui-expertmode"} = 1;
+ } else {
+ tlwarn("Unknown value >$val< for gui-expertmode in $fn\n");
+ }
+ } elsif ($key eq "persistent-downloads") {
+ if (($val eq "0") || ($val eq "1")) {
+ $config{'persistent-downloads'} = $val;
+ } else {
+ tlwarn("Unknown value $val for persistent-downloads in $fn\n");
+ }
+ } elsif ($key eq "gui-lang") {
+ $config{'gui-lang'} = $val;
+ } else {
+ tlwarn("Unknown key $key in $fn\n");
+ }
+ }
+}
+
+sub write_config_file
+{
+ if (!defined($tlmgr_config_file)) {
+ chomp (my $TEXMFCONFIG = `kpsewhich -var-value=TEXMFCONFIG`);
+ my $dn = "$TEXMFCONFIG/tlmgr";
+ my $fn = "$dn/config";
+ # create a new one
+ $tlmgr_config_file = TeXLive::TLConfFile->new($fn, "#", "=");
+ }
+ for my $k (keys %config) {
+ # it doesn't hurt to save all config settings as we check in TLConfFile
+ # if the value has actually changed
+ $tlmgr_config_file->value($k, $config{$k});
+ }
+ # make sure that deleted config entries are carried over
+ for my $k ($tlmgr_config_file->keys) {
+ if (not(defined($config{$k}))) {
+ $tlmgr_config_file->delete_key($k);
+ }
+ }
+ if ($tlmgr_config_file->is_changed) {
+ $tlmgr_config_file->save;
+ }
+}
+
+# if the packagelog variable is set then write to PACKAGELOG filehandle
+#
+sub logpackage
+{
+ if ($packagelogfile) {
+ $packagelogged++;
+ my $tim = localtime();
+ print PACKAGELOG "[$tim] @_\n";
+ }
+}
+
+# resolve relative paths from tlpdb wrt tlroot
+sub norm_tlpdb_path
+{
+ my ($path) = @_;
+ return if (!defined($path));
+ $path =~ s!\\!/!;
+ # just return if absolute path
+ return $path if ($path =~ m!^/|:!);
+ init_local_db() unless defined($localtlpdb);
+ return $localtlpdb->root . "/$path";
+}
+
+# clear the backup dir for $pkg and keep only $autobackup packages
+# mind that with $autobackup == 0 all packages are cleared
+sub clear_old_backups
+{
+ my ($pkg, $backupdir, $autobackup, $dry) = @_;
+
+ my $dryrun = 0;
+ $dryrun = 1 if ($dry);
+ # keep arbitrary many backups
+ return if ($autobackup == -1);
+
+ opendir (DIR, $backupdir) || die "opendir($backupdir) failed: $!";
+ my @dirents = readdir (DIR);
+ closedir (DIR) || warn "closedir($backupdir) failed: $!";
+ my @backups;
+ for my $dirent (@dirents) {
+ next if (-d $dirent);
+ next if ($dirent !~ m/^$pkg\.r([0-9]+)\.tar\.xz$/);
+ push @backups, $1;
+ }
+ my $i = 1;
+ for my $e (reverse sort {$a <=> $b} @backups) {
+ if ($i > $autobackup) {
+ log ("Removing backup $backupdir/$pkg.r$e.tar.xz\n");
+ unlink("$backupdir/$pkg.r$e.tar.xz") unless $dryrun;
+ }
+ $i++;
+ }
+}
+
+# check for updates to tlcritical packages
+#
+sub check_for_critical_updates
+{
+ my ($localtlpdb, $mediatlpdb) = @_;
+
+ my $criticalupdate = 0;
+ my @critical = $localtlpdb->expand_dependencies("-no-collections",
+ $localtlpdb, @CriticalPackagesList);
+ my @critical_upd;
+ for my $pkg (sort @critical) {
+ my $tlp = $localtlpdb->get_package($pkg);
+ if (!defined($tlp)) {
+ # that should not happen, we expanded in the localtlpdb so why
+ # should it not be present, any anyway, those are so fundamental
+ # so they have to be there
+ tlwarn("\nFundamental package $pkg not present, uh oh, goodbye");
+ die "Serious error, $pkg not found";
+ }
+ my $localrev = $tlp->revision;
+ my $mtlp = $mediatlpdb->get_package($pkg);
+ if (!defined($mtlp)) {
+ debug("Very surprising, $pkg is not present in the remote tlpdb.\n");
+ next;
+ }
+ my $remoterev = $mtlp->revision;
+ push (@critical_upd, $pkg) if ($remoterev > $localrev);
+ }
+ return(@critical_upd);
+}
+
+sub critical_updates_warning
+{
+ tlwarn("Updates for tlmgr itself are present.\n");
+ tlwarn("=" x 79, "\n");
+ tlwarn("Please update the package manager first, via either\n");
+ tlwarn(" tlmgr update --self\n");
+ tlwarn("or by getting the latest updater for Unix-ish systems:\n");
+ tlwarn(" $TeXLiveURL/update-tlmgr-latest.sh\n");
+ tlwarn("and/or Windows systems:\n");
+ tlwarn(" $TeXLiveURL/update-tlmgr-latest.exe\n");
+ tlwarn("Then continue with other updates.\n");
+ tlwarn("=" x 79, "\n");
+}
+
+#
+# our compare function for package sorting, which makes sure that
+# packages with .ARCH names are sorted *before* the main packages
+sub packagecmp {
+ my $aa = $a;
+ my $bb = $b;
+ # remove the part after the . if at all present
+ $aa =~ s/\..*$//;
+ $bb =~ s/\..*$//;
+ if ($aa lt $bb) {
+ return -1;
+ } elsif ($aa gt $bb) {
+ return 1;
+ } else {
+ # the parts before the . are the same
+ # sort the .something *before* the ones without
+ if ($a eq $aa && $b eq $bb) {
+ return 0;
+ } elsif ($a eq $aa) {
+ # so $a = foobar
+ # and $b = foobar.something
+ # this is the special case where we want to invert the order
+ return 1;
+ } elsif ($b eq $bb) {
+ # so $a = foobar.something
+ # and $b = foobar
+ return -1;
+ } else {
+ return ($a cmp $b);
+ }
+ }
+}
+
+sub check_on_writable {
+ if (!TeXLive::TLUtils::dir_writable("$Master/tlpkg")) {
+ tlwarn("You don't have permission to change the installation in any way,\n");
+ tlwarn("specifically, the directory $Master/tlpkg/ is not writable.\n");
+ tlwarn("Please run this program as administrator, or contact your local admin.\n");
+ if ($opts{"dry-run"}) {
+ tlwarn("Continuing due to --dry-run\n");
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+tlmgr - the TeX Live Manager
+
+=head1 SYNOPSIS
+
+tlmgr [I<option>]... I<action> [I<option>]... [I<operand>]...
+
+=head1 DESCRIPTION
+
+B<tlmgr> manages an existing TeX Live installation, both packages and
+configuration options. For information on initially downloading and
+installing TeX Live, see L<http://tug.org/texlive/acquire.html>.
+
+The most up-to-date version of this documentation (updated nightly from
+the development sources) is available at
+L<http://tug.org/texlive/tlmgr.html>, along with procedures for updating
+C<tlmgr> itself and information about test versions.
+
+TeX Live is organized into a few top-level I<schemes>, each of which is
+defined as a different set of I<collections> and I<packages>, where a
+collection is a set of packages, and a package is what contains actual
+files. Schemes typically contain a mix of collections and packages, but
+each package is included in exactly one collection, no more and no less.
+Installation can be customized and managed at any level.
+
+For the full documentation of TeX Live, see
+L<http://tug.org/texlive/doc>.
+
+=head1 EXAMPLES
+
+After successfully installing TeX Live, here are a few common operations
+with C<tlmgr>:
+
+=over 4
+
+=item C<tlmgr update --all>
+
+Make your local TeX installation correspond to what is in the package
+repository (typically on CTAN).
+
+=item C<tlmgr update --list>
+
+Report what would be updated without actually updating anything.
+
+=item C<tlmgr option repository http://mirror.ctan.org/systems/texlive/tlnet>
+
+Tell C<tlmgr> to use a nearby CTAN mirror for future updates; useful if
+you installed TeX Live from the DVD image.
+
+=item C<tlmgr show> I<pkgname>
+
+Display detailed information about I<pkgname>, such as the installation
+status and description.
+
+=back
+
+For all the capabilities and details of C<tlmgr>, please see the
+following voluminous information.
+
+=head1 OPTIONS
+
+The following options to C<tlmgr> are global options, not specific to
+any action. All options, whether global or action-specific, can be
+given at any place and in arbitrary order. The first non-option
+argument will be the main action. In all cases, C<-->I<option> and
+C<->I<option> are equivalent, and an C<=> is optional between an option
+name and its value.
+
+=over 4
+
+=item B<--repository> I<url|path>
+
+Specifies the package repository from which packages should be installed
+or updated, overriding the default package repository found in the
+installation's TeX Live Package Database (TLPDB, a file named
+C<texlive.tlpdb>). The documentation for C<install-tl> has more details
+about this (L<http://tug.org/texlive/doc/install-tl.html>).
+
+C<--repository> changes the repository location only for the current
+run; to make a permanent change, use C<option repository> (see the
+L<option> action).
+
+For backward compatibility and convenience, C<--location> and C<--repo>
+are accepted as aliases for this option.
+
+
+=item B<--gui> [I<action>]
+
+You can give this option together with an action to be brought directly
+into the respective screen of the GUI. For example, running
+
+ tlmgr --gui update
+
+starts you directly at the update screen.
+
+=item B<--gui-lang> I<llcode>
+
+Normally the GUI tries to deduce your language from the environment (on
+Windows via the registry, on Unix via C<LC_MESSAGES>). If that fails you
+can select a different language by giving this option a two-letter
+(ISO 639-1) language code (with the exception for selecting simplified
+or traditional Chinese). Currently supported (but not necessarily
+completely translated) are:
+English (en, default), Czech (cs), German (de), French (fr),
+Italian (it), Dutch (nl), Polish (pl), Russian (ru), Slovak (sk),
+Slovenian (sl), Serbian (sr), Vietnamese (vi), simplified Chinese (zh-cn),
+and traditional Chinese (zh-tw).
+
+
+=item B<--machine-readable>
+
+In lieu of the normal output intended for human consumption, write to
+standard output in a fixed format more suitable for machine parsing.
+See the L</"MACHINE-READABLE OUTPUT"> section below for details.
+
+=item B<--package-logfile> I<file>
+
+B<tlmgr> logs all package actions (install, remove, update, failed
+updates, failed restores) to a separate log file, by default
+C<TEXMFSYSVAR/web2c/tlmgr.log>. This option allows you to select a
+different file for that. This is separate from normal logging; for
+that, see the option C<-v> below, and TeXLive::TLUtils.
+
+=item B<--pause>
+
+This option makes B<tlmgr> wait for user input before exiting. Useful on
+Windows to avoid command windows disappearing.
+
+=item B<--persistent-downloads>
+
+=item B<--no-persistent-downloads>
+
+For net installs, activating this option makes tlmgr try to set up a
+persistent connection using the Net::LWP Perl module.
+This opens only one connection between your computer and the server
+per session and reuses it, instead of initiating a new download for
+each package.
+
+This option is turned on by default, and tlmgr will
+fall back to using wget if this is not possible. If you want to disable
+usage of LWP and persistent connections, please use
+B<--no-persistent-downloads>.
+
+=item B<--no-execute-actions>
+
+Suppress the execution of the execute actions as defined in the tlpsrc
+files. Only use at your own risk.
+
+=item B<--debug-translation>
+
+In GUI mode, this switch makes C<tlmgr> report any missing, or more
+likely untranslated, messages to standard error. Helpful for
+translators to see what remains to be done.
+
+=back
+
+The standard options for TeX Live programs are also accepted:
+C<--help/-h/-?>, C<--version>, C<-q> (no informational messages), C<-v>
+(debugging messages, can be repeated). For the details about these, see
+the TeXLive::TLUtils documentation.
+
+The C<--version> option shows version information about the TeX Live
+release and about the C<tlmgr> script itself. If paired with C<-v>,
+revision number for the used TeX Live Perl modules are shown, too.
+
+=head1 ACTIONS
+
+=head2 help
+
+Gives this help information (same as C<--help>).
+
+=head2 version
+
+Gives version information (same as C<--version>).
+
+If C<-v> has been given the revisions of the used modules are reported, too.
+
+=head2 gui
+
+Start the graphical user interface. See B<GUI> below.
+
+
+=head2 install [I<option>]... I<pkg>...
+
+Install each I<pkg> given on the command line. By default this installs
+all packages on which the given I<pkg>s are dependent, also. Options:
+
+=over 4
+
+=item B<--reinstall>
+
+Reinstall a package (including dependencies for collections) even if it
+seems to be already installed (i.e, is present in the TLPDB). This is
+useful to recover from accidental removal of files in the hierarchy.
+
+When re-installing, only dependencies on normal packages are followed
+(not those of category Scheme or Collection).
+
+=item B<--no-depends>
+
+Do not install dependencies. (By default, installing a package ensures
+that all dependencies of this package are fulfilled.)
+
+=item B<--no-depends-at-all>
+
+When you install a package which ships binary files the respective
+binary package will also be installed. That is, for a package C<foo>,
+the package C<foo.i386-linux> will also be installed on an C<i386-linux>
+system. This switch suppresses this behavior, and also implies
+C<--no-depends>. Don't use it unless you are sure of what you are
+doing.
+
+=item B<--dry-run>
+
+Nothing is actually installed; instead, the actions to be performed are
+written to the terminal.
+
+=item B<--force>
+
+If updates to C<tlmgr> itself (or other parts of the basic
+infrastructure) are present, C<tlmgr> will bail out and not perform the
+installation unless this option is given. Not recommended.
+
+=back
+
+
+=head2 update [I<option>]... [I<pkg>]...
+
+Updates the packages given as arguments to the latest version available
+at the installation source. Either C<--all> or at least one I<pkg> name
+must be specified. Options:
+
+=over 4
+
+=item B<--all>
+
+Update all installed packages except for C<tlmgr> itself. Thus, if
+updates to C<tlmgr> itself are present, this will simply give an error,
+unless also the option C<--force> or C<--self> is given. (See below.)
+
+In addition to updating the installed packages, during the update of a
+collection the local installation is (by default) synchronized to the
+status of the collection on the server, for both additions and removals.
+
+This means that if a package has been removed on the server (and thus
+has also been removed from the respective collection), C<tlmgr> will
+remove the package in the local installation. This is called
+``auto-remove'' and is announced as such when using the option
+C<--list>. This auto-removal can be suppressed using the option
+C<--no-auto-remove>.
+
+Analogously, if a package has been added to a collection on the server
+that is also installed locally, it will be added to the local
+installation. This is called ``auto-install'' and is announced as such
+when using the option C<--list>. This auto-installation can be
+suppressed using the option C<--no-auto-install>.
+
+An exception to the collection dependency checks (including the
+auto-installation of packages just mentioned) are those that have been
+``forcibly removed'' by you, that is, you called C<tlmgr remove --force>
+on them. (See the C<remove> action documentation.) To reinstall any
+such forcibly removed packages use C<--reinstall-forcibly-removed>.
+
+If you want to exclude some packages from the current update run (e.g.,
+due to a slow link), see the C<--exclude> option below.
+
+=item B<--self>
+
+Update C<tlmgr> itself (that is, the infrastructure packages) if updates
+to it are present. On Windows this includes updates to the private Perl
+interpreter shipped inside TeX Live.
+
+If this option is given together with either C<--all> or a list of
+packages, then C<tlmgr> will be updated first and, if this update
+succeeds, the new version will be restarted to complete the rest of the
+updates.
+
+In short:
+
+ tlmgr update --self # update infrastructure only
+ tlmgr update --self --all # update infrastructure and all packages
+ tlmgr update --force --all # update all packages but *not* infrastructure
+ # ... this last at your own risk, not recommended!
+
+=item B<--dry-run>
+
+Nothing is actually installed; instead, the actions to be performed are
+written to the terminal. This is a more detailed report than C<--list>.
+
+=item B<--list> [I<pkg>]
+
+Concisely list the packages which would be updated, newly installed, or
+removed, without actually changing anything.
+If C<--all> is given, it lists all available updates.
+If C<--self> is given, but not C<--all> lists only updates the the
+critical packages (tlmgr, texlive infrastructure, perl on Windows, etc.).
+If neither C<--all> nor C<--self> is given, and in addition no I<pkg>
+is passed on the command line, then C<--all> is assumed (so
+C<tlmgr update --list> behaves like C<tlmgr update --list --all>).
+If neither C<--all> nor C<--self> is given, but package names passed
+on the command line, those packages are checked for updates.
+
+=item B<--exclude> I<pkg>
+
+Exclude I<pkg> from the update process. If this option is given more
+than once, its arguments accumulate.
+
+An argument I<pkg> excludes both the package I<pkg> itself and all
+its related platform-specific packages I<pkg.ARCH>. For example,
+
+ tlmgr update --all --exclude a2ping
+
+will not update C<a2ping>, C<a2ping.i386-linux>, or
+any other C<a2ping.>I<ARCH> package.
+
+If this option specifies a package that would otherwise be a candidate
+for auto-installation, auto-removal, or reinstallation of a forcibly
+removed package, C<tlmgr> quits with an error message. Excludes are not
+supported in these circumstances.
+
+=item B<--no-auto-remove> [I<pkg>]...
+
+Under normal circumstances C<tlmgr> tries to remove packages which have
+disappeared on the server, as described above under C<--all>. This
+option prevents any such removals, either for all packages (with
+C<--all>), or the given I<pkg> names.
+
+=item B<--no-auto-install> [I<pkg>]...
+
+Under normal circumstances C<tlmgr> will install packages which are new
+on the server, as descrbed above under C<--all>. This option prevents
+any such automatic installation, either for all packages (with
+C<--all>), or the given I<pkg> names.
+
+Furthermore, after the C<tlmgr> run using this has finished, the
+packages that would have been auto-installed I<will be considered as
+forcibly removed>. So, if C<foobar> is the only new package on the
+server, then
+
+ tlmgr update --all --no-auto-install
+
+is equivalent to
+
+ tlmgr update --all
+ tlmgr remove --force foobar
+
+=item B<--reinstall-forcibly-removed>
+
+Under normal circumstances C<tlmgr> will not install packages that have
+been forcibly removed by the user; that is, removed with C<remove
+--force>, or whose installation was prohibited by C<--no-auto-install>
+during an earlier update.
+
+This option makes C<tlmgr> ignore the forcible removals and re-install
+all such packages. This can be used to completely synchronize an
+installation with the server's idea of what is available:
+
+ tlmgr update --reinstall-forcibly-removed --all
+
+=item B<--backup> and B<--backupdir> I<directory>
+
+These two options control the creation of backups of packages I<before>
+updating; that is, backup of packages as currently installed. If
+neither of these options are given, no backup package will be saved. If
+C<--backupdir> is given and specifies a writable directory then a backup
+will be made in that location. If only C<--backup> is given, then a
+backup will be made to the directory previously set via the C<option>
+action (see below). If both are given then a backup will be made to the
+specified I<directory>.
+
+You can set options via the C<option> action to automatically create
+backups for all packages, and/or keep only a certain number of
+backups. Please see the C<option> action for details.
+
+C<tlmgr> always makes a temporary backup when updating packages, in case
+of download or other failure during an update. In contrast, the purpose
+of this C<--backup> option is to allow you to save a persistent backup
+in case the actual I<content> of the update causes problems, e.g.,
+introduces an incompatibility.
+
+The C<restore> action explains how to restore from a backup.
+
+=item B<--no-depends>
+
+If you call for updating a package normally all depending packages
+will also be checked for updates and updated if necessary. This switch
+suppresses this behavior.
+
+=item B<--no-depends-at-all>
+
+See above under B<install> (and beware).
+
+=item B<--force>
+
+Force update of normal packages, without updating C<tlmgr> itself
+(unless the C<--self> option is also given). Not recommended.
+
+Also, C<update --list> is still performed regardless of this option.
+
+=back
+
+If the package on the server is older than the package already installed
+(e.g., if the selected mirror is out of date), C<tlmgr> does not
+downgrade. Also, packages for uninstalled platforms are not installed.
+
+
+=head2 backup [--clean[=I<N>]] [--backupdir I<dir>] [--all | I<pkg>]...
+
+If the C<--clean> option is not specified, this action makes a backup of
+the given packages, or all packages given C<--all>. These backups are
+saved to the value of the C<--backupdir> option, if that is an existing and
+writable directory. If C<--backupdir> is not given, the C<backupdir>
+option setting in the TLPDB is used, if present. If both are missing,
+no backups are made.
+
+If the C<--clean> option is specified, backups are pruned (removed)
+instead of saved. The optional integer value I<N> may be specified to
+set the number of backups that will be retained when cleaning. If C<N>
+is not given, the value of the C<autobackup> option is used. If both are
+missing, an error is issued. For more details of backup pruning, see
+the C<option> action.
+
+Options:
+
+=over 4
+
+=item B<--backupdir> I<directory>
+
+Overrides the C<backupdir> option setting in the TLPDB.
+The I<directory> argument is required and must specify an existing,
+writable directory where backups are to be placed.
+
+=item B<--all>
+
+If C<--clean> is not specified, make a backup of all packages in the TeX
+Live installation; this will take quite a lot of space and time. If
+C<--clean> is specified, all packages are pruned.
+
+=item B<--clean>[=I<N>]
+
+Instead of making backups, prune the backup directory of old backups, as
+explained above. The optional integer argument I<N> overrides the
+C<autobackup> option set in the TLPDB. You must use C<--all> or a list
+of packages together with this option, as desired.
+
+=item B<--dry-run>
+
+Nothing is actually backed up or removed; instead, the actions to be
+performed are written to the terminal.
+
+=back
+
+
+=head2 restore [--backupdir I<dir>] [--all | I<pkg> [I<rev>]]
+
+Restore a package from a previously-made backup.
+
+If C<--all> is given, try to restore the latest revision of all
+package backups found in the backup directory.
+
+Otherwise, if neither I<pkg> nor I<rev> are given, list the available backup
+revisions for all packages.
+
+With I<pkg> given but no I<rev>, list all available backup revisions of
+I<pkg>.
+
+When listing available packages tlmgr shows the revision and in
+parenthesis the creation time if available (in format yyyy-mm-dd hh:mm).
+
+With both I<pkg> and I<rev>, tries to restore the package from the
+specified backup.
+
+Options:
+
+=over 4
+
+=item B<--all>
+
+Try to restore the latest revision of all package backups found in the
+backup directory. Additional non-option arguments (like I<pkg>) are not
+allowed.
+
+=item B<--backupdir> I<directory>
+
+Specify the directory where the backups are to be found. If not given it
+will be taken from the configuration setting in the TLPDB.
+
+=item B<--dry-run>
+
+Nothing is actually restored; instead, the actions to be performed are
+written to the terminal.
+
+=item B<--force>
+
+Don't ask questions.
+
+=back
+
+
+=head2 remove [I<option>]... I<pkg>...
+
+Remove each I<pkg> specified. Removing a collection removes all package
+dependencies (unless C<--no-depends> is specified), but not any
+collection dependencies of that collection. However, when removing a
+package, dependencies are never removed. Options:
+
+=over 4
+
+=item B<--no-depends>
+
+Do not remove dependent packages.
+
+=item B<--no-depends-at-all>
+
+See above under B<install> (and beware).
+
+=item B<--force>
+
+By default, removal of a package or collection that is a dependency of
+another collection or scheme is not allowed. With this option, the
+package will be removed unconditionally. Use with care.
+
+A package that has been removed using the C<--force> option because it
+is still listed in an installed collection or scheme will not be
+updated, and will be mentioned as B<forcibly removed> in the output of
+B<tlmgr update --list>.
+
+=item B<--dry-run>
+
+Nothing is actually removed; instead, the actions to be performed are
+written to the terminal.
+
+=back
+
+
+=head2 option
+
+=over 4
+
+=item B<option [show]>
+
+=item B<option showall>
+
+=item B<option I<key> [I<value>]>
+
+=back
+
+The first form shows the global TeX Live settings currently saved in the
+TLPDB with a short description and the C<key> used for changing it in
+paranthesis.
+
+The second form acts like the first, but also shows options which can
+be defined but are not currently set to any value.
+
+In the third form, if I<value> is not given, the setting for I<key> is
+displayed. If I<value> is present, I<key> is set to I<value>.
+
+Possible values for I<key> are (but see B<tlmgr option showall> for
+the definitive list):
+
+ repository (default package repository),
+ formats (create formats at installation time),
+ docfiles (install documentation files),
+ srcfiles (install source files),
+ backupdir (default directory for backups),
+ autobackup (number of backups to keep).
+ sys_bin (directory to which executables are linked by the path action)
+ sys_man (directory to which man pages are linked by the path action)
+ sys_info (directory to which Info files are linked by the path action)
+
+One common use of C<option> is to permanently change the installation to
+get further updates from the Internet, after originally installing from
+DVD. To do this, you can run
+
+ tlmgr option repository http://mirror.ctan.org/systems/texlive/tlnet
+
+The C<install-tl> documentation has more information about the possible
+values for repository.
+
+To keep backward compatibility C<location> can be used as alternative
+name for C<repository>.
+
+The two options C<autobackup> and C<backupdir> determine defaults for
+the C<update>, C<backup> and C<restore> actions. These three actions
+need a directory to write to or read from the backups. If
+C<--backupdir> is not specified on the command line, the C<backupdir>
+option value is used (if set).
+
+The C<autobackup> option (de)activates automatic generation of backups.
+Its value is an integer. If the C<autobackup> value is C<-1>, no
+backups are removed. If C<autobackup> is 0 or more, it specifies the
+number of backups to keep. Thus, backups are disabled if the value is
+0. In the C<--clean> mode of the C<backup> action this option also
+specifies the number to be kept.
+
+To setup C<autobackup> to C<-1> on the command line, use
+
+ tlmgr option autobackup infty
+
+Or you can use:
+
+ tlmgr option -- autobackup -1
+
+The C<--> avoids having the C<-1> treated as an option. (For most
+programs, C<--> stops parsing for arguments at the point where it
+appears; this is a general feature of option parsing.)
+
+
+=head2 conf [texmf|tlmgr [I<key> [I<value>]]]
+
+With only the C<conf>, show general configuration information for TeX
+Live, including active configuration files, path settings, and more.
+This is like the C<texconfig conf> call, but works on all supported
+platforms.
+
+With either C<texmf> or C<tlmgr> given in addition, shows all key/value
+pairs (i.e., all settings) as saved in C<ROOT/texmf.cnf> or the tlmgr
+configuration file (see below), respectively.
+
+If I<key> is given in addition, shows the value of only that given
+I<key> in the respective file.
+
+If I<value> is given in addition, I<key> is set to I<value> in the
+respective file. I<No error checking is done!>
+
+Practical application: if the execution of (some or all) system commands
+via C<\write18> was left enabled during installation, you can disable
+it afterwards:
+
+ tlmgr conf texmf shell_escape 0
+
+B<WARNING:> The general facility is here, but tinkering with settings in
+this way is very strongly discouraged. Again, no error checking is
+done, so any sort of breakage is possible.
+
+
+=head2 paper
+
+=over 4
+
+=item B<paper [a4|letter]>
+
+=item B<S<xdvi|pdftex|dvips|dvipdfmx|dvipdfm|context paper [I<papersize>|--list]>>
+
+=back
+
+With no arguments (C<tlmgr paper>), shows the default paper size setting
+for all known programs.
+
+With one argument (e.g., C<tlmgr paper a4>), sets the default for all
+known programs to that paper size.
+
+With a program given as the first argument and no paper size specified
+(e.g., C<tlmgr dvips paper>), shows the default paper size for that
+program.
+
+With a program given as the first argument and a paper size as the last
+argument (e.g., C<tlmgr dvips paper a4>), set the default for that
+program to that paper size.
+
+With a program given as the first argument and C<--list> given as the
+last argument (e.g., C<tlmgr dvips paper --list>), shows all valid paper
+sizes for that program. The first size shown is the default.
+
+Incidentally, this syntax of having a specific program name before the
+C<paper> keyword may seem strange. It is inherited from the
+longstanding C<texconfig> script, which supports other configuration
+settings for some programs, notably C<dvips>. C<tlmgr> does not support
+those extra settings at present.
+
+
+=head2 platform list|add|remove I<platform>...
+
+C<platform list> lists the TeX Live names of all the platforms
+(a.k.a. architectures), (C<i386-linux>, ...) available at the package
+repository.
+
+C<platform add> I<platform>... adds the executables for each given platform
+I<platform> to the installation from the repository.
+
+C<platform remove> I<platform>... removes the executables for each given
+platform I<platform> from the installation, but keeps the currently
+running platform in any case.
+
+C<arch> is a synonym for C<platform>.
+
+Options:
+
+=over 4
+
+=item B<--dry-run>
+
+Nothing is actually installed; instead, the actions to be performed are
+written to the terminal.
+
+=back
+
+
+=head2 print-platform
+
+Print the TeX Live identifier for the detected platform
+(hardware/operating system) combination to standard output, and exit.
+C<--print-arch> is a synonym.
+
+
+=head2 search [I<options>] I<what>
+
+=head2 search [I<options>] --file I<what>
+
+=head2 search [I<options>] --taxonomy I<what>
+
+=head2 search [I<options>] --keyword I<what>
+
+=head2 search [I<options>] --functionality I<what>
+
+=head2 search [I<options>] --characterization I<what>
+
+=head2 search [I<options>] --all I<what>
+
+By default, search the names, short descriptions, and long descriptions
+of all locally installed packages for the argument I<what>, interpreted
+as a regular expression.
+
+Options:
+
+=over 4
+
+=item B<--global>
+
+Search the TeX Live Database of the installation medium, instead of the
+local installation.
+
+=item B<--word>
+
+Restrict the search to match only full words. For example, searching for
+C<table> with this option will not output packages containing the
+word C<tables> (unless they also contain the word C<table> on its own).
+
+=item B<--list>
+
+If a search for any (or all) taxonomies is done, by specifying one of
+the taxonomy options below, then instead of searching for packages, list
+the entire corresponding taxonomy (or all of them). See
+L</"TAXONOMIES"> below.
+
+=back
+
+Other search options are selected by specifying one of the following:
+
+=over 4
+
+=item B<--file>
+
+List all filenames containing I<what>.
+
+=item B<--taxonomy>
+
+=item B<--keyword>
+
+=item B<--functionality>
+
+=item B<--characterization>
+
+Search in the corresponding taxonomy (or all) instead of the package
+descriptions. See L</"TAXONOMIES"> below.
+
+=item B<--all>
+
+Search for package names, descriptions, and taxonomies, but not files.
+
+=back
+
+
+=head2 show [I<options>] I<pkg>...
+
+Display information about I<pkg>: the name, category, installation status,
+short and long description. Searches in the remote installation source
+for the package if it is not locally installed.
+
+It also displays the information taken from the TeX Catalogue (license,
+date, version), but note that there is a high probability that this
+information is slightly off due to timing issues.
+
+Options:
+
+=over 4
+
+=item B<--list>
+
+If the option C<--list> is given with a package, the list of contained
+files is also shown, including those for platform-specific dependencies.
+When given with schemes and collections, C<--list> outputs their
+dependencies in a similar way.
+
+=item B<--taxonomy>
+
+=item B<--keyword>
+
+=item B<--functionality>
+
+=item B<--characterization>
+
+In addition to the normal data displayed, also display information for
+given packages from the corresponding taxonomy (or all of them). See
+L</"TAXONOMIES"> below for details.
+
+=back
+
+
+=head2 list [--only-installed] [collections|schemes|I<pkg>...]
+
+With no argument, lists all packages available at the package
+repository, prefixing those already installed with C<i>.
+
+With the single word C<collections> or C<schemes> as the argument, lists
+the request type.
+
+With anything else as arguments, operates as the C<show> action.
+
+If the option C<--only-installed> is given the installation source will
+not be used and only locally installed packages, collections, or schemes
+are listed.
+
+
+=head2 check [I<option>]... [files|depends|executes|runfiles|all]
+
+Executes one (or all) check(s) on the consistency of the installation.
+
+=over 4
+
+=item B<files>
+
+Checks that all files listed in the local TLPDB (C<texlive.tlpdb>) are
+actually present, and lists those missing.
+
+=item B<depends>
+
+Lists those packages which occur as dependencies in an installed collections,
+but are themselves not installed, and those packages that are not
+contained in any collection.
+
+If you call C<tlmgr check collections> this test will be carried out
+instead since former versions for C<tlmgr> called it that way.
+
+=item B<executes>
+
+Check that the files referred to by C<execute> directives in the TeX
+Live Database are present.
+
+=item B<runfiles>
+
+List those filenames that are occurring more than one time in the runfiles.
+
+=back
+
+Options:
+
+=over 4
+
+=item B<--use-svn>
+
+Use the output of C<svn status> instead of listing the files; for
+checking the TL development repository.
+
+=back
+
+
+=head2 path [--w32mode=user|admin] [add|remove]
+
+On Unix adds or removes symlinks for binaries, man pages, and info pages
+in the directories specified by the respective options (see above).
+
+On Windows, the registry part where the binary directory is
+added or removed is determined in the following way:
+
+If the user has admin rights, and the option C<--w32mode> is not given,
+the setting I<w32_multi_user> determines the location (i.e., if it is
+on then the system path, otherwise the user path is changed).
+
+If the user has admin rights, and the option C<--w32mode> is given, this
+option determines the path to be adjusted.
+
+If the user does not have admin rights, and the option C<--w32mode>
+is not given, and the setting I<w32_multi_user> is off, the user path
+is changed, while if the setting I<w32_multi_user> is on, a warning is
+issued that the caller does not have enough privileges.
+
+If the user does not have admin rights, and the option C<--w32mode>
+is given, it must be B<user> and the user path will be adjusted. If a
+user without admin rights uses the option C<--w32mode admin> a warning
+is issued that the caller does not have enough provileges.
+
+=cut
+
+# keep the following on *ONE* line otherwise Windows perldoc does
+# not show it!!!!
+
+=pod
+
+=head2 postaction [--w32mode=user|admin] [--fileassocmode=1|2] [--all] [install|remove] [shortcut|fileassoc|script] [I<pkg>]...
+
+Carry out the postaction C<shortcut>, C<fileassoc>, or C<script> given
+as the second required argument in install or remove mode (which is the
+first required argument), for either the packages given on the command
+line, or for all if C<--all> is given.
+
+The option C<--w32mode> is C<user> all actions will only carried out in
+the user accessible parts of the registry/filesystem, while the C<admin>
+mode selects the system wide parts of the registry for the file
+associations. Note that if you do not have enough permissions using
+C<--w32mode=admin> will not succeed.
+
+For the postaction C<fileassoc> the mode can be set with
+C<--fileassocmode>. If it is set to 1, only new assocations are added,
+if it is set to 2, all associations are set to the TeX Live programs.
+
+
+=head2 uninstall
+
+Uninstalls the entire TeX Live installation. Options:
+
+=over 4
+
+=item B<--force>
+
+Do not ask for confirmation, remove immediately.
+
+=back
+
+
+=head2 generate [I<option>]... I<what>
+
+=over 4
+
+=item B<generate language>
+
+=item B<generate language.dat>
+
+=item B<generate language.def>
+
+=item B<generate language.dat.lua>
+
+=item B<generate fmtutil>
+
+=item B<generate updmap>
+
+=back
+
+The C<generate> action overwrites any manual changes made in the
+respective files: it recreates them from scratch.
+
+For C<fmtutil> and the language files, this is normal, and both the TeX
+Live installer and C<tlmgr> routinely call C<generate> for them.
+
+For C<updmap>, however, C<tlmgr> does I<not> use
+C<generate>, because the result would be to disable all maps which have
+been manually installed via S<C<updmap-sys --enable>>, e.g., for
+proprietary or local fonts. The C<generate> action only incorporates
+the changes in the C<--localcfg> file mentioned below. Furthermore,
+C<tlmgr> updates and maintains the final C<updmap.cfg> in
+C<TEXMFSYSCONFIG> (while the other files are in C<TEXMFSYSVAR>), because
+that is the location that C<updmap-sys> (via C<tcfmgr>) uses.
+
+Notwithstanding the above, if you only use the fonts and font packages
+within TeX Live, and rigorously maintain your local fonts (if any) using
+C<updmap-local.cfg>, there is nothing wrong with using C<generate
+updmap>. It can be helpful in moving from release to release,
+especially. We use it ourselves to generate the C<updmap.cfg> file in
+the live source repository.
+
+In more detail: C<generate> remakes any of the five config files
+C<language.dat>, C<language.def>, C<language.dat.lua>, C<fmtutil.cnf>,
+and C<updmap.cfg> from the information present in the local TLPDB, plus
+locally-maintained files.
+
+The locally-maintained files are C<language-local.dat>,
+C<language-local.def>, C<language-local.dat.lua>, C<fmtutil-local.cnf>,
+or C<updmap-local.cfg>, searched for in C<TEXMFLOCAL> in the respective
+directories. If they are present, the final file is made by starting
+with the main file, omitting any entries that the local file specifies
+to be disabled, and finally appending the local file.
+
+Local files specify entries to be disabled with a comment line like this:
+
+ #!NAME
+ %!NAME
+ --!NAME
+
+where C<fmtutil.cnf> and C<updmap.cfg> use C<#>, C<language.dat> and
+C<language.def> use C<%>, and C<language.dat.lua> use C<-->. In any
+case, the I<name> is the respective format name, map file name (include
+the C<.map> extension), or hyphenation pattern identifier. Examples:
+
+ #!pdflatex
+ #!lm.map
+ %!german
+ --!usenglishmax
+
+(Of course, you're not likely to actually want to disable those
+particular items. They're just examples.)
+
+After such a disabling line, the local file can include another entry
+for the same item, if a different definition is desired. In general,
+except for the special disabling lines, the local files follow the same
+syntax as the master files.
+
+The form C<generate language> recreates both the C<language.dat>, the
+C<language.def> and the C<language.dat.lua>files, while the forms with
+extension recreates only the given language file.
+
+Special consideration for updmap.cfg: in addition to font map files,
+this file specifies the setting of five options: C<dvipsPreferOutline>,
+C<LW35>, C<dvipsDownloadBase35>, C<pdftexDownloadBase14>, and
+C<dvipdfmDownloadBase14>. The defaults for these as set in
+C<updmap-hdr.cfg> are usually fine. If you want to change them, you can
+include changed settings for any or all of these five options in your
+C<updmap-local.cfg> file and they will be respected by C<generate
+updmap>. For example:
+
+ dvipsDownloadBase35 true
+
+Options:
+
+=over 4
+
+=item B<--dest> I<output_file>
+
+specifies the output file (defaults to the respective location in
+C<TEXMFSYSVAR> for C<language*> and C<fmtutil>, and C<TEXMFSYSCONFIG>
+for C<updmap>). If C<--dest> is given to C<generate language>, it serves
+as a basename onto which C<.dat> will be appended for the name of the
+C<language.dat> output file, C<.def> will be
+appended to the value for the name of the C<language.def> output file,
+and C<.dat.lua> to the name of the C<language.dat.lua> file. (This is
+just to avoid overwriting; if you want a specific name for each output
+file, we recommend invoking C<tlmgr> twice.)
+
+=item B<--localcfg> I<local_conf_file>
+
+specifies the (optional) local additions (defaults to the respective
+location in C<TEXMFLOCAL>).
+
+=item B<--rebuild-sys>
+
+tells tlmgr to run necessary programs after config files have been
+regenerated. These are: C<updmap-sys> after C<generate updmap>,
+C<fmtutil-sys --all> after C<generate fmtutil>,
+C<fmtutil-sys --byhyphen .../language.dat> after C<generate language.dat>,
+and
+C<fmtutil-sys --byhyphen .../language.def> after C<generate language.def>.
+
+These subsequent calls cause the newly-generated files to actually take
+effect. This is not done by default since those calls are lengthy
+processes and one might want to made several related changes in
+succession before invoking these programs.
+
+=back
+
+The respective locations are as follows:
+
+ tex/generic/config/language.dat (and language-local.dat);
+ tex/generic/config/language.def (and language-local.def);
+ tex/generic/config/language.dat.lua (and language-local.dat.lua);
+ web2c/fmtutil.cnf (and fmtutil-local.cnf);
+ web2c/updmap.cfg (and updmap-local.cfg).
+
+Final repetition: as explained above, C<tlmgr> does I<not> use
+C<generate updmap> for font map files. Therefore, if you want to make
+use of C<updmap-local.cfg>, you must run C<tlmgr generate updmap>
+and C<updmap-sys> yourself after making any local changes.
+
+
+=head1 CONFIGURATION FILE
+
+A small subset of the command line options can be set in a config file
+for C<tlmgr> which resides in C<TEXMFCONFIG/tlmgr/config>. By default, the
+config file is in C<~/.texlive2010/texmf-config/tlmgr/config>. This is I<not>
+C<TEXMFSYSVAR>, so that the file is specific to a single user.
+
+In this file, empty lines and lines starting with # are ignored. All
+other lines must look like
+
+ key = value
+
+where the allowed keys are
+C<gui-expertmode> (values 0 or 1),
+C<persistent-downloads> (values 0 or 1),
+and C<gui-lang> (values like the command line arguments).
+C<persistent-downloads> and C<gui-lang> correspond to the command line
+options of the same name. C<gui-expertmode> switches between the full
+GUI and a simplified GUI with only the important and mostly used
+settings.
+
+
+=head1 TAXONOMIES
+
+tlmgr allows searching and listing of various categorizations, which we
+call I<taxonomies>, as provided by an enhanced TeX Catalogue (available
+for testing at L<http://az.ctan.org>). This is useful when, for
+example, you don't know a specific package name but have an idea of the
+functionality you need; or when you want to see all packages relating to
+a given area.
+
+There are three different taxonomies, specified by the following
+options:
+
+=over 4
+
+=item B<--keyword>
+
+The keywords, as specified at L<http://az.ctan.org/keyword>.
+
+=item B<--functionality>
+
+The ``by-topic'' categorization created by J\"urgen Fenn, as specified
+at L<http://az.ctan.org/characterization/by-function>.
+
+=item B<--characterization>
+
+Both the primary and secondary functionalities, as specified at
+L<http://az.ctan.org/characterization/choose_dimen>.
+
+=item B<--taxonomy>
+
+Operate on all the taxonomies.
+
+=back
+
+The taxonomies are updated nightly and stored within TeX Live, so
+Internet access is not required to search them.
+
+Examples:
+
+ tlmgr search --taxonomy exercise # check all taxonomies for "exercise"
+ tlmgr search --taxonomy --word table # check for "table" on its own
+ tlmgr search --list --keyword # dump entire keyword taxonomy
+ tlmgr show --taxonomy pdftex # show pdftex package information,
+ # including all taxonomy entries
+
+
+=head1 GUI FOR TLMGR
+
+The graphical user interface for C<tlmgr> needs Perl/TK being installed.
+For Windows the necessary modules are shipped within TeX Live, for all
+other (i.e., Unix-based) systems Perl/Tk (as well as perl of course) has
+to be installed.
+
+When started with C<tlmgr gui> the graphical user interface will be
+shown. The main window contains a menu bar, the main display, and a
+status area where messages normally shown on the console are displayed.
+
+Within the main display there are three main parts: The I<Display
+configuration>, the list of packages, and the buttons for actions. In
+addition to these three on the top right there is some text showing the
+currently loaded repository (this text also acts as button and will load
+the default repository).
+
+=head2 Menu bar
+
+The following entries can be found in the menu bar:
+
+=over 4
+
+=item B<tlmgr>
+
+Provides access to load various repositories (the default as specified
+in the texlive database, the default network repository, if given the
+repository specified on the command line, and an arbitrary other one.
+Furthermore is allows to quit C<tlmgr>.
+
+=item B<Options>
+
+Provides access to three groups of options, I<General> (for almost all
+options), I<Paper> (configuration of default paper sizes), I<Platforms>
+(only on Unix, configuration of the supported/installed platforms), as
+well as some toggles to turn on and off various options.
+
+There is also a toggle for C<Expert options> which defaults to on. If
+you turn this off the next time you start the GUI a simplified screen
+will be shown that exhibits only the most important and necessary
+functionality. This setting is saved in the configuration file of
+C<tlmgr>, see L<CONFIGURATION FILE> for details.
+
+=item B<Actions>
+
+Provides access to a variety of items, such as updating the filename
+database (aka ls-R, mktexlsr, texhash), rebuilding of all formats
+(C<fmtutil-sys --all>), updating the font map database (C<updmap-sys>),
+and handling of symbolic links in system directories (only Unix),
+restoring backups of packages, as well as removal of the full TeX Live
+installation (only Unix).
+
+=item B<Help>
+
+Provides access to the manual and other basic information on the
+installed version, authors, license.
+
+=back
+
+=head2 Main display
+
+The main display lists all packages, installed and, if a repository is
+loaded, also those that are available but not installed.
+
+Each line of the package list contains of the following items:
+
+=over 4
+
+=item a checkbox
+
+used for selecting packages, some of the action buttons (see below) will
+work only on the selected packages.
+
+=item the package name
+
+that is the name of the package as given in the database.
+
+=item local revision (and version)
+
+If the package is installed the revision of the installed package will
+be shown. If there is a catalogue version given in the database for this
+package, it will be shown in parenthesis.
+
+=item remote revision (and version)
+
+If a repository has been loaded the revision of the package in the
+repository (if present) is shown. As with the local variant, if a
+catalogue version is provided it will be displayed.
+
+=item short description
+
+The short description (or title) is shown.
+
+=back
+
+Double clicking on one of the lines pops up an informational window with
+further details, the long description, included files, etc.
+
+Above the list of package there are options to configure the list of
+packages to be shown. Changing any of them will automatically update the
+list of packages. The different display configurations are:
+
+=over 4
+
+=item Status
+
+allows selecting to show all, only the installed, only those packages
+that are not installed, or only those with packages available.
+
+=item Category
+
+allows to select which categories are shown.
+
+=item Match
+
+allows searching for a specific pattern. This uses the same algorithm as
+C<tlmgr search>, i.e., searches in the title, short and long
+descriptions.
+
+=item Selection
+
+allows restricting the list of packages to those selected, not selected,
+or all (selected means that the checkbox in the beginning of the line of
+a package is selected).
+
+=item buttons
+
+To the right there are three buttons, one to select all packages, one to
+select none (deselect all), and one to reset all filters to the
+defaults, i.e., show all available.
+
+=back
+
+Below the list of packages there are five buttons:
+
+=over 4
+
+=item Update all installed
+
+This calls C<tlmgr update --all> internally, i.e., tries to update all
+available packages.
+
+Below this button there is a toggle that allows reinstallation of
+previously removed packages.
+
+The following four buttons only work on the I<selected> packages, i.e.,
+those where the checkbutton at the beginning of the line is ticked.
+
+=item Update
+
+only update the selcted packages
+
+=item Install
+
+install the selected packages, acts like C<tlmgr install>, i.e., also
+installs dependencies. This installing a collection will install
+all depending packages, too.
+
+=item Remove
+
+removes the selected packages, acts like C<tlmgr remove>, i.e., it will
+also remove dependencies of collections (but not dependencies of normal
+packages).
+
+=item Backup
+
+makes a backup of the selected packages, acts like C<tlmgr backup>. This
+action needs the option C<backupdir> set (see Options -> General).
+
+=back
+
+Finally, the status area at the bottom of the window gives additional
+information about what is going on.
+
+
+=head1 MACHINE-READABLE OUTPUT
+
+Given the C<--machine-readable> option, C<tlmgr> writes to stdout in the
+fixed line-oriented format described here, and the usual informational
+messages for human consumption are written to stderr (normally they are
+written to stdout). The idea is that a program can get all the
+information it needs by reading stdout.
+
+Currently this option only applies to the
+L<update|/"update [I<option>]... [I<pkg>]...">, the
+L<install|"install [I<option>]... I<pkg>...">, and the
+L<option|"option"> actions.
+
+
+=head2 Update and install actions
+
+The output format is as follows:
+
+ fieldname "\t" value
+ ...
+ "end-of-header"
+ pkgname status localrev serverrev size runtim esttot
+ ...
+ "end-of-updates"
+ other output from post actions, not in machine readable form
+
+The header section currently has two fields: C<location-url> (the
+repository source from which updates are being drawn), and
+C<total-bytes> (the total number of bytes to be downloaded).
+
+The I<localrev> and I<serverrev> fields for each package are the
+revision numbers in the local installation and server repository,
+respectively. The I<size> field is the number of bytes to be
+downloaded, i.e., the size of the compressed tar file for a network
+installation, not the unpacked size. The runtim and esttot fields
+are only present for updated and auto-install packages, and contain
+the currently passed time since start of installation/updates
+and the estimated total time.
+
+Line endings may be either LF or CRLF depending on the current platform.
+
+=over 4
+
+=item C<location-url> I<location>
+
+The I<location> may be a url (including C<file:///foo/bar/...>), or a
+directory name (C</foo/bar>). It is the package repository from which
+the new package information was drawn.
+
+=item C<total-bytes> I<count>
+
+The I<count> is simply a decimal number, the sum of the sizes of all the
+packages that need updating or installing (which are listed subseqently).
+
+=back
+
+Then comes a line with only the literal string C<end-of-header>.
+
+Each following line until a line with literal string C<end-of-updates>
+reports on one package. The fields on
+each line are separated by a tab. Here are the fields.
+
+=over 4
+
+=item I<pkgname>
+
+The TeX Live package identifier, with a possible platform suffix for
+executables. For instance, C<pdftex> and C<pdftex.i386-linux> are given
+as two separate packages, one on each line.
+
+=item I<status>
+
+The status of the package update. One character, as follows:
+
+=over 8
+
+=item C<d>
+
+The package was removed on the server.
+
+=item C<f>
+
+The package was removed in the local installation, even though a
+collection depended on it. (E.g., the user ran C<tlmgr remove
+--force>.)
+
+=item C<u>
+
+Normal update is needed.
+
+=item C<r>
+
+Reversed non-update: the locally-installed version is newer than the
+version on the server.
+
+=item C<a>
+
+Automatically-determined need for installation, the package is new on
+the server and is (most probably) part of an installed collection.
+
+=item C<i>
+
+Package will be installed and isn't present in the local installation
+(action install).
+
+=item C<I>
+
+Package is already present but will be reinstalled (action install).
+
+=back
+
+=item I<localrev>
+
+The revision number of the installed package, or C<-> if it is not
+present locally.
+
+=item I<serverrev>
+
+The revision number of the package on the server, or C<-> if it is not
+present on the server.
+
+=item I<size>
+
+The size in bytes of the package on the server. The sum of all the
+package sizes is given in the C<total-bytes> header field mentioned above.
+
+=item I<runtime>
+
+The run time since start of installations or updates.
+
+=item I<esttot>
+
+The estimated total time.
+
+=back
+
+=head2 option action
+
+The output format is as follows:
+
+ key "\t" value
+
+If a value is not saved in the database the string C<(not set)> is shown.
+
+If you are developing a program that uses this output, and find that
+changes would be helpful, do not hesitate to write the mailing list.
+
+
+=head1 AUTHORS AND COPYRIGHT
+
+This script and its documentation were written for the TeX Live
+distribution (L<http://tug.org/texlive>) and both are licensed under the
+GNU General Public License Version 2 or later.
+
+=cut
+
+# to remake HTML version: pod2html --cachedir=/tmp tlmgr.pl >/foo/tlmgr.html.
+
+### Local Variables:
+### perl-indent-level: 2
+### tab-width: 2
+### indent-tabs-mode: nil
+### End:
+# vim:set tabstop=2 expandtab: #
diff --git a/Build/source/texk/texlive/linked_scripts/texloganalyser/texloganalyser b/Build/source/texk/texlive/linked_scripts/texloganalyser/texloganalyser
index e69de29bb2d..0d54d2f2274 100755
--- a/Build/source/texk/texlive/linked_scripts/texloganalyser/texloganalyser
+++ b/Build/source/texk/texlive/linked_scripts/texloganalyser/texloganalyser
@@ -0,0 +1,351 @@
+#!/usr/bin/env perl
+# This is texloganalyser, for parsing of TeX logs
+# Copyright (c) 2006-2009 Thomas van Oudenhove
+# All rights reserved
+
+# README - important notice:
+# you should feed the first line with the path of your Perl executable
+# (on *nix systems, the output of 'which perl')
+#
+# (this program works at least with perl 5.8.8)
+# Getopt::Long MUST be installed on your system for this program to work
+
+# License:
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+use warnings;
+use strict;
+use Getopt::Long;
+Getopt::Long::Configure ("bundling");
+
+my $version = "0.7";
+my $copyright = "2006-2010";
+my $opt = {
+ help => '',
+ version => '',
+ h => '',
+ o => '',
+ u => '',
+ v => '',
+ p => '',
+ f => '',
+ w => '',
+ e => '',
+ i => '',
+ s => '',
+ r => '',
+ t => '',
+ };
+
+GetOptions(
+ 'help' => \$opt->{help},
+ 'version' => \$opt->{version},
+ 'h' => \$opt->{h},
+ 'o' => \$opt->{o},
+ 'u' => \$opt->{u},
+ 'v' => \$opt->{v},
+ 'p' => \$opt->{p},
+ 'f' => \$opt->{f},
+ 'w' => \$opt->{w},
+ 'e' => \$opt->{e},
+ 'i' => \$opt->{i},
+ 't' => \$opt->{t},
+ 's' => \$opt->{s},
+ 'r' => \$opt->{r},
+ );
+
+my $abstract = {
+ warnings => 0,
+ };
+
+my $texlog = $ARGV[-1];
+
+die "Usage: $0 ".options()." <TeX log>\n"
+ if (not defined $texlog and (not $opt->{help} and
+ not $opt->{version}));
+
+if ($opt->{help}) {
+ printhelp();
+ exit();
+}
+
+if ($opt->{version}) {
+ printversion();
+ exit();
+}
+
+analyze();
+
+sub analyze {
+ my $line;
+ my $output;
+ my $test = 0;
+
+ open my $source, "<", $texlog;
+ while (defined ($line = <$source>)) {
+ undef $output;
+ # display summary and page numbers (default)
+ summary($line);
+ pagenumber($line);
+
+ if ($opt->{e}) {
+ if ($line =~ m/Here is how much/) {
+ $test = 1;
+ $output = "\n";
+ }
+ $output .= $line if $test;
+ }
+ # look for boxes warnings
+ if ($opt->{o} and $opt->{h}) {
+ $output = $line if ($line =~ m/\\hbox/ and
+ $line =~ m/Overfull/);
+ } elsif ($opt->{u} and $opt->{h}) {
+ $output = $line if ($line =~ m/\\hbox/ and
+ $line =~ m/Underfull/);
+ } elsif ($opt->{o} and $opt->{v}) {
+ $output = $line if ($line =~ m/\\vbox/ and
+ $line =~ m/Overfull/);
+ } elsif ($opt->{u} and $opt->{v}) {
+ $output = $line if ($line =~ m/\\vbox/ and
+ $line =~ m/Underfull/);
+ } elsif ($opt->{h} and $opt->{v}) {
+ $output = $line if ($line =~ m/\\vbox/ or
+ $line =~ m/\\hbox/);
+ } elsif ($opt->{o} and $opt->{u}) {
+ $output = $line if ($line =~ m/Underfull/ or
+ $line =~ m/Overfull/);
+ } elsif ($opt->{h}) {
+ $output = $line if ($line =~ m/\\hbox/);
+ } elsif ($opt->{v}) {
+ $output = $line if ($line =~ m/\\vbox/);
+ } elsif ($opt->{o}) {
+ $output = $line if ($line =~ m/Overfull/);
+ } elsif ($opt->{u}) {
+ $output = $line if ($line =~ m/Underfull/);
+ }
+ # look for LaTeX Font Info
+ if ($opt->{f}) {
+ $output = $line if ($line =~ m/Font Info/);
+ }
+ # packages infos
+ if ($opt->{p}) {
+ $output = $line if ($line =~ m/Package/);
+ }
+ # references warnings
+ if ($opt->{r}) {
+ $output = $line if ($line =~ m/Reference/ or
+ $line =~ m/Label(.*)/ and $1 =~ m/multiply defined/);
+ }
+ # TeX files used
+ if ($opt->{t}) {
+ scannedtexfiles($line, "tex");
+ }
+ # sty and cls files used
+ if ($opt->{s}) {
+ scannedfiles($line, "sty");
+ scannedfiles($line, "cls");
+ }
+ # images (pdf, ps, jpg, png)
+ if ($opt->{i}) {
+ foreach my $t ('pdf', 'eps', 'ps', 'png', 'jpg') {
+ scannedfiles($line, $t);
+ }
+ }
+ # display all warnings
+ if ($opt->{w}) {
+ $output = $line if ($line =~ m/Warning/);
+ }
+ print "$output" if defined $output;
+ }
+ print "\n";
+ print_abstract();
+ close $source;
+}
+
+sub pagenumber {
+ my $logline = shift;
+ if ($logline =~ m/.*(\[[0-9]+)/) {
+ print $1."]";
+ }
+}
+
+sub scannedfiles {
+ my ($logline, $filetype) = @_;
+ if ($logline =~ m/^\((.*\.$filetype)/i) {
+ print "(".$1.")";
+ }
+}
+
+sub scannedtexfiles {
+ my ($logline, $filetype) = @_;
+ if ($logline =~ m/\((.*\.$filetype)/i) {
+ print "(".$1.")";
+ }
+}
+
+sub summary {
+ my $line = shift;
+ # prints log summary ()
+ $abstract->{warnings} += 1 if ($line =~ m/Warning/);
+}
+
+sub options {
+ my $stropts = "[";
+ my $multopt = '';
+ foreach my $k (sort keys %{$opt}) {
+ if ($k eq 'version') {
+ $stropts .= "--version|";
+ } elsif ($k eq 'help') {
+ $stropts .= "--help|";
+ } else {
+ $multopt .= $k;
+ }
+ }
+ $stropts .= '-'.$multopt.']';
+ return $stropts;
+}
+
+sub print_abstract {
+ print "The log contained ".$abstract->{warnings}." warnings.\n";
+}
+
+sub printversion {
+ print "This is texloganalyser, version $version\n";
+ print "\t Copyright $copyright Thomas van Oudenhove\n";
+}
+
+sub printhelp {
+ printversion();
+ print "Usage: texloganalyser ".options()." <TeX log>\n";
+ print "\t Displays selective infos of LaTeX logs\n";
+ print "These flags may be used:\n";
+ print "\t e: displays the end of the log\n";
+ print "\t f: outputs the LaTeX Font Infos\n";
+ print "\t h: outputs only warnings about horizontal boxes\n";
+ print "\t i: displays 'images' (pdf, [e]ps, png, jpg) used\n";
+ print "\t o: outputs only warnings about overfull boxes\n";
+ print "\t p: outputs the LaTeX Packages infos\n";
+ print "\t r: displays warnings about references\n";
+ print "\t s: displays .sty and .cls files used\n";
+ print "\t t: displays .tex files used\n";
+ print "\t u: outputs only warnings about underfull boxes\n";
+ print "\t v: outputs only warnings about vertical boxes\n";
+ print "\t w: displays all Warnings\n";
+ print "You may want to have a look at `perldoc texloganalyser`.\n";
+}
+
+__END__
+
+
+=pod
+
+=head1 texloganalyser Documentation
+
+=head2 NAME
+
+texloganalyser -- displays selective infos from TeX log files
+
+=head2 SYNOPSIS
+
+texloganalyser [-efhioprstuvw] tex_log_file
+
+texloganalyser [--version]
+
+texloganalyser [--help]
+
+=head2 DESCRIPTION
+
+Various options may be used to select which information from the TeX log you want to see/analyze (see next section). The default is to display only page numbers (page numbers are B<always> displayed).
+
+=head2 OPTIONS
+
+=over 1
+
+=item B<--version>: displays version information.
+
+=item B<--help>: displays help as a summary of options.
+
+=item B<-e>: displays the end of the log, about the TeX's memory.
+
+=item B<-f>: outputs the LaTeX Font Infos warnings and calculations.
+
+=item B<-h>: outputs only warnings about horizontal boxes.
+
+=item B<-i>: displays 'images' (pdf, [e]ps, png, jpg) used.
+
+=item B<-o>: outputs only warnings about overfull boxes.
+
+=item B<-p>: outputs the LaTeX Packages infos and warnings.
+
+=item B<-r>: displays warnings about references (missing or multiply-defined).
+
+=item B<-s>: displays .sty and .cls files used.
+
+=item B<-t>: displays .tex files used. This option is B<very> useful when you parse logs of master files, to know in which file to look to correct errors/warnings.
+
+=item B<-u>: outputs only warnings about underfull boxes.
+
+=item B<-v>: outputs only warnings about vertical boxes.
+
+=item B<-w>: displays all TeX, LaTeX and font Warnings.
+
+=back
+
+=head2 BUGS
+
+None yet identified. However, the display should be improved...
+
+=head2 HISTORY
+
+This program was first released in 2006. Its purpose was to display selective information of my PhD's TeX log.
+
+=head3 Changelog
+
+=over
+
+=item v0.7: bug patch for .tex files (-t option)
+
+=item v0.6: default displays number of warnings (and page numbers).
+
+ added option: references and label warnings
+
+=item v0.5: use of GetOpt::Long and code cleaning
+
+ added options: display of files used
+ improved documentation
+
+=item v0.4: added features and bugs corrections (options h, v, o and u)
+
+=item v0.3: display of Warnings and last lines of the log
+
+=item v0.2: display of "Font Info" and "Packages" added
+
+=item v0.1: first version, displays only warnings about boxes
+
+=back
+
+=head2 AUTHORS
+
+I<texloganalyser> is a program by Thomas van Oudenhove (L<vanouden@enstimac.fr>). Feel free to contact him for features requests or bugs.
+
+=cut
diff --git a/Build/source/texk/texlive/linked_scripts/thumbpdf/thumbpdf.pl b/Build/source/texk/texlive/linked_scripts/thumbpdf/thumbpdf.pl
index 02ed86e190f..9dc544b77e8 100755
--- a/Build/source/texk/texlive/linked_scripts/thumbpdf/thumbpdf.pl
+++ b/Build/source/texk/texlive/linked_scripts/thumbpdf/thumbpdf.pl
@@ -26,10 +26,10 @@ $^W=1; # turn warning on
#
my $file = "thumbpdf.pl";
my $program = uc($&) if $file =~ /^\w+/;
-my $version = "3.10";
-my $date = "2008/04/16";
+my $version = "3.11";
+my $date = "2010/07/07";
my $author = "Heiko Oberdiek";
-my $copyright = "Copyright (c) 1999-2008 by $author.";
+my $copyright = "Copyright (c) 1999-2010 by $author.";
#
# Reqirements: Perl5, Ghostscript
# History:
@@ -145,6 +145,8 @@ my $copyright = "Copyright (c) 1999-2008 by $author.";
# 2007/11/07 v3.9:
# * Deprecation warning of perl 5.8.8 fixed.
# 2008/04/16 v3.10
+# 2010/07/07 v3.11
+# * \input is used with file name extension for "thumbpdf.tex".
#
### program identification
@@ -180,7 +182,7 @@ my $thumbprefix = "thb";
my $envvar = "THUMBPDF";
my $pdffile = "thumbpdf.pdf";
my $logfile = "thumbpdf.log";
-my $texfile = "thumbpdf";
+my $texfile = "thumbpdf.tex";
my $package = "thumbpdf.sty";
my $readme = "readme.txt";
my $gscnffile = "thumbpdf.gs";