From edb2136c619d2c59bcb6dfd431c4c7636ff8913d Mon Sep 17 00:00:00 2001 From: Karl Berry Date: Sun, 7 Apr 2013 18:42:53 +0000 Subject: texmf -> texmf-dist: scripts git-svn-id: svn://tug.org/texlive/trunk@29719 c570f23f-e606-0410-a88d-b1316a301751 --- .../texmf-dist/scripts/bitmap2eps/bitmap2eps.vbs | 108 + Master/texmf-dist/scripts/chktex/deweb.pl | 122 + Master/texmf-dist/scripts/ps2eps/ps2eps.pl | 945 + Master/texmf-dist/scripts/psutils/extractres.pl | 101 + Master/texmf-dist/scripts/psutils/fixdlsrps.pl | 54 + Master/texmf-dist/scripts/psutils/fixfmps.pl | 21 + Master/texmf-dist/scripts/psutils/fixpsditps.pl | 25 + Master/texmf-dist/scripts/psutils/fixpspps.pl | 58 + Master/texmf-dist/scripts/psutils/fixscribeps.pl | 19 + Master/texmf-dist/scripts/psutils/fixtpps.pl | 28 + Master/texmf-dist/scripts/psutils/fixwfwps.pl | 33 + Master/texmf-dist/scripts/psutils/fixwpps.pl | 31 + Master/texmf-dist/scripts/psutils/fixwwps.pl | 20 + Master/texmf-dist/scripts/psutils/includeres.pl | 45 + Master/texmf-dist/scripts/psutils/psmerge.pl | 86 + Master/texmf-dist/scripts/tetex/fmtutil-sys.sh | 29 + Master/texmf-dist/scripts/tetex/fmtutil.sh | 1017 + .../texmf-dist/scripts/tetex/texconfig-dialog.sh | 486 + Master/texmf-dist/scripts/tetex/texconfig-sys.sh | 31 + Master/texmf-dist/scripts/tetex/texconfig.sh | 1432 + Master/texmf-dist/scripts/tetex/texlinks.sh | 335 + Master/texmf-dist/scripts/tex4ht/htcopy.pl | 46 + Master/texmf-dist/scripts/tex4ht/htmove.pl | 45 + Master/texmf-dist/scripts/texlive/NEWS | 103 + .../scripts/texlive/lua/texlive/getopt.tlu | 99 + .../scripts/texlive/lua/texlive/tlpdb.tlu | 259 + .../scripts/texlive/lua/texlive/utils.tlu | 78 + Master/texmf-dist/scripts/texlive/rungs.tlu | 43 + Master/texmf-dist/scripts/texlive/test-tlpdb.tlu | 45 + Master/texmf-dist/scripts/texlive/texconf.tlu | 728 + Master/texmf-dist/scripts/texlive/tlmgr.pl | 7503 ++++ Master/texmf-dist/scripts/texlive/tlmgrgui.pl | 2703 ++ .../texmf-dist/scripts/texlive/uninstall-win32.pl | 93 + .../scripts/texlive/var/texcatalogue.keywords | 45008 +++++++++++++++++++ Master/texmf-dist/scripts/tlgs/gswin32/eps2eps.tlu | 70 + Master/texmf-dist/scripts/tlgs/gswin32/pdf2dsc.tlu | 56 + Master/texmf-dist/scripts/tlgs/gswin32/pdfopt.tlu | 64 + .../texmf-dist/scripts/tlgs/gswin32/ps2ascii.tlu | 74 + Master/texmf-dist/scripts/tlgs/gswin32/ps2pdf.tlu | 113 + .../texmf-dist/scripts/tlgs/gswin32/ps2pdf12.tlu | 113 + .../texmf-dist/scripts/tlgs/gswin32/ps2pdf13.tlu | 113 + .../texmf-dist/scripts/tlgs/gswin32/ps2pdf14.tlu | 113 + Master/texmf-dist/scripts/tlgs/gswin32/ps2ps.tlu | 76 + Master/texmf-dist/scripts/tlgs/gswin32/ps2ps2.tlu | 79 + Master/texmf-dist/scripts/xindy/texindy.pl | 639 + Master/texmf-dist/scripts/xindy/xindy.pl | 927 + 46 files changed, 64216 insertions(+) create mode 100644 Master/texmf-dist/scripts/bitmap2eps/bitmap2eps.vbs create mode 100755 Master/texmf-dist/scripts/chktex/deweb.pl create mode 100755 Master/texmf-dist/scripts/ps2eps/ps2eps.pl create mode 100755 Master/texmf-dist/scripts/psutils/extractres.pl create mode 100755 Master/texmf-dist/scripts/psutils/fixdlsrps.pl create mode 100755 Master/texmf-dist/scripts/psutils/fixfmps.pl create mode 100755 Master/texmf-dist/scripts/psutils/fixpsditps.pl create mode 100755 Master/texmf-dist/scripts/psutils/fixpspps.pl create mode 100755 Master/texmf-dist/scripts/psutils/fixscribeps.pl create mode 100755 Master/texmf-dist/scripts/psutils/fixtpps.pl create mode 100755 Master/texmf-dist/scripts/psutils/fixwfwps.pl create mode 100755 Master/texmf-dist/scripts/psutils/fixwpps.pl create mode 100755 Master/texmf-dist/scripts/psutils/fixwwps.pl create mode 100755 Master/texmf-dist/scripts/psutils/includeres.pl create mode 100755 Master/texmf-dist/scripts/psutils/psmerge.pl create mode 100755 Master/texmf-dist/scripts/tetex/fmtutil-sys.sh create mode 100755 Master/texmf-dist/scripts/tetex/fmtutil.sh create mode 100755 Master/texmf-dist/scripts/tetex/texconfig-dialog.sh create mode 100755 Master/texmf-dist/scripts/tetex/texconfig-sys.sh create mode 100755 Master/texmf-dist/scripts/tetex/texconfig.sh create mode 100755 Master/texmf-dist/scripts/tetex/texlinks.sh create mode 100644 Master/texmf-dist/scripts/tex4ht/htcopy.pl create mode 100644 Master/texmf-dist/scripts/tex4ht/htmove.pl create mode 100644 Master/texmf-dist/scripts/texlive/NEWS create mode 100644 Master/texmf-dist/scripts/texlive/lua/texlive/getopt.tlu create mode 100644 Master/texmf-dist/scripts/texlive/lua/texlive/tlpdb.tlu create mode 100644 Master/texmf-dist/scripts/texlive/lua/texlive/utils.tlu create mode 100755 Master/texmf-dist/scripts/texlive/rungs.tlu create mode 100755 Master/texmf-dist/scripts/texlive/test-tlpdb.tlu create mode 100755 Master/texmf-dist/scripts/texlive/texconf.tlu create mode 100755 Master/texmf-dist/scripts/texlive/tlmgr.pl create mode 100644 Master/texmf-dist/scripts/texlive/tlmgrgui.pl create mode 100644 Master/texmf-dist/scripts/texlive/uninstall-win32.pl create mode 100644 Master/texmf-dist/scripts/texlive/var/texcatalogue.keywords create mode 100755 Master/texmf-dist/scripts/tlgs/gswin32/eps2eps.tlu create mode 100755 Master/texmf-dist/scripts/tlgs/gswin32/pdf2dsc.tlu create mode 100755 Master/texmf-dist/scripts/tlgs/gswin32/pdfopt.tlu create mode 100755 Master/texmf-dist/scripts/tlgs/gswin32/ps2ascii.tlu create mode 100755 Master/texmf-dist/scripts/tlgs/gswin32/ps2pdf.tlu create mode 100755 Master/texmf-dist/scripts/tlgs/gswin32/ps2pdf12.tlu create mode 100755 Master/texmf-dist/scripts/tlgs/gswin32/ps2pdf13.tlu create mode 100755 Master/texmf-dist/scripts/tlgs/gswin32/ps2pdf14.tlu create mode 100755 Master/texmf-dist/scripts/tlgs/gswin32/ps2ps.tlu create mode 100755 Master/texmf-dist/scripts/tlgs/gswin32/ps2ps2.tlu create mode 100755 Master/texmf-dist/scripts/xindy/texindy.pl create mode 100755 Master/texmf-dist/scripts/xindy/xindy.pl (limited to 'Master/texmf-dist') diff --git a/Master/texmf-dist/scripts/bitmap2eps/bitmap2eps.vbs b/Master/texmf-dist/scripts/bitmap2eps/bitmap2eps.vbs new file mode 100644 index 00000000000..fbfc850e851 --- /dev/null +++ b/Master/texmf-dist/scripts/bitmap2eps/bitmap2eps.vbs @@ -0,0 +1,108 @@ +option explicit +On Error Resume next + +dim oWsh, oArgs, oFS, oDia, oTS, infile, outfile, f, c, result + +set oWsh = createobject( "wscript.Shell" ) +Set oFS = CreateObject("Scripting.FileSystemObject") +Set oArgs = wscript.arguments + +' assume sam2p and bmeps on searchpath +If oArgs.count = 0 Then + ' This appears to fail on win7: 'No file to convert' messagebox + Set oDia = CreateObject("UserAccounts.CommonDialog") + 'oDia.InitialDir = oWsh.SpecialFolders("MyDocuments") + oDia.filter = _ + "Bitmaps|*.bmp;*.png;*.gif;*.bmp;*.jpg;*.jpeg;*.tif;*.tiff|All files|*.*" + oDia.filterindex = 1 + oDia.flags = 1 + If oDia.ShowOpen = False then + msgBox "No file to convert" + wscript.quit + End If + infile = oDia.filename + ' MsgBox "infile: " & infile +Else + ' may have to flip slashes if the file was on a network drive !!! + infile = replace(oArgs(0), "/", "\") + infile = ofs.getabsolutepathname(infile) +End If + +' name of output file +outfile = ofs.buildpath(ofs.getparentfoldername(infile), _ + ofs.getbasename(infile) & ".eps") + +If ofs.fileexists(outfile) Then + result = MsgBox(outfile & vbcrlf & "exists; overwrite?", _ + vbyesno+vbquestion) + If result <> vbyes Then + wscript.quit + Else + oFS.deletefile(outfile) + Err.clear + If ofs.fileexists(outfile) Then + MsgBox "Unable to remove old " & outfile, vbcritical + wscript.quit + End If + End If +End If + +' MsgBox "trying sam2p" +owsh.run "sam2p """ & infile & """ EPS: """ & outfile & """", 0 +If Err Then + ' This is a failure of this script to start sam2p, + ' not just a failure of sam2p to do its job + MsgBox "Unspecified failure", vbcritical + wscript.quit +End If +wscript.sleep(1000) + +If ofs.fileexists(outfile) Then + MsgBox ofs.getbasename(infile) & "." & ofs.getextensionname(infile) & _ + " successfully converted", vbInformation +Else + Err.clear + ' clear sam2p temp files + For Each f in ofs.getfolder(ofs.getparentfoldername(infile)).files + If InStr(f.Name, "tmp__sam2p") = 1 Then + f.delete + Err.clear + End If + Next + ' MsgBox "trying bmeps" + owsh.run "bmeps -c """ & infile & """ """ & outfile & """", 0 + If Err Then + MsgBox "Unspecified failure", vbcritical + wscript.quit + End If + wscript.sleep(1000) + If ofs.fileexists(outfile) Then + result = True + ' check the eps file; it may contain just an error mesage from bmeps + Set oTS = ofs.opentextfile (outfile, 1) + If Err Then + result = False + Err.clear + Else + c = oTS.read(2) + If Err Then + result = false + Err.clear + Else + If c <> "%!" Then + result = False + End If + End If + oTS.close + End If + If result then + MsgBox ofs.getbasename(infile) & "." & ofs.getextensionname(infile) & _ + " successfully converted with bmeps", vbInformation + Else + 'ofs.deletefile(outfile) + MsgBox "Conversion failed", vbCritical + End If + Else + MsgBox "Conversion failed", vbCritical + End If +End If diff --git a/Master/texmf-dist/scripts/chktex/deweb.pl b/Master/texmf-dist/scripts/chktex/deweb.pl new file mode 100755 index 00000000000..83de358bc2e --- /dev/null +++ b/Master/texmf-dist/scripts/chktex/deweb.pl @@ -0,0 +1,122 @@ +#! /usr/bin/env perl +# deweb v1.2, kills the C sections of a CWEB file, for passing to ChkTeX. +# Copyright (C) 1996 Jens T. Berger Thielemann +# +# 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. +# +# 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# Contact the author at: +# Jens Berger +# Spektrumvn. 4 +# N-0666 Oslo +# Norway +# E-mail: +# +# +# + +print STDERR "DeWEB v1.2 - Copyright 1996 Jens T. Berger Thielemann\n"; + +undef $/; +$texmode = 1; +$_ = <>; + +while(/\@/) +{ + &out($`); + $_ = $'; + + if(/^@/) { + $_ = $'; + &out('@'); + next; + } + + if(/^([\s\n])/) { + $_ = $'; + print "\n" if $1 eq "\n"; + $texmode = 1; + next; + } + + if(/^[cpd]/i) { + $_ = $'; + $texmode = 0; + next; + } + + if(/^\,/i) { + $_ = $'; + print '\,'; + next; + } + + if(m!^/!) { + $_ = $'; + print '\\\\'; + next; + } + + if(/^[h\&\|\;\#\+]/i || /^i.*/i) { + $_ = $'; + next; + } + + if(/^\*[0-9\*]?((.|\n)*?\.)/) { + $_ = $'; + print $1; + $texmode = 1; + next; + } + + if(/^[<(^.t!]((.|\n)*?)\@\>/i) { + $_ = $'; + print '{'.$1.'}'; + $texmode = 0; + next; + } + + if(/^[=]((.|\n)*?)\@\>/) { + $_ = $'; + print &printnl($1); + next; + } + + if(/^[fsl](\s+\S+\s+\S+)|^\'(.|\n)*?\'|^\[((.|\n)*?)\@\]/i) { + $_ = $'; + print &printnl($+); + next; + } + + @line = split(/\n/, $_, 2); + print STDERR "Unknown opcode, ignored. Buffer:\n$line[0]\n"; + +} + +print $_; + +sub printnl { + my($foo); + if(defined $_[0]) { + $foo = $_[0]; + $foo =~ s/.//g; + } else { + $foo = ""; + } + $foo; +} + +sub out { + print $texmode? $_[0] : &printnl($_[0]); +} diff --git a/Master/texmf-dist/scripts/ps2eps/ps2eps.pl b/Master/texmf-dist/scripts/ps2eps/ps2eps.pl new file mode 100755 index 00000000000..7daa8752bff --- /dev/null +++ b/Master/texmf-dist/scripts/ps2eps/ps2eps.pl @@ -0,0 +1,945 @@ +#!/usr/bin/env perl +# The expression in the previous line replaces the unix specific line +# {#!/usr/bin/perl}. +# ps2eps - convert PostScript to EPS (Encapsulated PostScript) files +# ------------------------------------------------------------------- +# $Id: ps2eps,v 1.68 2010-05-07 19:42:35 bless Exp $ +# ------------------------------------------------------- +# (C)opyright 1999-2009 Roland Bless +# +# 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. +# +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Author: Roland Bless +# Send bug reports to roland bless.de +# ------------------------------------------------------------------- +# Additional filtering is performed when Windows generated PostScript files +# are processed. Some instructions will otherwise lead to bad output +# if EPS-file gets embedded into other PostScript files. +# +# Requirements: +# + perl +# + gs (ghostscript supporting pbm output) +# + bbox (a little C program [ANSI-C - should work on every platform] +# for calculation of the actual BoundingBox) + +use POSIX; + +#use warnings; + +#use Getopt package +use Getopt::Long; +Getopt::Long::Configure("no_ignore_case"); + +$prgname= "ps2eps"; + +if (! -d "/usr/bin") +{ # we assume that we are running under native windows + $ghostscriptname = "gswin32c"; + $NULLDEV = "nul"; +} +else +{ # Unix or cygwin + $ghostscriptname = "gs"; + $NULLDEV = "/dev/null 2>&1"; +} + +$bboxver=`bbox >$NULLDEV -V`; +$bboxname= ($?== -1) ? "" : "bbox"; +$version= '$Id: ps2eps,v 1.68 2010-05-07 19:42:35 bless Exp $'; #' +$insertPScode= 1; # Insert surrounding Postscript code +$infhandle = STDIN; # Standard input is the default input file +$outfhandle = STDOUT; # Standard output is default output if STDIN is input +$infname= '-'; +$outfname= '-'; +$forceoverwrite=0; # do not overwrite existing files +$ignoreBB= 0; # ignore existing Bounding Box comment +$removeDSC= 1; # remove Adobe document structure comments +$removeADO= 1; # remove Adobe printer Driver console Output [Page: ...] +$ignoreEOFDSC= 0; # ignore %%EOF DSC hint +$removePreview= 0; # remove preview +$quiet= 0; # write progress to stdout +$resolution= 144; # resolution for bounding box calculation is 2x72 dpi +$trytofixps= 1; # try to fix postscript code +$forcefixps= 0; # fix postscript code unconditionally if eq 1 +$filterorientation= 1;# filter Orientation line +$looseBB=''; # default: tight bounding box +$clip=0; # do not clip +$warnings=0; # do not print warnings concerning postscript sanity +$debuggs=0; # no debugging of ghostscript call, turn this on if you want to see the gs call +$inch=2.54; # one inch is 2.54cm +$fullcolors= 1; # use ppm format (24-bit full color) +$trailerseen= 0; # Trailer comment seen? +$PSversion="2.0"; # default Postscript Version +$PSDSCversion="2.0"; # default Postscript DSC Version +$translate_x= 0; # translate by x postscript points +$translate_y= 0; # translate by y postscript points +$allbinary= 0; # treat postscript as binary do not filter or change stuff +$alphaopt=""; # rendering option for ghostscript "-dTextAlphaBits=4 -dGraphicsAlphaBits=4" +$hiresprecision=0.5; # amount that is changed of HiresBB in case that looseBB was requested + +$defaultext = '(ps|prn)'; # default extension +$defaultoutext = '.eps'; # default output extension +$envname_size = 'PS2EPS_SIZE'; +$envname_gsbbox = 'PS2EPS_GSBBOX'; + +$gpar=""; +$known_papersizes="11x17|ledger|legal|letter(small)?|arch[A-E]|a([0-9]|10)|isob[0-6]|b[0-5]|c[0-6]|jisb[0-6]|fls(a|e)|halfletter"; +$papersize_help="11x17,ledger,legal,letter,lettersmall,archA,archB,archC,archD,archE\ +a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,isob0,isob1,isob2,isob3,isob4,isob5,isob6,\ +b0,b1,b2,b3,b4,b5,c0,c1,c2,c3,c4,c5,c6,\ +jisb0,jisb1,jisb2,jisb3,jisb4,jisb5,jisb6,flsa,flse,halfletter\n"; +$trigger= 0; +$notsane= 0; +$dummy=""; + +@ver= split(/ /,$version); + +# filename for temporary files +if ($^O =~ /MSWin32/i or $^O =~ /cygwin/i) +{ + # it is less trouble to use the current directory if working on + # cygwin and nevertheless using gswin32c. + $tmpfname= "$prgname.$$"; + $win=1; +} +elsif (defined($ENV{'TMP'})) +{ + $tmpdir= $ENV{'TMP'}; + $filesep= ($tmpdir =~ /^?\:\\/) ? '\\' : '/'; + if ($tmpdir =~ /$filesep$/) + { $tmpfname= $tmpdir . "$prgname.$$"; } + else + { $tmpfname= $tmpdir . "$filesep$prgname.$$"; } + $win=1; +} +else #assume we're on a UNIXBOX +{ + $tmpfname= "/tmp/" . "$prgname.$$"; + $win=0; +} + + +$licensetxt= "\ + 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.\ +\ + 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, write to the Free Software\ + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n"; + +@prgidtxt= ( "$prgname - convert PostScript to EPS (Encapsulated PostScript) files\n", + "(C)opyright 1998-2009 Roland Bless\n\n" ); + +@helptxt= ("Version: $ver[2]\n", + "Operation:\n", + " Without any argument, $prgname reads from standard input\n", + " and writes to standard output.\n", + " If filenames are given as arguments they are processed\n", + " one by one and output files are written to filenames\n", + " with extension '$defaultoutext'. If input filenames have the extension\n", + " '.ps' or '.prn', this extension is replaced with '$defaultoutext'.\n", + " In all other cases '$defaultoutext' is appended to the input filename.\n", + " Please note that PostScript files for input should contain\n", + " only one single page.\n\n", + " If BoundingBox in output seems to be wrong, please try options --size or --ignoreBB.\n\n" ); + +@usagetxt= ("Syntax:\n", + " $prgname [-f] [-q] [-N] [-O] [-n] [-P] [-c] [-b] [-C] [-m] [-B] [-E] [-s ] [-R +|-|^] [-t ] [-l] [-g] [-d] [-H] [-h|--help] [-g] [-a] [-W] [-L] [-V|--version] [--] [psfile1] [psfile2] [...]\n", + "Options:\n", + " -f, --force force overwriting existing files\n", + " -q, --quiet quiet operation (no output while processing files)\n", + " -N, --noinsert do not insert any postscript code\n", + " -O, --preserveorientation do not filter Orientation: header comment\n", + " -n, --nofix do not try to fix postscript code\n", + " -P, --removepreview remove preview image (smaller file, but no preview)\n", + " -F, --fixps fix postscript code unconditionally\n", + " -c, --comments preserve document structure comments\n", + " -b, --binary treat postscript as binary, do not modify characters\n", + " -C, --clip insert postscript code for clipping\n", + " -m, --mono use black/white bitmap as base for calculation\n", + " -s, --size= page size (a0-a10,letter,...) or in format XxY[cm|in] (default:cm), where X and Y are numbers\n", + " use --size=list to list pre-defined page sizes\n", + " -R, --rotate= rotate resulting EPS. : +=+90 (clockw.),-=-90 (counter-clockw.) ^=180 degrees\n", + " -t, --translate specify x,y offset (may be negative) in postscript points (1/72 dpi)\n", + " -r, --resolution specify dpi resolution to render with ghostscript (default 144)\n", + " -l, --loose expand the original bounding box by one point in each direction\n", + " -B, --ignoreBB do not use existing bounding box as page size for rendering\n", + " -E, --ignoreEOF do not use %%EOF as hint for end of file\n", + " -g, --gsbbox use internal bbox device of ghostscript\n", + " -H, --no-hires do not use a HiResBoundingBox\n", + " -h, --help help information\n", + " -L, --license show licensing information\n", + " -V, --version show version information\n", + " -d, --debuggs show ghostscript call\n", + " -a, --accuracy improve accuracy during rendering (maybe slower)\n", + " -W, --warnings show warnings about sanity of generated eps file\n", + " -- all following arguments are treated as files\n", + " (allows filenames starting with -)\n", + "\n", + "Arguments:\n", + " One or more names of PostScript files for input\n"); + +## %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +## -- argument checking -- +## %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +#environment variable for papersize +if (defined($ENV{"$envname_size"})) +{ + $opt_s= $ENV{"$envname_size"}; +} +else +{ + $opt_s = ''; # for s-option +} + +if (defined($ENV{"$envname_gsbbox"})) +{ + $bboxname=""; +} + +$opt_t = ''; # for t-option +$opt_R = ''; # for R-option +$opt_r = ''; # for r-option +$stopnow = 0; +die "Wrong option(s), please check usage with $prgname --help\n" unless +GetOptions('f|force' => \$forceoverwrite, + 'q|quiet' => \$quiet, + 'm|mono' => sub { $fullcolors = 0 }, + 'n|nofix' => sub { $trytofixps = 0 }, + 'F|fixps' => \$forcefixps, + 'N|noinsert' => sub { $insertPScode = 0 }, + 'O|preserveorientation' => sub { $filterorientation= 0 }, + 'P|removepreview' => \$removePreview, + 'c|comments' => sub { $removeDSC = 0 }, + 'b|binary' => sub { $allbinary = 1 }, + 'C|clip' => \$clip, + 'l|loose' => sub { $looseBB = '-l' }, + 'B|ignoreBB' => \$ignoreBB, + 'E|ignoreEOF'=> \$ignoreEOFDSC, + 's|size=s' => \$opt_s, + 't|translate=s' => \$opt_t, + 'r|resolution=s' => \$opt_r, + 'R|rotate=s' => \$opt_R, + 'g|gsbbox' => sub { $bboxname=""; }, + 'H|nohires' => \$nohires, + 'h|help' => sub { $stopnow = 1; print @prgidtxt,@helptxt,@usagetxt,"\nAuthor: Roland Bless (roland\@bless.de)\n\n"; }, + 'L|license' => sub { $stopnow = 1; print @prgidtxt,$licensetxt,"\nAuthor: Roland Bless (roland\@bless.de)\n\n"; }, + 'a|accuracy' => sub { $alphaopt = '-dTextAlphaBits=4 -dGraphicsAlphaBits=4' }, + 'd|debuggs' => \$debuggs, + 'W|warnings' => \$warnings, + 'V|version' => sub { $stopnow = 1; print @prgidtxt,"Version: $ver[2]\n"; });exit if ($stopnow); + +## %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +## -- wildcard processing -- +## %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +## internal wildcard processing for current directory, +## only used for non UNIX-based OSs (which may lack shell wildcard expansion) +@filenames = (); +foreach $object (@ARGV) { + if ($win && $object =~ m/\*/o) # asterisk is present in filename + { + $wildcard = $object; + $wildcard =~ s/\./\\\./g; # replace . in pattern with \. + $wildcard =~ s/\*/\(\.\*\)/g; # replace * in pattern with (.*) + opendir(DIR,'.') || die 'Unable to open current directory, stopped'; # open current directory + print STDERR $wildcard; + @fid = grep { /$wildcard(\.$defaultext)?/i } readdir(DIR); + foreach (@fid) { push @filenames, $_; } + closedir DIR; + } + else { push @filenames, $object; } +} # end foreach +$filenames[0]= '-' if (scalar(@filenames) == 0); # no file arguments, use STDIN as input + +print STDERR "Input files: @filenames\n" if (!$quiet); + +if ($opt_r ne '') +{ + $resolution=$opt_r; +} + +# papersize stuff +if ($opt_s ne '') +{ + # if explicit size is given, ignore existing BoundingBox always + $ignoreBB = 1; + $pagedimension = $opt_s; + if ($opt_s eq "list") + { + print STDERR "Available paper sizes: $papersize_help"; + exit 0; + } + + #explicit format XxY[cm|in] + if ($pagedimension =~ /(\d*\.?\d+)x(\d*\.?\d+)/) + { + ($x_dim, $dummy, $y_dim, $unit)= split(/(x|cm|in)/,$pagedimension); + if ( $x_dim !~ /^\d*\.?\d+$/ ) + { die "$x_dim in $arg is not a valid number, stopped"; } + if ( $y_dim !~ /^\d*\.?\d+$/ ) + { die "$y_dim in $arg is not a valid number, stopped"; } + + #print STDERR "xdim: $x_dim ydim: $y_dim unit:$unit\n" ; + if (!defined($unit) ) + { + $unit='cm'; + $opt_s=$opt_s . $unit; + } + if ( $unit ne 'in' ) # assume centimeters + { # calculate dimension in pixels (remember: resolution is in dpi) + $xpixels= int(($x_dim * $resolution) / $inch)+1; + $ypixels= int(($y_dim * $resolution) / $inch)+1; + $gpar= "-g${xpixels}x${ypixels}"; + } + else + { + $xpixels= int($x_dim * $resolution)+1; + $ypixels= int($y_dim * $resolution)+1; + $gpar= "-g${xpixels}x${ypixels}"; + } + } #endif XxY in opt_s + else + { + if ($opt_s =~ /$known_papersizes/) + { + $gpar="-sPAPERSIZE=$opt_s"; + } + else + { + print STDERR "Error: Unknown paper size: $opt_s\n Acceptable papersizes are:$papersize_help\n"; + exit 1; + } + } +} + +#translate option +if ($opt_t ne '') +{ + ($translate_x,$translate_y)= split(/\,/,$opt_t); +} + +#rotate +$rotright='-90 rotate'; +$rotleft='90 rotate'; +$rotupsidedown='180 rotate'; +$rotate=''; +if ($opt_R ne '') +{ + if ($opt_R eq '+') { $rotate=$rotright; } + elsif ($opt_R eq '-') { $rotate=$rotleft; } + elsif ($opt_R eq '^') { $rotate=$rotupsidedown; } + else { die "Wrong parameter for option -R: Valid are only +,-,^\n"; }; +} + +$device= $fullcolors ? "ppmraw" : "pbmraw"; + +## %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +## -- iterate over different input files -- +## %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +PROCESSFILE: +while ($infname= (shift @filenames)) +{ + # reset filter definitions for each file + undef $linefilter; + undef $rangefilter_begin; + undef $rangefilter_end; + $fixthisps= $trytofixps; + $fixmsgprinted= 0; + + if (!$quiet) { print STDERR "Processing: $infname\n"; } + unless (open($infhandle,"<$infname")) + { # skip over this file + print STDERR "$prgname: Can't open $infname: $!\n"; + next PROCESSFILE; + } + + # buffer input from stdin into temporary file, because it has to be read twice + # one time for ghostscript processing, the second time for generating output + if ($infname eq '-') # input is stdin + { + $tmpfhandle=''; + open($tmpfhandle,">$tmpfname") or + die "Cannot open temporary file $tmpfname for writing: $!\n"; + } + else # input is not stdin + { + undef $tmpfhandle; + #if filename ends with $defaultext usually .ps or .prn, replace the extension with $defaultoutext + if ($infname =~ /\.$defaultext$/i) + { + $outfname= $infname; $outfname =~ s/\.$defaultext$/$defaultoutext/i; + } + else # otherwise simply append the extension $defaultoutext + { + $outfname= $infname . "$defaultoutext"; + } + } #end else input is not stdin + + ## %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + ## -- process input file -- + ## %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + $linefilter= '^$'; #'# filter empty lines by default + while (<$infhandle>) + { + # get postscript and DSC version + if (/%!PS-Adobe-(\S+).*EPSF-(\S+)/) + { + $PSversion=$1; + $PSDSCversion=$2; + if (! ($PSversion =~ /\d+\.\d+/)) + { + $PSDSCversion="2.0"; + } + if (! ($PSDSCversion =~ /\d+\.\d+/)) + { + $PSDSCversion="2.0"; + } + } + + # check for existing BoundingBox parameters + if ( /^%%\s*BoundingBox:\s*(.*)/ && !defined($eBBllx) ) + { + $BBarg= $1; + # accept even negative and fractional BBs + if ( $BBarg =~ /(\-?\d+\.?\d*\s+){3,}\d+/ ) # ignore %% BoundingBox: (atend) comments + { + ($eBBllx,$eBBlly,$eBBurx,$eBBury,$dummy)= split /\s/,$BBarg; + #print STDERR "Existing BB: $eBBllx,$eBBlly,$eBBurx,$eBBury\n"; + if (int($eBBllx) < 0) { $translate_x= - int($eBBllx-0.5); } + if (int($eBBlly) < 0) { $translate_y= - int($eBBlly-0.5); } + + $xpixels= int((($eBBurx-$eBBllx) * $resolution)/72 + 0.5); + $ypixels= int((($eBBury-$eBBlly) * $resolution)/72 + 0.5); + if (!$ignoreBB) + { + $gpar= "-g${xpixels}x${ypixels}"; + # check for meaningful values + if (($xpixels <= 1) || ($ypixels <= 1)) + { + $gpar=""; undef $eBBllx; undef $eBBlly; + } + else + { + if (!$quiet) + { + print STDERR "Rendering with existing $_"; + if (int($eBBllx) < 0 || int($eBBlly) < 0) + { + print STDERR "WARNING: existing Bounding Box shows negative values - shifting\n"; + } + } + } + } #endif !$ignoreBB + } #endif $BBarg =~ + } + + + if ($fixthisps) # try to fix bad postscript code + { + # check for Windows 3.x output + if ( /^Win.*Dict/ ) + { + if (!$quiet && !$fixmsgprinted) + { print STDERR "Windows 3.5 generated Postscript file detected, fixing\n"; } + $linefilter= '^(EJ|RS)'; + $rangefilter_begin= '^statusdict'; + $rangefilter_end= 'cvx\ settransfer$'; #' + $fixmsgprinted= 1; # stop printing message + } + else + { + if ( /^%%Creator:\s*Wind.U\s*Xprinter/ ) + { + if (!$quiet && !$fixmsgprinted) + { print STDERR "Star/OpenOffice generated Postscript file detected, fixing\n"; } + $linefilter= '^rs'; + $fixmsgprinted= 1; # stop printing message + } + else + { + if ( $forcefixps || + /^\/NTPS/ || + /Creator:\s*(AdobePS|Pscript|.*Windows)/i ) #check for NT generated output + { + if (!$quiet && !$fixmsgprinted) + { + if ($forcefixps) + { + print STDERR "Postscript filtering requested, fixing\n"; + } + else + { + print STDERR "Windows generated Postscript file detected, fixing\n"; + } + } + $rangefilter_begin= '^((\[\{)|(featurebegin\{))$'; #' + $rangefilter_end= '^(\} stopped cleartomark|\}featurecleanup)'; + $exclude_rangefilter_begin= '^(?i)%%BeginNonPPDFeature'; #' + $exclude_rangefilter_end= '^(?i)%%EndNonPPDFeature'; + #$triggered_rangefilter_begin= ''; #' + #$triggered_rangefilter_end= ''; #' + $fixsearchpat='(^|\s)(initmatrix|initclip|initgraphics)(\s|$)'; + $fixreplacepat=' '; + $fixmsgprinted= 1; # stop printing message + } # end if NTPS + } #end else + } + } #end if trytofixps + + if (defined($tmpfhandle)) + { + print $tmpfhandle $_ or die "$prgname: Failure during writing to temporary file $tmpfname"; + } + + if (/^%%EOF\s*$/) + { + $totalEOFDSC++ + } + } #end while <$infhandle> + + if (defined($tmpfhandle)) + { + close($tmpfhandle); + } + else + { + $tmpfhandle= $infhandle; + $tmpfname= $infname; + } + + ## %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + ## -- calculate the bounding box -- + ## %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + if ($translate_x!=0 || $translate_y!=0) + { + $translation="$translate_x $translate_y translate"; + $translatecmd="-c \'$translation\'"; + } + else + { + $translation=""; + $translatecmd=""; + } + + if (!$quiet) + { + print STDERR "Calculating Bounding Box..."; + if ($opt_s) + { + print STDERR "using page size $opt_s..."; + } + } + + $rotatecmd=''; + if ($rotate ne '') + { + $rotatecmd="-c \"$rotate\"" + } + if ($bboxname ne '') + { + $cmdline="$ghostscriptname -dSAFER -dNOPAUSE $alphaopt -q $gpar -r$resolution -sDEVICE=$device -sOutputFile=- $translatecmd -f \"$tmpfname\" -c showpage -c quit | $bboxname -r $resolution"; + } + else + { + if (!$quiet) { + print STDERR "...using bbox device of $ghostscriptname..."; + } + $cmdline = "$ghostscriptname -dSAFER -dBATCH -dNOPAUSE -q $gpar -r$resolution -sDEVICE=bbox -sOutputFile=- -c \"/setpagedevice {pop} def\" $translatecmd -f \"$tmpfname\" -c quit 2>&1"; + } + + if ($debuggs) { print STDERR "\nCalling: $cmdline\n"; } + + # actual ghostscript call + $boundingbox=`$cmdline`; + if ($debuggs) { print STDERR "Call result: $boundingbox"; } + + # check result of gs call + if ($boundingbox !~ /^%%BoundingBox/m) + { + die "Error: Could not determine bounding box!\n", + "I suppose $ghostscriptname had some trouble interpreting the postscript-file $infname. Exiting now.\n"; + } + + $boundingbox =~ /^%%HiResBoundingBox:\s*(.*)/m; + if (defined($1)) # HiResBoundingBox given + { + ($hcBBllx,$hcBBlly,$hcBBurx,$hcBBury,$dummy)= split(/\s/,$1); + $hiresboundingbox="%%HiResBoundingBox: $hcBBllx $hcBBlly $hcBBurx $hcBBury\n"; + $cBBllx = floor($hcBBllx); + $cBBlly = floor($hcBBlly); + $cBBurx = ceil($hcBBurx); + $cBBury = ceil($hcBBury); + } + else + { #use normal BoundingBox + $boundingbox =~ /^%%.*BoundingBox:\s*(.*)/; + ($cBBllx,$cBBlly,$cBBurx,$cBBury,$dummy)= split(/\s/,$1); + } + # if loose BB is requested + # apply changes to resulting bounding box if needed + if ($looseBB ne '') + { + if ($cBBllx > 0) { $cBBllx--; } + if ($cBBlly > 0) { $cBBlly--; } + $cBBurx++; + $cBBury++; + + if ($hcBBllx-$hiresprecision >= 0.0) { $hcBBllx-= $hiresprecision; } + if ($hcBBlly-$hiresprecision >= 0.0) { $hcBBlly-= $hiresprecision; } + $hcBBurx+= $hiresprecision; + $hcBBury+= $hiresprecision; + $hiresboundingbox="%%HiResBoundingBox: $hcBBllx $hcBBlly $hcBBurx $hcBBury\n"; + + } + if ($cBBllx < 0 || $cBBlly < 0) + { + if (!$quiet) + { + print STDERR "WARNING! Your drawing had a negative Bounding Box which is deprecated and may cause problems!. I'll shift it.\n"; + } + $translate_x= -int($cBBllx); + $translate_y= -int($cBBlly); + $cBBllx=0; + $cBBurx= $cBBurx + $translate_x; + $cBBlly=0; + $cBBury= $cBBury + $translate_y; + + $hcBBurx= $hcBBurx + $hcBBllx; + $hcBBury= $hcBBury + $hcBBlly; + $hcBBllx= 0; + $hcBBlly= 0; + + $hiresboundingbox="%%HiResBoundingBox: $hcBBllx $hcBBlly $hcBBurx $hcBBury\n"; + + $translation="$translate_x $translate_y translate"; + $translatecmd="-c \'$translation\'"; + } + $boundingbox = "%%BoundingBox: $cBBllx $cBBlly $cBBurx $cBBury\n"; + + if (!$quiet) { print STDERR "ready. $boundingbox" }; + + $before_startps= 1; + $inserted_prolog= 0; + $prolog_passed= 0; + + ## %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + ## -- Create output file -- + ## %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + if (!$quiet) { print STDERR "Creating output file $outfname ... "; } + if (!$forceoverwrite and -s "$outfname") + { + die "$prgname: Sorry, file named $outfname already exists,", + " will not overwrite it.\n", + " You will have to use the -f option, delete it or rename it", + " before running $prgname again.\n"; + } + else + { + open($outfhandle,">$outfname") or die "Can't open file $outfname for writing: $!\n"; + } + + + open($tmpfhandle,"<$tmpfname") or die "Cannot open file $tmpfname for reading"; + CREATEOUTPUT: + while (<$tmpfhandle>) + { + # check whether we are in a binary section + + $binarysection=$allbinary || + (/^(%%Begin(Binary|Data))|(beginimage)\r?\n?$/ ... /^(%%End(Binary|Data))|^(endimage)/) || + (/^(doNimage)|(doclutimage)\r?\n?$/ ... /(^|~> )Z\r?\n?$/) || # Pscript_Win_Dib_L2 5.0 0 + (/^beginjpeg / ... /~> endjpeg\r?\n?$/) || # Pscript_Win_Dib_L2 5.0 0 + (/^pdfIm/ ... /^%-EOD-/); + + if ( !$binarysection ) + { + s/\r?\n?$//; # remove CR and/or LF at end of line if not in binary section + } + + # check where magic Postscript header starts - skip leading binary stuff, e.g., HP PCL/PJL code + if ($before_startps) + { + if ( /%!/ ) # This is usually the smallest magic sequence + { # Note: Adobe Photoshop generated a binary heading, so ^ is not applicable + # %! may be part of a binary sequence, but then control characters follow + # so skip %! if non alphanumeric characters follow + if ( ! /%!.*[^\w]{2,}/ ) + { + # some heading without two control characters found + $before_startps= 0; + if (! /%!PS-Adobe.*/i) # some strange programs use other magics + { + print STDERR "** Warning **: Weird heading line -- ",$_," -- "; + } + } + } + next CREATEOUTPUT; + } + else # we are hopefully in regular postscript code now + { + # count %%EOFs as we want to know when we got the last EOF + if ( /^%%EOF\s*$/ ) + { + $seenEOF++; + } + + # We should insert our own prologue including the newly calculated BoundingBox + if (! $inserted_prolog) + { + print $outfhandle "%!PS-Adobe-$PSversion EPSF-$PSDSCversion\n"; + # check if we need to rotate + $transrotcmd=''; + if ($rotatecmd) + { + if ($rotate eq $rotright) + { + $transrotcmd="-$cBBlly $cBBurx translate"; + $boundingbox='%%BoundingBox: 0 0 ' . ($cBBury-$cBBlly) . ' ' . ($cBBurx-$cBBllx) . "\n"; + if ($hiresboundingbox ne "") + { + $hiresboundingbox='%%HiResBoundingBox: 0 0 ' . ($hcBBury-$hcBBlly) . ' ' . ($hcBBurx-$hcBBllx) . "\n"; + } + + } + elsif ($rotate eq $rotleft) + { + $transrotcmd="$cBBury -$cBBllx translate"; + $boundingbox='%%BoundingBox: 0 0 ' . ($cBBury-$cBBlly) . ' ' . ($cBBurx-$cBBllx) . "\n"; + if ($hiresboundingbox ne "") + { + $hiresboundingbox= '%%HiResBoundingBox: 0 0 ' . ($hcBBury-$hcBBlly) . ' ' . ($hcBBurx-$hcBBllx) . "\n"; + } + } + elsif ($rotate eq $rotupsidedown) + { + $transrotcmd="$cBBurx $cBBury translate"; + $boundingbox='%%BoundingBox: 0 0 ' . ($cBBurx-$cBBllx) . ' ' . ($cBBury-$cBBlly) . "\n"; + if ($hiresboundingbox ne "") + { + $hiresboundingbox='%%HiResBoundingBox: 0 0 ' . ($hcBBurx-$hcBBllx) . ' ' . ($hcBBury-$hcBBlly) . "\n"; + } + } + } + print $outfhandle $boundingbox; + if (!defined($hiresboundingbox)) + { + $nohires=1; + } + if (defined($hiresboundingbox) && !defined($nohires)) { print $outfhandle $hiresboundingbox; } + $inserted_prolog= 1; + redo CREATEOUTPUT; + } + else # already inserted_prolog + { + if (! $prolog_passed) + { + #ignore the following lines in the prologue + if ( /^%%(HiRes)?BoundingBox/ || + /^%%Pages/ || + /^%%BeginProlog/ || + /^%%EndProlog/ || + ($filterorientation && /^%%Orientation/) || + ($removeDSC && /^%%.*: \(atend\)/) || + ($removePreview && (/^%%BeginPreview/ ... /^%%EndPreview/)) ) + { + next CREATEOUTPUT; + } + else + { + if ( /^[^%].*/ || + /^%%EndComments/ ) # line is not a comment + { + #output postscript code for proper EPS file + if ($insertPScode) + { + print $outfhandle "%%EndComments\n", + "% EPSF created by ps2eps $ver[2]\n", + "%%BeginProlog\n"; + } + # Insert own postscript code for clipping + if ($clip) + { + if (!defined($nohires)) + { + printf $outfhandle "newpath %f %f moveto %f %f lineto %f %f lineto %f %f lineto closepath clip\n",$hcBBllx,$hcBBlly,$hcBBurx,$hcBBlly,$hcBBurx,$hcBBury,$hcBBllx,$hcBBury; + } + else + { + printf $outfhandle "newpath %d %d moveto %d %d lineto %d %d lineto %d %d lineto closepath clip\n",$cBBllx,$cBBlly,$cBBurx,$cBBlly,$cBBurx,$cBBury,$cBBllx,$cBBury; + } + } #endif clip + + if ($rotate ne '') + { + print $outfhandle "$transrotcmd\n"; + print $outfhandle "$rotate\n"; + } + if ($translation ne '') + { + print $outfhandle "$translation\n"; + } + + #insert surrounding postscript code + if ($insertPScode) + { + print $outfhandle "save\n", + "countdictstack\n", + "mark\n", + "newpath\n", + "/showpage {} def\n", + "/setpagedevice {pop} def\n", + "%%EndProlog\n", + "%%Page 1 1\n"; + } + $prolog_passed= 1; + if (/^%%EndComments/) { next CREATEOUTPUT; } + } #endif line is not a comment + } #end else + } #endif (we are in the prologue section) + else #we are in the main part of postscript file + { + #end of postscript file reached? + #Usually the DSC %%EOF signifies the end + if ( eof($tmpfhandle) || + ($ignoreEOFDSC == 0 && /^%%EOF\s*$/ && $seenEOF == $totalEOFDSC) + || ( $trailerseen && /^II\*\000.*/ ) + ) + { + #do not forget to print last line if not terminated by LF + if ( eof($tmpfhandle) && !/^$/ && !/^%%EOF\s*$/ ) # do not insert %%EOF twice + { + print $outfhandle $_,"\n"; + } + #add appropriate trailer + if ($insertPScode) + { + print $outfhandle "%%Trailer\n", + "cleartomark\n", + "countdictstack\n", + "exch sub { end } repeat\n", + "restore\n", + "%%EOF\n"; + } + last CREATEOUTPUT; + } # stop output + + # Trailer comment seen? + if ( /^%%Trailer\s*$/ ) + { + $trailerseen=1; + } + else + { + if (!/^\s*$/) #non empty lines follow + { + $trailerseen=0; + } + } + + # check for trigger + if (defined($triggerstring) && /^$triggerstring$/) + { + $trigger= 1; + }; + + # remove complete lines if one of the expression matches + if ( !$binarysection # only when not in binary section + && + ( + ($removePreview && (/^%%BeginPreview/ ... /^%%EndPreview/)) + || # no preview + (defined($rangefilter_begin) && + (/$rangefilter_begin/ ... /$rangefilter_end/) && + (!(/$exclude_rangefilter_begin/ ... /$exclude_rangefilter_end/)) + ) + || + (defined($triggered_rangefilter_begin) && defined($triggered_rangefilter_end) && + $trigger && + (/$triggered_rangefilter_begin/ ... /$triggered_rangefilter_end/) + ) + || + /$linefilter/ # lines by linefilter + || + ($removeDSC && (/^%( |!)(\w )+/ || /^%%([A-Za-z]+\s)/)) # any type of structured comment + || + ($removeADO && + (/^statusdict begin.*ProductName.*print product print.*flush end\r?\n?$/ || + /^\(%%\[\s*(Page:.*|LastPage)\s*\]%%\)\s*=\s*\w*\s*\r?\n?/ )) + || + /^$/ # empty lines + ) + ) + { + next CREATEOUTPUT; + } + + # replacement + if ( defined($fixsearchpat) ) + { + #if (/$fixsearchpat/) { print STDERR "**filter** before:",$_,"\n"; } + #if (s/$fixsearchpat/$fixreplacepat/) { print STDERR "**filter** after:",$_,"\n";} + s/$fixsearchpat/$fixreplacepat/; + } + + # sanity check for potential dangerous commands + if ( /(^|\s)(clear|erasepage|initmatrix|initclip|initgraphics|startjob|cleardictstack|setmatrix|setpagedevice|copypage|grestoreall|exitserver|quit)(\s|$)/ ) + { + $notsane= 1; + #print STDERR "Warning: dangerous command in line: ",$_,"\n"; + } + } # end else (this is main part) + + # Output the postscript line to result file + print $outfhandle $_; + + if (!$binarysection) + { + print $outfhandle "\n"; # terminate line with LF + } + } # end else prolog_passed + } # end else inserted_prolog + } # end while CREATEOUTPUT + + close($tmpfhandle); + + if ($tmpfname ne $infname) { unlink "$tmpfname"; } #remove temporary file + + close ($outfhandle); + + # print warning if magic sequence not found + if ( $before_startps ) + { + print STDERR "\n ** Error!! **: could not identify begin of postscript code in file $infname, please check header line!\n First line should start with %!. No output generated.\n"; + } + + if (!$quiet) { print STDERR "ready.\n"; } + if ($warnings and $notsane and !$quiet) + { + print STDERR "** Warning **: EPS-output for $infname is not sane, at least one\n", + "of the following commands was still present:\n", + "clear erasepage initmatrix initclip initgraphics startjob\n", + "cleardictstack setmatrix setpagedevice copypage grestoreall\n", + "exitserver quit\n"; + } +} #end while PROCESSFILE + +# ---- end of perl-script ------- diff --git a/Master/texmf-dist/scripts/psutils/extractres.pl b/Master/texmf-dist/scripts/psutils/extractres.pl new file mode 100755 index 00000000000..adee938c95d --- /dev/null +++ b/Master/texmf-dist/scripts/psutils/extractres.pl @@ -0,0 +1,101 @@ +#!/usr/bin/env perl +# extractres: extract resources from PostScript file +# +# Copyright (C) Angus J. C. Duggan 1991-1995 +# See file LICENSE for details. + +$0 =~ s=.*/==; +$prog = $0; + +%resources = (); # list of resources included +%merge = (); # list of resources extracted this time +%extn = ("font", ".pfa", "file", ".ps", "procset", ".ps", # resource extns + "pattern", ".pat", "form", ".frm", "encoding", ".enc"); +%type = ("%%BeginFile:", "file", "%%BeginProcSet:", "procset", + "%%BeginFont:", "font"); # resource types + +while (@ARGV) { + $_ = shift; + if (/^-m(erge)?$/) { $merge = 1; } + elsif (/^-/) { + print STDERR "Usage: $prog [-merge] [file]\n"; + exit 1; + } else { + unshift(@ARGV, $_); + last; + } +} + +if (defined($ENV{TMPDIR})) { # set body file name + $body = "$ENV{TMPDIR}/body$$.ps"; +} elsif (defined($ENV{TEMP})) { + $body = "$ENV{TEMP}/body$$.ps"; +} elsif (defined($ENV{TMP})) { + $body = "$ENV{TMP}/body$$.ps"; +} else { + $body = "body$$.ps"; +} + +open(BODY, $body) && die "Temporary file $body already exists"; +open(BODY, ">$body") || die "Can't write file $body"; + +sub filename { # make filename for resource in @_ + local($name); + foreach (@_) { # sanitise name + s/[!()\$\#*&\\\|\`\'\"\~\{\}\[\]\<\>\?]//g; + $name .= $_; + } + $name =~ s@.*/@@; # drop directories + die "Filename not found for resource ", join(" ", @_), "\n" + if $name =~ /^$/; + $name; +} + +$output = STDOUT; # start writing header out +while (<>) { + if (/^%%BeginResource:/ || /^%%BeginFont:/ || /^%%BeginProcSet:/) { + local($comment, @res) = split(/\s+/); # look at resource type + local($type) = defined($type{$comment}) ? $type{$comment} : shift(@res); + local($name) = &filename(@res, $extn{$type}); # make file name + $saveout = $output; + if (!$resources{$name}) { + print "%%IncludeResource: $type ", join(" ", @res), "\n"; + if (!open(RES, $name)) { + open(RES, ">$name") || die "Can't write file $name"; + $resources{$name} = $name; + $merge{$name} = $merge; + $output = RES; + } else { # resource already exists + close(RES); + undef $output; + } + } elsif ($merge{$name}) { + open(RES, ">>$name") || die "Can't append to file $name"; + $output = RES; + } else { # resource already included + undef $output; + } + } elsif (/^%%EndResource/ || /^%%EndFont/ || /^%%EndProcSet/) { + if (defined $output) { + print $output $_; + close($output); + } + $output = $saveout; + next; + } elsif ((/^%%EndProlog/ || /^%%BeginSetup/ || /^%%Page:/)) { + $output = BODY; + } + print $output $_ + if defined $output; +} + +close(BODY); # close body output file + +open(BODY, $body); # reopen body for input +while () { # print it all + print $_; +} +close(BODY); + +unlink($body); # dispose of body file + diff --git a/Master/texmf-dist/scripts/psutils/fixdlsrps.pl b/Master/texmf-dist/scripts/psutils/fixdlsrps.pl new file mode 100755 index 00000000000..2a1d85a654c --- /dev/null +++ b/Master/texmf-dist/scripts/psutils/fixdlsrps.pl @@ -0,0 +1,54 @@ +#!/usr/bin/env perl +# fixdlsrps: fix DviLaser/PS document to work with PSUtils +# +# Copyright (C) Angus J. C. Duggan 1991-1995 +# See file LICENSE for details. + +$nesting = 0; +$page = 1; +$infont = 0; + +@fonts = (); +@body = (); +$header = 1; + +while (<>) { + if (/^XP/) { + $infont++; + push(@fonts, $_); + $infont-- if /PXL.*RP/ || /DN?F.*RP/; + } elsif ($infont) { + push(@fonts, $_); + $infont-- if /PXL.*RP/ || /DN?F.*RP/; + } elsif ((/^%%EndSetup/ || /^%%Page:/) && $header) { + print @body; + @body = ("%%EndSetup\n"); + $header = 0; + } elsif (/^%%EndProlog/ && !$nesting) { + push(@body, + "\$DviLaser begin/GlobalMode{}bdef/LocalMode{}bdef/XP{}def/RP{}def", + "/DoInitialScaling{72.0 Resolution div dup scale}bdef end\n", $_); + } elsif (/^%%BeginPageSetup/ && !$nesting) { + push(@body, "%%Page: $page $page\n", $_, + "Resolution 72 div dup scale Magnification 1000 div dup scale\n", + "/DocumentInitState where {\n", + "/DocumentInitState [ matrix currentmatrix currentlinewidth", + " currentlinecap currentlinejoin currentdash currentgray", + " currentmiterlimit] cvx put}if\n"); + $page++; + } elsif (/^%%BeginDocument:/ || /^%%BeginBinary:/ || /^%%BeginFile:/) { + push(@body, $_); + $nesting++; + } elsif (/^%%EndDocument/ || /^%%EndBinary/ || /^%%EndFile/) { + push(@body, $_); + $nesting--; + } elsif (!/^%%PageBoundingBox:/ && !/^%%Page:/) { + push(@body, $_); + } +} + +print @fonts; +print @body; + +exit 0; + diff --git a/Master/texmf-dist/scripts/psutils/fixfmps.pl b/Master/texmf-dist/scripts/psutils/fixfmps.pl new file mode 100755 index 00000000000..039ac8e5a3e --- /dev/null +++ b/Master/texmf-dist/scripts/psutils/fixfmps.pl @@ -0,0 +1,21 @@ +#!/usr/bin/env perl +# fixfmps: get conforming PostScript out of FrameMaker version 2 file +# move all FMDEFINEFONTs to start of pages +# +# Copyright (C) Angus J. C. Duggan 1991-1995 +# See file LICENSE for details. + +%fonts=(); + +while (<>) { + if (/^([0-9]+) [0-9]+ .* FMDEFINEFONT$/) { + $fonts{$1} = $_; + } elsif (/^[0-9.]+ [0-9.]+ [0-9]+ FMBEGINPAGE$/) { + print $_, join('',values(%fonts)); + } elsif (m%(.*/PageSize \[paperwidth paperheight\]put )setpagedevice(.*)%) { + print "$1pop$2\n"; + } else { + print $_; + } +} + diff --git a/Master/texmf-dist/scripts/psutils/fixpsditps.pl b/Master/texmf-dist/scripts/psutils/fixpsditps.pl new file mode 100755 index 00000000000..2a1728a8204 --- /dev/null +++ b/Master/texmf-dist/scripts/psutils/fixpsditps.pl @@ -0,0 +1,25 @@ +#!/usr/bin/env perl +# fixpsditps: fix psdit output for use in psutils +# +# Copyright (C) Angus J. C. Duggan 1991-1995 +# See file LICENSE for details. + +$nesting = 0; + +while (<>) { + if (/^\/p{pop showpage pagesave restore \/pagesave save def}def$/) { + print "/p{pop showpage pagesave restore}def\n"; + } elsif (/^%%BeginDocument/ || /^%%BeginBinary/ || /^%%BeginFile/ ) { + print $_; + $nesting++; + } elsif (/^%%EndDocument/ || /^%%EndBinary/ || /^%%EndFile/) { + print $_; + $nesting--; + } elsif (/^%%Page:/ && $nesting == 0) { + print $_; + print "xi\n"; + } elsif (! /^xi$/) { + print $_; + } +} + diff --git a/Master/texmf-dist/scripts/psutils/fixpspps.pl b/Master/texmf-dist/scripts/psutils/fixpspps.pl new file mode 100755 index 00000000000..77bba66d6b7 --- /dev/null +++ b/Master/texmf-dist/scripts/psutils/fixpspps.pl @@ -0,0 +1,58 @@ +#!/usr/bin/env perl +# mangle PostScript produced by PSPrint to make it almost conforming +# +# Copyright (C) Angus J. C. Duggan 1991-1995 +# See file LICENSE for details. + +$header = 1; $ignore = 0; +$verbose = 0; +@body = (); +%fonts = (); $font = ""; +$inchar = 0; @char = (); + +while (<>) { + if (/^\@end$/) { + $ignore = 1; + } elsif (/^[0-9]+ \@bop0$/) { + $ignore = 0; + $header = 1; + } elsif ($header) { + if (/^\/([a-z.0-9]+) \@newfont$/) { + if (! defined($fonts{$1})) { + $fonts{$1} = 1; + print; + } elsif ($verbose) { + print STDERR "$font already defined\n"; + } + } elsif (/^([a-z.0-9]+) sf$/) { + $font = $1; + print; + } elsif (/^\[) { + if (/^([0-9]+ [0-9]+ [0-9]+ PB) (%!.*)/) { + print "$1\n%%BeginDocument: Scribe-EPSF $epsfn 0\n$2\n"; + $inepsf++; + } elsif (/^ PE/ && $inepsf) { + print "%%EndDocument\n", $_; + } else { + print $_; + } +} + diff --git a/Master/texmf-dist/scripts/psutils/fixtpps.pl b/Master/texmf-dist/scripts/psutils/fixtpps.pl new file mode 100755 index 00000000000..4239bb9dfc5 --- /dev/null +++ b/Master/texmf-dist/scripts/psutils/fixtpps.pl @@ -0,0 +1,28 @@ +#!/usr/bin/env perl +# fixtpps: fix tpscript document to work with PSUtils +# +# Copyright (C) Angus J. C. Duggan 1991-1995 +# See file LICENSE for details. + +$nesting = 0; +$header = 1; + +while (<>) { + if (/^%%Page:/ && $nesting == 0) { + print $_; + print "save home\n"; + $header = 0; + } elsif (/^%%BeginDocument/ || /^%%BeginBinary/ || /^%%BeginFile/) { + print $_; + $nesting++; + } elsif (/^%%EndDocument/ || /^%%EndBinary/ || /^%%EndFile/) { + print $_; + $nesting--; + } elsif (/save home/) { + s/save home//; + print $_; + } elsif (!$header || (! /^save$/ && ! /^home$/)) { + print $_; + } +} + diff --git a/Master/texmf-dist/scripts/psutils/fixwfwps.pl b/Master/texmf-dist/scripts/psutils/fixwfwps.pl new file mode 100755 index 00000000000..92d88443fe1 --- /dev/null +++ b/Master/texmf-dist/scripts/psutils/fixwfwps.pl @@ -0,0 +1,33 @@ +#!/usr/bin/env perl +# fixwfwps: fix Word for windows PostScript for printing. +# +# Copyright (C) Angus J. C. Duggan 1991-1995 +# See file LICENSE for details. + +$nesting = 0; +while (<>) { + tr/\000-\010\012-\014\016-\037//d; + foreach (grep($_ ne "", split("\015"))) { + s!/SVDoc\s+save\s+def!!g; + s!SVDoc\s+restore!!g; + if (/^(%!PS-Adobe-\d*\.\d*) EPSF-/ && !$nesting) { + print "$1\n"; + $wfwepsf = 1; + } elsif (/^SS\s*$/ && $wfwepsf) { + print "%%Page: $wfwepsf $wfwepsf\n"; + $wfwepsf++; + print "$_\n"; + } elsif (/^%MSEPS Preamble/) { + print "%%BeginDocument: (Included EPSF)\n"; + print "$_\n"; + $nesting++; + } elsif (/^%MSEPS Trailer/) { + $nesting--; + print "$_\n"; + print "%%EndDocument\n"; + } elsif (! /^%%BoundingBox/) { + print "$_\n"; + } + } +} + diff --git a/Master/texmf-dist/scripts/psutils/fixwpps.pl b/Master/texmf-dist/scripts/psutils/fixwpps.pl new file mode 100755 index 00000000000..83a5fdfaf0b --- /dev/null +++ b/Master/texmf-dist/scripts/psutils/fixwpps.pl @@ -0,0 +1,31 @@ +#!/usr/bin/env perl +# fixwpps: get semi-conforming PostScript out of WordPerfect 5.0 file +# +# Copyright (C) Angus J. C. Duggan 1991-1995 +# See file LICENSE for details. + +$page = 1; +$nesting = 0; + +while (<>) { + s/([^\/]_t)([0-9]+)/\1 \2/g; # fix wp 5.0 bug + if (m!/_[be][dp]! || m!_bp \d+ \d+ roll!) { + print $_; + } elsif (/^(.*)(_bp.*)$/) { + print "$1\n" if $1 ne ""; + print "%%Page: $page $page\n"; + print "$2\n"; + $page++; + $nesting++; + } elsif (/_ep$/) { + print $_; + $nesting--; + } elsif (/^(.*)(_ed.*)/) { + print "$1\n" if $1 ne ""; + print "%%Trailer:\n"; + print "$2\n"; + } else { + print $_; + } +} + diff --git a/Master/texmf-dist/scripts/psutils/fixwwps.pl b/Master/texmf-dist/scripts/psutils/fixwwps.pl new file mode 100755 index 00000000000..bfd58c086da --- /dev/null +++ b/Master/texmf-dist/scripts/psutils/fixwwps.pl @@ -0,0 +1,20 @@ +#!/usr/bin/env perl +# fixwwps: get semi-conforming PostScript out of Windows Write file +# +# Copyright (C) Angus J. C. Duggan 1991-1995 +# See file LICENSE for details. + +$page = 1; + +while (<>) { + if (/^(%!.*) EPSF-\d.\d/) { + print $1, "\n"; + } elsif (/^SS/) { + print "%%Page: $page $page\n"; + print $_; + $page++; + } else { + print $_; + } +} + diff --git a/Master/texmf-dist/scripts/psutils/includeres.pl b/Master/texmf-dist/scripts/psutils/includeres.pl new file mode 100755 index 00000000000..c9d0eee61cb --- /dev/null +++ b/Master/texmf-dist/scripts/psutils/includeres.pl @@ -0,0 +1,45 @@ +#!/usr/bin/env perl +# includeres: include resources in PostScript file +# +# Copyright (C) Angus J. C. Duggan 1991-1995 +# See file LICENSE for details. + +$0 =~ s=.*/==; +$prog = $0; + +%extn = ("font", ".pfa", "file", ".ps", "procset", ".ps", # resource extns + "pattern", ".pat", "form", ".frm", "encoding", ".enc"); +%type = ("%%BeginFile:", "file", "%%BeginProcSet:", "procset", + "%%BeginFont:", "font"); # resource types + +sub filename { # make filename for resource in @_ + local($name); + foreach (@_) { # sanitise name + s/[!()\$\#*&\\\|\`\'\"\~\{\}\[\]\<\>\?]//g; + $name .= $_; + } + $name =~ s@.*/@@; # drop directories + die "Filename not found for resource ", join(" ", @_), "\n" + if $name =~ /^$/; + $name; +} + +while (<>) { + if (/^%%IncludeResource:/ || /^%%IncludeFont:/ || /^%%IncludeProcSet:/) { + local($comment, @res) = split(/\s+/); + local($type) = defined($type{$comment}) ? $type{$comment} : shift(@res); + local($name) = &filename(@res); + if (open(RES, $name) || open(RES, "$name$extn{$type}")) { + while () { + print $_; + } + close(RES); + } else { + print "%%IncludeResource: ", join(" ", $type, @res), "\n"; + print STDERR "Resource $name not found\n"; + } + } else { + print $_; + } +} + diff --git a/Master/texmf-dist/scripts/psutils/psmerge.pl b/Master/texmf-dist/scripts/psutils/psmerge.pl new file mode 100755 index 00000000000..ff1b8039b4d --- /dev/null +++ b/Master/texmf-dist/scripts/psutils/psmerge.pl @@ -0,0 +1,86 @@ +#!/usr/bin/env perl +# psmerge: merge PostScript files produced by same application and setup +# usage: psmerge [-oout.ps] [-thorough] file1.ps file2.ps ... +# +# Copyright (C) Angus J. C. Duggan 1991-1995 +# See file LICENSE for details. + +$0 =~ s=.*/==; +$prog = $0; + +while ($ARGV[0] =~ /^-/) { + $_ = shift; + if (/^-o(.+)/) { + if (!close(STDOUT) || !open(STDOUT, ">$1")) { + print STDERR "$prog: can't open $1 for output\n"; + exit 1; + } + } elsif (/^-t(horough)?$/) { + $thorough = 1; + } else { + print STDERR "Usage: $prog [-oout] [-thorough] file...\n"; + exit 1; + } +} + +$page = 0; +$first = 1; +$nesting = 0; + +@header = (); +$header = 1; + +@trailer = (); +$trailer = 0; + +@pages = (); +@body = (); + +@resources = (); +$inresource = 0; + +while (<>) { + if (/^%%BeginFont:/ || /^%%BeginResource:/ || /^%%BeginProcSet:/) { + $inresource = 1; + push(@resources, $_); + } elsif ($inresource) { + push(@resources, $_); + $inresource = 0 if /^%%EndFont/ || /^%%EndResource/ || /^%%EndProcSet/; + } elsif (/^%%Page:/ && $nesting == 0) { + $header = $trailer = 0; + push(@pages, join("", @body)) if @body; + $page++; + @body = ("%%Page: ($page) $page\n"); + } elsif (/^%%Trailer/ && $nesting == 0) { + push(@trailer, $_); + push(@pages, join("", @body)) if @body; + @body = (); + $trailer = 1; + $header = 0; + } elsif ($header) { + push(@trailer, $_); + push(@pages, join("", @body)) if @body; + @body = (); + $trailer = 1; + $header = 0; + } elsif ($trailer) { + if (/^%!/ || /%%EOF/) { + $trailer = $first = 0; + } elsif ($first) { + push(@trailer, $_); + } + } elsif (/^%%BeginDocument/ || /^%%BeginBinary/ || /^%%BeginFile/) { + push(@body, $_); + $nesting++; + } elsif (/^%%EndDocument/ || /^%%EndBinary/ || /^%%EndFile/) { + push(@body, $_); + $nesting--; + } else { + print $_ if $print; + } +} + +print @trailer; + +exit 0; + diff --git a/Master/texmf-dist/scripts/tetex/fmtutil-sys.sh b/Master/texmf-dist/scripts/tetex/fmtutil-sys.sh new file mode 100755 index 00000000000..bfb52f94e1f --- /dev/null +++ b/Master/texmf-dist/scripts/tetex/fmtutil-sys.sh @@ -0,0 +1,29 @@ +#!/bin/sh + +# fmtutil-sys: Thomas Esser, public domain. + +# wrapper script for fmtutil with TEXMFVAR and TEXMFCONFIG set to +# TEXMFSYSVAR / TEXMFSYSCONFIG + +test -f /bin/ksh && test -z "$RUNNING_KSH" \ + && { UNAMES=`uname -s`; test "x$UNAMES" = xULTRIX; } 2>/dev/null \ + && { RUNNING_KSH=true; export RUNNING_KSH; exec /bin/ksh $0 ${1+"$@"}; } +unset RUNNING_KSH + +test -f /bin/bsh && test -z "$RUNNING_BSH" \ + && { UNAMES=`uname -s`; test "x$UNAMES" = xAIX; } 2>/dev/null \ + && { RUNNING_BSH=true; export RUNNING_BSH; exec /bin/bsh $0 ${1+"$@"}; } +unset RUNNING_BSH + +# hack around a bug in zsh: +test -n "${ZSH_VERSION+set}" && alias -g '${1+"$@"}'='"$@"' +export PATH + +v=`kpsewhich -var-value TEXMFSYSVAR` +c=`kpsewhich -var-value TEXMFSYSCONFIG` + +TEXMFVAR="$v" +TEXMFCONFIG="$c" +export TEXMFVAR TEXMFCONFIG + +exec fmtutil ${1+"$@"} diff --git a/Master/texmf-dist/scripts/tetex/fmtutil.sh b/Master/texmf-dist/scripts/tetex/fmtutil.sh new file mode 100755 index 00000000000..06ef11380b4 --- /dev/null +++ b/Master/texmf-dist/scripts/tetex/fmtutil.sh @@ -0,0 +1,1017 @@ +#!/bin/sh +# fmtutil - utility to maintain format files. +# Public domain. Originally written by Thomas Esser. +# Run with --help for usage. + +# program history: +# further changes in texk/tetex/ChangeLog. +# 2007-01-04 patch by JK to support $engine subdir (enabled by default) +# Fr Apr 8 19:15:05 CEST 2005 cleanup now has an argument for the return code +# Do Mar 02 10:42:31 CET 2006 add tmpdir to TEXFORMATS +# So Ma 27 18:52:06 CEST 2005 honor $TMPDIR, $TEMP and $TMP, not just $TMP +# Sa Jan 15 18:13:46 CET 2005 avoid multiple variable assignments in one statement +# Di Jan 11 11:42:36 CET 2005 fix --byhyphen with relative hyphenfile +# Fr Dez 31 16:51:29 CET 2004 option catcfg added (for being called by texconfig) +# Do Dez 30 21:53:27 CET 2004 rename variable verbose to verboseFlag +# Sa Dez 25 12:44:23 CET 2004 implementation adopted for teTeX-3.0 (tcfmgr) +# Do Okt 28 11:09:36 CEST 2004 added --refresh +# Fr Sep 17 19:25:28 CEST 2004 save $0 in a variable before calling a function +# Sun May 9 23:24:06 CEST 2004 changes for new web2c: format names +# are now *.fmt, nothing else, disable +# "plain" symlinks +# Thu May 6 14:16:19 CEST 2004: "mv .../dev/null \ + && { RUNNING_KSH=true; export RUNNING_KSH; exec /bin/ksh $0 ${1+"$@"}; } +unset RUNNING_KSH + +test -f /bin/bsh && test -z "$RUNNING_BSH" \ + && { UNAMES=`uname -s`; test "x$UNAMES" = xAIX; } 2>/dev/null \ + && { RUNNING_BSH=true; export RUNNING_BSH; exec /bin/bsh $0 ${1+"$@"}; } +unset RUNNING_BSH + +# hack around a bug in zsh: +test -n "${ZSH_VERSION+set}" && alias -g '${1+"$@"}'='"$@"' + +progname=fmtutil +argv0=$0 +version='$Id: fmtutil 25899 2012-04-09 23:09:26Z karl $' + +cnf=fmtutil.cnf # name of the config file +export PATH + +############################################################################### +# cleanup() +# clean up the temp area and exit with proper exit status +############################################################################### +cleanup() +{ + rc=$1 + # for debugging, exit $rc here so $tmpdir with its logs sticks around. + $needsCleanup && test -n "$tmpdir" && test -d "$tmpdir" \ + && { cd / && rm -rf "$tmpdir"; } + (exit $rc); exit $rc +} + +############################################################################### +# help() and version() +# display help (or version) message and exit +############################################################################### +help() +{ + cat < +eof + cleanup 0 +} + +versionfunc() +{ + cat </dev/null; then + ed "$file" >/dev/null 2>&1 <<-eof + /$pat/ + c + $line + . + w + q +eof + else + echo "$line" >> $file + fi +} + +############################################################################### +# setmatch(match) +# setting the "match state" to true or false. Used to see if there was at +# least one match. +############################################################################### +setmatch() +{ + match=$1 +} + +############################################################################### +# getmatch() +# return success if there was at least one match. +############################################################################### +getmatch() +{ + test "x$match" = xtrue +} + +############################################################################### +# initTexmfMain() +# get $MT_TEXMFMAIN from $TEXMFMAIN +############################################################################### +initTexmfMain() +{ + case $MT_TEXMFMAIN in + "") MT_TEXMFMAIN=`kpsewhich --var-value=TEXMFMAIN`;; + esac + export MT_TEXMFMAIN +} + +############################################################################### +# cache_vars() +# locate files / kpathsea variables and export variables to environment +# this speeds up future calls to e.g. mktexupd +############################################################################### +cache_vars() +{ + : ${MT_VARTEXFONTS=`kpsewhich --expand-var='$VARTEXFONTS' | sed 's%^!!%%'`} + : ${MT_MKTEXNAM=`kpsewhich --format='web2c files' mktexnam`} + : ${MT_MKTEXNAM_OPT=`kpsewhich --format='web2c files' mktexnam.opt`} + : ${MT_MKTEXDIR=`kpsewhich --format='web2c files' mktexdir`} + : ${MT_MKTEXDIR_OPT=`kpsewhich --format='web2c files' mktexdir.opt`} + : ${MT_MKTEXUPD=`kpsewhich --format='web2c files' mktexupd`} + : ${MT_MKTEX_CNF=`kpsewhich --format='web2c files' mktex.cnf`} + : ${MT_MKTEX_OPT=`kpsewhich --format='web2c files' mktex.opt`} + export MT_VARTEXFONTS MT_MKTEXNAM MT_MKTEXNAM_OPT MT_MKTEXDIR + export MT_MKTEXDIR_OPT MT_MKTEXUPD MT_MKTEX_CNF MT_MKTEX_OPT +} + +############################################################################### +# abort(errmsg) +# print `errmsg' to stderr and exit with error code 1 +############################################################################### +abort() +{ + echo "$progname: $1." >&2 + cleanup 1 +} + +############################################################################### +# maybe_abort(errmsg) +# print `errmsg' to stderr and +# unless noAbortFlag is set exit with error code 1 +############################################################################### +maybe_abort() +{ + echo "$progname: $1." >&2 + $noAbortFlag || cleanup 1 +} + +############################################################################### +# verboseMsg(msg) +# print `msg' to stderr is $verbose is true +############################################################################### +verboseMsg() { + $verboseFlag && verbose echo ${1+"$@"} +} + +############################################################################### +# byebye() +# report any failures and exit the program +############################################################################### +byebye() +{ + if $has_warnings; then + { + cat <&2 + fi + + if $has_errors; then + { + cat <&2 + cleanup 1 + else + cleanup 0 + fi +} + +############################################################################### +# init_log_warning() +# reset the list of warning messages +############################################################################### +init_log_warning() +{ + log_warning_msg= + has_warnings=false +} + +############################################################################### +# init_log_failure() +# reset the list of failure messages +############################################################################### +init_log_failure() +{ + log_failure_msg= + has_errors=false +} + +############################################################################### +# log_warning(errmsg) +# report and save warning message `errmsg' +############################################################################### +log_warning() +{ + echo "Warning: $@" >&2 + if test -z "$log_warning_msg"; then + log_warning_msg="$@" + else + OLDIFS=$IFS; IFS= + log_warning_msg="$log_warning_msg +$@" + IFS=$OLDIFS + fi + has_warnings=true +} + +############################################################################### +# log_failure(errmsg) +# report and save failure message `errmsg' +############################################################################### +log_failure() +{ + echo "Error: $@" >&2 + if test -z "$log_failure_msg"; then + log_failure_msg="$@" + else + OLDIFS=$IFS; IFS= + log_failure_msg="$log_failure_msg +$@" + IFS=$OLDIFS + fi + has_errors=true +} + +############################################################################### +# verbose (cmd) +# execute cmd. Redirect output depending on $mktexfmtMode. +############################################################################### +verbose() +{ + $mktexfmtMode && ${1+"$@"} >&2 || ${1+"$@"} +} + +############################################################################### +# mktexdir(args) +# call mktexdir script, disable all features (to prevent sticky directories) +############################################################################### +mktexdir() +{ + initTexmfMain + MT_FEATURES=none "$MT_TEXMFMAIN/web2c/mktexdir" "$@" >&2 +} + +############################################################################### +# tcfmgr(args) +# call tcfmgr script +############################################################################### +tcfmgr() +{ + initTexmfMain + "$MT_TEXMFMAIN/texconfig/tcfmgr" "$@" +} + +############################################################################### +# mktexupd(args) +# call mktexupd script +############################################################################### +mktexupd() +{ + initTexmfMain + "$MT_TEXMFMAIN/web2c/mktexupd" "$@" +} + +############################################################################### +# main() +# parse commandline arguments, initialize variables, +# switch into temp. direcrory, execute desired command +############################################################################### +main() +{ + destdir= # global variable: where do we put the format files? + cnf_file= # global variable: full name of the config file + cmd= # desired action from command line + needsCleanup=false + need_find_hyphenfile=false + cfgparam= + cfgmaint= + verboseFlag=true + noAbortFlag=false + # eradicate double slashes to avoid kpathsea expansion. + tmpdir=`echo ${TMPDIR-${TEMP-${TMP-/tmp}}}/$progname.$$ | sed s,//,/,g` + + # mktexfmtMode: if called as mktexfmt, set to true. Will echo the + # first generated filename after successful generation to stdout then + # (and nothing else), since kpathsea can only deal with one. + mktexfmtMode=false + case $argv0 in + mktexfmt|*/mktexfmt) + mktexfmtMode=true + fullfmt=$1; shift + case $fullfmt in + ""|--help) help ;; + --version) versionfunc ;; + --*) abort "unknown option $fullfmt, try --help" ;; + *.fmt|*.mem|*.base) + set x --byfmt `echo $fullfmt | sed 's@\.[a-z]*$@@'` ${1+"$@"} + shift + ;; + *.*) abort "unknown format type: $fullfmt" ;; + *) set x --byfmt $fullfmt; shift ;; + esac + ;; + esac + + use_engine_dir=true # whether to use web2c/$engine subdirs + while + case $1 in + --cnffile) + shift; cnf_file=$1; cfgparam=1;; + --cnffile=*) + cnf_file=`echo "$1" | sed 's/--cnffile=//'`; cfgparam=1; shift ;; + --fmtdir) + shift; destdir=$1;; + --fmtdir=*) + destdir=`echo "$1" | sed 's/--fmtdir=//'`; shift ;; + --no-engine-subdir) + use_engine_dir=false;; + --all|-a) + cmd=all;; + --edit|-e) + cmd=edit; cfgmaint=1;; + --missing|-m) + cmd=missing;; + --refresh|-r) + cmd=refresh;; + --byengine) + shift; cmd=byengine; arg=$1;; + --byengine=*) + cmd=byengine; arg=`echo "$1" | sed 's/--byengine=//'`; shift ;; + --byfmt|-f) + shift; cmd=byfmt; arg=$1;; + --byfmt=*) + cmd=byfmt; arg=`echo "$1" | sed 's/--byfmt=//'`; shift ;; + --byhyphen|-h) + shift; cmd=byhyphen; arg=$1;; + --byhyphen=*) + cmd=byhyphen; arg=`echo "$1" | sed 's/--byhyphen=//'`; shift ;; + --showhyphen|-s) + shift; cmd=showhyphen; arg=$1;; + --showhyphen=*) + cmd=showhyphen; arg=`echo "$1" | sed 's/--showhyphen=//'`; shift ;; + --help|-help) + cmd=help;; + --version) + cmd=version;; + --enablefmt) + shift; cmd=enablefmt; arg=$1; cfgmaint=1;; + --enablefmt=*) + cmd=enablefmt; arg=`echo "$1" | sed 's/--enablefmt=//'`; cfgmaint=1; shift;; + --disablefmt) + shift; cmd=disablefmt; arg=$1; cfgmaint=1;; + --disablefmt=*) + cmd=disablefmt; arg=`echo "$1" | sed 's/--disablefmt=//'`; cfgmaint=1; shift;; + --catcfg) + cmd=catcfg;; + --listcfg) + cmd=listcfg;; + --no-error-if-no-format) + noAbortFlag=true;; + --quiet|-q|--silent) + verboseFlag=false;; + --test|--dolinks|--force) + ;; + "") break;; + *) abort "unknown option \`$1'; try $progname --help if you need it";; + esac + do test $# -gt 0 && shift; done + + case "$cmd" in + "") abort "missing command; try $progname --help if you need it";; + help) help;; + version) versionfunc;; + esac + + if test -n "$cfgparam"; then + test -f "$cnf_file" || abort "config file \`$cnf_file' not found (ls-R missing?)" + fi + + if test -n "$cfgmaint"; then + if test -z "$cfgparam"; then + setupTmpDir + co=`tcfmgr --tmp $tmpdir --cmd co --file $cnf` + test $? = 0 || cleanup 1 + set x $co; shift + id=$1; cnf_file=$3; orig=$4 + verboseMsg "$progname: initial config file is \`$orig'" + fi + else + if test -z "$cfgparam"; then + cnf_file=`tcfmgr --cmd find --file $cnf` + test -f "$cnf_file" || abort "config file \`$cnf' not found" + fi + fi + + # these commands need no temp directory, so do them here: + case "$cmd" in + catcfg) + grep -v '^ *#' "$cnf_file" | sed 's@^ *@@; s@ *$@@' | grep . | sort + cleanup $? ;; + edit) + echo "$0: fmtutil --edit is disabled in TeX Live;" >&2 + echo "$0: use a file fmtutil-local.cnf instead." >&2 + echo "$0: See tlmgr --help or http://tug.org/texlive/doc/tlmgr.html." >&2 + cleanup 0 ;; + enablefmt|disablefmt) + $cmd $arg ;; # does not return + listcfg) + listcfg_loop + cleanup $? ;; + showhyphen) + show_hyphen_file "$arg" + cleanup $? ;; + esac + + if test -n "$cfgmaint"; then + if test -z "$cfgparam"; then + ci=`tcfmgr --tmp $tmpdir --cmd ci --id $id` + if test $? = 0; then + if test -n "$ci"; then + verboseMsg "$progname: configuration file updated: \`$ci'" + else + verboseMsg "$progname: configuration file unchanged." + fi + else + abort "failed to update configuration file." + fi + fi + cleanup $? + fi + + # set up destdir: + if test -z "$destdir"; then + : ${MT_TEXMFVAR=`kpsewhich -var-value=TEXMFVAR`} + destdir=$MT_TEXMFVAR/web2c + fi + test -d "$destdir" || mktexdir "$destdir" >/dev/null 2>&1 + test -d "$destdir" || abort "format directory \`$destdir' does not exist" + test -w "$destdir" || abort "format directory \`$destdir' is not writable" + + thisdir=`pwd` + + : ${KPSE_DOT=$thisdir} + export KPSE_DOT + + # due to KPSE_DOT, we don't search the current directory, so include + # it explicitly for formats that \write and later on \read + TEXINPUTS="$tmpdir:$TEXINPUTS"; export TEXINPUTS + # for formats that load other formats (e.g., jadetex loads latex.fmt), + # add the current directory to TEXFORMATS, too. Currently unnecessary + # for MFBASES and MPMEMS. + TEXFORMATS="$tmpdir:$TEXFORMATS"; export TEXFORMATS + + setupTmpDir + cd "$tmpdir" || cleanup 1 + + # make local paths absolute: + case "$destdir" in + /*) ;; + *) destdir="$thisdir/$destdir";; + esac + case "$cnf_file" in + /*) ;; + *) cnf_file="$thisdir/$cnf_file";; + esac + + cache_vars + init_log_failure + init_log_warning + # execute the desired command: + case "$cmd" in + all) + recreate_all;; + missing) + create_missing;; + refresh) + recreate_existing;; + byengine) + recreate_by_engine "$arg";; + byfmt) + recreate_by_fmt "$arg";; + byhyphen) + recreate_by_hyphenfile "$arg";; + esac + + byebye +} + +############################################################################### +# parse_line(config_line) sets global variables: +# format: name of the format, e.g. pdflatex +# engine: name of the TeX engine, e.g. tex, etex, pdftex +# texargs: flags for initex and name of the ini file (e.g. -mltex frlatex.ini) +# fmtfile: name of the format file (without directory, but with extension) +# +# Support for building internationalized formats sets: +# pool: base name of pool file (to support translated pool files) +# tcx: translation file used when creating the format +# +# Example (for fmtutil.cnf): +# mex-pl tex mexconf.tex nls=tex-pl,il2-pl mex.ini +# +# The nls parameter (pool,tcx) can only be specified as the first argument +# inside the 4th field in fmtutil.cnf. +# +# exit code: returns error code if the ini file is not installed +############################################################################### +parse_line() +{ + case $1 in + '#!') disabled=true; shift;; + *) disabled=false;; + esac + format=$1 + engine=$2 + hyphenation=$3 + shift; shift; shift + + # handle nls support: pool + tcx + pool=; tcx= + case $1 in + nls=*) + pool=`echo $1 | sed 's@nls=@@; s@,.*@@'` + tcx=`echo $1 | sed 's@nls=[^,]*@@; s@^,@@'` + shift # nls stuff is not handled by the engine directly, + # so we shift this away + ;; + esac + + texargs="$@" + + case "$engine" in + mpost) fmtfile="$format.mem"; kpsefmt=mp; texengine=metapost;; + mf|mfw|mf-nowin) fmtfile="$format.base"; kpsefmt=mf; texengine=metafont;; + *) fmtfile="$format.fmt"; kpsefmt=tex; texengine=$engine;; + esac + + # remove any * for the sake of the kpsewhich lookup. + eval lastarg=\$$# + inifile=`echo $lastarg | sed 's%^\*%%'` + + # See if we can find $inifile for return code: + kpsewhich -progname=$format -format=$kpsefmt $inifile >/dev/null 2>&1 +} + +############################################################################### +# find_hyphenfile(format, hyphenation) searches for hyphenation along +# searchpath of format +# exit code: returns error is file is not found +############################################################################### +find_hyphenfile() +{ + format="$1"; hyphenation="`echo $2 | sed 's/,/ /g'`" + case $hyphenation in + -) ;; + *) kpsewhich -progname="$format" -format=tex $hyphenation;; + esac +} + +############################################################################### +# find_info_for_name(format) +# Look up the config line for format `format' and call parse_line to set +# global variables. +############################################################################### +find_info_for_name() +{ + format="$1" + + # set x `awk '$1 == format {print; exit}' format="$format" "$cnf_file"`; shift + set x `egrep "^$format( | )" "$cnf_file" | sed q`; shift + test $# = 0 && abort "no info for format \`$format'" + parse_line "$@" +} + +############################################################################### +# run_initex() +# Calls initex. Assumes that global variables are set by parse_line. +############################################################################### +run_initex() +{ + + # install a pool file and set tcx flag if requested in lang= option: + rm -f *.pool + poolfile= + tcxflag= + test -n "$pool" \ + && poolfile=`(kpsewhich -progname=$engine $pool.pool) 2>/dev/null` + if test -n "$poolfile" && test -f "$poolfile"; then + verboseMsg "$progname: attempting to create localized format using pool=$pool and tcx=$tcx." + cp "$poolfile" $engine.pool + test -n "$tcx" && tcxflag=-translate-file=$tcx + localpool=true + else + localpool=false + fi + + jobswitch="-jobname=$format" + case "$format" in + metafun) prgswitch=-progname=mpost;; + mptopdf|cont-??) prgswitch=-progname=context;; + *) prgswitch=-progname=$format;; + esac + + rm -f $fmtfile + + # Check for infinite recursion before running the iniTeX: + # We do this check only if we are running in mktexfmt mode + # otherwise double format definitions will create an infinite loop, too + $mktexfmtMode || mktexfmt_loop= + case :$mktexfmt_loop: in + *:"$format/$engine":*) + abort "Infinite recursion detected, giving up!" ;; + esac + mktexfmt_loop=$mktexfmt_loop:$format/$engine + export mktexfmt_loop + + verboseMsg "$progname: running \`$engine -ini $tcxflag $jobswitch $prgswitch $texargs' ..." + + # run in a subshell to get a local effect of TEXPOOL manipulation: + ( + # If necessary, set TEXPOOL. Use absolute path, because of KPSE_DOT. + $localpool && { TEXPOOL="`pwd`:$TEXPOOL"; export TEXPOOL; } + verbose $engine -ini $tcxflag $jobswitch $prgswitch $texargs + ) /dev/null 2>&1 && + log_warning "\`$engine -ini $tcxflag $jobswitch $prgswitch $texargs' possibly failed." + + # We don't want user-interaction for the following "mv" commands: + mv "$format.log" "$fulldestdir/$format.log" /dev/null" + + # No match before the loop: + setmatch false + + recreate_loop + + # Now check if there was at least one match: + getmatch || maybe_abort "no format depends on hyphen file \`$hyphenfile'" +} + +############################################################################### +# recreate_by_engine(enginename) +# recreate all formats that are based on enginename +############################################################################### +recreate_by_engine() +{ + enginename=$1 + + match_cmd="test x\$engine = x$enginename" + + # No match before the loop: + setmatch false + + recreate_loop + + # Now check if there was at least one match: + getmatch || maybe_abort "no format depends on engine \`$enginename'" +} + + + +############################################################################### +# show_hyphen_file(format) +# prints full name of the hyphenfile for format +# +# exit code: returns error code if the ini file is not installed or if +# the hyphen file cannot be found +############################################################################### +show_hyphen_file() +{ + fmtname=$1 + + find_info_for_name "$fmtname" || abort "no info for format \`$fmtname'" + if test "x$hyphenation" = x-; then + echo - + cleanup 0 + fi + find_hyphenfile "$format" "$hyphenation" \ + || abort "hyphenfile \`$hyphenation' not found" +} + +############################################################################### +# disablefmt(format) +# disables format in configuration file +############################################################################### +disablefmt() +{ + grep "^$1[ ]" $cnf_file >/dev/null || { (exit 0); return 0; } + + ed $cnf_file >/dev/null 2>&1 <<-eof + g/^$1[ ]/s/^/#! / + w + q +eof + (exit 0); return 0 +} + +############################################################################### +# enablefmt(format) +# enables format in configuration file +############################################################################### +enablefmt() +{ + grep "^#![ ]*$1[ ]" $cnf_file >/dev/null || { (exit 0); return 0; } + ed $cnf_file >/dev/null 2>&1 <<-eof + g/^#![ ]*$1[ ]/s/..[ ]*// + w + q +eof + (exit 0); return 0 +} + +main ${1+"$@"} +cleanup 0 diff --git a/Master/texmf-dist/scripts/tetex/texconfig-dialog.sh b/Master/texmf-dist/scripts/tetex/texconfig-dialog.sh new file mode 100755 index 00000000000..e1868d91574 --- /dev/null +++ b/Master/texmf-dist/scripts/tetex/texconfig-dialog.sh @@ -0,0 +1,486 @@ +#!/bin/sh + +# TeX-Config version 3.0 +# Thomas Esser, te@dbs.uni-hannover.de. Public domain. + +# invoke the right shell: + +test -f /bin/ksh && test -z "$RUNNING_KSH" \ + && { UNAMES=`uname -s`; test "x$UNAMES" = xULTRIX; } 2>/dev/null \ + && { RUNNING_KSH=true; export RUNNING_KSH; exec /bin/ksh $0 ${1+"$@"}; } +unset RUNNING_KSH + +test -f /bin/bsh && test -z "$RUNNING_BSH" \ + && { UNAMES=`uname -s`; test "x$UNAMES" = xAIX; } 2>/dev/null \ + && { RUNNING_BSH=true; export RUNNING_BSH; exec /bin/bsh $0 ${1+"$@"}; } +unset RUNNING_BSH + +export PATH + +# hack around a bug in zsh: +test -n "${ZSH_VERSION+set}" && alias -g '${1+"$@"}'='"$@"' + +# the version string +version='$Id: texconfig-dialog 23826 2011-09-05 17:59:15Z karl $' + +: ${PAGER=more} +progname=texconfig-dialog +tmpdir=${TMPDIR-${TEMP-${TMP-/tmp}}}/tcdtmp.$$ +log=$tmpdir/log +tmpmenu=$tmpdir/tmpmenu +needsCleanup=false + +############################################################################### +# cleanup() +# clean up the temp area and exit with proper exit status +############################################################################### +cleanup() +{ + rc=$1 + $needsCleanup && test -n "$tmpdir" && test -d "$tmpdir" \ + && { cd / && rm -rf "$tmpdir"; } + termCtl reset + (exit $rc); exit $rc +} + +############################################################################### +# setupTmpDir() +# set up a temp directory and a trap to remove it +############################################################################### +setupTmpDir() +{ + case $needsCleanup in + true) return;; + esac + + trap 'cleanup 1' 1 2 3 7 13 15 + needsCleanup=true + (umask 077; mkdir "$tmpdir") \ + || abort "could not create directory \`$tmpdir'" +} + +############################################################################### +# abort(errmsg) +# print `errmsg' to stderr and exit with error code 1 +############################################################################### +abort() +{ + echo "$progname: $1." >&2 + cleanup 1 +} + +logexec() +{ + (echo; echo ">>> Executing \`$@' <<<") >> $log + "$@" 2>&1 | tee -a $log +} + +############################################################################### +# runDialog(args, ...) +# execute the right dialog program with the right default parameters +############################################################################### +runDialog() +{ + if test -n "$DIALOG_PROG"; then + termCtl clear + $DIALOG_PROG --title "TeX setup utility" ${1+"$@"} + runDialogRc=$? + termCtl clear + else + abort "could not find dialog or whiptail program to run" + fi + (exit $runDialogRc) + return $runDialogRc +} + +############################################################################### +# findDialog(void) +# set DIALOG_PROG to the system dialog program, or the empty string. +############################################################################### +findDialog() +{ + for bin in whiptail dialog + do + binLoc=`texconfig findprog $bin` + case $binLoc in + "") DIALOG_PROG="";; + *) DIALOG_PROG=$binLoc; break;; + esac + done +} + +############################################################################### +# mktexdir(args) +# call mktexdir script, disable all features (to prevent sticky directories) +############################################################################### +mktexdir() +{ + MT_FEATURES=none "$TEXMFMAIN/web2c/mktexdir" "$@" >&2 +} + +############################################################################### +# termCtl(arg) +# some convenience utilities for terminal control +############################################################################### +termCtl() +{ + case $1 in + clear) + test -n "$NO_CLEAR" && return + tty >/dev/null 2>&1 && clear + ;; + reset) + test -n "$NO_CLEAR" && return + reset 2>/dev/null + stty sane 2>/dev/null + reset 2>/dev/null + termCtl clear + ;; + readln) + echo + echo "press return to continue..." + read a + ;; + esac +} + +############################################################################### +# menuMain(void) +# the main menu +############################################################################### +menuMain() +{ + cat <<-'eof' + The interactive texconfig utility will be started now. Make sure + your screen has at least 24 rows and 80 columns. If texconfig + crashes now, you can still set up your teTeX system using the + batch mode of texconfig. Try 'texconfig help' to get a list + of options. + + The interactive mode works best with a real vt100 terminal or + inside an xterm window. +eof + termCtl readln + + while :; do + logMessage='view logfile' + + runDialog \ + --menu " +Hint: all output of external commands (e.g. tex) is logged into +a file. You can look at this file using "LOG". If cursor keys make +trouble, you may have more luck with +/- and TAB. +" \ + 23 80 14 \ + EXIT 'exit' \ + PAPER 'default paper: A4 or letter (us)' \ + MODE 'default metafont mode and resolution' \ + REHASH 'rebuild filename databases' \ + FORMATS 'edit format definitions' \ + HYPHENATION 'customize hyphenation' \ + DVIPS 'dvips configuration' \ + FAQ 'view frequently asked questions + answers' \ + CONF 'show configuration' \ + LOG "$logMessage" \ + 2>"$tmpmenu" || break + + case `cat "$tmpmenu"` in + EXIT) + break + ;; + PAPER) + menuPaper + ;; + MODE) + menuMode + ;; + REHASH) + logexec texconfig rehash + termCtl readln + ;; + FORMATS) + texconfig formats + termCtl readln + ;; + HYPHENATION) + menuHyphenation + ;; + DVIPS) + menuDvips + ;; + FAQ) + texconfig faq + termCtl readln + ;; + CONF) + logexec texconfig conf + termCtl readln + ;; + LOG) + <"$log" eval $PAGER + termCtl readln + ;; + esac + done +} + +menuGetMode() +{ + # we need eval to get the command line right... :-( + eval \ + runDialog \ + --menu \"" +Chosse a mode to be used when metafont generates font bitmaps. The resolution is the most important point, but there might +be differences between modes of the same resolution. See the comments in the file modes.mf for more details. +\"" \ + 23 80 14 \ + `texconfig mode-list` 2>"$tmpmenu" +} + +menuMode() +{ + menuGetMode + mode=`cat "$tmpmenu"` + + if test -n "$mode"; then + logexec texconfig mode "$mode" + termCtl readln + fi +} + +menuDvips() +{ + menuDvipsDest=ps + + while :; do + case $menuDvipsDest in + ps) + menuDvipsPrinterOpt= + menuDvipsMsg=" +dvips GLOBAL section. Define the most common default settings (config.ps). +To define settings for a specific printer, first ADD a printer definition, +then CHANGE it. +" + ;; + *) + menuDvipsPrinterOpt="-P $menuDvipsDest" + menuDvipsMsg=" +dvips settings for printer $menuDvipsDest (config.$menuDvipsDest). Define +local settings for this printer. To switch back to global settings mode, +select GLOBAL. +" + ;; + esac + + runDialog \ + --menu "$menuDvipsMsg +You can use dvips for non PostScript printers, if you can setup your +printing system to convert PostScript to a format that your printer can +handle. Maybe, you can use GhostScript to do the conversion (if your +printer is supported)." \ + 23 80 9 \ + RETURN 'back to the main menu' \ + DEST 'define default destination of the generated Postscript' \ + MODE 'change metafont mode/resolution' \ + OFFSET 'shift output by some offset' \ + PAPER 'define the default paper' \ + GLOBAL 'change global settings (config.ps)' \ + CHANGE 'change printer settings (config.$PRINTER)' \ + ADD 'add a printer configuration' \ + DEL 'remove a printer configuration' \ + 2>"$tmpmenu" || break + + menuDvipsAns=`cat "$tmpmenu"` + case $menuDvipsAns in + RETURN) + break + ;; + DEST) + runDialog --inputbox "Enter the command to print. + +In general, you need a command like 'lpr' or 'lpr -Pfoo'. + +NOTE: If you just press return, printing will be disabled and the output saved to a file by default. +" 23 80 2>"$tmpmenu" + if test $? = 0; then + menuDvipsAns=`cat "$tmpmenu"` + case $menuDvipsAns in + "") + menuDvipsPrintOpt=- + ;; + *) + menuDvipsPrintOpt=$menuDvipsAns + ;; + esac + logexec texconfig dvips $menuDvipsPrinterOpt printcmd "$menuDvipsPrintOpt" + termCtl readln + fi + ;; + MODE) + menuGetMode + mode=`cat "$tmpmenu"` + + if test -n "$mode"; then + logexec texconfig dvips $menuDvipsPrinterOpt mode "$mode" + termCtl readln + fi + ;; + OFFSET) + runDialog --inputbox "Enter a dimension pair (a rightwards offset and a downwards +offset), e.g. 2mm,-0.5in (right 2mm and up .5in):" 23 80 2>"$tmpmenu"; menuDvipsAns=`cat "$tmpmenu"` + case $menuDvipsAns in + "") : ;; + *) + logexec texconfig dvips $menuDvipsPrinterOpt offset "$menuDvipsAns" + termCtl readln + ;; + esac + ;; + PAPER) + eval runDialog \ + --menu \'\\n\\nChoose the default papersize definition for dvips.\\n\\n\' \ + 23 80 8 `texconfig dvips paper-list` \ + 2>"$tmpmenu" + paper=`cat "$tmpmenu"` + if test -n "$paper"; then + logexec texconfig dvips paper "$paper" + termCtl readln + fi + ;; + GLOBAL) + menuDvipsDest=ps + ;; + CHANGE) + runDialog --inputbox "Printer name (for future settings of DEST / MODE / OFFSET)" 23 80 2>"$tmpmenu" + menuDvipsAns=`cat $tmpmenu` + case $menuDvipsAns in + "") : ;; + *) menuDvipsDest=$menuDvipsAns;; + esac + ;; + ADD) + runDialog --inputbox "Printer name (for printer to add)" 23 80 2>"$tmpmenu" + menuDvipsAns=`cat $tmpmenu` + case $menuDvipsAns in + "") : ;; + *) + logexec texconfig dvips add $menuDvipsAns + termCtl readln + ;; + esac + ;; + DEL) + runDialog --inputbox "Printer name (for printer to delete)" 23 80 2>"$tmpmenu" + menuDvipsAns=`cat $tmpmenu` + case $menuDvipsAns in + "") : ;; + *) + logexec texconfig dvips del $menuDvipsAns + termCtl readln + ;; + esac + ;; + esac + done +} + +menuPaper() +{ + runDialog \ + --menu " +Select your default paper format. +" \ + 23 80 14 \ + RETURN 'return to the main menu' \ + A4 'ISO A4 (210x297mm)' \ + LETTER 'US (8.5x11in)' 2>"$tmpmenu" + + p=`cat "$tmpmenu"` + case $p in + A4) + logexec texconfig paper a4 + termCtl readln + ;; + LETTER) + logexec texconfig paper letter + termCtl readln + ;; + esac +} + +menuHyphenation() +{ + runDialog \ + --menu " +Choose format to set up hyphenation for. +" \ + 23 80 14 \ + `texconfig hyphen-list | sed 's@\(.*\)@\1 \1@'` 2>"$tmpmenu" + p=`cat "$tmpmenu"` + case $p in + "") + return + ;; + *) + texconfig hyphen "$p" + termCtl readln + esac +} + +# main() + +case $1 in + help|--help) + cat <<-eof + Usage: $progname [--help|--version] +eof + exit 0 + ;; + --version) + cat <<-eof + $progname version $version. +eof + exit 0 + ;; +esac + + +: ${TEXMFCONFIG=`kpsewhich -var-value=TEXMFCONFIG`} +: ${TEXMFVAR=`kpsewhich -var-value=TEXMFVAR`} +: ${TEXMFMAIN=`kpsewhich -var-value=TEXMFMAIN`} +export TEXMFCONFIG TEXMFVAR TEXMFMAIN + +setupTmpDir +echo "$progname: started `date`" > $log +findDialog + +test -d "$TEXMFCONFIG" \ + || mktexdir "$TEXMFCONFIG" >/dev/null 2>&1 \ + || echo "$progname: directory \`$TEXMFCONFIG' (from TEXMFCONFIG variable) does not exist and cannot be created" >&2 + +canWriteConfig=false +if test -d "$TEXMFCONFIG"; then + if test -w "$TEXMFCONFIG"; then + canWriteConfig=true + else + echo "$progname: directory \`$TEXMFCONFIG' (from TEXMFCONFIG variable) is not writable." + echo "$progname: configuration data cannot be changed." >&2 + fi +fi + +test -d "$TEXMFVAR" \ + || mktexdir "$TEXMFVAR" >/dev/null 2>&1 \ + || echo "$progname: directory \`$TEXMFVAR' (from TEXMFVAR variable) does not exist and cannot be created" >&2 + +canWriteVardata=false +if test -d "$TEXMFVAR"; then + if test -w "$TEXMFVAR"; then + canWriteVardata=true + else + echo "$progname: directory \`$TEXMFVAR' (from TEXMFVAR variable) is not writable." + echo "$progname: cached variable runtime data files cannot be written." >&2 + fi +fi + +menuMain +cleanup 0 diff --git a/Master/texmf-dist/scripts/tetex/texconfig-sys.sh b/Master/texmf-dist/scripts/tetex/texconfig-sys.sh new file mode 100755 index 00000000000..ff549003ad2 --- /dev/null +++ b/Master/texmf-dist/scripts/tetex/texconfig-sys.sh @@ -0,0 +1,31 @@ +#!/bin/sh +# $Id: texconfig-sys 20962 2011-01-07 11:45:38Z peter $ + +# texconfig-sys: Thomas Esser, public domain. + +# wrapper script for texconfig with TEXMFVAR and TEXMFCONFIG set to +# TEXMFSYSVAR / TEXMFSYSCONFIG + +test -f /bin/ksh && test -z "$RUNNING_KSH" \ + && { UNAMES=`uname -s`; test "x$UNAMES" = xULTRIX; } 2>/dev/null \ + && { RUNNING_KSH=true; export RUNNING_KSH; exec /bin/ksh $0 ${1+"$@"}; } +unset RUNNING_KSH + +test -f /bin/bsh && test -z "$RUNNING_BSH" \ + && { UNAMES=`uname -s`; test "x$UNAMES" = xAIX; } 2>/dev/null \ + && { RUNNING_BSH=true; export RUNNING_BSH; exec /bin/bsh $0 ${1+"$@"}; } +unset RUNNING_BSH + +export PATH + +# hack around a bug in zsh: +test -n "${ZSH_VERSION+set}" && alias -g '${1+"$@"}'='"$@"' + +v=`kpsewhich -var-value TEXMFSYSVAR` +c=`kpsewhich -var-value TEXMFSYSCONFIG` + +TEXMFVAR="$v" +TEXMFCONFIG="$c" +export TEXMFVAR TEXMFCONFIG + +exec texconfig ${1+"$@"} diff --git a/Master/texmf-dist/scripts/tetex/texconfig.sh b/Master/texmf-dist/scripts/tetex/texconfig.sh new file mode 100755 index 00000000000..c9cc73c8305 --- /dev/null +++ b/Master/texmf-dist/scripts/tetex/texconfig.sh @@ -0,0 +1,1432 @@ +#!/bin/sh + +# TeXConfig version 3.0 +# Originally written by Thomas Esser. Public domain. +# Now maintained as part of TeX Live; correspondence to tex-live@tug.org. + +# invoke the right shell: + +test -f /bin/ksh && test -z "$RUNNING_KSH" \ + && { UNAMES=`uname -s`; test "x$UNAMES" = xULTRIX; } 2>/dev/null \ + && { RUNNING_KSH=true; export RUNNING_KSH; exec /bin/ksh $0 ${1+"$@"}; } +unset RUNNING_KSH + +test -f /bin/bsh && test -z "$RUNNING_BSH" \ + && { UNAMES=`uname -s`; test "x$UNAMES" = xAIX; } 2>/dev/null \ + && { RUNNING_BSH=true; export RUNNING_BSH; exec /bin/bsh $0 ${1+"$@"}; } +unset RUNNING_BSH + +export PATH + +# hack around a bug in zsh: +test -n "${ZSH_VERSION+set}" && alias -g '${1+"$@"}'='"$@"' + +# initializations... +progname=texconfig + +# the version string +version='$Id$' + +envVars=" + 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 +" +tmpdir=${TMPDIR-${TEMP-${TMP-/tmp}}}/tctmp.$$ +needsCleanup=false +lastUpdatedFile= + +# +############################################################################### +# setupFMT(void) - find a suitable version of fmt / adjust +# +setupFMT() +{ + case $FMT in + "") + FMT=fmt + test ! -x /bin/fmt && test ! -f /usr/bin/fmt && + { test -x /bin/adjust || test -x /usr/bin/adjust; } && FMT=adjust + ;; + *) + return + ;; + esac +} + +############################################################################### +# myFmt(args) - run $FMT +# +myFmt() +{ + setupFMT + $FMT ${1+"$@"} +} + +############################################################################### +# echoShowVariable(args ...) +# show environment variables which names are as args and their values +# +echoShowVariable() +{ + for esv + do + var=$esv + eval val=\"\${$var+=}\${$var- is unset}\" + echo "$var$val" + done | grep -v 'is unset$' +} + +############################################################################### +# echoShowKpseVariable(args ...) +# show kpathsea variables which names are as args and their values +# +echoShowKpseVariable() +{ + for eskv + do + var=$eskv + val=`kpsewhich -var-value="$eskv"` + echo "$var=$val" + done +} + +############################################################################### +# echoLocateBinary(args ...) - show where programs actually exist +# +echoLocateBinary() +{ + for elb + do + elbLoc=`checkForBinary "$elb"` + if test -n "$ELB_PATH_ONLY"; then + test -n "$elbLoc" && echo "$elbLoc" + else + case $elbLoc in + "") echo "$elb: not found";; + *) echo "$elb: $elbLoc";; + esac + fi + done +} + +############################################################################### +# echoLocateCfgfile(args ...) - show where files actually exist +# +echoLocateCfgfile() +{ + for elc + do + case $elc in + texmf.cnf) elcLoc=`kpsewhich $elc`;; + *) elcLoc=`tcfmgr --cmd find --file "$elc"`;; + esac + case $elcLoc in + "") echo "$elc: not found";; + *) echo "$elcLoc";; + esac + done +} + +############################################################################### +# checkForBinary(prog) - echo full path of prog +# +checkForBinary() +{ + cfbBinary=$1 + + OLDIFS=$IFS + IFS=: + set x `echo "$PATH" | sed 's/^:/.:/; s/:$/:./; s/::/:.:/g'`; shift + found=false + for pathElem + do + case $pathElem in + "") continue;; + *) test -f "$pathElem/$cfbBinary" && { echo "$pathElem/$cfbBinary"; found=true; break; } + esac + done + IFS=$OLDIFS + case $found in + true) (exit 0); return 0;; + false) (exit 1); return 1;; + esac +} + +############################################################################### +# cleanup() - clean up the temp area and exit with proper exit status +# +cleanup() +{ + rc=$1 + $needsCleanup && test -n "$tmpdir" && test -d "$tmpdir" \ + && { cd / && rm -rf "$tmpdir"; } + (exit $rc); exit $rc +} + +############################################################################### +# setupTmpDir() - set up a temp directory and a trap to remove it +# +setupTmpDir() +{ + case $needsCleanup in + true) return;; + esac + + trap 'cleanup 1' 1 2 3 7 13 15 + needsCleanup=true + (umask 077; mkdir "$tmpdir") \ + || abort "could not create directory \`$tmpdir'" +} + +############################################################################### +# setupTexmfmain() - get value for MT_TEXMFMAIN (with caching) +# +setupTexmfmain() +{ + case $MT_TEXMFMAIN in + "") MT_TEXMFMAIN=`kpsewhich -var-value=TEXMFMAIN`;; + *) return;; + esac +} + +############################################################################### +# setupTexmfmain() - get value for MT_TEXMFDIST (with caching) +# +setupTexmfdist() +{ + case $MT_TEXMFDIST in + "") MT_TEXMFDIST=`kpsewhich -var-value=TEXMFDIST`;; + *) return;; + esac +} + +############################################################################### +# setupTexmfvar() - get value for MT_TEXMFVAR (with caching) +# +setupTexmfvar() +{ + case $MT_TEXMVAR in + "") MT_TEXMVAR=`kpsewhich -var-value=TEXMFVAR`;; + *) return;; + esac +} + +############################################################################### +# setupSystexmf() - get value for MT_SYSTEXMF (with caching) +# +setupSystexmf() +{ + case $MT_SYSTEXMF in + "") MT_SYSTEXMF=`kpsewhich -var-value=SYSTEXMF`;; + *) return;; + esac +} + +############################################################################### +# abort(errmsg) +# print `errmsg' to stderr and exit with error code 1 +# +abort() +{ + echo "$progname: $1." >&2 + cleanup 1 +} + +############################################################################### +# mktexdir(args) +# call mktexdir script, disable all features (to prevent sticky directories) +# +mktexdir() +{ + setupTexmfmain + MT_FEATURES=none "$MT_TEXMFMAIN/web2c/mktexdir" "$@" >&2 +} + +############################################################################### +# tcfmgr(args) - call tcfmgr script +# +tcfmgr() +{ + setupTexmfmain + "$MT_TEXMFMAIN/texconfig/tcfmgr" "$@" +} + +############################################################################### +# mktexupd(args) - call mktexupd script +# +mktexupd() +{ + setupTexmfmain + "$MT_TEXMFMAIN/web2c/mktexupd" "$@" +} + +############################################################################### +# getRelDir(file) +# matches file against SYSTEXMF. Returns relative directory of file within +# a texmf tree in variable relPart. +# +getRelDir() +{ + file=$1 + relPart= + + setupSystexmf + OLDIFS=$IFS + IFS=' +' + set x `echo "$MT_SYSTEXMF" | tr : ' +'`; shift + IFS=$OLDIFS + + # now loop over all components of SYSTEXMF + for dir + do + test -n "$dir" || continue + case "$file" in + $dir/*) + relPart=`echo "$file" | sed "s%$dir/*%%"` + break + ;; + esac + done + + # now check for success / failure + case $relPart in + ""|$file) + # empty or full filename -> getRelDir failed! + (exit 1); return 1 + ;; + *) + # relPart should just have the "dirname" part: + relPart=`echo "$relPart" | sed 's%/*[^/]*$%%'` + (exit 0); return 0 + ;; + esac +} + +############################################################################### +# configReplace(file pattern line) +# The first line in file that matches pattern gets replaced by line. +# line will be added at the end of the file if pattern does not match. +# +configReplace() +{ + configReplaceFile=$1; configReplacePat=$2; configReplaceLine=$3 + + if grep "$configReplacePat" "$configReplaceFile" >/dev/null; then + ed "$configReplaceFile" >/dev/null 2>&1 <<-eof + /$configReplacePat/c + $configReplaceLine + . + w + q +eof + else + echo "$configReplaceLine" >> $configReplaceFile + fi +} + +############################################################################### +# fmgrConfigReplace (file regex value) +# replaces line matching regex by value in file +# +fmgrConfigReplace() +{ + fmgrConfigReplaceChanged=false + + moreArgs="" + while + case $1 in + --*) moreArgs="$moreArgs $1 $2";; + *) break;; + esac + do shift; shift; done + fmgrConfigReplaceFile=$1 + fmgrConfigReplaceRegex=$2 + fmgrConfigReplaceValue=$3 + + setupTmpDir + co=`tcfmgr $moreArgs --tmp $tmpdir --cmd co --file $fmgrConfigReplaceFile` + if test $? != 0; then + echo "$progname: fmgrConfigReplace co failed for \`$fmgrConfigReplaceFile'" >&2 + (exit 1); return 1 + fi + set x $co; shift + fmgrConfigReplaceID=$1; fmgrConfigReplaceCfgFile=$3; fmgrConfigReplaceOrigFile=$4 + configReplace "$fmgrConfigReplaceCfgFile" "$fmgrConfigReplaceRegex" "$fmgrConfigReplaceValue" + ci=`tcfmgr --tmp $tmpdir --cmd ci --id "$fmgrConfigReplaceID"` + if test $? != 0; then + echo "$progname: fmgrConfigReplace ci failed for \`$fmgrConfigReplaceFile'" >&2 + (exit 1); return 1 + fi + case $ci in + "") :;; + $lastUpdatedFile) + fmgrConfigReplaceChanged=true;; + *) echo "$progname: updated configuration saved as file \`$ci'" >&2 + fmgrConfigReplaceChanged=true + lastUpdatedFile=$ci;; + esac + (exit 0); return 0 +} + +############################################################################### +# setupDvipsPaper(paper) +# rearranges config.ps to make paper the first paper definition +# +setupDvipsPaper() +{ + setupDvipsPaperChanged=false + setupDvipsPaperFile=config.ps + setupDvipsPaperDftPaper=$1 + + setupTmpDir + co=`tcfmgr --tmp $tmpdir --cmd co --file $setupDvipsPaperFile` + if test $? != 0; then + echo "$progname: setupDvipsPaper co failed for \`$setupDvipsPaperFile'" >&2 + (exit 1); return 1 + fi + set x $co; shift + setupDvipsPaperID=$1; setupDvipsPaperCfgFile=$3; setupDvipsPaperOrigFile=$4 + + ed "$setupDvipsPaperCfgFile" > /dev/null 2>&1 <<-eof + /@ /ka + \$a + @ + . + /@ $setupDvipsPaperDftPaper /;/@ /-1m'a-1 + \$d + w + q +eof + + ci=`tcfmgr --tmp $tmpdir --cmd ci --id "$setupDvipsPaperID"` + if test $? != 0; then + echo "$progname: setupDvipsPaper ci failed for \`$setupDvipsPaperFile'" >&2 + (exit 1); return 1 + fi + case $ci in + "") :;; + $lastUpdatedFile) + setupDvipsPaperChanged=true;; + *) echo "$progname: updated configuration saved as file \`$ci'" >&2 + setupDvipsPaperChanged=true + lastUpdatedFile=$ci;; + esac + (exit 0); return 0 +} + +############################################################################### +# setupModesMfFile(void) - find modes.mf file (with caching) +# +setupModesMfFile() +{ + case $modesMfFile in + "") + modesMfFile=`tcfmgr --cmd find --file modes.mf` + ;; + *) + return + ;; + esac +} + +############################################################################### +# locateConfigPsFile(void) - find config.ps file (with caching) +# +locateConfigPsFile() +{ + case $configPsFile in + "") + configPsFile=`tcfmgr --cmd find --file config.ps` + ;; + *) + return + ;; + esac +} + +############################################################################### +# listMfModes(file) - list modes from modes.mf file +# +listMfModes() +{ + grep mode_def "$modesMfFile" | + sed -e "s/mode_def //" \ + -e "s/ .*%[^ ]* / '/" \ + -e "s/\$/' /" | + egrep -v "^(help|%)" | sort +} + +############################################################################### +# listDvipsPapers(void) - list paper definitions from config.ps +# +listDvipsPapers() +{ + grep '@ ' $configPsFile | sed "s/..//;s/ / '/;s/\$/' /" +} + +############################################################################### +# getFormatsForHyphen(void) +# list all formats which have customizable hyphenation +# +getFormatsForHyphen() +{ + fmtutil --catcfg | awk '$3 != "-" {print $1}' | sort +} + +############################################################################### +# getRes(mode) - print resolution (both X and Y axis) to metafont mode +# +getRes() +{ + getResMode=$1 + ( + cd $tmpdir + cat >mftmp.mf <<-'eof' + let myexit = primitive_end_; + mode_setup; + string xdpi; + xdpi := decimal round pixels_per_inch; + message "XDPI = " & xdpi; + string ydpi; + ydpi := decimal round (pixels_per_inch * aspect_ratio); + message "YDPI = " & ydpi; + fontmaking := 0; + myexit; +eof + mf '\mode='"$getResMode"'; \input ./mftmp' &2; rc=1;; + *) + echo "$progname: unknown PAPER \`$3' given as argument for \`$progname dvipdfm paper'" >&2 + echo "$progname: try \`$progname dvipdfm paper' for help" >&2 + rc=1 ;; + esac ;; + # texconfig dvipdfm "" + "") + echo "$help" >&2; rc=1 ;; + # texconfig dvipdfm + *) + echo "$progname: unknown option \`$2' given as argument for \`$progname dvipdfm'" >&2 + echo "$progname: try \`$progname dvipdfm' for help" >&2 + rc=1 + ;; + esac + ;; + + # texconfig dvipdfmx + dvipdfmx) + help="Usage: $progname dvipdfmx paper PAPER + +Valid PAPER settings: + letter legal ledger tabloid a4 a3" + case $2 in + # texconfig dvipdfmx paper + paper-list) + for p in letter legal ledger tabloid a4 a3; do echo $p; done + ;; + paper) + case $3 in + letter|legal|ledger|tabloid|a4|a3) + fmgrConfigReplace dvipdfmx.cfg '^p' "p $3";; + "") echo "$help" >&2; rc=1;; + *) + echo "$progname: unknown PAPER \`$3' given as argument for \`$progname dvipdfmx paper'" >&2 + echo "$progname: try \`$progname dvipdfmx paper' for help" >&2 + rc=1 ;; + esac ;; + # texconfig dvipdfmx "" + "") + echo "$help" >&2; rc=1 ;; + # texconfig dvipdfmx + *) + echo "$progname: unknown option \`$2' given as argument for \`$progname dvipdfmx'" >&2 + echo "$progname: try \`$progname dvipdfmx' for help" >&2 + rc=1 + ;; + esac + ;; + + # texconfig dvips + dvips) + shift + help="Usage: $progname dvips add PRINTER + $progname dvips del PRINTER + $progname dvips paper PAPER + $progname dvips [-P PRINTER] mode MODE + $progname dvips [-P PRINTER] offset OFFSET + $progname dvips [-P PRINTER] printcmd CMD" + case $1 in + -P) + case $2 in + "") + echo "$progname: missing arg for parameter -P" >&2 + rc=1; (exit $rc); return $rc + ;; + *) + otherPrinter=true + otherPrinterName=$2 + otherPrinterFile=`kpsewhich -format='dvips config' "config.$otherPrinterName"` + case $otherPrinterFile in + "") + echo "$progname: configuration file \`config.$otherPrinterName' for printer \`$otherPrinterName' not found" >&2 + rc=1; (exit $rc); return $rc + ;; + *) shift; shift;; + esac + ;; + esac + ;; + *) + otherPrinter=false + ;; + esac + case $otherPrinter in + true) + tcBatchDvipsPrinter=$otherPrinterName + moreFmgrArgs="--reldir dvips/config --infile $otherPrinterFile" + ;; + *) + tcBatchDvipsPrinter=ps + ;; + esac + case $1 in + add) + case $2 in + "") + echo "Usage: $progname dvips add PRINTER" >&2 + rc=1 + ;; + *) + printerName=$2 + pFile=`kpsewhich -format='dvips config' "config.$printerName"` + case $pFile in + "") + setupTmpDir + tcfRet=`tcfmgr --emptyinfile --reldir dvips/config --cmd co --tmp $tmpdir --file "config.$printerName"` + if test $? != 0; then + echo "$progname: failed to add new configuration file \`config.$printerName'" >&2 + rc=1 + else + set x $tcfRet; shift + tcBatchDvipsAddID=$1; tcBatchDvipsAddFile=$3 + echo "% file config.$printerName; added by texconfig" > "$tcBatchDvipsAddFile" + tcfRet=`tcfmgr --tmp $tmpdir --id "$tcBatchDvipsAddID" --cmd ci` + if test $? != 0; then + echo "$progname: failed to add new configuration file \`config.$printerName'" >&2 + rc=1 + else + echo "$progname: file $tcfRet added" >&2 + fi + fi + ;; + *) + echo "$progname: configuration file for printer \`$printerName' already exists (\`$pFile')" >&2 + rc=1 + ;; + esac + ;; + esac + ;; + del) + case $2 in + "") + echo "Usage: $progname dvips del PRINTER" >&2 + rc=1 + ;; + *) + printerName=$2 + pFile=`kpsewhich -format='dvips config' "config.$printerName"` + case $pFile in + "") + echo "$progname: configuration file for printer \`$printerName' (config.$printerName) not found" >&2 + rc=1 + ;; + *) + if rm "$pFile"; then + echo "$progname: file \`$pFile' removed" >&2 + else + echo "$progname: failed to remove file \`$pFile'" >&2 + rc=1 + fi + ;; + esac + ;; + esac + ;; + paper-list) + locateConfigPsFile + listDvipsPapers + ;; + paper) + case $2 in + "") + echo "Usage: $progname dvips paper PAPER" >&2 + echo >&2; echo "Valid PAPER settings:" >&2 + locateConfigPsFile + listDvipsPapers | sed 's@ .*@@; s@^@ @' | myFmt + rc=1 + ;; + *) + tcBatchDvipsPaper=$2 + locateConfigPsFile + case "$configPsFile" in + "") + echo "$progname: file config.ps not found" >&2; rc=1 + ;; + *) + if grep "@ $tcBatchDvipsPaper " $configPsFile >/dev/null 2>&1; then + setupDvipsPaper "$tcBatchDvipsPaper" + else + echo "$progname: paper \`$tcBatchDvipsPaper' not found in file \`$configPsFile'" >&2; rc=1 + fi + ;; + esac + ;; + esac + ;; + mode) + case $2 in + "") + echo "Usage: $progname dvips mode MODE + +Valid MODE settings:" + setupModesMfFile + listMfModes | sed 's@ .*@@; s@^@ @' | myFmt + rc=1 + ;; + *) + tcBatchDvipsMode=$2 + setupTmpDir + setupModesMfFile + if checkElemInList "$tcBatchDvipsMode" `listMfModes | sed 's@ .*@@'`; then + set x `getRes "$tcBatchDvipsMode"`; shift + resX=$1; resY=$2 + fmgrConfigReplace $moreFmgrArgs config.$tcBatchDvipsPrinter '^M' "M $tcBatchDvipsMode" + fmgrConfigReplace $moreFmgrArgs config.$tcBatchDvipsPrinter '^D' "D $resX" + fmgrConfigReplace $moreFmgrArgs config.$tcBatchDvipsPrinter '^X' "X $resX" + fmgrConfigReplace $moreFmgrArgs config.$tcBatchDvipsPrinter '^Y' "Y $resY" + else + echo "$progname: unknown MODE \`$tcBatchDvipsMode' given as argument for \`$progname dvips mode'" >&2 + echo "$progname: try \`$progname dvips mode' for help" >&2 + rc=1 + fi + ;; + esac + ;; + offset) + offset=$2 + case $offset in + "") + echo "Usage: $progname dvips offset OFFSET" + rc=1 + ;; + *) + fmgrConfigReplace $moreFmgrArgs config.$tcBatchDvipsPrinter '^O' "O $offset" + esac + ;; + printcmd) + printcmd=$2 + case $printcmd in + "") + echo "Usage: $progname dvips printcmd CMD" + rc=1 + ;; + -) + fmgrConfigReplace $moreFmgrArgs config.$tcBatchDvipsPrinter '^o' o + ;; + *) + fmgrConfigReplace $moreFmgrArgs config.$tcBatchDvipsPrinter '^o' "o |$printcmd" + ;; + esac + ;; + "") + echo "$help" >&2; rc=1 + ;; + *) + echo "$progname: unknown option \`$1' given as argument for \`$progname dvips'" >&2 + echo "$progname: try \`$progname dvips' for help" >&2 + rc=1 + ;; + esac + ;; + + faq) + setupTexmfmain + if test -f $MT_TEXMFMAIN/doc/tetex/teTeX-FAQ; then + <$MT_TEXMFMAIN/doc/tetex/teTeX-FAQ eval ${PAGER-more} + else + echo "$progname: faq not found (usually in \$TEXMFMAIN/doc/tetex/teTeX-FAQ)" >&2 + rc=1 + fi + ;; + + findprog) + shift + ELB_PATH_ONLY=1 echoLocateBinary "$@" + ;; + + # handle "texconfig font" + font) + help="Usage: $progname font vardir DIR + $progname font ro + $progname font rw + +The vardir option changes the VARTEXFONTS variable in the texmf.cnf file. + +The rw option makes the VARTEXFONTS directory (and subtrees pk, tfm, +source) world writable and sets the features appendonlydir:varfonts +in mktex.cnf. + +The ro option makes the VARTEXFONTS directory (and subtrees pk, tfm, +source) writable for the owner only and sets the feature texmfvar in +mktex.cnf. + +For more information about these \`features', consult the teTeX manual +(e.g. by running \`texdoc TETEXDOC')." + + case $2 in + vardir) + case $3 in + "") + echo "$help" >&2 + rc=1 + ;; + *) + tcBatchFontVardir=$3 + tfc=`kpsewhich texmf.cnf` + if test -n "$tfc"; then + if test -w "$tfc"; then + configReplace "$tfc" '^VARTEXFONTS' "VARTEXFONTS = $tcBatchFontVardir" + else + echo "$progname: setting up vardir failed. Reason: no permission to write file \`$tfc'" >&2 + rc=1 + fi + else + echo "$progname: setting up vardir failed. Reason: failed to find file texmf.cnf" >&2 + rc=1 + fi + ;; + esac + ;; + rw) + MT_VARTEXFONTS=`kpsewhich -var-value VARTEXFONTS` + if test -z "$MT_VARTEXFONTS"; then + echo "$progname: failed to set \`font rw'; reason: could not determine VARTEXFONTS variable." >&2; rc=1 + return + fi + test -d "$MT_VARTEXFONTS" || mktexdir "$MT_VARTEXFONTS" + if test ! -d "$MT_VARTEXFONTS"; then + echo "$progname: failed to set \`font rw'; reason: directory \`$MT_VARTEXFONTS' does not exist." >&2; rc=1 + return + fi + chmod 1777 "$MT_VARTEXFONTS" || { + echo "$progname: failed to modify permissions in \`$MT_VARTEXFONTS'." >&2; rc=1 + return; + } + ( + cd "$MT_VARTEXFONTS" || exit + echo "$progname: modifying permissions in \`$MT_VARTEXFONTS' ..." >&2 + for d in pk tfm source; do + test -d "$d" && find $d -type d -exec chmod 1777 '{}' \; + done + echo "$progname: all permissions set." >&2 + ) + setupTmpDir + fmgrConfigReplace mktex.cnf '^: ..MT_FEATURES=' ": \${MT_FEATURES=appendonlydir:varfonts}" + ;; + ro) + MT_VARTEXFONTS=`kpsewhich -var-value VARTEXFONTS` + if test -z "$MT_VARTEXFONTS"; then + echo "$progname: failed to set \`font ro'; reason: could not determine VARTEXFONTS variable." >&2; rc=1 + return + fi + test -d "$MT_VARTEXFONTS" || mktexdir "$MT_VARTEXFONTS" + if test ! -d "$MT_VARTEXFONTS"; then + echo "$progname: failed to set \`font ro'; reason: directory \`$MT_VARTEXFONTS' does not exist." >&2; rc=1 + return + fi + chmod 755 "$MT_VARTEXFONTS" || { + echo "$progname: failed to modify permissions in \`$MT_VARTEXFONTS'." >&2; rc=1 + return; + } + ( + cd "$MT_VARTEXFONTS" || exit + echo "$progname: modifying permissions in \`$MT_VARTEXFONTS' ..." >&2 + for d in pk tfm source; do + test -d "$d" && find "$d" -type d -exec chmod 755 '{}' \; + done + echo "$progname: all permissions set." >&2 + ) + setupTmpDir + fmgrConfigReplace mktex.cnf '^: ..MT_FEATURES=' ": \${MT_FEATURES=texmfvar}" + ;; + "") echo "$help" >&2; rc=1;; + *) echo "$progname: unknown option \`$2' given as argument for \`$progname font'" >&2 + echo "$progname: try \`$progname font' for help" >&2 + rc=1 + ;; + esac + ;; + + formats) + cat >&2 <&2 + fmtutil --catcfg > $tmpdir/pre + fmtutil --edit + echo "$progname: analyzing new configuration..." >&2 + fmtutil --catcfg > $tmpdir/post + + if cmp $tmpdir/pre $tmpdir/post >/dev/null 2>&1; then + echo "$progname: no new/updated formats available ..." >&2 + else + echo "$progname: updating formats ..." >&2 + comm -13 $tmpdir/pre $tmpdir/post > $tmpdir/addOrChange + for i in `awk '{print $1}' $tmpdir/addOrChange`; do + fmtutil --byfmt "$i" || rc=1 + done + texlinks --multiplatform || rc=1 + fi + ;; + + help|--help|-h) + echo "$help" + ;; + + # "hyphen FORMAT" + hyphen) + cat >&2 <&2; rc=1 + ;; + *) + if checkElemInList "$tcBatchHyphenFormat" $formatsForHyphen; then + + tcBatchHyphenFile=`fmtutil --showhyphen "$tcBatchHyphenFormat"` + case $tcBatchHyphenFile in + "") + echo "$progname: could not find hyphen setup file for format \`$tcBatchHyphenFormat'" >&2 + rc=1 + return + ;; + esac + + getRelDir "$tcBatchHyphenFile" + case $relPart in + "") + # edit tcBatchHyphenFile directly + tcBatchHFID= + setupTmpDir + tcBatchHFEdit=$tcBatchHyphenFile + tcBatchHFOrig=$tmpdir/hforig + cp "$tcBatchHyphenFile" "$tcBatchHFOrig" + ;; + *) + # use tcfmgr + tcBatchHyphenFileBasename=`echo "$tcBatchHyphenFile" | sed 's@.*/@@'` + setupTmpDir + co=`tcfmgr --tmp $tmpdir --cmd co --file "$tcBatchHyphenFileBasename" --reldir "$relPart" --infile "$tcBatchHyphenFile"` + if test $? != 0; then + echo "$progname: failed to check out file \`$tcBatchHyphenFile'" >&2 + rc=1 + return 1 + else + set x $co; shift + tcBatchHFID=$1; tcBatchHFEdit=$3; tcBatchHFOrig=$4 + fi + ;; + esac + ${VISUAL-${EDITOR-vi}} "$tcBatchHFEdit" + if cmp "$tcBatchHFEdit" "$tcBatchHFOrig" >/dev/null 2>&1; then + echo "$progname: configuration unchanged." >&2 + else + case $tcBatchHFID in + "") + tcBatchHFOut=$tcBatchHFEdit + echo "$progname: updated configuration saved as file \`$tcBatchHFOut'" >&2 + lastUpdatedFile=$ci + ;; + *) + ci=`tcfmgr --tmp $tmpdir --cmd ci --id "$tcBatchHFID"` + if test $? != 0; then + echo "$progname: failed to check in file \`$tcBatchHyphenFileBasename'" >&2 + rc=1 + return + else + tcBatchHFOut=$ci + echo "$progname: updated configuration saved as file \`$tcBatchHFOut'" >&2 + lastUpdatedFile=$ci + fi + ;; + esac + fmtutil --byhyphen "$tcBatchHFOut" + fi + else + echo "$progname: invalid format \`$tcBatchHyphenFormat' specified as argument for \`$progname hyphen'" >&2 + echo "$progname: for getting help, try \`$progname hyphen'" >&2 + rc=1 + fi + ;; + esac + ;; + + hyphen-list) + getFormatsForHyphen + ;; + + init) + case $2 in + "") + if fmtutil --all \ + && texlinks --multiplatform \ + && updmap; then + : + else + rc=1 + fi + ;; + *) + shift 1 + for i in "$@"; do + fmtutil --byfmt "$i" || rc=1 + done + ;; + esac + ;; + + mode-list) + setupModesMfFile + listMfModes + ;; + + mode) + case $2 in + "") + echo "Usage: $progname mode MODE + +Valid MODE settings:" + setupModesMfFile + listMfModes | sed 's@ .*@@; s@^@ @' | myFmt + rc=1 + ;; + *) + tcBatchMode=$2 + setupModesMfFile + if checkElemInList $tcBatchMode `listMfModes | sed 's@ .*@@'`; then + + # modify mktex.cnf + setupTmpDir + fmgrConfigReplace mktex.cnf '^: ..MODE=' ": \${MODE=$tcBatchMode}" + set x `getRes "$tcBatchMode"`; shift + tcBatchRes=$1 + fmgrConfigReplace mktex.cnf '^: ..BDPI=' ": \${BDPI=$tcBatchRes}" + + if checkForBinary dvips >/dev/null && tcfmgr --cmd find --file config.ps >/dev/null 2>&1; then + tcBatch dvips mode "$tcBatchMode" + fi + if checkForBinary pdftex >/dev/null && tcfmgr --cmd find --file pdftexconfig.tex >/dev/null 2>&1; then + tcBatch pdftex mode "$tcBatchMode" + fi + else + echo "$progname: unknown mode \`$tcBatchMode' specified as argument for \`$progname mode'" >&2; rc=1 + fi + ;; + esac + ;; + + paper) + help="Usage: $progname paper PAPER + +Valid PAPER settings: + letter a4" + + p=$2; pXdvi=$2; pDvips=$2 + case $2 in + letter) + pXdvi=us;; + a4) + pXdvi=a4;; + "") echo "$help" >&2; rc=1; return;; + *) + echo "$progname: unknown PAPER \`$2' given as argument for \`$progname paper'" >&2 + echo "$progname: try \`$progname paper' for help" >&2 + rc=1 + return;; + esac + if checkForBinary dvips >/dev/null && tcfmgr --cmd find --file config.ps >/dev/null 2>&1; then + tcBatch dvips paper $pDvips + fi + if checkForBinary dvipdfm >/dev/null && tcfmgr --cmd find --file config >/dev/null 2>&1; then + tcBatch dvipdfm paper $p + fi + if checkForBinary dvipdfmx >/dev/null && tcfmgr --cmd find --file dvipdfmx.cfg >/dev/null 2>&1; then + tcBatch dvipdfmx paper $p + fi + if checkForBinary xdvi >/dev/null && tcfmgr --cmd find --file XDvi >/dev/null 2>&1; then + tcBatch xdvi paper $pXdvi + fi + if checkForBinary pdftex >/dev/null && tcfmgr --cmd find --file pdftexconfig.tex >/dev/null 2>&1; then + tcBatch pdftex paper $p + fi + ;; + + pdftex) + help="Usage: $progname pdftex paper PAPER + +Valid PAPER settings: + a4 letter" + case $2 in + + mode) + case $3 in + "") + echo "Usage: $progname pdftex mode MODE" + rc=1 + ;; + *) + tcBatchPdftexMode=$3 + setupTmpDir + setupModesMfFile + if checkElemInList "$tcBatchPdftexMode" `listMfModes | sed 's@ .*@@'`; then + set x `getRes "$tcBatchPdftexMode"`; shift + fmgrConfigReplace pdftexconfig.tex 'pdfpkresolution' "\\pdfpkresolution=$1" + if $fmgrConfigReplaceChanged; then + fmtutil --refresh + fi + else + echo "$progname: unknown MODE \`$tcBatchPdftexMode' given as argument for \`$progname pdftex mode'" >&2 + rc=1 + fi + ;; + esac + ;; + + paper) + case $3 in + letter) + w="8.5 true in"; h="11 true in" + setupTmpDir + fmgrConfigReplace pdftexconfig.tex pdfpagewidth '\pdfpagewidth='"$w" + wChanged=$fmgrConfigReplaceChanged + fmgrConfigReplace pdftexconfig.tex pdfpageheight '\pdfpageheight='"$h" + if $wChanged || $fmgrConfigReplaceChanged; then + fmtutil --refresh + fi + ;; + a4) + w="210 true mm"; h="297 true mm" + fmgrConfigReplace pdftexconfig.tex pdfpagewidth '\pdfpagewidth='"$w" + wChanged=$fmgrConfigReplaceChanged + fmgrConfigReplace pdftexconfig.tex pdfpageheight '\pdfpageheight='"$h" + if $wChanged || $fmgrConfigReplaceChanged; then + fmtutil --refresh + fi + ;; + "") echo "$help" >&2; rc=1;; + *) + echo "$progname: unknown PAPER \`$3' given as argument for \`$progname pdftex paper'" >&2 + echo "$progname: try \`$progname pdftex paper' for help" >&2 + rc=1 ;; + esac ;; + "") + echo "$help" >&2; rc=1;; + *) + echo "$progname: unknown option \`$2' given as argument for \`$progname pdftex'" >&2 + echo "$progname: try \`$progname pdftex' for help" >&2 + rc=1 + ;; + esac + ;; + + rehash) + mktexlsr + ;; + + # + version|--version) + echo "$progname version $version" + setupTexmfmain + setupTexmfdist + showDistVersionInfo + (exit 0); exit 0;; + + # handle "xdvi paper PAPER" + xdvi) + tcBatchXdviPapers='us "8.5x11" +usr "11x8.5" +legal "8.5x14" +foolscap "13.5x17.0" +a1 "59.4x84.0cm" +a2 "42.0x59.4cm" +a3 "29.7x42.0cm" +a4 "21.0x29.7cm" +a5 "14.85x21.0cm" +a6 "10.5x14.85cm" +a7 "7.42x10.5cm" +a1r "84.0x59.4cm" +a2r "59.4x42.0cm" +a3r "42.0x29.7cm" +a4r "29.7x21.0cm" +a5r "21.0x14.85cm" +a6r "14.85x10.5cm" +a7r "10.5x7.42cm" +b1 "70.6x100.0cm" +b2 "50.0x70.6cm" +b3 "35.3x50.0cm" +b4 "25.0x35.3cm" +b5 "17.6x25.0cm" +b6 "13.5x17.6cm" +b7 "8.8x13.5cm" +b1r "100.0x70.6cm" +b2r "70.6x50.0cm" +b3r "50.0x35.3cm" +b4r "35.3x25.0cm" +b5r "25.0x17.6cm" +b6r "17.6x13.5cm" +b7r "13.5x8.8cm" +c1 "64.8x91.6cm" +c2 "45.8x64.8cm" +c3 "32.4x45.8cm" +c4 "22.9x32.4cm" +c5 "16.2x22.9cm" +c6 "11.46x16.2cm" +c7 "8.1x11.46cm" +c1r "91.6x64.8cm" +c2r "64.8x45.8cm" +c3r "45.8x32.4cm" +c4r "32.4x22.9cm" +c5r "22.9x16.2cm" +c6r "16.2x11.46cm" +c7r "11.46x8.1cm"' + help="Usage: $progname xdvi paper PAPER + +Valid PAPER settings: + a1 a1r a2 a2r a3 a3r a4 a4r a5 a5r a6 a6r a7 a7r + b1 b1r b2 b2r b3 b3r b4 b4r b5 b5r b6 b6r b7 b7r + c1 c1r c2 c2r c3 c3r c4 c4r c5 c5r c6 c6r c7 c7r + foolscap legal us usr" + case $2 in + paper-list) + echo "$tcBatchXdviPapers" + ;; + paper) + case $3 in + a1|a1r|a2|a2r|a3|a3r|a4|a4r|a5|a5r|a6|a6r|a7|a7r|b1|b1r|b2|b2r|b3|b3r|b4|b4r|b5|b5r|b6|b6r|b7|b7r|c1|c1r|c2|c2r|c3|c3r|c4|c4r|c5|c5r|c6|c6r|c7|c7r|foolscap|legal|us|usr) + fmgrConfigReplace XDvi paper: "*paper: $3" + ;; + "") echo "$help" >&2; rc=1;; + *) + echo "$progname: unknown PAPER \`$3' given as argument for \`$progname xdvi paper'" >&2 + echo "$progname: try \`$progname xdvi paper' for help" >&2 + rc=1 ;; + esac ;; + "") + echo "$help" >&2; rc=1;; + *) + echo "$progname: unknown option \`$2' given as argument for \`$progname xdvi'" >&2 + echo "$progname: try \`$progname xdvi' for help" >&2 + rc=1 + ;; + esac + ;; + *) + echo "$progname: unknown option \`$1' given as argument for \`$progname'" >&2 + echo "$progname: try \`$progname help' for help" >&2 + rc=1 + esac +} + +############################################################################### +# tcInteractive(void) +# handle interactive mode +############################################################################### +tcInteractive() +{ + texconfig-dialog +} + +############################################################################### +# main() +############################################################################### +rc=0 +case $# in + 0) tcInteractive;; + *) tcBatch "$@";; +esac + +cleanup $rc diff --git a/Master/texmf-dist/scripts/tetex/texlinks.sh b/Master/texmf-dist/scripts/tetex/texlinks.sh new file mode 100755 index 00000000000..09b3488d263 --- /dev/null +++ b/Master/texmf-dist/scripts/tetex/texlinks.sh @@ -0,0 +1,335 @@ +#!/bin/sh +# $Id: texlinks 18035 2010-04-28 17:41:52Z mpg $ + +# Thomas Esser, 1999, 2002, 2003. public domain. + +# texlinks: script to maintain symlinks from format to engine. Interprets +# the lines given in fmtutil.cnf. + +# History: +# (Further changes in ChangeLog.) +# Tue Oct 9 14:23:01 BST 2007 +# Added unlink option (-u) to aid OpenBSD package uninstall +# Edd Barrett +# Sun Aug 28 21:41:06 CEST 2005 +# remove special cases for csplain,cslatex,pdfcslatex,pdfcsplain +# Fr Apr 8 19:15:05 CEST 2005 +# cleanup now has an argument for the return code +# So Mar 27 18:52:06 CEST 2005 +# honor $TMPDIR, $TEMP and $TMP, not just $TMP +# Mon May 10 20:52:48 CEST 2004 +# kpseaccess instead of access +# Thu Dec 25 22:11:53 CET 2003, te: +# add version string +# Tue Apr 9 22:46:34 CEST 2002, te: +# do not create symlinks for cont-??, metafun and mptopdf + +test -f /bin/sh5 && test -z "$RUNNING_SH5" \ + && { UNAMES=`uname -s`; test "x$UNAMES" = xULTRIX; } 2>/dev/null \ + && { RUNNING_SH5=true; export RUNNING_SH5; exec /bin/sh5 $0 ${1+"$@"}; } +unset RUNNING_SH5 + +test -f /bin/bsh && test -z "$RUNNING_BSH" \ + && { UNAMES=`uname -s`; test "x$UNAMES" = xAIX; } 2>/dev/null \ + && { RUNNING_BSH=true; export RUNNING_BSH; exec /bin/bsh $0 ${1+"$@"}; } +unset RUNNING_BSH + +export PATH + +# hack around a bug in zsh: +test -n "${ZSH_VERSION+set}" && alias -g '${1+"$@"}'='"$@"' + +version='$Id: texlinks 18035 2010-04-28 17:41:52Z mpg $' +progname=texlinks +cnf=fmtutil.cnf # name of the config file + +usage='Usage: texlinks [OPTION]... [DIRECTORY]... + +Create symbolic links format -> engine according to fmtutil setup. + +Mandatory arguments to long options are mandatory for short options too. + -e, --exeext EXT append EXT to symlink targets (default: none) + -f, --cnffile FILE use FILE as config file (default: fmtutil.cnf) + -m, --multiplatform operate in all platform specific directories + (default: operate only in directory for this platform) + -q, --quiet silently skip existing scripts / binaries + (default: issue warning) + -s, --silent same as -q + -u, --unlink remove symlinks created by texlinks + -v, --verbose enable verbose messages (default: off) + -h, --help show this help text + --version show version string + +The DIRECTORY arguments are an optional list of directories in which to +operate. If no directories are specified and --multiplatform is +likewise not specified, the directory of this script itself is used. +With --multiplatform, all child dirs of an upper-level bin/ dir are used. + +Report bugs to: tex-k@tug.org +TeX Live home page: +' + +############################################################################### +# abort(errmsg) +# print `errmsg' to stderr and exit with error code 1 +############################################################################### +abort() { errmsg "texlinks: $1."; cleanup 1; } + +# error message to stderr: +errmsg() { echo "$@" >&2; } + +# give message to stderr only if "verbose" mode is on: +verbose_echo() { $verbose && errmsg "$@"; } + +# in verbose mode: show command that is executed: +verbose_do() { verbose_echo "$@"; "$@"; } + +############################################################################### +# cleanup() +# clean up the temp area and exit with proper exit status +############################################################################### +cleanup() +{ + rc=$1 + $needsCleanup && test -n "$tmpdir" && test -d "$tmpdir" \ + && { rm -f "$tmpdir"/*; cd /; rmdir "$tmpdir"; } + exit $rc +} + +############################################################################### +# setupTmpDir() +# set up a temp directory and a trap to remove it +############################################################################### +setupTmpDir() +{ + $needsCleanup && return + + trap 'cleanup 1' 1 2 3 7 13 15 + needsCleanup=true + (umask 077; mkdir "$tmpdir") \ + || abort "could not create directory \`$tmpdir'" +} + + +# search a binary along $PATH: +check_for_binary() +{ + testbin=$1 + set x `echo "$PATH" | sed 's/^:/.:/; s/:$/:./; s/::/:.:/g; s/:/ /g'`; shift + for i + do + if [ -x "$i/$testbin" ]; then + echo "$i/$testbin" + return 0 + fi + done + return 1 +} + + +############################################################################### +# install_link(dest src) +# create a symlink like ln -s dest src, but make sure that src is not +# an existing binary, possibly adding the executable extension if +# passed on the command. +############################################################################### +install_link() +{ + # make symlink src -> dest + dest=$1; src=$2 + + case $src in + */mf) + if test "$dest" = mf-nowin; then + if test -f $selfautoloc/mfw; then + dest=mfw # name for windows-enabled mf, once upon a time + verbose_echo "both mfw and mf-nowin exists, $src linked to $dest" + fi + if test -f $selfautoloc/mf && test -f $selfautoloc/mf-nowin; then + # have both mf and mf-nowin binaries. no link. + verbose_echo "skipped metafont symlink $src -> $dest (special case)" + return + fi + fi + ;; + esac + + # append .exe if supplied (for cygwin). + test -n "$exeext" && dest="$dest$exeext" + + case $src in + */cont-??|*/mptopdf) + # context includes wrapper scripts that create/run these. + verbose_echo "skipped ConTeXtish symlink $src -> $dest (special case)" + ;; + *) + test "x$src" != "x`(ls -ld $src | awk '{print $NF}') 2>/dev/null`" && + rm -f "$src" + + if test -f "$src"; then + case $silent in + true) + ;; + *) + errmsg "install_link $src -> $dest failed: file already exists." + ;; + esac + else + verbose_do ln -s "$dest" "$src" + fi + ;; + esac +} + +############################################################################### +# search_symlinkdir() +# look if $PATH has only symlinks to the real binaries and find that +# directory. Also check if this directory is writable. +############################################################################### +search_symlinkdir() +{ + kpsewhich=`check_for_binary kpsewhich` + test -z "$kpsewhich" && return 1 + symlinkdir=`echo $kpsewhich | sed 's@/*kpsewhich$@@'` + kpseaccess -w "$symlinkdir" || return 1 + touch "$symlinkdir/tl$$" + if test -f "$selfautoloc/tl$$"; then + rm -f "$symlinkdir/tl$$" + return 1 + else + rm -f "$symlinkdir/tl$$" + return 0 + fi +} + +############################################################################### +# upd_symlinkdir() +# if $PATH has only symlinks to the real binaries, update that directory +# that holds the symlinks +############################################################################### +upd_symlinkdir() +{ + search_symlinkdir || return 0 + for i in `sed 's@ .*@@' cnf_file_ln.$$`; do + install_link "$selfautoloc/$i" "$symlinkdir/$i" + done +} + +############################################################################### +# rm_link() +# Delete a previously installed link +############################################################################### +rm_link() +{ + link=$1; + if test -e $link; then + if test -h $link; then + verbose_do rm -Rf $link + else + verbose_echo "kept $link, since not a symlink" + fi + else + verbose_echo "skipped $link, non-existent" + fi +} + +############################################################################### +# main() +# parse commandline arguments, initialize variables, +# switch into temp. direcrory, execute desired command +############################################################################### +main() +{ + cnf_file= # global variable: full name of the config file + dirs= + needsCleanup=false + + exeext= + multiplatform=false + verbose=false + unlink=false + silent=false + thisdir=`pwd` + : ${KPSE_DOT=$thisdir}; export KPSE_DOT + selfautoloc=`kpsewhich --expand-var='$SELFAUTOLOC'` + while + case $1 in + --h*|-h) + echo "$usage"; exit 0;; + --version) + echo "$progname version $version"; exit 0;; + --cnffile|-f) + shift; cnf_file=$1;; + --e*|-e) shift; exeext=$1;; + --m*|-m) multiplatform=true;; + --s*|-s|--q*|-q) silent=true;; + --u*|-u) unlink=true;; + --v*|-v) verbose=true;; + -*) errmsg "fmtutil: unknown option \`$1' ignored.";; + *) break;; + esac + do test $# -gt 0 && shift; done + dirs="$*" + + # if no cnf_file from command-line, look it up with kpsewhich: + test -z "$cnf_file" && cnf_file=`kpsewhich --format='web2c files' $cnf` + test -f "$cnf_file" || abort "config file \`$cnf' not found" + + + tmpdir=${TMPDIR-${TEMP-${TMP-/tmp}}}/texlinks.$$ + setupTmpDir + cd "$tmpdir" || cleanup 1 + + sed '/^[ ]*#/d; /^[ ]*$/d' $cnf_file \ + | awk '{print $1, $2}' > cnf_file_ln.$$ + + if test -z "$dirs"; then + if test $multiplatform = true; then + case $selfautoloc in + */bin) dirs=$selfautoloc;; + *) parent=`kpsewhich --expand-var='$SELFAUTODIR'` + dirs=`find $parent -type f -name kpsewhich -print \ + | sed 's@/kpsewhich$@@'`;; + esac + else + dirs=$selfautoloc + fi + fi + + for d in $dirs; do + kpseaccess -w $d \ + || { errmsg "$d: no write permissions. Skipping..."; continue; } + # cnf_file_ln.$$ has lines with "format engine" pairs + set x `cat cnf_file_ln.$$`; shift + while test $# != 0; do + fmt=$1; engine=$2; shift; shift + + # Some broken shells destroy the positional arguments when calling a + # shellfunction. Therefore, we save and restore them "by hand" in the + # main_args_while variable. + main_args_while="$@" + + test "x$fmt" = "x$engine" && continue + if test -f "$d/$engine$exeext"; then + case $unlink in + true) + rm_link "$d/$fmt";; + *) + install_link "$engine" "$d/$fmt";; + esac + else + verbose_echo "skipped $d/$engine, engine does not exist" + fi + + # restore positional arguments: + set x $main_args_while; shift + + done + done + upd_symlinkdir +} + +main ${1+"$@"} + +# set successful return code +cleanup 0 diff --git a/Master/texmf-dist/scripts/tex4ht/htcopy.pl b/Master/texmf-dist/scripts/tex4ht/htcopy.pl new file mode 100644 index 00000000000..c8d2193b21d --- /dev/null +++ b/Master/texmf-dist/scripts/tex4ht/htcopy.pl @@ -0,0 +1,46 @@ +use File::Copy; +use File::Path; +use File::Basename; + +&main ; + +0; + +sub is_absolute { + my ($f) = @_; + return $f =~ m@^([a-zA-Z]:)?[\\/]@ ; +} + +sub canonicalize { + my ($f) = @_; + $f =~ s@/@\\@g; + return $f; +} + +sub main { + my $src = &canonicalize($ARGV[0]); + my $dst = &canonicalize($ARGV[1]); + + if (&is_absolute($src)) { + if (-d $dst) { + $dst = $dst . "\\" . &basename($src); + } + } + else { + if (-d $dst) { + $dst = "$dst\\$src"; + &mkpath(&dirname($dst)); + } + } + + if ($0 =~ m/htmove\.pl$/) { + &move($src, $dst); + } + elsif ($0 =~ m/htcopy\.pl$/) { + ©($src, $dst); + } + else { + print STDERR "$0: bad verb $ARGV[0]\n"; + } +} + diff --git a/Master/texmf-dist/scripts/tex4ht/htmove.pl b/Master/texmf-dist/scripts/tex4ht/htmove.pl new file mode 100644 index 00000000000..2d52965ebf5 --- /dev/null +++ b/Master/texmf-dist/scripts/tex4ht/htmove.pl @@ -0,0 +1,45 @@ +use File::Copy; +use File::Path; +use File::Basename; + +&main ; + +0; + +sub is_absolute { + my ($f) = @_; + return $f =~ m@^([a-zA-Z]:)?[\\/]@ ; +} + +sub canonicalize { + my ($f) = @_; + $f =~ s@/@\\@g; + return $f; +} + +sub main { + my $src = &canonicalize($ARGV[0]); + my $dst = &canonicalize($ARGV[1]); + + if (&is_absolute($src)) { + if (-d $dst) { + $dst = $dst . "\\" . &basename($src); + } + } + else { + if (-d $dst) { + $dst = "$dst\\$src"; + &mkpath(&dirname($dst)); + } + } + + if ($0 =~ m/htmove\.pl$/) { + &move($src, $dst); + } + elsif ($0 =~ m/htcopy\.pl$/) { + ©($src, $dst); + } + else { + print STDERR "$0: bad verb $ARGV[0]\n"; + } +} diff --git a/Master/texmf-dist/scripts/texlive/NEWS b/Master/texmf-dist/scripts/texlive/NEWS new file mode 100644 index 00000000000..5cd9121f29c --- /dev/null +++ b/Master/texmf-dist/scripts/texlive/NEWS @@ -0,0 +1,103 @@ +(This file public domain. Originally written by Norbert Preining and +Karl Berry, 2010.) + +
  • new tlperl (version 5.16.2) for Windows, compiled without + optimization and with debugging + +

    tlmgr revision 28216 (released 09nov12):
    +

  • menu shortcuts are installed unconditionally, the desktop_integration + option only controls shortcuts on the desktop (W32) +
  • fix --self updates on W32 when multiple remote repositories are used + +

    tlmgr revision 27748 (released 25oct12):
    +

  • support tlmgr repository list ... for listing of any repository +
  • work around Cygwin Perl returning undef from an empty file + +

    tlmgr revision 27601 (released 07sep12):
    +

  • allow for spaces in the repository path (again) +
  • make tlmgr --help more robust against broken perldoc + +

    tlmgr revision 27413 (released 16aug12):
    +

  • fix Perl syntax warning in trans.pl +
  • tar -m in Windows update to avoid spurious problems + +

    tlmgr revision 27258 (released 30jul12):
    +

  • more fixes to updmap multi-tree support for TEXMFLOCAL + +

    tlmgr revision 27170 (released 26jul12):
    +

  • consistently use ll_RR in .po file naming, per general gettext convention +
  • make sure that an outdated updmap.cfg file is backed up and removed + +

    texlive.infra (tlmgr) revision 26923 (released 07jul12):
    +

  • version with TeX Live 2012 +
  • generate updmap is no more, since updmap.cfg is now per-tree +
  • multiple repository support +
  • tlmgr can now install a package from a file + +

    tlmgr revision 24419 (released 01nov11):
    +

  • support addKanjiMap execute + +

    tlmgr revision 23514 (released 12aug11):
    +

  • dependent formats are regenerated when necessary. +
  • on Windows only, do not update time stamps when unpacking, + to partially avoid spurious errors. +
  • detect mipsel and mipsel64 platforms. + +

    tlmgr revision 23117 (released 23jun11):
    +

  • version with TeX Live 2011. +
  • support gui-lang in tlmgr config file, and add GUI + interface for it. + +

    tlmgr revision 20975 (released 24jan11):
    +

  • fix usage of persistent connections in tlmgr. +
  • override locale settings when using wget as a subprocess. + +

    tlmgr revision 20243 (released 1nov10):
    +

  • try (again) not to load the remote tlpdb if tlmgr update is called + without arguments. +
  • fix bugs in autobackup/backupdir handling; unify error messages; + on clean, also remove package backups for packages no longer present + in tlpdb. +
  • support keyword searching as provided by the beta CTAN characterizations + at http://az.ctan.org/ (no Internet + access needed for the search, though). tlmgr documentation + has details. Support is also present in tlmgrgui. + +

    tlmgr revision 19923 (released 28sep10):
    +

  • undo change for not loading remote db, needs more work. + +

    tlmgr revision 19896 (released 27sep10):
    +

  • add -R to the LESS environment variable, and unset + LESSOPEN and LESSPIPE, before calling pod2usage. +
  • don't load the remote db if tlmgr update is called without arguments. + +

    tlmgr revision 19414 (TeX Live 2010 release version):
    + +

  • implement download selection mode in the file + TEXMFCONFIG/tlmgr/config, + option persistent-downloads, with value 0 or 1. +
  • remove support for old GUI (tlmgr gui-old). + +tlmgr rev 17811 (2010-04-25) + + * tlmgr restore --all: restoring the latest, using all available backups. + * GUI: add support for restore action. + * do not auto-install packages contained in forcibly removed collections. + * GUI: add support for the three command line options: --no-auto-install, + --no-auto-remove (under Menu->Options), and --reinstall-forcibly-removed + (just below the "Update all installed" button). + * GUI: provide access to the manual (tlmgr help) in the Help menu + * GUI: provide simple and expert modes (Options->Expert options) + +tlmgr rev 17611 (2010-03-30) + + * Support automatic installation of new collections (when they are + included in the selected scheme). + +tlmgr rev 17455 (2010-03-19) + + * persistent HTTP connections enabled by default + (use --no-persistent-downloads to turn off) + * package update algorithm changed to avoid incorrect + "forcibly removed" inferences. Should be invisible. + * report of the number of pending updates in tlmgr gui. diff --git a/Master/texmf-dist/scripts/texlive/lua/texlive/getopt.tlu b/Master/texmf-dist/scripts/texlive/lua/texlive/getopt.tlu new file mode 100644 index 00000000000..7e52ddd357f --- /dev/null +++ b/Master/texmf-dist/scripts/texlive/lua/texlive/getopt.tlu @@ -0,0 +1,99 @@ +#! /usr/bin/env texlua +--*-Lua-*- +-- getopt.tlu +-- +-- (tex)lua library function for argument processing +-- $Id$ +-- +-- Copyright 2008 Norbert Preining +-- +-- This code will presumably be changed in the near future. Don't use +-- it for your own scripts! +-- +-- This file is licensed under the GNU General Public License version 2 +-- or any later version. +-- +-- The file provides a basic getopt function +-- it takes as argument a table with the argument names as keys and the +-- number of argument options as values +-- +-- It returns a tables with the found arguments as keys and +-- . true as value for an option not taking any values +-- . a single value for an option taking one value +-- . an array of values for an option taking more than one value +-- +-- Example: +-- options = getopt( { help = 0, foo = 1, bar = 2 } ) +-- and calling the script with +-- script a --help b --bar c d e --foo f g h +-- will leave a b e g h in the arguments and will return +-- options["help"] = true +-- options["foo"] = "f" +-- options["bar"] = { "c", "d" } +-- +-- Options can start with a single or a double dash, but values may NOT be +-- separated with a =, but need to be seprate arguments! +-- +-- Example code for printing out the options: +-- print ("OPTIONS:") +-- for o,v in pairs(options) do +-- if (type(v) == "boolean") then +-- print ("option "..o) +-- elseif (type(v) == "table") then +-- for a,b in ipairs(v) do +-- print ("option "..o.."["..a.."] = "..b) +-- end +-- else +-- print ("option "..o.." = "..v) +-- end +-- end + +function getopt(spec) + local newarg = {} + local retargs = {} + do + local i, limit = 1, #arg + while (i <= limit) do + local found = false + for k,v in pairs(spec) do + if (arg[i] == "--"..k or arg[i] == "-"..k) then + found = true + if (v == 0) then + -- boolean argument + retargs[k] = true + elseif (v == 1) then + -- one single argument + retargs[k] = arg[i+1] + i = i + 1 + else + local bla = {} + for n=1, v do + bla[n] = arg[i+n] + end + retargs[k] = bla + -- we have to shift the next i!!! + i = i + v + end + -- we have found the arg, so stop processing further spec arguments + break + end + end + if not(found) then + -- non of the spec args did hit, so we push the arg to the newargs + newarg[#newarg+1] = arg[i] + end + i = i + 1 + end + end + -- replace the global arg with the cleaned newarg + arg = newarg + -- return the found arguments + return retargs +end + +-- Local Variables: +-- lua-indent-level: 2 +-- tab-width: 2 +-- indent-tabs-mode: nil +-- End: +-- vim:set tabstop=2 expandtab: # diff --git a/Master/texmf-dist/scripts/texlive/lua/texlive/tlpdb.tlu b/Master/texmf-dist/scripts/texlive/lua/texlive/tlpdb.tlu new file mode 100644 index 00000000000..d83f02ceccf --- /dev/null +++ b/Master/texmf-dist/scripts/texlive/lua/texlive/tlpdb.tlu @@ -0,0 +1,259 @@ +#! /usr/bin/env texlua +-- texlive/tlpdb.tlu +-- $Id$ +-- +-- Copyright (C) 2008 Norbert Preining +-- This file is licensed under the GNU General Public License version 2 +-- or any later version. +-- +-- TODO: tag evaluation of docfiles lines ... +-- +-- providing the tlpdb as a lua table +-- +-- format +-- tlpobj = tlpdb[packagename] +-- tlpobj.name string +-- tlpobj.revision string +-- tlpobj.category string +-- tlpobj.shortdesc string +-- tlpobj.longdesc string +-- tlpobj.catalogue string +-- tlpobj.cataloguedata table indexed by various catalogue values +-- tlpobj.containersize string +-- tlpobj.srccontainersize string +-- tlpobj.doccontainersize string +-- tlpobj.containermd5 string +-- tlpobj.srccontainermd5 string +-- tlpobj.doccontainermd5 string +-- tlpobj.binfiles table indexed by archs giving table indexed by numbers +-- tlpobj.binsize table indexed by archs giving strings +-- tlpobj.docfiles = table indexed by numbers +-- tlpobj.docfiledata[docfile][tag] = value +-- tlpobj.docsize string +-- tlpobj.srcfiles = table indexed by numbers +-- tlpobj.srcsize string +-- tlpobj.runfiles = table indexed by numbers +-- tlpobj.runsize string +-- tlpobj.depend = table indexed by numbers +-- tlpobj.execute = table of strings indexed by numbers +-- tlpobj.postaction = table of strings indexed by numbers +-- tlpobj.relocated = boolean +-- +--*-Lua-*- + +function read_tlpdb(root) + local fn = root..'/tlpkg/texlive.tlpdb' + if (lfs.isfile(fn)) then -- tlpdb exists + local lineno = 0 -- current line number + local errline = '' -- current line + local function die(msg) + io.stderr:write(msg..' in '..fn..' at line '..tostring(lineno) + ..'\n>>'..errline..'<<\n') + -- for now, exit, but this is bad behaviour if we want to be used + -- as a library. FIXME. + os.exit(1) + end + local started = false + local lastcmd = "" + local arch + local size + local tlpobj = {} + local name + local tlpdb = {} + for line in io.lines(fn) do + lineno = lineno + 1 + errline = line + if string.match(line, '^%s*$') then + if started then + -- we have encountered an empty line while creating a tlpobj, so + -- stop that tlpobj, add it to the tlpdb, and restart + --tlpdb[name] = tlpobj + tlpdb[name] = tlpobj + tlpobj = {} + --print ("adding "..name.." to tlpdb") + started = false + end + else + if string.match(line, '^ ') then + if lastcmd == 'runfiles' or + lastcmd == 'binfiles' or + lastcmd == 'docfiles' or + lastcmd == 'srcfiles' or + lastcmd == 'execute' or + lastcmd == 'depend' then + line = string.gsub(line, '^ ', lastcmd.."continued ") + else + die('Continuation of '..lastcmd..' not allowed') + end + end + local first + local rest + first, rest = string.match(line,'^(%S+)%s*(.*)%s*$') + --print ("DEBUG: line = "..line) + --print ("DEBUG: first = "..first) + lastcmd = first + if first == "name" then + tlpobj.name = rest + name = rest + started = true + elseif first == "revision" then + tlpobj.revision = rest + elseif first == "category" then + tlpobj.category = rest + elseif first == "shortdesc" then + tlpobj.shortdesc = rest + elseif first == "catalogue" then + tlpobj.catalogue = rest + elseif string.match(first,'^catalogue%-') then + --local data = string.match(first,'^catalogue-(.*)$') + local cdata = string.gsub(first,'^catalogue%-', '') + --print("DEBUG: cdata = "..cdata..", first = "..first) + if not(tlpobj.cataloguedata) then + tlpobj.cataloguedata = {} + end + tlpobj.cataloguedata[cdata] = rest + elseif first == "containersize" then + tlpobj.containersize = rest + elseif first == "srccontainersize" then + tlpobj.srccontainersize = rest + elseif first == "doccontainersize" then + tlpobj.doccontainersize = rest + elseif first == "containermd5" then + tlpobj.containermd5 = rest + elseif first == "srccontainermd5" then + tlpobj.srccontainermd5 = rest + elseif first == "doccontainermd5" then + tlpobj.doccontainermd5 = rest + elseif first == "runfiles" then + local rsize = string.match(rest,'size=([0-9]*)') + if (rsize) then + tlpobj.runsize = rsize + else + if #rest > 0 then + die('Unknown tag') + end + end + elseif first == "docfiles" then + local dsize = string.match(rest,'size=([0-9]*)') + if (dsize) then + tlpobj.docsize = dsize + else + if #rest > 0 then + die('Unknown tag') + end + end + elseif first == "srcfiles" then + local ssize = string.match(rest,'size=([0-9]*)') + if (ssize) then + tlpobj.srcsize = ssize + else + if #rest > 0 then + die('Unknown tag') + end + end + elseif first == "binfiles" then + arch = "" + local barch + local bsize + for aaa in string.gmatch(rest,'%S+') do + if string.match(aaa,'^arch=(%S*)$') then + barch = string.match(aaa,'^arch=(%S*)$') + elseif string.match(aaa,'^size=(%S*)$') then + bsize = string.match(aaa,'^size=(%S*)$') + else + die('Unknown tag') + end + end + if barch and bsize then + if not(tlpobj.binsize) then + tlpobj.binsize = {} + end + tlpobj.binsize[barch] = bsize + arch = barch + else + die('Incomplete binfile tags ') + end + elseif first == "runfilescontinued" then + if not(tlpobj.runfiles) then + tlpobj.runfiles = {} + end + table.insert(tlpobj.runfiles,rest) + lastcmd = "runfiles" + elseif first == "docfilescontinued" then + local dfile,tags = string.match(rest,'^(%S+)%s*(.*)%s*$') + if not(tlpobj.docfiles) then + tlpobj.docfiles = {} + end + table.insert(tlpobj.docfiles,dfile) + for aaa,bbb in string.gmatch(tags,'(%S+)=(%b"")') do + if not(tlpobj.docfiledata) then + tlpobj.docfiledata = {} + end + if aaa == "details" or aaa == "language" then + if not(tlpobj.docfiledata[dfile]) then + tlpobj.docfiledata[dfile] = {} + end + tlpobj.docfiledata[dfile][aaa] = bbb + else + die('Unknown docfile tag') + end + end + lastcmd = "docfiles" + elseif first == "srcfilescontinued" then + if not(tlpobj.srcfiles) then + tlpobj.srcfiles = {} + end + table.insert(tlpobj.srcfiles,rest) + lastcmd = "srcfiles" + elseif first == "binfilescontinued" then + if not(tlpobj.binfiles) then + tlpobj.binfiles = {} + end + if not(tlpobj.binfiles[arch]) then + tlpobj.binfiles[arch] = {} + end + table.insert(tlpobj.binfiles[arch],rest) + lastcmd = "binfiles" + elseif first == "depend" then + if not(tlpobj.depend) then + tlpobj.depend = {} + end + table.insert(tlpobj.depend,rest) + elseif first == "longdesc" then + if tlpobj.longdesc then + tlpobj.longdesc = tlpobj.longdesc.." "..rest + else + tlpobj.longdesc = rest + end + elseif first == "execute" then + if not(tlpobj.execute) then + tlpobj.execute = {} + end + table.insert(tlpobj.execute,rest) + elseif first == 'postaction' then + tlpobj.postaction = tlpobj.postaction or {} + table.insert(tlpobj.postaction, rest) + elseif first == 'relocated' then + -- don't even try to validate the value + tlpobj.relocated = (rest == '1') + else + die('Unknown directive') + end + if not(first) then + die('Missing directive') + end + end + end + return tlpdb + else + print (fn..' not found.') + end +end + + +-- Local Variables: +-- lua-indent-level: 2 +-- tab-width: 2 +-- indent-tabs-mode: nil +-- End: +-- vim:set tabstop=2 shiftwidth=2 expandtab: # diff --git a/Master/texmf-dist/scripts/texlive/lua/texlive/utils.tlu b/Master/texmf-dist/scripts/texlive/lua/texlive/utils.tlu new file mode 100644 index 00000000000..a31475b351c --- /dev/null +++ b/Master/texmf-dist/scripts/texlive/lua/texlive/utils.tlu @@ -0,0 +1,78 @@ +-- texlive/utils.tlu +-- $Id$ +-- +-- Copyright (C) 2008 Reinhard Kotucha, Norbert Preining +-- You may freely use, modify and/or distribute this file. +-- +-- general utilities for TeX Live lua scripts + +function setupperl() + -- For Windows we use the shipped perl interpreter, otherwise we expect + -- a perl to be installed + local perlbin + if os.type == 'windows' then + perlbin=TEXDIR..'/tlpkg/tlperl/bin/perl.exe' + os.setenv('PERL5LIB', TEXDIR..'/tlpkg/tlperl/lib') + else + perlbin = 'perl' + end + return perlbin +end + +function findscript(scriptname) + local script = kpse.find_file(scriptname, 'texmfscripts') + if script then + return script + else + io.stderr:write(filename..': Cannot find script '..scriptname) + return false + end +end + +-- fix arguments for windows +function fixwin(args_unix) + if os.type == 'windows' then + local args_win={} -- new table + args_win[0]=args_unix[1] + for i=1, #args_unix do + args_win[i]='"'..args_unix[i]..'"' + end + return args_win + else + return args_unix + end +end + +function rmtree (path) + if lfs.isdir(path) then + for file in lfs.dir(path) do + if file ~= '.' and file ~= '..' then + local f = path..'/'..file + if lfs.isdir(f) then + rmtree(f) + lfs.rmdir(f) + else + os.remove(f) + end + end + end + lfs.rmdir(path) + end +end + + +function list (t) + local i = 0 + return function () + i = i + 1 + return t[i] + end +end + + +-- Local Variables: +-- lua-indent-level: 2 +-- tab-width: 2 +-- indent-tabs-mode: nil +-- End: +-- vim:set tabstop=2 expandtab: # diff --git a/Master/texmf-dist/scripts/texlive/rungs.tlu b/Master/texmf-dist/scripts/texlive/rungs.tlu new file mode 100755 index 00000000000..1987af06362 --- /dev/null +++ b/Master/texmf-dist/scripts/texlive/rungs.tlu @@ -0,0 +1,43 @@ +#!/usr/bin/env texlua +--*-Lua-*- +-- $Id$ + +-- Copyright (C) 2008 Reinhard Kotucha. +-- You may freely use, modify and/or distribute this file. + +-- Run Ghostscript (gs on Unix, gswin32c on Windows) + +function fixwin(args_unix) + if os.type == 'windows' then + local args_win={} -- new table + args_win[0]=args_unix[1] + for i=1, #args_unix do + args_win[i]='"'..args_unix[i]..'"' + end + return args_win + else + return args_unix + end +end + +if os.type == 'windows' then + command={'gswin32c'} +else + command={'gs'} +end + +for i=1, #arg do + command[#command+1]=arg[i] +end + +command=fixwin(command) + +--[[ prepend an additional hyphen to activate this code +for i=0, #command do + print (command[i]) +end +os.exit(ret) +--]] + +ret=os.spawn(command) +os.exit(ret) diff --git a/Master/texmf-dist/scripts/texlive/test-tlpdb.tlu b/Master/texmf-dist/scripts/texlive/test-tlpdb.tlu new file mode 100755 index 00000000000..2004e958fb1 --- /dev/null +++ b/Master/texmf-dist/scripts/texlive/test-tlpdb.tlu @@ -0,0 +1,45 @@ +#! /usr/bin/env texlua +--*- Lua -*- + +-- Copyright (C) 2008 Norbert Preining. +-- You may freely use, modify and/or distribute this file. + +-- test the functionality of tlpdb.tlu + +kpse.set_program_name(arg[-1]) +texmfmain = kpse.var_value('TEXMFMAIN') +package.path = texmfmain.."/scripts/texlive/lua/?.tlu" +require("texlive.tlpdb") +master=kpse.var_value('SELFAUTOPARENT') +io.stderr:write('reading tlpdb ... ') +tlpdb = read_tlpdb(master) +io.stderr:write('done\n') + +--os.exit(0) + +for a,b in pairs(tlpdb) do + print ("package = "..a) + for c,d in pairs(b) do + if (type(d) == "table") then + print (" "..c..":") + for cdata,cvalue in pairs(d) do + if (type(cvalue) == "table") then + print (" "..cdata..":") + for aaa,bbb in pairs(cvalue) do + print (" "..aaa.." = "..bbb) + end + else + print (" "..cdata.." = "..cvalue) + end + end + else + print (" "..c.." = "..tostring(d)) + end + end +end +-- Local Variables: +-- lua-indent-level: 2 +-- tab-width: 2 +-- indent-tabs-mode: nil +-- End: +-- vim:set tabstop=2 shiftwidth=2 expandtab: # diff --git a/Master/texmf-dist/scripts/texlive/texconf.tlu b/Master/texmf-dist/scripts/texlive/texconf.tlu new file mode 100755 index 00000000000..e41500cc447 --- /dev/null +++ b/Master/texmf-dist/scripts/texlive/texconf.tlu @@ -0,0 +1,728 @@ +#! /usr/bin/env texlua +--*- Lua -*- +-- $Id$ + +-- Copyright (C) 2008 Reinhard Kotucha, Norbert Preining. +-- You may freely use, modify and/or distribute this file. + +-- texconf is a subset of Thomas Esser's texconfig shell script. + +if string.find(arg[0], '/') then -- UNIX path + filename=select(1, string.gsub(arg[0], '.*/', '')) +elseif string.find(arg[0], '\\') then -- Windows path + filename=select(1, string.gsub(arg[0], '.*\\', '')) +else -- no path + filename=arg[0] +end + +basename=select(1, string.gsub(filename, '\.tlu$', '')) + +usageprogname = basename + +domktexlsr = false +doformat = false +sys=false +noformatopt = false + +if string.find(basename, '-sys$') then + basename=select(1, string.gsub(basename, '-sys$', '')) + sys=true +end + +-- this script does NOT appear in the patch, so we cannot use its basename +-- for kpse.set_program_name +-- thus we use simply mktexlsr, which always is in the path and which +-- does not have any special settings in texmf.cnf +-- kpse.set_program_name(filename, basename) +kpse.set_program_name( "mktexlsr" ) + +-- set the search path for lua libraries +texmfmain = kpse.var_value('TEXMFMAIN') +texmfdist = kpse.var_value('TEXMFDIST') +package.path = texmfmain.."/scripts/texlive/lua/?.tlu" +require("texlive.getopt") +require("texlive.utils") + +-- do argument processing +options = getopt ( { sys = 0, prog = 1, noformat = 0 } ) +if (options["sys"]) then + sys = true +end +if (options["prog"]) then + usageprogname = options["prog"] +end +if (options["noformat"]) then + noformatopt = true +end + +if (sys) then + texmfconfig=kpse.var_value('TEXMFSYSCONFIG') + os.setenv('TEXMFCONFIG', texmfconfig) + texmfsysvar=kpse.var_value('TEXMFSYSVAR') + os.setenv('TEXMFVAR', texmfsysvar) +else + texmfconfig=kpse.var_value('TEXMFCONFIG') + if string.match(texmfconfig, '^~') then + -- workaround for old texlua, new kpathsea which expands '~' not + -- integrated yet. + if os.type == 'windows' then + home = os.getenv('USERPROFILE') + else + home = os.getenv('HOME') + end + texmfconfig = string.gsub(texmfconfig, '^~', home) + end +end + + +pdftex_papersize={} +pdftex_papersize['letter']={'8.5 true in', '11 true in'} +pdftex_papersize['a4']={'210 true mm', '297 true mm'} + +xdvi_papersize={} +xdvi_papersize['us']='8.5x11' +xdvi_papersize['usr']='11x8.5' +xdvi_papersize['legal']='8.5x14' +xdvi_papersize['foolscap']='13.5x17.0' +xdvi_papersize['a1']='59.4x84.0cm' +xdvi_papersize['a2']='42.0x59.4cm' +xdvi_papersize['a3']='29.7x42.0cm' +xdvi_papersize['a4']='21.0x29.7cm' +xdvi_papersize['a5']='14.85x21.0cm' +xdvi_papersize['a6']='10.5x14.85cm' +xdvi_papersize['a7']='7.42x10.5cm' +xdvi_papersize['a1r']='84.0x59.4cm' +xdvi_papersize['a2r']='59.4x42.0cm' +xdvi_papersize['a3r']='42.0x29.7cm' +xdvi_papersize['a4r']='29.7x21.0cm' +xdvi_papersize['a5r']='21.0x14.85cm' +xdvi_papersize['a6r']='14.85x10.5cm' +xdvi_papersize['a7r']='10.5x7.42cm' +xdvi_papersize['b1']='70.6x100.0cm' +xdvi_papersize['b2']='50.0x70.6cm' +xdvi_papersize['b3']='35.3x50.0cm' +xdvi_papersize['b4']='25.0x35.3cm' +xdvi_papersize['b5']='17.6x25.0cm' +xdvi_papersize['b6']='13.5x17.6cm' +xdvi_papersize['b7']='8.8x13.5cm' +xdvi_papersize['b1r']='100.0x70.6cm' +xdvi_papersize['b2r']='70.6x50.0cm' +xdvi_papersize['b3r']='50.0x35.3cm' +xdvi_papersize['b4r']='35.3x25.0cm' +xdvi_papersize['b5r']='25.0x17.6cm' +xdvi_papersize['b6r']='17.6x13.5cm' +xdvi_papersize['b7r']='13.5x8.8cm' +xdvi_papersize['c1']='64.8x91.6cm' +xdvi_papersize['c2']='45.8x64.8cm' +xdvi_papersize['c3']='32.4x45.8cm' +xdvi_papersize['c4']='22.9x32.4cm' +xdvi_papersize['c5']='16.2x22.9cm' +xdvi_papersize['c6']='11.46x16.2cm' +xdvi_papersize['c7']='8.1x11.46cm' +xdvi_papersize['c1r']='91.6x64.8cm' +xdvi_papersize['c2r']='64.8x45.8cm' +xdvi_papersize['c3r']='45.8x32.4cm' +xdvi_papersize['c4r']='32.4x22.9cm' +xdvi_papersize['c5r']='22.9x16.2cm' +xdvi_papersize['c6r']='16.2x11.46cm' +xdvi_papersize['c7r']='11.46x8.1cm' + +dvipdfm_papersize={'letter', 'legal', 'ledger', 'tabloid', 'a4', 'a3'} + +function mkdirhier(path) + local dirs={} + local dir=path + while string.match(dir, '/') do + dirs[#dirs+1]=dir + dir=select(1, string.gsub(dir, '/[^/]*$', '')) + end + -- reverse the list + local dirlist={} + for i=1, #dirs do + dirlist[#dirlist+1]=dirs[#dirs-i+1] + end + for i=1, #dirlist do + if not lfs.isdir(dirlist[i]) then + lfs.mkdir(dirlist[i]) + end + end +end + +function set_dvips_papersize(papersize) + -- If the argument is 'list' then a list of available paper sizes is + -- returned to stdout. It can be used by dialog(1) or */Tk + -- interfaces in order to build up the menu. The topmost entry is + -- the one which is actually used by dvips. + local i, key, val + local partab={} + local par=1 + local line + local lin=1 + partab[par]={} + -- Read the file. partab{} contains all paragraphs. + kpse.set_program_name('dvips') + local filename=kpse.find_file('config.ps', 'dvips config') + -- print ('< '..filename) + local infile=assert(io.open(filename, 'r')) + while true do + line=infile:read('*line') + if line == nil then break end + if string.find(line, '^\s*$') then + par=par+1 + partab[par]={} + lin=1 + else + partab[par][lin]=line + lin=lin+1 + end + end + -- par_papersize{} is a list which associates papersizes with + -- paragraphs. Paregraphs containing no papersize information are + -- denoted by 'NO_SIZE_PAR'. + local par_papersize={} + for i=1, #partab do + par_papersize[i]='NO_SIZE_PAR' + for k=1, #partab[i] do + if string.find(partab[i][k], '^@%s') then + par_papersize[i]=string.match(partab[i][k], '^@%s+(%w+)%s') + end + end + end + infile:close() + + local index=-1 --the papagraph wich contains the desired size. + if papersize=='--list' then + -- print a list of available sizes to stdout. + -- the topmost item is the one currently used by dvips. + for key, val in ipairs(par_papersize) do + if val ~= 'NO_SIZE_PAR' and val ~= 'unknown' then + io.stdout:write(val..'\n') + end + end + elseif papersize=='help' then + io.stdout:write('Usage: '..usageprogname..' dvips paper PAPER\n\n'.. + 'Valid PAPER settings:\n ') + local buffer='' + for key, val in ipairs(par_papersize) do + if val ~= 'NO_SIZE_PAR' and val ~= 'unknown' then + if #buffer + #val < 77 then + buffer=buffer..' '..val + else + io.stdout:write(buffer..'\n') + buffer=' '..val + end + end + end + io.stdout:write(buffer..'\n') + else --change paper size + for key, val in ipairs(par_papersize) do + if string.find(val, '^'..papersize..'$') then + index=key + end + end + if index == -1 then + io.stderr:write('Error: Paper Size "'..papersize.. + '" undefined. Aborting...\n') + os.exit(1) + end + -- print stuff which doesn't contain paper size information. + local outfile={} + for key, val in ipairs(par_papersize) do + if val == 'NO_SIZE_PAR' then + for i=1, #partab[key] do + outfile[#outfile+1]=partab[key][i]..'\n' + end + outfile[#outfile+1]='\n' + end + end + -- print selected par + for i=1, #partab[index] do + outfile[#outfile+1]=partab[index][i]..'\n' + end + outfile[#outfile+1]='\n' + -- print other pars to outfile table + for key, val in ipairs(par_papersize) do + if val ~= 'NO_SIZE_PAR' then + if key~=index then + for i=1, #partab[key] do + outfile[#outfile+1]=partab[key][i]..'\n' + end + outfile[#outfile+1]='\n' + end + end + end + -- write file + mkdirhier(texmfconfig..'/dvips/config') + local config=assert(io.open(texmfconfig..'/dvips/config/config.ps', 'w')) + for i=1, #outfile do + if not (string.match(outfile[i],'^%s*$') + and string.match(outfile[i-1],'^%s*$')) then + config:write(outfile[i]) + end + end + config:close() + print ('> '..texmfconfig..'/dvips/config/config.ps') + domktexlsr = true + end +end -- set_dvips_papersize + + +function set_xdvi_papersize(papersize) + local current_setting + -- Read the file. + local filename + if os.type == 'windows' then + return + end + + kpse.set_program_name('xdvi') + filename=kpse.find_file('XDvi', 'other text files') + + -- print ('< '..filename) + local infile=assert(io.open(filename, 'r')) + local lines={} + while true do + line=infile:read('*line') + if line == nil then break end + lines[#lines+1]=line + end + infile:close() + + if papersize=='--list' then + current_setting='a4' + for n, line in ipairs(lines) do + if string.find(line, '^\*paper:') then + current_setting=string.match(line, '^\*paper:%s+(%S+)') + for ptype, psize in pairs(xdvi_papersize) do + if current_setting==psize then + current_setting=ptype + end + end + end + end + print (current_setting) + for key, val in pairs(xdvi_papersize) do + if key ~= current_setting then + print(key) + end + end + elseif papersize=='help' then + io.stdout:write('Usage: '..usageprogname..' xdvi paper PAPER\n\n'.. + 'Valid PAPER settings:\n ') + local buffer='' + for ptype, psize in pairs(xdvi_papersize) do + if #buffer + #ptype < 77 then + buffer=buffer..' '..ptype + else + io.stdout:write(buffer..'\n') + buffer=' '..ptype + end + end + io.stdout:write(buffer..'\n') + else --change paper size + mkdirhier(texmfconfig..'/xdvi') + local config=assert(io.open(texmfconfig..'/xdvi/XDvi', 'w')) + for n=1, #lines do + if string.find(lines[n], '^!*\*paper:') then + config:write('*paper: '..papersize..'\n') + else + config:write(lines[n]..'\n') + end + end + config:close() + print ('> '..texmfconfig..'/xdvi/XDvi') + domktexlsr = true + end +end + + +function set_pdftex_papersize(papersize) + local current_setting + --Read the file. + kpse.set_program_name('pdftex') + local filename=kpse.find_file('pdftexconfig.tex', 'tex') + + -- print ('< '..filename) + local infile=assert(io.open(filename, 'r')) + local lines={} + while true do + line=infile:read('*line') + if line == nil then break end + if string.find(line, '^\\pdfpagewidth') then + pagewidth=string.match(line, '^\\pdfpagewidth%s*=?%s*(.+)%s*$') + end + if string.find(line, '^\\pdfpageheight') then + pageheight=string.match(line, '^\\pdfpageheight%s*=?%s*(.+)%s*$') + end + lines[#lines+1]=line + end + infile:close() + + current_setting='a4' + for ptype, sizes in pairs(pdftex_papersize) do + if pagewidth == pdftex_papersize[ptype][1] then + ptype_x=ptype + end + if pageheight == pdftex_papersize[ptype][2] then + ptype_y=ptype + end + end + if ptype_x == ptype_y then + current_setting=ptype_x + end + + if papersize=='--list' then + print(current_setting) + for ptype, sizes in pairs(pdftex_papersize) do + if ptype ~= current_setting then + print(ptype) + end + end + elseif papersize=='help' then + io.stdout:write('Usage: '..usageprogname..' pdftex paper PAPER\n\n'.. + 'Valid PAPER settings:\n ') + local buffer='' + for ptype, sizes in pairs(pdftex_papersize) do + if #buffer + #ptype < 77 then + buffer=buffer..' '..ptype + else + io.stdout:write(buffer..'\n') + buffer=' '..ptype + end + end + io.stdout:write(buffer..'\n') + else --change paper size + mkdirhier(texmfconfig..'/tex/generic/config') + local outfile=texmfconfig..'/tex/generic/config/pdftexconfig.tex' + local config=assert(io.open(outfile, 'w')) + for n=1, #lines do + line=lines[n] + if string.find(line, '^\\pdfpagewidth') then + config:write('\\pdfpagewidth='..pdftex_papersize[papersize][1]..'\n') + elseif string.find(line, '^\\pdfpageheight') then + config:write('\\pdfpageheight='..pdftex_papersize[papersize][2]..'\n') + else + config:write(line..'\n') + end + end + config:close() + print ('> '..outfile) + domktexlsr = true + doformat = true + end +end + + +function set_dvipdfm_papersize(papersize, program) + local current_setting + -- Read the file. + local filename + if program == 'dvipdfm' then + kpse.set_program_name('dvipdfm') + filename=kpse.find_file('config', 'other text files') + elseif program == 'dvipdfmx' then + kpse.set_program_name('dvipdfmx') + filename=kpse.find_file('dvipdfmx.cfg', 'other text files') + end + if not(filename) then + print ("Cannot find config file for "..program..", exiting!") + os.exit(1) + end + -- print ('< '..filename) + local infile=assert(io.open(filename, 'r')) + local lines={} + local current_setting='a4' + while true do + line=infile:read('*line') + if line == nil then break end + if string.find(line, '^p ') then + current_setting=string.match(line, '^p%s+(%S+)') + end + lines[#lines+1]=line + end + infile:close() + + if papersize=='--list' then + print(current_setting) + for i, ptype in ipairs(dvipdfm_papersize) do + if ptype ~= current_setting then + print (ptype) + end + end + elseif papersize=='help' then + io.stdout:write('Usage: '..usageprogname..' '..program..' paper PAPER\n\n'.. + 'Valid PAPER settings:\n ') + local buffer='' + for i, ptype in pairs(dvipdfm_papersize) do + if #buffer + #ptype < 77 then + buffer=buffer..' '..ptype + else + io.stdout:write(buffer..'\n') + buffer=' '..ptype + end + end + io.stdout:write(buffer..'\n') + else --change paper size + local outfile + mkdirhier(texmfconfig..'/dvipdfm/config') + if program == 'dvipdfm' then + outfile=texmfconfig..'/dvipdfm/config/config' + mkdirhier(texmfconfig..'/dvipdfm/config') + else + outfile=texmfconfig..'/dvipdfmx/dvipdfmx.cfg' + mkdirhier(texmfconfig..'/dvipdfmx') + end + print ('> '..outfile) + local config=assert(io.open(outfile, 'w')) + for n=1, #lines do + line=lines[n] + if string.find(line, '^p ') then + config:write('p '..papersize..'\n') + else + config:write(line..'\n') + end + end + config:close() + domktexlsr = true + end +end + + +function set_context_papersize(papersize) + local current_setting + + -- we cannot guarantee that context is installed, so we check for the + -- file we would need for creating the configuration, and if that is + -- missing we just return (with a warning). + if not(lfs.isfile(texmfdist..'/tex/context/user/cont-sys.rme')) then + io.stderr:write('context not installed, skipping paper setting for it\n') + return + end + + -- Read the file. + local filename + + kpse.set_program_name('tex') + filename=kpse.find_file('cont-sys.tex', 'tex') + if not(filename) then + filename=kpse.find_file('cont-sys.rme', 'other text files') + end + if not(filename) then + print ("Cannot find config file for context, exiting!") + os.exit(1) + end + -- print ('< '..filename) + + local infile=assert(io.open(filename, 'r')) + local lines={} + local current_setting='A4' + local papersize_regexp='^%s*\\setuppapersize%s*%[([^%[]*)%]' + while true do + line=infile:read('*line') + if line == nil then break end + if string.find(line, '^%s*\\setuppapersize') then + current_setting=string.match(line, papersize_regexp) + end + lines[#lines+1]=line + end + infile:close() + + -- print ('current setting: '..current_setting) + + if papersize=='--list' then + print(current_setting) + if (string.match (current_setting, '[aA]4')) then + print ('letter') + else + print ('A4') + end + elseif papersize=='help' then + io.stdout:write('Usage: '..usageprogname..' context paper PAPER\n\n'.. + 'Valid PAPER settings:\n\nA4 letter\n') + else --change paper size + -- make sure we have uppercase A4 for paper size + if (string.match (papersize, 'a4')) then + papersize = 'A4' + end + local outfile + mkdirhier(texmfconfig..'/tex/context/user') + outfile=texmfconfig..'/tex/context/user/cont-sys.tex' + print ('> '..outfile) + local config=assert(io.open(outfile, 'w')) + for n=1, #lines do + line=lines[n] + if string.find(line, '\setuppapersize') then + config:write('\\setuppapersize['..papersize..']['..papersize..']\n') + else + config:write(line..'\n') + end + end + config:close() + domktexlsr = true + end +end + + +function set_papersize(papersize) + if papersize == '--list' then + set_pdftex_papersize('--list') + elseif papersize == 'help' then + io.stdout:write('Usage: '..usageprogname..' paper PAPER\n\n'.. + 'Valid PAPER settings:\n a4 letter\n') + end + if papersize ~= 'a4' and papersize ~= 'letter' then + io.stdout:write('Invalid paper size "'..papersize..'".'.. + ' Valid sizes: a4 letter\n') + os.exit(1) + else + set_dvips_papersize(papersize) + set_pdftex_papersize(papersize) + set_xdvi_papersize(papersize) + set_context_papersize(papersize) + set_dvipdfm_papersize(papersize, 'dvipdfm') + set_dvipdfm_papersize(papersize, 'dvipdfmx') + end +end + + +function usage_main() + local usage={ + 'help', + 'xdvi paper PAPER (xdvi paper size)', + 'dvips paper PAPER (dvips paper size)', + 'pdftex paper PAPER (pdftex paper size)', + 'dvipdfm paper PAPER (dvipdfm paper size)', + 'dvipdfmx paper PAPER (dvipdfmx paper size)', + 'paper [a4|letter] (paper size for all programs)', + 'init [FORMAT] ... (rebuild FORMATs, or all formats plus run updmap)', + 'rehash (rebuild ls-R files with mktexlsr)' + } + io.stdout:write('Usage:\n') + for i, msg in ipairs(usage) do + io.stdout:write(' '..usageprogname..' '..msg..'\n') + end + os.exit(0) +end + + +function usage_paper(progname) + io.stdout:write('Usage: '..usageprogname..' '..progname..' paper PAPER\n') + io.stdout:write(' '..usageprogname..' '..progname..' help\n') + os.exit(0) +end + + +function set_prog_papersize(prog,size) + if (prog == 'dvips') then + set_dvips_papersize(size) + elseif (prog == 'xdvi') then + set_xdvi_papersize(size) + elseif (prog == 'pdftex') then + set_pdftex_papersize(size) + elseif (prog == 'dvipdfm') then + set_dvipdfm_papersize(size, "dvipdfm") + elseif (prog == 'dvipdfmx') then + set_dvipdfm_papersize(size, "dvipdfmx") + end +end + + +if arg[1] == 'help' then + usage_main() +elseif arg[1] == 'paper' then + if arg[2] == 'help' or arg[2] == nil then + usage_main() + else + set_papersize(arg[2]) + end +elseif arg[1] == 'rehash' then + command = { "mktexlsr" } + command = fixwin(command) + os.exec(command) + -- does not return +elseif arg[1] == 'init' then + if arg[2] == nil then + -- we have set TEXMFCONFIG and TEXMFVAR to the SYS variants above, + -- so we can run the normal fmtutil/updmap program + os.execute('fmtutil --all') + os.execute('updmap') + else + for i=2, #arg do + os.execute('fmtutil --byfmt "'..arg[i]..'"') + end + end +elseif arg[1] == 'paper' then + set_papersize(arg[2]) +elseif arg[1] == 'dvips' then + if arg[2] == 'help' or arg[3]==nil then + usage_paper('dvips') + elseif arg[2]=='paper' then + set_dvips_papersize(arg[3]) + end +elseif arg[1] == 'xdvi' then + if arg[2] == 'help' or arg[3]==nil then + usage_paper('xdvi') + elseif arg[2]=='paper' then + set_xdvi_papersize(arg[3]) + end +elseif arg[1] == 'pdftex' then + if arg[2] == 'help' or arg[3]==nil then + usage_paper(pdftex) + elseif arg[2]=='paper' then + set_pdftex_papersize(arg[3]) + end +elseif arg[1] == 'dvipdfm' then + if arg[2] == 'help' or arg[3]==nil then + usage_paper('dvipdfm') + elseif arg[2]=='paper' then + set_dvipdfm_papersize(arg[3], 'dvipdfm') + end +elseif arg[1] == 'dvipdfmx' then + if arg[2] == 'help' or arg[3]==nil then + usage_paper('dvipdfmx') + elseif arg[2]=='paper' then + set_dvipdfm_papersize(arg[3], 'dvipdfmx') + end +elseif arg[1] == 'context' then + if arg[2] == 'help' or arg[3]==nil then + usage_paper('context') + elseif arg[2]=='paper' then + set_context_papersize(arg[3]) + end +else + usage_main() +end + + +if sys and domktexlsr then + command = { "mktexlsr", texmfconfig } + command = fixwin(command) + os.spawn(command) +end + +if noformatopt then + doformat = false +end + +if doformat then + -- we have to update the formats + -- I don't know how to select only those formats for which pdftexconfig.tex + -- is necessary, so we call fmtutil-sys --refresh + -- that will recreate a lot, sorry + -- + -- on win32 texlua's os.spawn does not find fmtutil-sys.texlua as executable + -- even if I give it the full path. So we have to work around that + if sys then + command = 'fmtutil-sys --refresh' + else + command = 'fmtutil --refresh' + end + os.execute(command) +end + +-- Local Variables: +-- lua-indent-level: 2 +-- tab-width: 2 +-- indent-tabs-mode: nil +-- End: +-- vim:set tabstop=2 expandtab: # diff --git a/Master/texmf-dist/scripts/texlive/tlmgr.pl b/Master/texmf-dist/scripts/texlive/tlmgr.pl new file mode 100755 index 00000000000..e4a63e2bfcf --- /dev/null +++ b/Master/texmf-dist/scripts/texlive/tlmgr.pl @@ -0,0 +1,7503 @@ +#!/usr/bin/env perl +# $Id$ +# +# Copyright 2008-2013 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; +my $prg; +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; +our $pinfile; + +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 w32 $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 File::Spec; +use Digest::MD5; +use Pod::Usage; +use Getopt::Long qw(:config no_autoabbrev permute); +use strict; + +use TeXLive::TLConfig; +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; + +# +# set up $prg for warning messages +$prg = TeXLive::TLUtils::basename($0); + +binmode(STDOUT, ":utf8"); +binmode(STDERR, ":utf8"); + +our %config; # hash of config settings from config file +our $remotetlpdb; +our $location; # location from which the new packages come +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, + "pin-file" => "=s", + "pause" => 1, + "print-platform|print-arch" => 1, + "version" => 1, + "help" => 1, + "h|?" => 1); + + my %actionoptions = ( + "get-mirror" => { }, + "option" => { }, + "conf" => { }, + "version" => { }, + "repository" => { }, + "candidate" => { }, + "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, + "file" => 1, + "reinstall" => 1, + "force" => 1, + "dry-run|n" => 1 }, + "paper" => { "list" => 1 }, + "path" => { "w32mode" => "=s" }, + "pinning" => { "all" => 1 }, + "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 }, + "repository" => { "with-platforms" => 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 }, + "info" => { "list" => 1, + "taxonomy" => 1, + "keyword" => 1, + "characterization" => 1, + "functionality" => 1, + "only-installed" => 1 }, + "dump-tlpdb" => { "local" => 1, + "remote" => 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"} && !$opts{"h"}) { + die "$prg: missing action; try --help if you need it.\n"; + } + + if ($opts{"help"} || $opts{"h"}) { + # 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()) { + @noperldoc = ("-noperldoc", "1"); + } else { + if (!TeXLive::TLUtils::which("perldoc")) { + @noperldoc = ("-noperldoc", "1"); + } else { + # checking only for the existence of perldoc is not enough + # because stupid Debian/Ubuntu ships a stub that does nothing + # which is very very bad idea + # try to check for that, too + my $ret = system("perldoc -V > /dev/null 2>&1"); + if ($ret == 0) { + debug("Working perldoc found, using it.\n"); + } else { + tlwarn("Your perldoc seems to be non functional!\n"); + @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 { + if ($opts{"help"}) { + pod2usage(-exitstatus => 0, -verbose => 2, @noperldoc); + } else { + # give a short message about usage + print " +tlmgr revision $tlmgrrevision +usage: tlmgr OPTION... ACTION ARGUMENT... +where ACTION is one of:\n"; + for my $k (sort keys %actionoptions) { + print " $k\n"; + } + print "\nUse\n tlmgr ACTION --help +for more details on a specific option, and + tlmgr --help +for the full story.\n"; + exit 0; + } + } + } + + # 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"; + } + + # backward compatibility with action "show" and "list" from before + if ($action =~ /^(show|list)$/) { + $action = "info"; + } + + # --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; + } + ddebug("tlmgr:main: do persistent downloads = $do_persistent\n"); + if ($do_persistent) { + TeXLive::TLUtils::setup_persistent_downloads() ; + } + 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 = ; + $::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 .= "\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/^repository$/i) { + action_repository(); + finish(0); + } elsif ($action =~ m/^pinning$/i) { + action_pinning(); + finish(0); + } elsif ($action =~ m/^candidates$/i) { + action_candidates(); + 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(); + finish(0); + } 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/^dump-tlpdb$/i) { + action_dumptlpdb(); + finish(0); + } elsif ($action =~ m/^info$/i) { + action_info(@ARGV); + 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 "$prg: 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); + if ($opts{"dry-run"}) { + $ret = 0; + $out = ""; + } else { + ($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"); + if (defined($localtlpdb->get_package('context'))) { + $errors += do_cmd_and_check("mtxrun --generate"); + } + $::files_changed = 0; + } + + chomp(my $TEXMFSYSVAR = `kpsewhich -var-value=TEXMFSYSVAR`); + chomp(my $TEXMFSYSCONFIG = `kpsewhich -var-value=TEXMFSYSCONFIG`); + chomp(my $TEXMFLOCAL = `kpsewhich -var-value=TEXMFLOCAL`); + chomp(my $TEXMFDIST = `kpsewhich -var-value=TEXMFDIST`); + + # + # maps handling + { + my $updmap_run_needed = 0; + for my $m (keys %{$::execute_actions{'enable'}{'maps'}}) { + $updmap_run_needed = 1; + } + for my $m (keys %{$::execute_actions{'disable'}{'maps'}}) { + $updmap_run_needed = 1; + } + if ($updmap_run_needed) { + my $dest = "$TEXMFDIST/web2c/updmap.cfg"; + TeXLive::TLUtils::create_updmap($localtlpdb, $dest); + } + $errors += do_cmd_and_check("updmap-sys") if $updmap_run_needed; + } + + # format relevant things + # we first have to check if the config files, that is fmtutil.cnf + # or one of the language* files have changed, regenerate them + # if necessary, and then run the necessary fmtutil calls. + + { + # first check for language* files + my $regenerate_language = 0; + for my $m (keys %{$::execute_actions{'enable'}{'hyphens'}}) { + $regenerate_language = 1; + last; + } + for my $m (keys %{$::execute_actions{'disable'}{'hyphens'}}) { + $regenerate_language = 1; + last; + } + if ($regenerate_language) { + 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); + } + } + } + + # + # check if *depending* formats have been changed + # we are currently only caring for package "latex" and "tex". If + # one of these has changed, we search for all packages *depending* + # on latex/tex and regenerate all formats in these packages. + # + # do this only if we are not in --list or --dry-run mode + if (!$opts{"list"}) { + my @check_indirect_formats; + # TODO: + # in case that hyphenation patterns are changed, ie $regenerate_language + # then maybe we don't need to update latex based ones? + push @check_indirect_formats, $localtlpdb->needed_by("latex") + if ($::latex_updated); + push @check_indirect_formats, $localtlpdb->needed_by("tex") + if ($::tex_updated); + for my $p (@check_indirect_formats) { + my $tlp = $localtlpdb->get_package($p); + if (!defined($tlp)) { + tlwarn("$p mentioned but not found in local tlpdb, strange!\n"); + next; + } + TeXLive::TLUtils::announce_execute_actions("enable", $tlp, "format"); + } + } + + # format-regenerate is used when the paper size changes. In that + # case, if option("create_formats") is set, we simply want to generate + # all formats + # + 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; + } + } + + # now go back to the hyphenation patterns and regenerate formats + # based on the various language files + # this of course will in some cases duplicate fmtutil calls, + # but it is much easier than actually checking which formats + # don't need to be updated + + if ($regenerate_language) { + for my $ext ("dat", "def", "dat.lua") { + my $lang = "language.$ext"; + 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 ("$prg: removing $pkg\n"); + if (!$opts{"dry-run"}) { + $foo = $localtlpdb->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 ("$prg: removing package $pkg\n"); + if (!$opts{"dry-run"}) { + if ($localtlpdb->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: ultimately 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); + } +} + + +# PATH +# +sub action_path { + my $what = shift @ARGV; + if (!defined($what) || ($what !~ m/^(add|remove)$/i)) { + $what = "" if ! $what; + tlwarn("tlmgr: action path requires add or remove, not: $what\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 = $localtlpdb->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( + $localtlpdb->root . "/bin/win32", + $winadminmode); + TeXLive::TLWinGoo::broadcast_env(); + } else { + TeXLive::TLUtils::add_symlinks($localtlpdb->root, + $localtlpdb->platform(), + $localtlpdb->option("sys_bin"), + $localtlpdb->option("sys_man"), + $localtlpdb->option("sys_info")); + } + } elsif ($what =~ m/^remove$/i) { + if (win32()) { + TeXLive::TLUtils::w32_remove_from_path( + $localtlpdb->root . "/bin/win32", + $winadminmode); + TeXLive::TLWinGoo::broadcast_env(); + } else { + # remove symlinks + TeXLive::TLUtils::remove_symlinks($localtlpdb->root, + $localtlpdb->platform(), + $localtlpdb->option("sys_bin"), + $localtlpdb->option("sys_man"), + $localtlpdb->option("sys_info")); + } + } else { + # that should not happen + tlwarn("\ntlmgr: Should not happen, action_path what=$what\n"); + exit 1; + } + return; +} + +# DUMP TLPDB +# +sub action_dumptlpdb { + init_local_db(); + + # we are basically doing machine-readable output. + my $savemr = $::machinereadable; + $::machinereadable = 1; + + if ($opts{"local"} && !$opts{"remote"}) { + # for consistency we write out the location of the installation, + # too, in the same format as when dumping the remote tlpdb + print "location-url\t", $localtlpdb->root, "\n"; + $localtlpdb->writeout; + + } elsif ($opts{"remote"} && !$opts{"local"}) { + init_tlmedia_or_die(); + $remotetlpdb->writeout; + + } else { + tlwarn("tlmgr dump-tlpdb: need exactly one of --local and --remote.\n"); + } + + $::machinereadable = $savemr; + return; +} + +# INFO +# +sub action_info { + 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"); + } + } + my ($what, @todo) = @ARGV; + # + # tlmgr info + # tlmgr info collection + # tlmgr info scheme + # these commands just list the packages/collections/schemes installed with + # a short list + if (!defined($what) || ($what =~ m/^(collections|schemes)$/i)) { + show_list_of_packages($what); + return; + } + # we are still here, so $what is defined and neither collection nor scheme, + # so assume the arguments are package names + foreach my $ppp ($what, @todo) { + my ($pkg, $tag) = split '@', $ppp, 2; + my $tlpdb = $localtlpdb; + my $source_found; + my $tlp = $localtlpdb->get_package($pkg); + my $installed = 0; + if (!$tlp) { + if (!$remotetlpdb) { + init_tlmedia_or_die(); + } + if (defined($tag)) { + if (!$remotetlpdb->is_virtual) { + tlwarn("tlmgr: specifying implicit tags is not allowed for non-virtual databases!\n"); + next; + } else { + if (!$remotetlpdb->is_repository($tag)) { + tlwarn("tlmgr: no such repository tag defined: $tag\n"); + next; + } + } + } + $tlp = $remotetlpdb->get_package($pkg, $tag); + if (!$tlp) { + if (defined($tag)) { + # we already searched for the package in a specific tag, don't retry + # all candidates! + tlwarn("tlmgr: Cannot find package $pkg in repository $tag\n"); + next; + } + if ($remotetlpdb->is_virtual) { + # we might have a package that is available in a + # subsidiary repository, but not installable + # because it is not pinned + # we will list it but warn about this fact + my @cand = $remotetlpdb->candidates($pkg); + if (@cand) { + my $first = shift @cand; + if (defined($first)) { + tlwarn("strange, we have a first candidate but no tlp: $pkg\n"); + next; + } + # already shifted away the first element + if ($#cand >= 0) { + # recursively showing all tags, but warn + print "package: ", $pkg, "\n"; + print "WARNING: This package is not pinned but present in subsidiary repositories\n"; + print "WARNING: As long as it is not pinned it is not installable.\n"; + print "WARNING: Listing all available copies of the package.\n"; + my @aaa; + for my $a (@cand) { + my ($t,$r) = split(/\//, $a, 2); + push @aaa, "$pkg" . '@' . $t; + } + action_info(@aaa); + next; + } else { + tlwarn("strange, package listed but no residual candidates: $pkg\n"); + next; + } + } else { + tlwarn("strange, package listed but no candidates: $pkg\n"); + next; + } + } + tlwarn("tlmgr: cannot find package $pkg\n"); + next; + } + # we want to also show the source if it is known + if (defined($tag)) { + $source_found = $tag; + } else { + if ($remotetlpdb->is_virtual) { + my ($firsttag, @cand) = $remotetlpdb->candidates($pkg); + $source_found = $firsttag; + } else { + # might be single user repository, don't mention anything + } + } + $tlpdb = $remotetlpdb; + } else { + $installed = 1; + } + 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 (!$remotetlpdb) { + init_tlmedia_or_die(); + } + @colls = $remotetlpdb->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 "repository: ", $source_found, "\n" if (defined($source_found)); + 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\n"); + 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"; + } + 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 = ; + 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_or_die(); + $tlpdb = $remotetlpdb; + } 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 w32 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; + } + $localtlpdb->remove_package($pkg); + TeXLive::TLPDB->_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 = ; + 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 = ; + 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 = $remotetlpdb->media; + # we have to download/copy also the src/doc files if necessary! + my $container_src_split = $remotetlpdb->config_src_container; + my $container_doc_split = $remotetlpdb->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"; + TeXLive::TLUtils::mkdirhier($temp); + tlwarn("Backup option not implemented for infrastructure update.\n") if ($opts{"backup"}); + if ($media eq 'local_uncompressed') { + tlwarn("Creating updater from local_uncompressed 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 $repo; + my $mediatlp; + if ($media eq "virtual") { + my $maxtlpdb; + (undef, undef, $mediatlp, $maxtlpdb) = + $remotetlpdb->virtual_candidate($pkg); + $repo = $maxtlpdb->root . "/$Archive"; + } else { + $mediatlp = $remotetlpdb->get_package($pkg); + $repo = $remotetlpdb->root . "/$Archive"; + } + 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 'local_compressed') { + 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 -xmf 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 -xmf 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($remotetlpdb->get_package($p)); + } + for my $p ($localtlpdb->collections) { + push (@all_schmscolls, $p) if defined($remotetlpdb->get_package($p)); + } + my @localexpansion_full = + $localtlpdb->expand_dependencies($localtlpdb, @all_schmscolls); + my @remoteexpansion_full = + $remotetlpdb->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}; + my $remotetlp = $remotetlpdb->get_package($p); + if (!defined($remotetlp)) { + tlwarn("Strange, $p mentioned but not found anywhere!\n"); + next; + } + next if ($remotetlp->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 = + $remotetlpdb->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 +# +# TLPDB->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, $size, $runtime, $esttot, $tag) = @_; + $lrev ||= "-"; + $rrev ||= "-"; + $flag ||= "?"; + $size ||= "-"; + $runtime ||= "-"; + $esttot ||= "-"; + $tag ||= "-"; + my $str = join("\t", $pkg, $flag, $lrev, $rrev, $size, $runtime, $esttot, $tag); + $str .= "\n"; + return($str) if $ret; + print $str; +} + +sub upd_info { + my ($pkg, $kb, $lrev, $mrev, $txt) = @_; + my $flen = 25; + my $kbstr = ($kb >= 0 ? " [${kb}k]" : ""); + my $kbstrlen = length($kbstr); + my $pkglen = length($pkg); + my $is = sprintf("%-9s ", "$txt:"); + if ($pkglen + $kbstrlen > $flen) { + $is .= "$pkg$kbstr: "; + } else { + $is .= sprintf ('%*2$s', $pkg, -($flen-$kbstrlen)); + $is .= "$kbstr: "; + } + $is .= sprintf("local: %8s, source: %8s", + $lrev, $mrev); + info("$is\n"); +} + +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 + # if --list is given: nothing + # other options just change the behaviour + if (!($opts{"list"} || @ARGV || $opts{"all"} || $opts{"self"})) { + tlwarn("tlmgr update: specify --list, --all, --self, or a list of package names.\n"); + return; + } + + init_tlmedia_or_die(); + 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, $remotetlpdb); + 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("$prg: 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 "$prg: 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 = $remotetlpdb->expand_dependencies("-only-arch", $localtlpdb, @todo) + unless $opts{"no-depends-at-all"}; + # + # update general deps unless $opts{"no-depends"}: + @todo = $remotetlpdb->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"} && $config{"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 = $remotetlpdb->get_package($pkg); + if (!defined($mediatlp)) { + tlwarn("\nShould not happen: $pkg not found in $location\n"); + next; + } + my $mediarev = $mediatlp->revision; + push @new, $pkg; + next; + } + my $rev = $tlp->revision; + my $mediatlp; + my $maxtag; + if ($remotetlpdb->is_virtual) { + ($maxtag, undef, $mediatlp, undef) = + $remotetlpdb->virtual_candidate($pkg); + } else { + $mediatlp = $remotetlpdb->get_package($pkg); + } + if (!defined($mediatlp)) { + debug("$pkg cannot be found in $location\n"); + next; + } + my $mediarev = $mediatlp->revision; + my $mediarevstr = $mediarev; + my @addargs = (); + if ($remotetlpdb->is_virtual) { + push @addargs, $maxtag; + $mediarevstr .= "\@$maxtag"; + } + 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, "-", @addargs); + } else { + if ($opts{"list"}) { + # not issueing anything if we keep a package + upd_info($pkg, -1, $rev, $mediarevstr, "keep"); + } + } + } + } + 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"} && $config{"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 + # the third argument is undef to compute *all* platforms + my %sizes; + if (@alltodo) { + %sizes = %{$remotetlpdb->sizes_of_packages( + $localtlpdb->option("install_srcfiles"), + $localtlpdb->option("install_docfiles"), undef, @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 = $remotetlpdb->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) + } + } + + # parameters for field width + my $totalnrdigits = length("$totalnr"); + + # + # 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"} || !$config{"auto-remove"}) { + info("not removing $p due to -no-auto-remove or config file option (removed on server)\n"); + } else { + &ddebug("removing package $p\n"); + my $pkg = $localtlpdb->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 { + upd_info($p, -1, $rev, "", "autorm"); + } + $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("[" . sprintf ('%*2$s', $currnr, $totalnrdigits) . + "/$totalnr] auto-remove: $p ... "); + } + 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); + } + } + $localtlpdb->remove_package($p); + logpackage("remove: $p"); + } + info("done\n") unless $::machinereadable; + $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; + my $maxtag; + if ($remotetlpdb->is_virtual) { + ($maxtag, undef, $mediatlp, undef) = + $remotetlpdb->virtual_candidate($pkg); + } else { + $mediatlp = $remotetlpdb->get_package($pkg); + } + if (!defined($mediatlp)) { + debug("$pkg cannot be found in $location\n"); + next; + } + my $mediarev = $mediatlp->revision; + my $mediarevstr = $mediarev; + my @addargs = (); + if ($remotetlpdb->is_virtual) { + push @addargs, $maxtag; + $mediarevstr .= "\@$maxtag"; + } + $nrupdated++; + if ($opts{"list"}) { + if ($::machinereadable) { + machine_line($pkg, $FLAG_UPDATE, $rev, $mediarev, $sizes{$pkg}, "-", "-", @addargs); + } else { + my $kb = int($sizes{$pkg} / 1024) + 1; + upd_info($pkg, $kb, $rev, $mediarevstr, "update"); + if ($remotetlpdb->is_virtual) { + my @cand = $remotetlpdb->candidates($pkg); + shift @cand; # remove the top element + if (@cand) { + print "\tother candidates: "; + for my $a (@cand) { + my ($t,$r) = split(/\//, $a, 2); + print $r . '@' . $t . " "; + } + print "\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, @addargs); + } else { + my $kb = int ($sizes{$pkg} / 1024) + 1; + info("[" . sprintf ('%*2$s', $currnr, $totalnrdigits) . + "/$totalnr, $estrem/$esttot] update: $pkg [${kb}k] ($rev -> $mediarevstr)"); + } + $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$prg: 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 { + $localtlpdb->remove_package($pkg, + "remove-warn-files" => \%do_warn_on_move); + } + if ($remotetlpdb->install_package($pkg, $localtlpdb)) { + # installation succeeded because we got a reference + logpackage("update: $pkg ($rev -> $mediarevstr)"); + 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 -> $mediarevstr)"); + 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::TLPDB->_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; + my $maxtag; + if ($remotetlpdb->is_virtual) { + ($maxtag, undef, $mediatlp, undef) = + $remotetlpdb->virtual_candidate($pkg); + } else { + $mediatlp = $remotetlpdb->get_package($pkg); + } + if (!defined($mediatlp)) { + tlwarn("\nShould not happen: $pkg not found in $location\n"); + next; + } + my $mediarev = $mediatlp->revision; + my $mediarevstr = $mediarev; + my @addargs; + if ($remotetlpdb->is_virtual) { + $mediarevstr .= "\@$maxtag"; + push @addargs, $maxtag; + } + 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; + } else { + push @maargs, undef, undef; + } + machine_line(@maargs, @addargs); + } else { + my $kb = int($sizes{$pkg} / 1024) + 1; + if ($opts{"list"}) { + upd_info($pkg, $kb, "", $mediarevstr, "autoinst"); + } else { + info("[$currnr/$totalnr, $estrem/$esttot] auto-install: $pkg ($mediarevstr) [${kb}k] ... "); + } + } + $currnr++; + $donesize += $sizes{$pkg}; + next if ($opts{"dry-run"} || $opts{"list"}); + if ($remotetlpdb->install_package($pkg, $localtlpdb)) { + # installation succeeded because we got a reference + logpackage("auto-install new: $pkg ($mediarevstr)"); + $nrupdated++; + info("done\n") unless $::machinereadable; + } else { + tlwarn("$prg: couldn't install new package $pkg\n"); + } + } + } + } + + # + # special check for depending format updates: + # if one of latex or tex has been updated, we rebuild the formats + # defined in packages *depending* on these packages. + if (!$opts{"list"}) { + TeXLive::TLUtils::announce_execute_actions("latex-updated") if ($updated{"latex"}); + TeXLive::TLUtils::announce_execute_actions("tex-updated") if ($updated{"tex"}); + } + + 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 = $remotetlpdb->get_package($pkg); + } else { + # update failed but we could introduce new files, that + # should be removed now as a part of restoring backup + $oldtlp = $remotetlpdb->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 w32 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 w32 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; + $localtlpdb = undef; + $remotetlpdb = 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 ($remotetlpdb->media ne "NET" + && $remotetlpdb->media ne "virtual" + && !$opts{"dry-run"} + && !$opts{"repository"} + ) { + tlwarn(<install_package does ONLY INSTALL ONE PACKAGE, no deps whatsoever +# anymore! That has all to be done by the caller. +# +sub action_install { + init_local_db(1); + return if !check_on_writable(); + # initialize the TLPDB from $location + $opts{"no-depends"} = 1 if $opts{"no-depends-at-all"}; + init_tlmedia_or_die(); + + # check for updates to tlmgr itself, and die unless --force is given + if (check_for_critical_updates( $localtlpdb, $remotetlpdb)) { + 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"; + } + } + } + + # + # installation from a .tar.xz + if ($opts{"file"}) { + return $localtlpdb->install_package_files(@ARGV); + } + + $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 = $remotetlpdb->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 = $remotetlpdb->expand_dependencies("-no-collections", $localtlpdb, @packs) unless $opts{"no-depends"}; + } else { + @packs = $remotetlpdb->expand_dependencies($localtlpdb, @packs) unless $opts{"no-depends"}; + } + # + # expand dependencies returns a list pkg@tag in case of a virtual + # remote db. + my %packs; + for my $p (@packs) { + my ($pp, $aa) = split('@', $p); + $packs{$pp} = (defined($aa) ? $aa : 0); + } + # + # 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 keys %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; + # if the package name is asked from a specific repository, use + # that one, otherwise leave the decision to $remotetlpdb by not + # giving a final argument + my $mediatlp = $remotetlpdb->get_package($pkg, + ($packs{$pkg} ? $packs{$pkg} : undef)); + 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; + # undef here is a ref to array of platforms, if undef all are used + my %sizes = %{$remotetlpdb->sizes_of_packages( + $localtlpdb->option("install_srcfiles"), + $localtlpdb->option("install_docfiles"), undef, @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; + my @addargs = (); + my $tagstr = ""; + if ($remotetlpdb->is_virtual) { + if ($packs{$pkg} ne "0") { + push @addargs, $packs{$pkg}; + $tagstr = " \@" . $packs{$pkg}; + } else { + my ($maxtag,undef,undef,undef) = $remotetlpdb->virtual_candidate($pkg); + push @addargs, $maxtag; + $tagstr = " \@" . $maxtag; + } + } + if ($::machinereadable) { + machine_line($pkg, $flag, "-", $revs{$pkg}, $sizes{$pkg}, $estrem, $esttot, @addargs); + } else { + info("[$currnr/$totalnr, $estrem/$esttot] ${re}install: $pkg$tagstr [${kb}k]\n"); + } + if (!$opts{"dry-run"}) { + $remotetlpdb->install_package($pkg, $localtlpdb, + ($packs{$pkg} ? $packs{$pkg} : undef) ); + logpackage("${re}install: $pkg$tagstr"); + } + $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 show_list_of_packages { + init_local_db(); + # make sure that the @ARGV hash is not changed in case we switch to + # show mode + my ($what) = @_; + $what = "" if !$what; + my $tlm; + if ($opts{"only-installed"}) { + $tlm = $localtlpdb; + } else { + init_tlmedia_or_die(); + $tlm = $remotetlpdb; + } + my @whattolist; + if ($what =~ m/^collections/i) { + @whattolist = $tlm->collections; + } elsif ($what =~ m/^schemes/i) { + @whattolist = $tlm->schemes; + } else { + if ($tlm->is_virtual) { + @whattolist = $tlm->list_packages("-all"); + } else { + @whattolist = $tlm->list_packages; + } + } + foreach (@whattolist) { + next if ($_ =~ m/^00texlive/); + if (defined($localtlpdb->get_package($_))) { + print "i "; + } else { + print " "; + } + my $tlp = $tlm->get_package($_); + if (!$tlp) { + if ($remotetlpdb->is_virtual) { + # we might have the case that a package is present in a + # subsidiary repository, but not pinned, so it will + # not be found by ->get_package + # In this case we list all repositories shipping it, + # but warn that it is not pinned and thus not reachable. + my @cand = $remotetlpdb->candidates($_); + if (@cand) { + my $first = shift @cand; + if (defined($first)) { + tlwarn("strange, we have a first candidate but no tlp: $_\n"); + next; + } + # already shifted away the first element + if ($#cand >= 0) { + print "$_: --- no installable candidate found, \n"; + print " but present in subsidiary repositories without a pin.\n"; + print " This package is not reachable without pinning.\n"; + print " Repositories containing this package:\n"; + for my $a (@cand) { + my ($t,$r) = split(/\//, $a, 2); + my $tlp = $remotetlpdb->get_package($_, $t); + my $foo = $tlp->shortdesc; + print " $t: ", defined($foo) ? $foo : "(shortdesc missing)" , "\n"; + } + next; + } else { + tlwarn("strange, package listed but no residual candidates: $_\n"); + next; + } + } else { + tlwarn("strange, package listed but no candidates: $_\n"); + next; + } + } else { + tlwarn("strange, package cannot be found in remote tlpdb: $_\n"); + next; + } + } + my $foo = $tlp->shortdesc; + print "$_: ", defined($foo) ? $foo : "(shortdesc missing)" , "\n"; + } + return; +} + +# PINNING +# +# this action manages the pinning file +# of course it can be edited by hand, but we want to make this +# easier for people to use +# tlmgr pinning show +# tlmgr pinning check +# tlmgr pinning add [ ...] +# tlmgr pinning remove [ ...] +# tlmgr pinning remove --all +sub action_pinning { + my $what = shift @ARGV; + $what || ($what = 'show'); + init_local_db(); + init_tlmedia_or_die(); + if (!$remotetlpdb->is_virtual) { + tlwarn("$prg: only one repository configured, " + . "pinning actions not supported.\n"); + return; + } + my $pinref = $remotetlpdb->virtual_pindata(); + my $pf = $remotetlpdb->virtual_pinning(); + + if ($what =~ m/^show$/i) { + my @pins = @$pinref; + if (!@pins) { + tlwarn("$prg: no pinning data present.\n"); + return 0; + } + info("$prg: this pinning data is defined:\n"); + for my $p (@pins) { + info(" ", $p->{'repo'}, ":", $p->{'glob'}, "\n"); + } + return 1; + + } elsif ($what =~ m/^check$/i) { + tlwarn("$prg: not implemented yet, sorry!\n"); + return 0; + + } elsif ($what =~ m/^add$/i) { + # we need at least two more arguments + if (@ARGV < 2) { + tlwarn("$prg: need at least two arguments to pinning add\n"); + return; + } + my $repo = shift @ARGV; + my @new = (); + my @ov = $pf->value($repo); + for my $n (@ARGV) { + if (member($n, @ov)) { + info("$prg: already pinned to $repo: $n\n"); + } else { + push (@ov, $n); + push (@new, $n); + } + } + $pf->value($repo, @ov); + $remotetlpdb->virtual_update_pins(); + $pf->save; + info("$prg: new pinning data for $repo: @new\n") if @new; + return 1; + + } elsif ($what =~ m/^remove$/i) { + my $repo = shift @ARGV; + if (!defined($repo)) { + tlwarn("$prg: missing repository argument to pinning remove\n"); + return 0; + } + if ($opts{'all'}) { + if (@ARGV) { + tlwarn("$prg: additional argument(s) not allowed with --all: @ARGV\n"); + return 0; + } + $pf->delete_key($repo); + $remotetlpdb->virtual_update_pins(); + $pf->save; + info("$prg: all pinning data removed for repository $repo\n"); + return 1; + } + # complicated case, we want to remove only one setting + my @ov = $pf->value($repo); + my @nv; + for my $pf (@ov) { + push (@nv, $pf) if (!member($pf, @ARGV)); + } + if ($#ov == $#nv) { + info("$prg: no changes in pinning data for $repo\n"); + return 1; + } + if (@nv) { + $pf->value($repo, @nv); + } else { + $pf->delete_key($repo); + } + $remotetlpdb->virtual_update_pins(); + $pf->save; + info("$prg: removed pinning data for repository $repo: @ARGV\n"); + return 1; + + } else { + tlwarn("$prg: unknown argument for pinning action: $what\n"); + return 0; + } + # $pin{'repo'} = $repo; + # $pin{'glob'} = $glob; + # $pin{'re'} = $re; + # $pin{'line'} = $line; # for debug/warning purpose + return 0; +} + +# REPOSITORY +# +# this action manages the list of repositories +# tlmgr repository list -> lists repositories +# tlmgr repository list path|tag -> lists content of repo path|tag +# tlmgr repository add path [tag] -> add repository with optional tag +# tlmgr repository remove [path|tag] -> removes repository or tag +# tlmgr repository set path[#tag] [path[#tag] ...] -> sets the list +# + +sub array_to_repository { + my %r = @_; + my @ret; + my @k = keys %r; + if ($#k == 0) { + # only one repo, don't write any tag + return $r{$k[0]}; + } + for my $k (keys %r) { + my $v = $r{$k}; + if ($k ne $v) { + $v = "$v#$k"; + } + # encode spaces and % in the path and tags + $v =~ s/%/%25/g; + $v =~ s/ /%20/g; + push @ret, $v; + } + return "@ret"; +} +sub repository_to_array { + my $r = shift; + my %r; + my @repos = split ' ', $r; + if ($#repos == 0) { + # only one repo, this is the main one! + $r{'main'} = $repos[0]; + return %r; + } + for my $rr (@repos) { + my $tag; + my $url; + # decode spaces and % in reverse order + $rr =~ s/%20/ /g; + $rr =~ s/%25/%/g; + $tag = $url = $rr; + if ($rr =~ m/^([^#]+)#(.*)$/) { + $tag = $2; + $url = $1; + } + $r{$tag} = $url; + } + return %r; +} +sub merge_sub_packages { + my %pkgs; + for my $p (@_) { + if ($p =~ m/^(.*)\.([^.]*)$/) { + my $n = $1; + my $a = $2; + if ($p eq "texlive.infra") { + push @{$pkgs{$p}}, "all"; + } else { + push @{$pkgs{$n}}, $a; + } + } else { + push @{$pkgs{$p}}, "all"; + } + } + return %pkgs; +} +sub action_repository { + init_local_db(); + my $what = shift @ARGV; + $what = "list" if !defined($what); + my %repos = repository_to_array($localtlpdb->option("location")); + if ($what =~ m/^list$/i) { + if (@ARGV) { + # list what is in a repository + for my $repo (@ARGV) { + my $loc = $repo; + if (defined($repos{$repo})) { + $loc = $repos{$repo}; + } + my ($tlpdb, $errormsg) = setup_one_remotetlpdb($loc); + if (!defined($tlpdb)) { + tlwarn("cannot get TLPDB from location $loc\n\n"); + } else { + print "Packages at $loc:\n"; + my %pkgs = merge_sub_packages($tlpdb->list_packages); + for my $p (sort keys %pkgs) { + next if ($p =~ m/00texlive/); + print " $p"; + if (!$opts{'with-platforms'}) { + print "\n"; + } else { + my @a = @{$pkgs{$p}}; + if ($#a == 0) { + if ($a[0] eq "all") { + # no further information necessary + print "\n"; + } else { + print ".$a[0]\n"; + } + } else { + print " (@{$pkgs{$p}})\n"; + } + } + } + } + } + } else { + print "List of repositories (with tags if set):\n"; + for my $k (keys %repos) { + my $v = $repos{$k}; + print "\t$v"; + if ($k ne $v) { + print " ($k)"; + } + print "\n"; + } + } + return; + } + if ($what eq "add") { + my $p = shift @ARGV; + if (!defined($p)) { + tlwarn("$prg: no repository given (to add)\n"); + return; + } + # check if it is either url or absolute path + if (($p !~ m!^(http|ftp)://!i) && + !File::Spec->file_name_is_absolute($p)) { + tlwarn("$prg: neither http/ftp URL nor absolute path, no action: $p\n"); + return; + } + my $t = shift @ARGV; + $t = $p if (!defined($t)); + if (defined($repos{$t})) { + tlwarn("$prg: repository or its tag already defined, no action: $p\n"); + return; + } + # TODO more checks needed? + # if there was till now only *one* repository and that without + # a tag, we give that one the "main" tag which is necessary + # for proper operation! + my @tags = keys %repos; + if ($#tags == 0) { + # we have only one repository, check if it has the main tag + my $maintag = $tags[0]; + if ($maintag ne 'main') { + $repos{'main'} = $repos{$maintag}; + delete $repos{$maintag}; + } + } + $repos{$t} = $p; + $localtlpdb->option("location", array_to_repository(%repos)); + $localtlpdb->save; + if ($t eq $p) { + print "$prg: added repository: $p\n"; + } else { + print "$prg: added repository with tag $t: $p\n"; + } + return; + } + if ($what eq "remove") { + my $p = shift @ARGV; + if (!defined($p)) { + tlwarn("$prg: no repository given (to remove)\n"); + return; + } + my $found = 0; + for my $k (keys %repos) { + if ($k eq $p || $repos{$k} eq $p) { + $found = 1; + delete $repos{$k}; + } + } + if (!$found) { + tlwarn("$prg: repository not defined, cannot remove: $p\n"); + } else { + $localtlpdb->option("location", array_to_repository(%repos)); + $localtlpdb->save; + print "$prg: removed repository: $p\n"; + } + return; + } + if ($what eq "set") { + # TODO TODO + # we have to make sure that there is ONE main repository!!! + %repos = repository_to_array("@ARGV"); + $localtlpdb->option("location", array_to_repository(%repos)); + $localtlpdb->save; + return; + } + # we are still here, unknown command to repository + tlwarn("$prg: unknown subaction for tlmgr repository: $what\n"); + return; +} + +sub action_candidates { + my $what = shift @ARGV; + if (!defined($what)) { + tlwarn("$prg: candidates needs a package name as argument\n"); + return; + } + init_local_db(); + init_tlmedia_or_die(); + my @cand = $remotetlpdb->candidates($what); + if (@cand) { + my $first = shift @cand; + if (defined($first)) { + my ($t,$r) = split(/\//, $first, 2); + print "Install candidate for $what from $t ($r)\n"; + } else { + print "No install candidate for $what found.\n"; + } + # already shifted away the first element + if ($#cand >= 0) { + print "Other repositories providing this package:\n"; + for my $a (@cand) { + my ($t,$r) = split(/\//, $a, 2); + print "$t ($r)\n"; + } + } + } else { + print "Package $what not found.\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 w32 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 TLPDB from $location + init_tlmedia_or_die(); + my @already_installed_arch = $localtlpdb->available_architectures; + print "Available platforms:\n"; + foreach my $a ($remotetlpdb->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_or_die(); + my @already_installed_arch = $localtlpdb->available_architectures; + my @available_arch = $remotetlpdb->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 ($remotetlpdb->get_package("$pkg.$a")) { + info("install: $pkg.$a\n"); + $remotetlpdb->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"); + $remotetlpdb->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 = $localtlpdb->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"); + $localtlpdb->remove_package("$pkg.$a") if (!$opts{"dry-run"}); + } + } + } + } + } + if (TeXLive::TLUtils::member('win32', @todoarchs)) { + for my $p (@extra_w32_packs) { + info("remove: $p\n"); + $localtlpdb->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; + } + } elsif ($what =~ m/^set$/i) { + return if !check_on_writable(); + my $arg = shift @ARGV; + die "Missing argument to platform set" unless defined($arg); + my @already_installed_arch = $localtlpdb->available_architectures; + if ($arg =~ m/^auto$/i) { + info("Setting platform detection to auto mode.\n"); + $localtlpdb->setting('-clear', 'platform'); + $localtlpdb->save; + } else { + if (!TeXLive::TLUtils::member($arg, @already_installed_arch)) { + tlwarn("cannot set platform to a not installed one.\n"); + return; + } + $localtlpdb->setting('platform', $arg); + $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 TEXMFDIST. The reason is that we are now using an + # implementation of updmap that supports multiple updmap files. + # Local adaptions should not be made there, but only in TEXMFLOCAL + # or TEXMF(SYS)CONFIG updmap.cfg + # + chomp (my $TEXMFSYSVAR = `kpsewhich -var-value=TEXMFSYSVAR`); + chomp (my $TEXMFSYSCONFIG = `kpsewhich -var-value=TEXMFSYSCONFIG`); + chomp (my $TEXMFLOCAL = `kpsewhich -var-value=TEXMFLOCAL`); + chomp (my $TEXMFDIST = `kpsewhich -var-value=TEXMFDIST`); + + # 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("$prg: 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 ("$prg: 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("$prg: 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("$prg: 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) { + tlwarn("$prg: generate updmap is no longer needed or supported.\n"); + tlwarn("$prg: Please read the documentation of the `updmap' program.\n"); + tlwarn("$prg: Goodbye.\n"); + exit(1); + + } elsif ($what =~ m/^_updmap$/i) { + my $dest = $opts{"dest"} || "$TEXMFDIST/web2c/updmap.cfg"; + debug("$prg: writing new updmap.cfg to $dest\n"); + TeXLive::TLUtils::create_updmap($localtlpdb, $dest); + + 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 "$prg: 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 " +$prg: 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 the tlmgr GUI working. +(INC = @INC) + +See http://tug.org/texlive/distro.html#perltk for more details. +Goodbye. +"; + } + } else { + printf STDERR "$prg: unexpected 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 = ; + 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($localtlpdb->root, + $localtlpdb->platform(), + $localtlpdb->option("sys_bin"), + $localtlpdb->option("sys_man"), + $localtlpdb->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 = $localtlpdb->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 + tlpkg/backups/ + !; + 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 for runtime files with the same name but different contents. +# +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 + =~ /^((czech|slovak)\.sty + |Changes + |README + |cid2code\.txt + |etex\.src + |kinsoku\.tex + |language\.dat + |language\.def + |libertine\.sty + |m-tex4ht\.tex + |metatex\.tex + |ps2mfbas\.mf + |pstricks\.con + |sample\.bib + |tex4ht\.env + |texutil\.rb + |tlmgrgui\.pl + )$/x; + # + my @copies = grep (/\/$f$/, @runtime_files); + # map files can be duplicated between (but not within) formats. + 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|Kanji)?Map\s+(.*)$/) { + my $foo = $2; + chomp($foo); + if ($foo !~ m/\@kanjiEmbed@/) { + 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) { + tlwarn("$prg: mentioned map file not present 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] [...] + +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 $localtlpdb; + $localtlpdb = TeXLive::TLPDB->new ( root => $Master ); + die("cannot setup 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", $localtlpdb->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 + # - a url starting with neither 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 $remotetlpdb object, or die. +# uses the global $location. +# +sub init_tlmedia_or_die { + my ($ret, $err) = init_tlmedia(); + if (!$ret) { + tldie("$prg: $err\n"); + } +} + +sub init_tlmedia +{ + # first check if $location contains multiple locations + # in this case we go to virtual mode + #my %repos = repository_to_array($localtlpdb->option("location")); + my %repos = repository_to_array($location); + my @tags = keys %repos; + # if we have only one repo, but this one contains a name tag #.... + # then we remove it and save the local tlpdb + if ($#tags == 0 && ($location =~ m/#/)) { + $location = $repos{$tags[0]}; + $localtlpdb->option("location", $location); + $localtlpdb->save; + %repos = repository_to_array($location); + } + # check if we are only one tag/repo + if ($#tags == 0) { + # go to normal mode + return _init_tlmedia(); + } + # we are still here, so we have more tags + + # check that there is a main repository + if (!TeXLive::TLUtils::member('main', @tags)) { + return(0, "Cannot find main repository, you have to tag one as main!"); + } + + # TODO TODO + # - abstract the set up of a single media tlpdb + # - make clear how to check for a already loaded remotetlpdb + $remotetlpdb = TeXLive::TLPDB->new(); + $remotetlpdb->make_virtual; + + my $locstr = $repos{'main'}; + my ($tlmdb, $errormsg) = setup_one_remotetlpdb($locstr); + if (!defined($tlmdb)) { + return (0, $errormsg); + } + $remotetlpdb->virtual_add_tlpdb($tlmdb, "main"); + for my $t (@tags) { + if ($t ne 'main') { + my ($tlmdb, $errormsg) = setup_one_remotetlpdb($repos{$t}); + if (!defined($tlmdb)) { + return(0, $errormsg); + } + $remotetlpdb->virtual_add_tlpdb($tlmdb, $t); + $locstr .= " $repos{$t}"; + } + } + + # now check/setup pinning + if (!$opts{"pin-file"}) { + # check for pinning file in TEXMFLOCAL/tlpkg/pinning.txt + chomp (my $TEXMFLOCAL = `kpsewhich -var-value=TEXMFLOCAL`); + debug("trying to load pinning file $TEXMFLOCAL/tlpkg/pinning.txt\n"); + # since we use TLConfFile it does not matter if the file + # is not existing, it will be treated properly in TLConfFile + $opts{"pin-file"} = "$TEXMFLOCAL/tlpkg/pinning.txt"; + } + $pinfile = TeXLive::TLConfFile->new($opts{"pin-file"}, "#", ":", 'multiple'); + $remotetlpdb->virtual_pinning($pinfile); + # this "location-url" line should not be changed since GUI programs + # depend on it: + print "location-url\t$locstr\n" if $::machinereadable; + info("tlmgr: package repositories:\n"); + info("\tmain = " . $repos{'main'} . "\n"); + for my $t (@tags) { + if ($t ne 'main') { + info("\t$t = " . $repos{$t} . "\n"); + } + } + return 1; +} + + + + +sub _init_tlmedia +{ + + # if we are already initialized to the same location, nothing + # needs to be done. + # if we are initialized to a virtual tlpdb, then we have to + # do in any case an initialization + if (defined($remotetlpdb) && !$remotetlpdb->is_virtual && + ($remotetlpdb->root eq $location)) { + # nothing to be done + return 1; + } + + # 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,; + } + + my $errormsg; + ($remotetlpdb, $errormsg) = setup_one_remotetlpdb($location); + if (!defined($remotetlpdb)) { + return(0, $errormsg); + } + + + # 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"); + return 1; +} + +sub setup_one_remotetlpdb +{ + my $location = shift; + my $remotetlpdb; + + # TODO + # check if that is already loaded!!! + + # 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,; + } + + # 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(<new(root => $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"); + $remotetlpdb = TeXLive::TLPDB->new(root => $location, + tlpdbfile => $loc_copy_of_remote_tlpdb); + $local_copy_tlpdb_used = 1; + } + } + } + } + if (!$local_copy_tlpdb_used) { + $remotetlpdb = TeXLive::TLPDB->new(root => $location); + } + if (!defined($remotetlpdb)) { + return(undef, $loadmediasrcerror . $location); + } + # we allow a range of years to be specified by the remote tlpdb + # for which it might work. + # the lower limit is TLPDB->config_minrelease + # the upper limit is TLPDB->config_release + # 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 + # minrelease/2009-foobar + # release/2010-foobar + my $texlive_release = $remotetlpdb->config_release; + my $texlive_minrelease = $remotetlpdb->config_minrelease; + if (!defined($texlive_release)) { + return(undef, "The installation repository does not specify a " + . "release year for which it was prepared, goodbye."); + } + # 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]$/) { + return(undef, "The installation repository does not specify a " + . "valid release year, goodbye: $texlive_release"); + } + # so $texlive_release_year is numeric, good + if (defined($texlive_minrelease)) { + # we specify a range of years! + my $texlive_minrelease_year = $texlive_minrelease; + $texlive_minrelease_year =~ s/^(....).*$/$1/; + if ($texlive_minrelease_year !~ m/^[1-9][0-9][0-9][0-9]$/) { + return(undef, "The installation repository does not specify a " + . "valid minimal release year, goodbye: $texlive_minrelease"); + } + # ok, all numeric and fine, check for range + if ($TeXLive::TLConfig::ReleaseYear < $texlive_minrelease_year + || $TeXLive::TLConfig::ReleaseYear > $texlive_release_year) { + return (undef, "The TeX Live versions supported by the repository + ($texlive_minrelease_year--$texlive_release_year) +do not include the version of the local installation + ($TeXLive::TLConfig::ReleaseYear)."); + } + } else { + # $texlive_minrelease not defined, so only one year is valid + if ($texlive_release_year != $TeXLive::TLConfig::ReleaseYear) { + return(undef, "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?)."); + } + } + + # check for being frozen + if ($remotetlpdb->option("frozen")) { + my $frozen_msg = <: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"); + $remotetlpdb->writeout($tlfh); + close($tlfh); + } + } + + return($remotetlpdb); +} + + + +# 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"; + ; + } + + 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; + # + # by default we remove packages + $config{"auto-remove"} = 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; + } elsif ($key eq "auto-remove") { + if ($val eq "0") { + $config{"auto-remove"} = 0; + } elsif ($val eq "1") { + $config{"auto-remove"} = 1; + } else { + tlwarn("Unknown value >$val< for auto-remove in $fn\n"); + } + } 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("=" x 79, "\n"); + tlwarn("Updates for tlmgr itself are present.\n"); + tlwarn("So, 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