diff options
author | Peter Breitenlohner <peb@mppmu.mpg.de> | 2011-03-01 09:20:55 +0000 |
---|---|---|
committer | Peter Breitenlohner <peb@mppmu.mpg.de> | 2011-03-01 09:20:55 +0000 |
commit | 745b336f730a44982185b9218413f29352d55246 (patch) | |
tree | bece2f4f19ce54139b70dd29221b55d0d4e535de /Build/source | |
parent | 82e45746ba7c21a9b375025bcac04c5ab066cce1 (diff) |
update linked scripts Master -> Build
git-svn-id: svn://tug.org/texlive/trunk@21563 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source')
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"; |