diff options
author | Norbert Preining <preining@logic.at> | 2009-09-15 09:40:04 +0000 |
---|---|---|
committer | Norbert Preining <preining@logic.at> | 2009-09-15 09:40:04 +0000 |
commit | 60d44731afa4954a7324f3bbe3e111598874afd2 (patch) | |
tree | b44918fd5babb878b576cdf2a688af6542fcf7e8 /Master/tlpkg/dev | |
parent | 568f2a84fedd5654cc87f3408d72bd32b4363df3 (diff) |
move howto-translations to doc, and rename etc to dev in tlpkg
git-svn-id: svn://tug.org/texlive/trunk@15293 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/tlpkg/dev')
-rw-r--r-- | Master/tlpkg/dev/dev.multi-source-support.patch | 722 | ||||
-rw-r--r-- | Master/tlpkg/dev/dev.tempfile-for-tlpdb.patch | 39 | ||||
-rw-r--r-- | Master/tlpkg/dev/dev.usermode-tlmgr.patch | 436 | ||||
-rwxr-xr-x | Master/tlpkg/dev/makeself-runscript.sh | 163 | ||||
-rwxr-xr-x | Master/tlpkg/dev/mktextex.pl | 73 | ||||
-rwxr-xr-x | Master/tlpkg/dev/mktexupd.texlua | 124 | ||||
-rw-r--r-- | Master/tlpkg/dev/test-code-for-papersize-in-perl.pl | 528 | ||||
-rw-r--r-- | Master/tlpkg/dev/tlmgr1.pl-with-recreate-tlpdb | 3038 | ||||
-rw-r--r-- | Master/tlpkg/dev/tlnet-disabled-packages.txt | 2 | ||||
-rw-r--r-- | Master/tlpkg/dev/tlrepo-from-minimals.pl | 322 |
10 files changed, 5447 insertions, 0 deletions
diff --git a/Master/tlpkg/dev/dev.multi-source-support.patch b/Master/tlpkg/dev/dev.multi-source-support.patch new file mode 100644 index 00000000000..9e96668d938 --- /dev/null +++ b/Master/tlpkg/dev/dev.multi-source-support.patch @@ -0,0 +1,722 @@ +--- /src/TeX/texlive-svn/Master/tlpkg/TeXLive/TLPDB.pm 2008-09-06 19:11:52.000000000 +0200 ++++ tlpkg/TeXLive/TLPDB.pm 2008-09-09 15:11:23.000000000 +0200 +@@ -819,26 +819,25 @@ + =cut + + sub _set_option_value { +- my ($self,$key,$value) = @_; ++ my ($self,$key,@values) = @_; + my $pkg = $self->{'tlps'}{'00texlive-installation.config'}; + my @newdeps; + if (!defined($pkg)) { + $pkg = new TeXLive::TLPOBJ; + $pkg->name("00texlive-installation.config"); + $pkg->category("TLCore"); +- push @newdeps, "$key:$value"; ++ for my $v (@values) { ++ push @newdeps, "$key:$v"; ++ } + } else { + my $found = 0; + foreach my $d ($pkg->depends) { +- if ($d =~ m!^$key:!) { +- $found = 1; +- push @newdeps, "$key:$value"; +- } else { ++ if ($d !~ m!^$key:!) { + push @newdeps, $d; + } + } +- if (!$found) { +- push @newdeps, "$key:$value"; ++ for my $v (@values) { ++ push @newdeps, "$key:$v"; + } + } + $pkg->depends(@newdeps); +@@ -847,13 +846,14 @@ + + sub _option_value { + my ($self,$key) = @_; ++ my @l = (); + if (defined($self->{'tlps'}{'00texlive-installation.config'})) { + foreach my $d ($self->{'tlps'}{'00texlive-installation.config'}->depends) { + if ($d =~ m!^$key:(.*)$!) { +- return "$1"; ++ push @l, "$1"; + } + } +- return ""; ++ return(wantarray ? @l : (@l ? $l[0] : "") ); + } + return; + } +@@ -889,15 +889,74 @@ + if (@_) { $self->_set_option_value("opt_paper", shift); } + return $self->_option_value("opt_paper"); + } +-sub option_location { +- my $self = shift; +- if (@_) { $self->_set_option_value("location", shift); } +- my $loc = $self->_option_value("location"); +- if ($loc eq "__MASTER__") { +- return $self->root; ++# ++=pod ++ ++=item C<< $tlpdb->option_locations >> ++ ++Returns a list of tag and locations, so can be assigned to a hash. ++ ++=cut ++ ++sub option_locations { ++ my $self = shift; ++ if (@_) { ++ my %foo = @_; ++ my @l = (); ++ for my $k (keys %foo) { ++ if ($k eq $foo{$k}) { ++ # no specific tag ++ push @l, "$k"; ++ } else { ++ push @l, "$foo{$k} $k"; ++ } ++ } ++ $self->_set_option_value("location", @l); + } +- return $self->_option_value("location"); ++ my @locs = $self->_option_value("location"); ++ # the locations might contain a tag, the lines are of format ++ # location tag ++ my @ret = (); ++ for my $l (@locs) { ++ my $loc; ++ my $tag; ++ if ($l =~ m/^([^ ]+)[[:space:]]+(.*)[[:space:]]*$/) { ++ $loc = "$1"; ++ $tag = "$2"; ++ debug("reading locations, found loc=$loc, tag=$tag\n"); ++ } else { ++ $loc = $tag = $l; ++ } ++ # we return a hash as list ... ++ if ($loc eq "__MASTER__") { ++ push @ret, $self->root, $self->root; ++ } else { ++ push @ret, $tag, $loc; ++ } ++ } ++ return(@ret); ++} ++ ++sub add_locations { ++ my ($self, %locs) = @_; ++ my %foo = $self->option_locations; ++ for my $l (keys %locs) { ++ $foo{$l} = $locs{$l}; ++ } ++ $self->option_locations(%foo); ++ return(%foo); ++} ++ ++sub remove_locations { ++ my ($self, %locs) = @_; ++ my %foo = $self->option_locations; ++ for my $l (keys %locs) { ++ delete $foo{$l} ++ } ++ $self->option_locations(%foo); ++ return(%foo); + } ++ + sub option_sys_bin { + my $self = shift; + if (@_) { $self->_set_option_value("opt_sys_bin", shift); } +--- /src/TeX/texlive-svn/Master/texmf/scripts/texlive/tlmgr.pl 2008-09-08 17:19:03.000000000 +0200 ++++ texmf/scripts/texlive/tlmgr.pl 2008-09-10 15:54:25.000000000 +0200 +@@ -48,15 +48,19 @@ + + # used variables + # the TLMedia from which we install/update +-my $tlmediasrc; ++my %tlmediasrc; + # the tlpdb of the install media +-my $tlpdb; ++my %tlmediatlpdb; ++# flag indicating that the tlmedia has been loaded ++my $tlmedia_loaded = 0; ++# hash with *all* installable packages as keys and source as value ++my %media_packages; + # the local tlpdb + my $localtlpdb; + # a hash for saving the options saved into the local tlpdb + my %options; + # the location from where we install +-my $location; ++my %locations; + + # option handling + my $opt_location; +@@ -133,6 +137,8 @@ + merge_into(\%ret, action_install()); + } elsif ($action =~ m/^update$/i) { + merge_into(\%ret, action_update()); ++} elsif ($action =~ m/^candidates$/i) { ++ action_candidates(); + } elsif ($action =~ m/^backup$/i) { + merge_into(\%ret, action_backup()); + } elsif ($action =~ m/^restore$/i) { +@@ -414,15 +420,13 @@ + } + init_local_db(); + foreach my $pkg (@ARGV) { +- my $tlmediatlpdb; + my $tlp = $localtlpdb->get_package($pkg); + my $installed = 0; + if (!$tlp) { +- if (!$tlmediatlpdb) { +- init_tlmedia(); +- $tlmediatlpdb = $tlmediasrc->tlpdb; ++ init_tlmedia(); ++ if (defined($media_packages{$pkg})) { ++ $tlp = $tlmediatlpdb{$media_packages{$pkg}}->get_package($pkg); + } +- $tlp = $tlmediatlpdb->get_package($pkg); + } else { + $installed = 1; + } +@@ -432,6 +436,7 @@ + print "ShortDesc: ", $tlp->shortdesc, "\n" if ($tlp->shortdesc); + print "LongDesc: ", $tlp->longdesc, "\n" if ($tlp->longdesc); + print "Installed: ", ($installed?"Yes\n":"No\n"); ++ print "Source: ", $media_packages{$pkg}, "\n" if (!$installed); + print "\n"; + } else { + printf STDERR "Cannot find $pkg\n"; +@@ -448,17 +453,22 @@ + "file" => \$opt_file) or pod2usage(2); + my $r = shift @ARGV; + my $ret = ""; +- my $tlpdb; ++ my @tlpdbs; ++ my %packs; + init_local_db(); + if ($opt_global) { + init_tlmedia(); +- $tlpdb = $tlmediasrc->tlpdb; ++ for my $p (keys %media_packages) { ++ $packs{$p} = $tlmediatlpdb{$media_packages{$p}}; ++ } + } else { +- $tlpdb = $localtlpdb; ++ for my $p ($localtlpdb->list_packages) { ++ $packs{$p} = $localtlpdb; ++ } + } +- foreach my $pkg ($tlpdb->list_packages) { ++ for my $pkg (keys %packs) { + if ($opt_file) { +- my @ret = grep(m;$r;, $tlpdb->get_package($pkg)->all_files); ++ my @ret = grep(m;$r;, $packs{$pkg}->get_package($pkg)->all_files); + if (@ret) { + print "$pkg:\n"; + foreach (@ret) { +@@ -467,9 +477,9 @@ + } + } else { + next if ($pkg =~ m/\./); +- my $t = $tlpdb->get_package($pkg)->shortdesc; ++ my $t = $packs{$pkg}->get_package($pkg)->shortdesc; + $t |= ""; +- my $lt = $tlpdb->get_package($pkg)->longdesc; ++ my $lt = $packs{$pkg}->get_package($pkg)->longdesc; + $lt |= ""; + if (($pkg =~ m/$r/) || ($t =~ m/$r/) || ($lt =~ m/$r/)) { + $ret .= " $pkg - $t\n"; +@@ -614,7 +624,6 @@ + "dry-run" => \$opt_dry) or pod2usage(2); + my %ret; + init_tlmedia(); +- my $mediatlpdb = $tlmediasrc->tlpdb; + my @todo; + if ($opt_all || $opt_list) { + @todo = $localtlpdb->list_packages; +@@ -625,19 +634,22 @@ + # we check for new packages + # loop over all installed collections + for my $c ($localtlpdb->collections) { +- my $tlc = $mediatlpdb->get_package($c); +- if (!defined($tlc)) { ++ my ($maxsrc, $maxrev) = install_candidate($c); ++ if (!defined($maxsrc)) { + debug("collection $c has disappeared from the network!\n"); +- } else { +- # take all the dependencies of the installed collection +- # *as*found* in the network tlpdb +- for my $d ($tlc->depends) { +- my $tlp = $localtlpdb->get_package($d); +- if (!defined($tlp)) { +- # there is a dep that is either new or has been forcibly removed +- # report it as new/forcibly removed +- info("package $d ($c) is new (or has been forcibly removed)\n"); +- } ++ next; ++ } ++ my $tlc = $tlmediatlpdb{$maxsrc}->get_package($c); ++ if (!defined($tlc)) { ++ debug("That should not happen!!!\n"); ++ next; ++ } ++ for my $d ($tlc->depends) { ++ my $tlp = $localtlpdb->get_package($d); ++ if (!defined($tlp)) { ++ # there is a dep that is either new or has been forcibly removed ++ # report it as new/forcibly removed ++ info("package $d ($c) is new (or has been forcibly removed)\n"); + } + } + } +@@ -655,18 +667,22 @@ + next; + } + my $rev = $tlp->revision; +- my $mediatlp = $mediatlpdb->get_package($pkg); +- if (!defined($mediatlp)) { +- debug("$pkg cannot be found in $location\n"); ++ my ($maxsrc, $maxrev) = install_candidate($pkg); ++ if (!defined($maxsrc)) { ++ debug("$pkg cannot be found in any of " . values(%locations). "\n"); + next; + } +- my $mediarev = $mediatlp->revision; +- if ($rev < $mediarev) { ++ # if $maxsrc is defined then there is an installation candidate with ++ # an higher revision number ++ if ($rev < $maxrev) { ++ my $tlmediasrc = $tlmediasrc{$maxsrc}; ++ my $tlmediatlpdb = $tlmediatlpdb{$maxsrc}; ++ my $mediarev = $maxrev; + $nrupdated++; + if ($opt_list) { +- print "$pkg: local: $rev, source: $mediarev\n"; ++ print "$pkg: local: $rev, source: $mediarev ($maxsrc)\n"; + } elsif ($opt_dry) { +- print "Installing $pkg\n"; ++ print "Installing $pkg ($maxsrc)\n"; + } else { + # first remove the package, then reinstall it + # this way we get rid of useless files +@@ -706,7 +722,7 @@ + # these packages cannot be upgrade on win32 + # so we have to create a update program + my $media = $tlmediasrc->media; +- my $remoteroot = $mediatlpdb->root; ++ my $remoteroot = $tlmediatlpdb->root; + my $root = $localtlpdb->root; + my $temp = "$root/temp"; + TeXLive::TLUtils::mkdirhier($temp); +@@ -740,8 +756,8 @@ + print "update: $pkg done\n"; + } + } +- } elsif ($rev > $mediarev) { +- print "$pkg: revision in $location is less then local revision, not updating!\n"; ++ } else { ++ debug("$pkg: no installation candidate with higher rev found!\n"); + next; + } + } +@@ -761,17 +777,50 @@ + ); + tlwarn("UPDATER has been created, please execute tlpkg\\installer\\updater.bat\n"); + } +- if (($nrupdated == 0) && ($tlmediasrc->media ne "NET") && $opt_all) { +- # for all but net updates we warn if nothing is updated +- tlwarn("\nYour installation is set up to look on the disk for updates.\n"); +- tlwarn("If you want to install from the Internet for this one time only, run\n"); +- tlwarn(" tlmgr -location $TeXLiveURL\n"); +- tlwarn("\nIf you want to change the default for all future updates, run\n"); +- tlwarn(" tlmgr option location $TeXLiveURL\n\n"); ++ if (($nrupdated == 0) && $opt_all) { ++ my $any_net = 0; ++ for my $tm (values(%tlmediasrc)) { ++ if ($tm->media eq "NET") { ++ $any_net = 1; ++ last; ++ } ++ } ++ if (!$any_net) { ++ # for all but net updates we warn if nothing is updated ++ tlwarn("\nTEXT OUTDATED NEEDS REMARK FOR MULTIPLE LOCATIONS!!!\n"); ++ tlwarn("\nYour installation is set up to look on the disk for updates.\n"); ++ tlwarn("If you want to install from the Internet for this one time only, run\n"); ++ tlwarn(" tlmgr -location $TeXLiveURL\n"); ++ tlwarn("\nIf you want to change the default for all future updates, run\n"); ++ tlwarn(" tlmgr option location $TeXLiveURL\n\n"); ++ } + } + return(\%ret); + } + ++sub action_candidates { ++ init_local_db(1); ++ init_tlmedia(); ++ foreach my $pkg (@ARGV) { ++ my @l = (); ++ for my $s (keys %tlmediasrc) { ++ my $tlp = $tlmediatlpdb{$s}->get_package($pkg); ++ if ($tlp) { ++ push @l, "$s (" . $tlp->revision . ")"; ++ } ++ } ++ if (@l) { ++ print "$pkg:\n"; ++ for my $f (@l) { ++ print "\t$f\n"; ++ } ++ } else { ++ print "No installation candidate for $pkg\ņ"; ++ } ++ } ++} ++ ++ + sub action_install { + if ($opt_gui) { + action_gui("install"); +@@ -786,11 +835,38 @@ + my %ret; + init_tlmedia(); + foreach my $pkg (@ARGV) { ++ my $package = $pkg; ++ my $tag; ++ my $maxsrc; ++ if ($pkg =~ m!^([^/]+)/(.*)$!) { ++ $package = "$1"; ++ $tag = "$2"; ++ if (defined($tlmediatlpdb{$tag})) { ++ if (defined($tlmediatlpdb{$tag}->get_package($package))) { ++ # we can actually install from there ++ $maxsrc = $tag; ++ } else { ++ tlwarn("Package $package not found in source $tag\n"); ++ tlwarn("Skipping installation of $pkg\n"); ++ next; ++ } ++ } else { ++ tlwarn("The tag $tag for installing $package is not defined!\n"); ++ tlwarn("Skipping installation of $pkg\n"); ++ next; ++ } ++ } else { ++ ($maxsrc) = install_candidate($pkg); ++ if (!defined($maxsrc)) { ++ tlwarn("No installation candidate for $pkg found!\n"); ++ } ++ } ++ + if ($opt_dry) { +- print "install: $pkg\n"; ++ print "install: $package (from $maxsrc)\n"; + } else { + # install the packages and run postinstall actions (that is the 0) +- merge_into(\%ret, $tlmediasrc->install_package($pkg, $localtlpdb, $opt_nodepends, 0)); ++ merge_into(\%ret, $tlmediasrc{$maxsrc}->install_package($package, $localtlpdb, $opt_nodepends, 0)); + } + } + if ($opt_dry) { +@@ -805,13 +881,17 @@ + my $what = shift @ARGV; + $what || ($what = ""); + init_tlmedia(); +- my @whattolist; ++ my @whattolist = (); + if ($what =~ m/^collection/i) { +- @whattolist = $tlmediasrc->tlpdb->collections; ++ for my $s (values %tlmediatlpdb) { ++ push @whattolist, $s->collections; ++ } + } elsif ($what =~ m/^scheme/i) { +- @whattolist = $tlmediasrc->tlpdb->schemes; ++ for my $s (values %tlmediatlpdb) { ++ push @whattolist, $s->schemes; ++ } + } else { +- @whattolist = $tlmediasrc->tlpdb->list_packages; ++ @whattolist = keys(%media_packages); + } + foreach (@whattolist) { + if (defined($localtlpdb->get_package($_))) { +@@ -819,7 +899,7 @@ + } else { + print " "; + } +- my $foo = $tlmediasrc->tlpdb->get_package($_)->shortdesc; ++ my $foo = $tlmediatlpdb{$media_packages{$_}}->get_package($_)->shortdesc; + print "$_: ", defined($foo) ? $foo : "(shortdesc missing)" , "\n"; + } + return; +@@ -833,17 +913,68 @@ + $what = "show" unless defined($what); + init_local_db(); + if ($what =~ m/^location$/i) { +- # changes the default location +- my $loc = shift @ARGV; +- if ($loc) { +- # normalize the path +- $loc = abs_path($loc); +- print "Setting default installation source to $loc!\n"; +- $localtlpdb->option_location($loc); +- $localtlpdb->save; ++ # the location argument is treated as follows ++ # location loc1 loc2 loc3 ++ # location loc1,loc2,loc3 ++ # sets the list of locations to these three ++ # location add loc1 [loc2 ...] ++ # add the location loc to the list ++ # location remove loc1 [loc2 ...] ++ # remove the location from the list ++ my $first = shift @ARGV; ++ $first || ($first = "show"); ++ my $mode = "replace"; ++ my @locargs = (); ++ if ($first =~ m/^add$/i) { $mode = "add"; } ++ elsif ($first =~ m/^remove$/i) { $mode = "remove"; } ++ else { push @locargs, $first unless ($first eq "show"); } ++ push @locargs, @ARGV; ++ my @locs; ++ for my $a (@locargs) { ++ push @locs, split(/,/,$a); ++ } ++ my %locs; ++ for my $l (@locs) { ++ if ($l =~ m;^(.+)=(.+)$;) { ++ $locs{$2} = "$1"; ++ } else { ++ $locs{$l} = $l; ++ } ++ } ++ if ($mode eq "add") { ++ if (@locs) { ++ print "Adding @locs to the list of installation sources!\n"; ++ $localtlpdb->add_locations(%locs); ++ } else { ++ tlwarn("Need some argument for location add!\n"); ++ exit(1); ++ } ++ } elsif ($mode eq "remove") { ++ if (@locs) { ++ print "Removing @locs from the list of installation sources!\n"; ++ $localtlpdb->remove_locations(%locs); ++ } else { ++ tlwarn("Need some argument for location remove!\n"); ++ exit(1); ++ } + } else { +- print "Default installation source: ", $localtlpdb->option_location, "\n"; ++ if (@locs) { ++ print "Settings @locs as list of installation sources!\n"; ++ $localtlpdb->option_locations(%locs); ++ } else { ++ my %lo = $localtlpdb->option_locations; ++ for my $t (keys %lo) { ++ print "Installation source: "; ++ if ($t eq $lo{$t}) { ++ # no tag specified ++ print "$t\n"; ++ } else { ++ print "$lo{$t} (tag $t)\n"; ++ } ++ } ++ } + } ++ $localtlpdb->save; + } elsif ($what =~ m/^docfiles$/i) { + # changes the default docfiles + my $loc = shift @ARGV; +@@ -903,10 +1034,16 @@ + # list the available architectures + # initialize the TLMedia from $location + init_tlmedia(); +- my $mediatlpdb = $tlmediasrc->tlpdb; ++ # collect all available architectures from all media sources ++ my %avail_archs; ++ for my $tlpdb (values %tlmediatlpdb) { ++ for my $a ($tlpdb->available_architectures) { ++ $avail_archs{$a} = 1; ++ } ++ } + my @already_installed_arch = $localtlpdb->available_architectures; + print "Available architectures:\n"; +- foreach my $a ($mediatlpdb->available_architectures) { ++ foreach my $a (keys %avail_archs) { + if (member($a,@already_installed_arch)) { + print "(i) $a\n"; + } else { +@@ -918,9 +1055,15 @@ + exit(0); + } elsif ($what =~ m/^add$/i) { + init_tlmedia(); +- my $mediatlpdb = $tlmediasrc->tlpdb; ++ # collect all available architectures from all media sources ++ my %avail_archs; ++ for my $tlpdb (values %tlmediatlpdb) { ++ for my $a ($tlpdb->available_architectures) { ++ $avail_archs{$a} = 1; ++ } ++ } + my @already_installed_arch = $localtlpdb->available_architectures; +- my @available_arch = $mediatlpdb->available_architectures; ++ my @available_arch = keys %avail_archs; + my @todoarchs; + foreach my $a (@ARGV) { + if (TeXLive::TLUtils::member($a, @already_installed_arch)) { +@@ -943,7 +1086,13 @@ + if ($opt_dry) { + print "Installing $pkg.$a\n"; + } else { +- merge_into(\%ret, $tlmediasrc->install_package("$pkg.$a", $localtlpdb, 0, 0)); ++ my ($maxsrc) = install_candidate("$pkg.$a"); ++ if (defined($maxsrc)) { ++ debug("pkg = $pkg, maxsrc = $maxsrc\n"); ++ merge_into(\%ret, $tlmediasrc{$maxsrc}->install_package("$pkg.$a", $localtlpdb, 0, 0)); ++ } else { ++ tlwarn("No installation candidate for $pkg on $a found!\n"); ++ } + } + } + } +@@ -951,9 +1100,14 @@ + } + if (TeXLive::TLUtils::member('win32', @todoarchs)) { + # install the necessary win32 stuff +- merge_into (\%ret, $tlmediasrc->install_package("bin-tlperl.win32", $localtlpdb, 1, 0)); +- merge_into (\%ret, $tlmediasrc->install_package("bin-tlgs.win32", $localtlpdb, 1, 0)); +- merge_into (\%ret, $tlmediasrc->install_package("bin-tlpsv.win32", $localtlpdb, 1, 0)); ++ for my $p (qw/bin-tlperl bin-tlgs bin-tlpsv/) { ++ my ($maxsrc) = install_candidate("bin-tlperl.win32"); ++ if (defined($maxsrc)) { ++ merge_into (\%ret, $tlmediasrc{$maxsrc}->install_package("$p.win32", $localtlpdb, 1, 0)); ++ } else { ++ tlwarn("No installation candidate for $p on win32 found!\n"); ++ } ++ } + } + # update the option_archs list of installed archs + my @larchs = $localtlpdb->option_available_architectures; +@@ -1026,18 +1180,30 @@ + } + } + # let cmd line options override the settings in localtlpdb +- my $loc = $localtlpdb->option_location; +- if (defined($loc)) { +- $location = $loc; +- } ++ %locations = $localtlpdb->option_locations; + if (defined($opt_location)) { +- $location = $opt_location; ++ %locations = (); ++ for my $c (split /,/, $opt_location) { ++ $locations{$c} = $c; ++ } + } +- if (!defined($location)) { ++ if (! keys(%locations)) { + die("No installation source found, nor in the texlive.tlpdb nor on the cmd line.\nPlease specify one!"); + } +- # normalize the location +- $location = abs_path($location); ++ # normalize all paths ++ for my $c (keys %locations) { ++ if ($c eq $locations{$c}) { ++ my $newc = abs_path($c); ++ if ($c ne $newc) { ++ delete $locations{$c}; ++ $locations{$newc} = $newc; ++ } ++ } else { ++ my $foo = $locations{$c}; ++ $foo = abs_path($foo); ++ $locations{$c} = $foo; ++ } ++ } + } + + sub action_gui { +@@ -1209,18 +1375,66 @@ + # initialize a tlmedia object and returns it, or dies + # + sub init_tlmedia { +- if (($location =~ m/$TeXLiveServerURL/) || +- ($location =~ m/^ctan$/i)) { +- $location = give_ctan_mirror(); +- info("Using mirror $location\n"); +- } +- # $tlmediasrc is a global function +- $tlmediasrc = TeXLive::TLMedia->new($location); +- die($loadmediasrcerror . $location) unless defined($tlmediasrc); ++ return if $tlmedia_loaded; ++ my $found = 0; ++ for my $t (keys %locations) { ++ my $l = $locations{$t}; ++ my $location = $l; ++ if (($location =~ m/$TeXLiveServerURL/) || ++ ($location =~ m/^ctan$/i)) { ++ $location = give_ctan_mirror(); ++ info("Using mirror $location\n"); ++ } ++ # $tlmediasrc is a global function ++ $tlmediasrc{$t} = TeXLive::TLMedia->new($location); ++ if (defined($tlmediasrc{$t})) { ++ $found++; ++ $tlmediatlpdb{$t} = $tlmediasrc{$t}->tlpdb; ++ } else { ++ info($loadmediasrcerror . $location); ++ } ++ } ++ if (!$found) { ++ # we didn't find any useable installation media, die here! ++ die("Cannot find any useable installation source in one of the following locations:\n", values(%locations), "\nStopping here!"); ++ } ++ # now we initialize the %media_packages ++ my @allpkgs; ++ for my $s (keys %tlmediasrc) { ++ # will create duplicate entries, but who cares, we are doing hashing then ++ push @allpkgs, $tlmediatlpdb{$s}->list_packages; ++ } ++ for my $p (@allpkgs) { ++ next if defined($media_packages{$p}); ++ next if ($p =~ m/00texlive/); ++ ($media_packages{$p}) = install_candidate($p); ++ } ++ $tlmedia_loaded = 1; + } + + +- ++# ++# ++sub install_candidate { ++ my $pkg = shift; ++ my $maxrev = -1; ++ my $maxsrc; ++ for my $s (keys %tlmediasrc) { ++ my $tlp = $tlmediatlpdb{$s}->get_package($pkg); ++ if ($tlp) { ++ my $r = $tlp->revision; ++ if ($maxrev < $r) { ++ $maxrev = $r; ++ $maxsrc = $s; ++ } ++ } ++ } ++ if ($maxsrc) { ++ # something was found ($maxsrc, $maxrev, $maxtlp, $maxtlmedia) ++ return ($maxsrc, $maxrev, $tlmediatlpdb{$maxsrc}->get_package($pkg), $tlmediasrc{$maxsrc}); ++ } ++ return; ++} + + # + # return all the directories from which all content will be removed diff --git a/Master/tlpkg/dev/dev.tempfile-for-tlpdb.patch b/Master/tlpkg/dev/dev.tempfile-for-tlpdb.patch new file mode 100644 index 00000000000..82008f3f3e9 --- /dev/null +++ b/Master/tlpkg/dev/dev.tempfile-for-tlpdb.patch @@ -0,0 +1,39 @@ +Index: TeXLive/TLPDB.pm +=================================================================== +--- TeXLive/TLPDB.pm (revision 10575) ++++ TeXLive/TLPDB.pm (working copy) +@@ -60,6 +60,9 @@ + use TeXLive::TLUtils qw(dirname mkdirhier member win32 info debug ddebug tlwarn); + use TeXLive::TLPOBJ; + ++use File::Temp qw/tempfile/; ++ ++ + my $_listdir; + + =pod +@@ -211,13 +214,20 @@ + # if we have lzmadec available we try the lzma file + if (defined($::progs{'lzmadec'})) { + # we first try the lzma compressed file +- my $tmpdir = TeXLive::TLUtils::get_system_tmpdir(); +- my $bn = TeXLive::TLUtils::basename("$path"); +- my $lzmafile = "$tmpdir/$bn.lzma"; ++ # ++ # we have to create a temp file to download to ++ my ($lzmafh, $lzmafile) = tempfile(); ++ # now $lzmafh filehandle is open, the file created ++ # TLUtils::download_file will just overwrite what is there ++ # on windows that doesn't work, so we close the fh immediately ++ # is this secure??? ++ close($lzmafh); + my $lzmafile_quote = $lzmafile; + # this is a variable of the whole sub as we have to remove the file + # before returning +- $tlpdbfile = "$tmpdir/$bn"; ++ my $tlpdbfh; ++ ($tlpdbfh, $tlpdbfile) = tempfile(); ++ close($tlpdbfh); + my $tlpdbfile_quote = $tlpdbfile; + if (win32()) { + $lzmafile =~ s!/!\\!g; diff --git a/Master/tlpkg/dev/dev.usermode-tlmgr.patch b/Master/tlpkg/dev/dev.usermode-tlmgr.patch new file mode 100644 index 00000000000..bfe0c81c8ee --- /dev/null +++ b/Master/tlpkg/dev/dev.usermode-tlmgr.patch @@ -0,0 +1,436 @@ +Index: tlpkg/TeXLive/TLConfig.pm +=================================================================== +--- tlpkg/TeXLive/TLConfig.pm (revision 14577) ++++ tlpkg/TeXLive/TLConfig.pm (working copy) +@@ -183,7 +183,8 @@ + + our %TLPDBSettings = ( + "platform" => [ "s", "Main platform for this computer" ], +- "available_architectures" => [ "l", "All available/installed architectures" ] ++ "available_architectures" => [ "l", "All available/installed architectures" ], ++ "usertree" => [ "b", "This tree acts as user tree" ] + ); + + our $WindowsMainMenuName = "TeX Live $ReleaseYear"; +Index: tlpkg/TeXLive/TLPOBJ.pm +=================================================================== +--- tlpkg/TeXLive/TLPOBJ.pm (revision 14577) ++++ tlpkg/TeXLive/TLPOBJ.pm (working copy) +@@ -459,6 +459,21 @@ + sub cancel_reloc_prefix { + my $self = shift; + my @docfiles = $self->docfiles; ++ for (@docfiles) { s:^$RelocPrefix/::; } ++ $self->docfiles(@docfiles); ++ my @runfiles = $self->runfiles; ++ for (@runfiles) { s:^$RelocPrefix/::; } ++ $self->runfiles(@runfiles); ++ my @srcfiles = $self->srcfiles; ++ for (@srcfiles) { s:^$RelocPrefix/::; } ++ $self->srcfiles(@srcfiles); ++ # if there are bin files they have definitely NOT the ++ # texmf-dist prefix, so we cannot cancel it anyway ++} ++ ++sub replace_reloc_prefix { ++ my $self = shift; ++ my @docfiles = $self->docfiles; + for (@docfiles) { s:^$RelocPrefix/:$RelocTree/:; } + $self->docfiles(@docfiles); + my @runfiles = $self->runfiles; +Index: tlpkg/TeXLive/TLMedia.pm +=================================================================== +--- tlpkg/TeXLive/TLMedia.pm (revision 14577) ++++ tlpkg/TeXLive/TLMedia.pm (working copy) +@@ -60,6 +60,8 @@ + } + if (-d "$location/texmf/web2c") { + $media = 'DVD'; ++ } elsif (-d "$location/web2c") { ++ $media = 'DVD'; + } elsif (-d "$location/$Archive") { + $media = 'CD'; + } else { +@@ -247,8 +249,12 @@ + } + # if a package is relocatable we have to cancel the reloc prefix + # before we save it to the local tlpdb +- if ($tlpobj->relocated) { +- $tlpobj->cancel_reloc_prefix; ++ if ($reloc) { ++ if ($totlpdb->setting("usertree")) { ++ $tlpobj->cancel_reloc_prefix; ++ } else { ++ $tlpobj->replace_reloc_prefix; ++ } + } + # we have to write out the tlpobj file since it is contained in the + # archives (.tar.xz) but at DVD install time we don't have them +@@ -317,7 +323,11 @@ + } elsif ($what =~ m,\.tar(\.xz)?$,) { + my $type = defined($1) ? "xz" : "tar"; + +- $target .= "/$TeXLive::TLConfig::RelocTree" if $reloc; ++ if ($reloc) { ++ if (!$totlpdb->setting("usertree")) { ++ $target .= "/$TeXLive::TLConfig::RelocTree" if $reloc; ++ } ++ } + + # this is the case when we install from CD or the NET, or a backup + # +@@ -400,6 +410,7 @@ + my ($self, $pkg, $nopostinstall) = @_; + my $localtlpdb = $self->tlpdb; + my $tlp = $localtlpdb->get_package($pkg); ++ my $usertree = $localtlpdb->setting("usertree"); + if (!defined($tlp)) { + tlwarn ("$pkg: package not present, cannot remove\n"); + } else { +@@ -425,9 +436,12 @@ + # in user mode we just remove the prefix, in normal mode we + # replace it with texmf-dist + # since we don't have user mode ++ + if ($tlp->relocated) { + for (@files) { +- s:^$RelocPrefix/:$RelocTree/:; ++ if (!$usertree) { ++ s:^$RelocPrefix/:$RelocTree/:; ++ } + } + } + # +Index: tlpkg/TeXLive/TLUtils.pm +=================================================================== +--- tlpkg/TeXLive/TLUtils.pm (revision 14577) ++++ tlpkg/TeXLive/TLUtils.pm (working copy) +@@ -1198,7 +1198,7 @@ + # if a package is relocatable we have to cancel the reloc prefix + # before we save it to the local tlpdb + if ($tlpobj->relocated) { +- $tlpobj->cancel_reloc_prefix; ++ $tlpobj->replace_reloc_prefix; + } + $totlpdb->add_tlpobj($tlpobj); + # we have to write out the tlpobj file since it is contained in the +Index: texmf/scripts/texlive/tlmgr.pl +=================================================================== +--- texmf/scripts/texlive/tlmgr.pl (revision 14581) ++++ texmf/scripts/texlive/tlmgr.pl (working copy) +@@ -122,6 +122,8 @@ + "machine-readable" => 1, + "package-logfiles" => "=s", + "pause" => 1, ++ "usermode" => 1, ++ "usertree" => "=s", + "version" => 1, + "help|h|?" => 1); + +@@ -285,13 +287,30 @@ + pod2usage(-msg => $msg, -exitstatus => 1, -verbose => 1, @noperldoc); + } + ++ # ++ # the main tree we will be working on ++ $::maintree = $Master; ++ if ($opts{"usermode"}) { ++ # we could also try to detect that we don't have write permissions ++ # and switch to user mode automatically ++ if (defined($opts{"usertree"})) { ++ $::maintree = $opts{"usertree"}; ++ } else { ++ chomp($::maintree = `kpsewhich -var-value TEXMFHOME`); ++ } ++ } ++ + # 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"; ++ if ($opts{"usermode"}) { ++ $packagelogfile ||= "$::maintree/web2c/tlmgr.log"; ++ } else { ++ $packagelogfile ||= "$texmfsysvar/web2c/tlmgr.log"; ++ } + # + # Try to open the packagelog file, but do NOT die when that does not work + if (!open(PACKAGELOG, ">>$packagelogfile")) { +@@ -427,6 +446,9 @@ + } elsif ($action =~ m/^show$/i) { + action_show(); + finish(0); ++ } elsif ($action =~ m/^init-usertree$/i) { ++ action_init_usertree(); ++ finish(0); + } elsif ($action =~ m/^remove$/i) { + action_remove(); + } elsif ($action =~ /^paper$/) { +@@ -480,6 +502,8 @@ + # + sub handle_execute_actions { + ++ my $sysmode = ($opts{"usermode"} ? "" : "-sys"); ++ + my $errors = 0; + + if ($::files_changed) { +@@ -512,14 +536,14 @@ + } + } + for my $m (keys %do_disable) { +- $errors += do_cmd_and_check("updmap-sys --nomkmap --nohash --disable $m"); ++ $errors += do_cmd_and_check("updmap$sysmode --nomkmap --nohash --disable $m"); + } + for my $m (keys %do_enable) { +- my $str = "updmap-sys --nomkmap --nohash --enable " . ++ my $str = "updmap$sysmode --nomkmap --nohash --enable " . + $::execute_actions{'enable'}{'maps'}{$m} . "=$m"; + $errors += do_cmd_and_check($str); + } +- $errors += do_cmd_and_check("updmap-sys") if $run_full; ++ $errors += do_cmd_and_check("updmap$sysmode") if $run_full; + } + + +@@ -561,7 +585,7 @@ + if ($opt_fmt && !$::regenerate_all_formats) { + for my $f (keys %do_enable) { + log ("(re)creating format dump $f\n"); +- $errors += do_cmd_and_check("fmtutil-sys --byfmt $f"); ++ $errors += do_cmd_and_check("fmtutil$sysmode --byfmt $f"); + $done_formats{$f} = 1; + } + +@@ -576,7 +600,7 @@ + for my $e (@upen) { + if ($def{'engine'} eq $e) { + log ("updating format $def{'name'} (engine $e updated)\n"); +- $errors += do_cmd_and_check("fmtutil-sys --byfmt $def{'name'}"); ++ $errors += do_cmd_and_check("fmtutil$sysmode --byfmt $def{'name'}"); + $done_formats{$def{'name'}} = 1; + } + } +@@ -618,7 +642,7 @@ + $lang = "$TEXMFSYSVAR/tex/generic/config/$lang"; + } + if ($localtlpdb->option("create_formats") && !$::regenerate_all_formats) { +- $errors += do_cmd_and_check("fmtutil-sys --byhyphen $lang"); ++ $errors += do_cmd_and_check("fmtutil$sysmode --byhyphen $lang"); + } + } + } +@@ -627,7 +651,7 @@ + # + if ($::regenerate_all_formats) { + info("Regenerating all formats, this may take some time ..."); +- $errors += do_cmd_and_check("fmtutil-sys --all"); ++ $errors += do_cmd_and_check("fmtutil$sysmode --all"); + info("done\n"); + $::regenerate_all_formats = 0; + } +@@ -799,8 +823,13 @@ + action_gui("config") if $opts{"gui"}; + + init_local_db(); +- chomp(my $texmfsysconfig = `kpsewhich -var-value=TEXMFSYSCONFIG`); +- $ENV{"TEXMFCONFIG"} = $texmfsysconfig; ++ my $texmfconfig; ++ if ($opts{"usermode"}) { ++ chomp($texmfconfig = `kpsewhich -var-value=TEXMFCONFIG`); ++ } else { ++ chomp($texmfconfig = `kpsewhich -var-value=TEXMFSYSCONFIG`); ++ } ++ $ENV{"TEXMFCONFIG"} = $texmfconfig; + + my $action = shift @ARGV; + if ($action =~ m/^paper$/i) { # generic paper +@@ -812,14 +841,14 @@ + "as in: tlmgr pdftex paper --list\n"); + + } elsif (!defined($newpaper)) { # tlmgr paper => show all current sizes. +- TeXLive::TLPaper::paper_all($texmfsysconfig,undef); ++ TeXLive::TLPaper::paper_all($texmfconfig,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. +- TeXLive::TLPaper::paper_all($texmfsysconfig,$newpaper); ++ TeXLive::TLPaper::paper_all($texmfconfig,$newpaper); + } + + } else { # program-specific paper +@@ -833,7 +862,7 @@ + # the do_paper progs check for the argument --list, so if given + # restore it to the cmd line. + unshift(@ARGV, "--list") if $opts{"list"}; +- TeXLive::TLPaper::do_paper($prog,$texmfsysconfig,@ARGV); ++ TeXLive::TLPaper::do_paper($prog,$texmfconfig,@ARGV); + } + } + +@@ -874,13 +903,14 @@ + # include arbitrary package in the list of collections, but + # only collectons: + @colls = grep {m;^collection-;} @colls; +- print "Package: ", $tlp->name, "\n"; +- print "Category: ", $tlp->category, "\n"; +- print "ShortDesc: ", $tlp->shortdesc, "\n" if ($tlp->shortdesc); +- print "LongDesc: ", $tlp->longdesc, "\n" if ($tlp->longdesc); +- print "Installed: ", ($installed ? "Yes" : "No"), "\n"; +- print "Revision: ", $tlp->revision, "\n" if ($installed); +- print "Collection: ", @colls, "\n" if (@colls); ++ print "Package: ", $tlp->name, "\n"; ++ print "Category: ", $tlp->category, "\n"; ++ print "ShortDesc: ", $tlp->shortdesc, "\n" if ($tlp->shortdesc); ++ print "LongDesc: ", $tlp->longdesc, "\n" if ($tlp->longdesc); ++ print "Installed: ", ($installed ? "Yes" : "No"), "\n"; ++ print "Revision: ", $tlp->revision, "\n" if ($installed); ++ print "Relocatable: ", ($tlp->relocated ? "Yes" : "No"), "\n"; ++ print "Collection: ", @colls, "\n" if (@colls); + if ($opts{"list"}) { + if ($tlp->category eq "Collection" || $tlp->category eq "Scheme") { + # in the case of collections of schemes we list the deps +@@ -944,6 +974,10 @@ + # SYMLINKS + # + sub action_path { ++ if ($opts{"usermode"}) { ++ tlwarn("action `path' not supported in usermode!\n"); ++ exit 1; ++ } + my $what = shift @ARGV; + if (!defined($what) || ($what !~ m/^(add|remove)$/i)) { + tlwarn("action path needs one argument, either add or remove\n"); +@@ -1424,7 +1458,10 @@ + + # check for updates to tlmgr and die unless either --force or --list or --self + # is given +- my @critical = check_for_critical_updates($localtlpdb, $tlmediatlpdb); ++ my @critical; ++ if (!$opts{"usermode"}) { ++ @critical = check_for_critical_updates($localtlpdb, $tlmediatlpdb); ++ } + my $dry_run_cont = $opts{"dry-run"} && ($opts{"dry-run"} < 0); + if ( !$dry_run_cont && !$opts{"self"} && @critical) { + critical_updates_warning(); +@@ -2060,16 +2097,18 @@ + init_tlmedia(); + + # check for updates to tlmgr itself, and die unless --force is given +- if (check_for_critical_updates( $localtlpdb, $tlmediatlpdb)) { +- critical_updates_warning(); +- if ($opts{"force"}) { +- tlwarn("Continuing due to --force\n"); +- } else { +- if ($::gui_mode) { +- # return here and don't do any updates +- return; ++ if (!$opts{"usermode"}) { ++ if (check_for_critical_updates( $localtlpdb, $tlmediatlpdb)) { ++ critical_updates_warning(); ++ if ($opts{"force"}) { ++ tlwarn("Continuing due to --force\n"); + } else { +- die "tlmgr: Not continuing, please see warning above!\n"; ++ if ($::gui_mode) { ++ # return here and don't do any updates ++ return; ++ } else { ++ die "tlmgr: Not continuing, please see warning above!\n"; ++ } + } + } + } +@@ -2106,6 +2145,15 @@ + } + } + foreach my $pkg (@inst_packs, @inst_colls, @inst_schemes) { ++ my $tlp = $tlmediasrc->tlpdb->get_package($pkg); ++ if (!defined($tlp)) { ++ info("Unknown package $pkg\n"); ++ next; ++ } ++ if (!$tlp->relocated && $opts{"usermode"}) { ++ info("Package $pkg is not relocatable, cannot install it in user mode!\n"); ++ next; ++ } + my $re = ""; + if (defined($localtlpdb->get_package($pkg))) { + if ($opts{"reinstall"}) { +@@ -2321,6 +2369,10 @@ + if ($^O=~/^MSWin(32|64)$/i) { + warn("action `arch' not supported on Windows\n"); + } ++ if ($opts{"usermode"}) { ++ tlwarn("action `arch' not supported in usermode\n"); ++ exit 1; ++ } + if ($opts{"gui"}) { + action_gui("arch"); + } +@@ -2402,6 +2454,10 @@ + # GENERATE + # + sub action_generate { ++ if ($opts{"usermode"}) { ++ tlwarn("action `generate' not supported in usermode!\n"); ++ exit 1; ++ } + my $dest = defined($opts{"dest"}) ? $opts{"dest"} : ""; + my $localconf = defined($opts{"localcfg"}) ? $opts{"localcfg"} : ""; + my $what = shift @ARGV; +@@ -3209,6 +3265,34 @@ + + + # ++# action init_user_tree ++# sets up TEXMFHOME or --usertree argument for user mode tlmgr ++# by creating a minimal tlpdb ++# ++sub action_init_usertree { ++ init_local_db(); ++ my $tlpdb = TeXLive::TLPDB->new; ++ my $usertree; ++ if ($opts{"usertree"}) { ++ $usertree = $opts{"usertree"}; ++ } else { ++ chomp($usertree = `kpsewhich -var-value TEXMFHOME`); ++ } ++ $tlpdb->root($usertree); ++ my $inst = TeXLive::TLPOBJ->new; ++ $inst->name("00texlive.installation"); ++ $inst->category("TLCore"); ++ my @deps; ++ push @deps, "opt_location:$location"; ++ push @deps, "setting_available_architectures:"; ++ push @deps, "setting_usertree:1"; ++ $inst->depends(@deps); ++ $tlpdb->add_tlpobj($inst); ++ $tlpdb->save; ++} ++ ++ ++# + # action conf + # tries to mimick texconfig conf but probably will add some more things + # +@@ -3272,10 +3356,10 @@ + # stuff changed via the GUI + return if defined $localtlmedia; + return if defined $localtlpdb; +- $localtlmedia = TeXLive::TLMedia->new ( $Master ); +- die("cannot setup TLMedia in $Master") unless (defined($localtlmedia)); ++ $localtlmedia = TeXLive::TLMedia->new ( $::maintree ); ++ die("cannot setup TLMedia in $::maintree") unless (defined($localtlmedia)); + $localtlpdb = $localtlmedia->tlpdb; +- die("cannot find tlpdb in $Master") unless (defined($localtlpdb)); ++ die("cannot find tlpdb in $::maintree") unless (defined($localtlpdb)); + # setup the programs, for w32 we need the shipped wget/xz etc, so we + # pass the location of these files to setup_programs. + if (!setup_programs("$Master/tlpkg/installer", $localtlmedia->platform)) { diff --git a/Master/tlpkg/dev/makeself-runscript.sh b/Master/tlpkg/dev/makeself-runscript.sh new file mode 100755 index 00000000000..f1a48e59c5d --- /dev/null +++ b/Master/tlpkg/dev/makeself-runscript.sh @@ -0,0 +1,163 @@ +#!/bin/sh +# Updater for tlmgr and infrastructure on Unix. +# Runs in unpacked archive directory. + +ROOT=`kpsewhich --var-value=SELFAUTOPARENT` + +if test -r "$ROOT/tlpkg/texlive.tlpdb"; then + # nothing to do here + answer=0 +else + answer=x + echo "No installation found, please select from the following options:" + echo "[1] make new installation" + echo "[2] enter path to an existing installation" + echo "[x] exit (default)" + echo -n "Your selection: " + read answer +fi + +case "X$answer" in + X0) NEWINST=0 + ;; + X1) echo -n "Enter destination folder: " + read newpath + if [ -z "$newpath" ] ; then + echo "No destination folder given, exiting." + exit 1 + fi + ROOT="$newpath" + NEWINST=1 + set -e + mkdir -p "$ROOT/tlpkg/tlpobj" + mkdir -p "$ROOT/bin" + ;; + X2) echo -n "Path to an existing installation: " + read oldpath + notgood=1 + if [ -d $oldpath ] ; then + if [ -r "$oldpath/tlpkg/texlive.tlpdb" ] ; then + notgood=0; + fi + fi + if [ $notgood = 1 ] ; then + echo "Cannot find TeX Live root in $oldpath, exiting." + exit 1 + fi + ;; + *) exit 0;; +esac + +# +if [ $NEWINST = 1 ] ; then + echo "$0: installing to $ROOT..." +else + echo "$0: updating in $ROOT..." +fi + +# move the architecture-specific files to the top level. +mv ./master/bin . +mv ./master/tlpkg/installer . +mv ./master/tlpkg/tlpobj . + +# install the architecture-independent files. +(cd master && tar cf - *) | (cd $ROOT && tar xf -) + +# try to get the list of installed architectures by listing the +# directories in $ROOT/bin. +t_objdir=$ROOT/tlpkg/tlpobj # target tlpobj directory +t_instdir=$ROOT/tlpkg/installer # target installer dir + +# ensure these target directories exist. +mkdir -p $t_instdir/lzma +mkdir -p $t_instdir/wget + +# start the list of tlpobjs we will install +tlpobjs="$t_objdir/bin-texlive.tlpobj $t_objdir/texlive.infra.tlpobj" + +if [ $NEWINST = 1 ] ; then + # use config.guess and platform code to get the current platform + archs=`perl installer/platform.pl installer/config.guess` +else + archs=`ls -d $ROOT/bin/*` +fi +echo "archs = $archs" + +cp tlpobj/bin-texlive.tlpobj tlpobj/texlive.infra.tlpobj $t_objdir +for a in $archs; do + if [ $NEWINST = 0 ] ; then + test -d "$a" || continue # skip any cruft files + b=`basename $a` # just the architecture name + else + b=$a + fi + + cp tlpobj/bin-texlive.$b.tlpobj tlpobj/texlive.infra.$b.tlpobj $t_objdir + # add the tlpobjs for this platform t the list. + tlpobjs="$tlpobjs $t_objdir/bin-texlive.$b.tlpobj" + tlpobjs="$tlpobjs $t_objdir/texlive.infra.$b.tlpobj" + + # install the bin dir for this platform. + (cd bin && tar cf - $b) | (cd $ROOT/bin && tar xf -) + + # copy the installer binaries. + cp installer/lzma/lzmadec.$b $t_instdir/lzma/ + cp installer/lzma/lzma.$b $t_instdir/lzma/ + test -r installer/wget/wget.$b \ + && cp installer/wget/wget.$b $t_instdir/wget +done + +# move the architecture-specific files back to the right place +mv bin ./master/ +mv installer ./master/tlpkg/ +mv tlpobj ./master/tlpkg/ + +# +if [ $NEWINST = 1 ] ; then + # if we are installing a new we have to create a minimal tlpdb + echo "name 00texlive-installation.config +category TLCore +depend platform:$archs +depend location:http://mirror.ctan.org/systems/texlive/tlnet/2008 +depend opt_paper:a4 +depend opt_create_formats:0 +depend opt_create_symlinks:0 +depend opt_sys_bin:/usr/local/bin +depend opt_sys_info:/usr/local/info +depend opt_sys_man:/usr/local/man +depend opt_install_docfiles:1 +depend opt_install_srcfiles:1 +depend available_architectures:$archs +" > $ROOT/tlpkg/texlive.tlpdb +fi + +# invoke secret tlmgr action with the tlpobjs we found. +# Hopefully the result will be a clean tlpdb state. +if [ $NEWINST = 1 ] ; then + export PATH="$ROOT/bin/$archs:$PATH" + echo "PATH = $PATH\n"; +fi +tlmgr -v _include_tlpobj $tlpobjs + +if [ $NEWINST = 1 ] ; then + mkdir -p $ROOT/texmf-config/web2c + mkdir -p $ROOT/texmf-var/tex/generic/config + mkdir -p $ROOT/texmf-var/web2c + tlmgr option location /var/www/norbert/tlnet/2008 + tlmgr install bin-kpathsea + tlmgr install hyphen-base + tlmgr install bin-tetex + tlmgr install bin-texconfig + tlmgr generate updmap + tlmgr generate language + tlmgr generate fmtutil + mktexlsr + updmap-sys + fmtutil-sys --all # should not do anything! + # + # should we install collection-basic now??? + # otherwise we don't have pdftex etc etc?!?! + #tlmgr install collection-basic +fi +echo "$0: done." + diff --git a/Master/tlpkg/dev/mktextex.pl b/Master/tlpkg/dev/mktextex.pl new file mode 100755 index 00000000000..1dd107ada9c --- /dev/null +++ b/Master/tlpkg/dev/mktextex.pl @@ -0,0 +1,73 @@ +#!/usr/bin/env perl +# $Id: mktextex.pl 12269 2009-03-01 16:55:53Z trzeciak $ +# +# Copyright 2009 Norbert Preining +# This file is licensed under the GNU General Public License version 2 +# or any later version. +# +# + +my $svnrev = '$Revision: 12269 $'; +my $datrev = '$Date: 2009-03-01 17:55:53 +0100 (Sun, 01 Mar 2009) $'; + +our $Master; + +BEGIN { + $^W = 1; + # make subprograms (including kpsewhich) have the right path: + $mydir = $0; + $mydir =~ s,/[^/]*$,,; + if (!$^O=~/^MSWin(32|64)$/i) { + $ENV{"PATH"} = "$mydir:$ENV{PATH}"; + } + # + chomp($Master = `kpsewhich -var-value=SELFAUTOPARENT`); + # + # make Perl find our packages first: + unshift (@INC, "$Master/tlpkg"); + unshift (@INC, "$Master/texmf/scripts/texlive"); +} + +use Cwd qw/abs_path/; +use strict; + +use TeXLive::TLConfig; +use TeXLive::TLPDB; + +my $localtlpdb = TeXLive::TLPDB->new ("root" => $Master); +die("cannot find tlpdb in $Master") unless (defined($localtlpdb)); +my $instloc = $localtlpdb->option_location; +my $mediatlpdb = TeXLive::TLPDB->new ("root" => $instloc); +die("cannot find tlpdb in $instloc.") unless defined($mediatlpdb); + + +my $fn = shift @ARGV; + +printf STDERR "searching tlpdb fof $fn\n"; + +my @found = $mediatlpdb->find_file($fn); + +if ($#found == 0) { + # we found only one package, so install it + my ($pkg,$file) = split ":", $found[0]; + printf STDERR "installing $pkg for $file\n"; + system("tlmgr install $pkg >&2"); + print "$Master/$file\n"; +} elsif ($#found >= 0) { + # too many packages found: + printf STDERR "too many packages provide $fn:\n"; + for my $f (@found) { + printf STDERR "$f\n"; + } +} else { + printf STDERR "$fn not found in tlpdb\n"; +} + +exit 1; + +### Local Variables: +### perl-indent-level: 2 +### tab-width: 2 +### indent-tabs-mode: nil +### End: +# vim:set tabstop=2 expandtab: # diff --git a/Master/tlpkg/dev/mktexupd.texlua b/Master/tlpkg/dev/mktexupd.texlua new file mode 100755 index 00000000000..13bb58771ad --- /dev/null +++ b/Master/tlpkg/dev/mktexupd.texlua @@ -0,0 +1,124 @@ +#!/usr/bin/env texlua +-- Written in lua by Norbert Preining (2008) based on the shell script by +-- Thomas Esser. +-- Copyright 2008 Norbert Preining +-- License: GPL + + +-- Changelog +-- 0.1 +-- - first initial version + +progname = 'mktexupd (texlua version)'; +version = '0.1'; +usage = 'Usage: ' .. progname .. ' [-h|--help] DIR FILE\ +\ + Update the ls-R file with an entry for FILE in DIR.\ +\ + -h|--help\t\t Show this help\ + -V|--version\t\t Print the version of the program\ +'; + + +function die (str) + io.stderr:write(str) + os.exit(1) +end + +while table.maxn(arg) > 0 and string.match(arg[1],'^%-') do + curr_arg = table.remove(arg,1) + if string.match (curr_arg,'-h') or string.match (curr_arg,'--help') then + print (usage); + os.exit(0); + elseif string.match (curr_arg,'-V') or string.match (curr_arg,'--version') then + print (progname .. ' version: ' .. version ); + os.exit(0); + end +end + +if not arg[2] then + print (usage); + return +end + +-- initialize kpathsea +kpse.set_program_name("mktexupd") + +dir = arg[1] +file = arg[2] +fullfile = dir .. '/' .. file + +if (not lfs.isdir(dir)) then + die (progname .. ': ' .. dir .. ' not a directory.\n') +end +if (not lfs.isfile(fullfile)) then + die (progname .. ': ' .. fullfile .. ' not a file.\n') +end + +-- we have to get the list of all directories where ls-R files are present +-- using TEXMFDBS. In the shell code `kpsewhich -show-path=ls-R was used +-- we do it with kpse.expand_braces("$TEXMFDBS") and then split at : and +-- remove the !! + +texmfdbs = kpse.expand_braces("$TEXMFDBS") +for d in string.gmatch(texmfdbs,"[^:]+") do + d = d:gsub("^!!","") + pel = string.match(dir,d.."/(.*)$") + if pel then + target = d + pathcomponent = pel + end +end +if (not target) then + --[[ no path found, just give up ]] + os.exit(0) +end + +db_file = target..'/ls-R' +db_file_lc = target..'/ls-r' + +if (not(lfs.isfile(db_file))) then + if lfs.isfile(db_file_lc) then + db_file = db_file_lc + end +end + +if (not(lfs.isfile(db_file))) then + print ('Calling mktexlsr for '..target) + os.execute("mktexlsr "..target) + os.exit() +end + +-- Old ls-R files should continue to work. +ls_R_magic = '% ls-R -- filename database for kpathsea; do not change this line.' +old_ls_R_magic='% ls-R -- maintained by MakeTeXls-R; do not change this line.' + + +-- read first line of ls-R file +lsrfh = io.open(db_file) +if (not(lsrfh)) then + die('that should not happen ...\n') +end +firstline = lsrfh:read() +io.close(lsrfh) + +if (firstline ~= ls_R_magic) then + if (firstline ~= old_ls_R_magic) then + die(progname..': '..db_file..' lacks magic string '..ls_R_magic..'\n') + end +end + +-- ok we are that far, try to open the file for writing +lsrfh,erro = io.open(db_file,"a+") +if (not(lsrfh)) then + die(progname..': cannot open '..db_file..' for writing: '..erro..'\n') +end + +-- May as well always put in a new directory entry; presumably cron will +-- come along soon enough and clean things up. +lsrfh:write('./'..pathcomponent..':\n') +lsrfh:write(file..'\n') +io.close(lsrfh) + +os.exit(0) + diff --git a/Master/tlpkg/dev/test-code-for-papersize-in-perl.pl b/Master/tlpkg/dev/test-code-for-papersize-in-perl.pl new file mode 100644 index 00000000000..1c9b6e4ba54 --- /dev/null +++ b/Master/tlpkg/dev/test-code-for-papersize-in-perl.pl @@ -0,0 +1,528 @@ + +# +# TODO TODO TODO +# if no paperconfig line is found in do_simple it adds to the end of the +# file the new line, but this does not work on context because it +# has an \endinput at the end ... needs to be fixed +# +# output is done to stdout, not the right config file +# +$^W = 1; +use strict; + + +my %pdftex_papersize = ( + "letter" => [ '8.5 true in', '11 true in' ], + "a4" => [ '210 true mm', '297 true mm' ], +); + +my %xdvi_papersize; +$xdvi_papersize{'us'}='8.5x11'; +$xdvi_papersize{'letter'}='8.5x11'; +$xdvi_papersize{'usr'}='11x8.5'; +$xdvi_papersize{'legal'}='8.5x14'; +$xdvi_papersize{'ledger'}='17.0x11.0'; +$xdvi_papersize{'tabloid'}= '11.0x17.0'; +$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'; + +my %dvipdfm_papersize = ( 'letter' => 1, 'legal' => 1, 'ledger' => 1, + 'tabloid' => 1, 'a4' => 1, 'a3' => 1 ); + +my $texmfsysvar = `kpsewhich -var-value=TEXMFSYSVAR`; +chomp($texmfsysvar); +my $texmfsysconfig = `kpsewhich -var-value=TEXMFSYSCONFIG`; +chomp($texmfsysconfig); + +# we are doing -sys variants +$ENV{"TEXMFVAR"} = $texmfsysvar; +$ENV{"TEXMFCONFIG"} = $texmfsysconfig; + +my $what = shift; + +if (!$what) { + print "need an argument\n"; + exit 1; +} + +# all subs do_* work the same way +# do_foobar --list +# list all available sizes with the selected first +# do_foobar +# informational string which paper has been selected +# doo_foobar <paper> +# selects the given <paper> + +if ($what eq "dvips") { + do_dvips(@ARGV); +} elsif ($what eq "dvipdfm") { + do_dvipdfm(@ARGV); +} elsif ($what eq "dvipdfmx") { + do_dvipdfmx(@ARGV); +} elsif ($what eq "context") { + do_context(@ARGV); +} elsif ($what eq "pdftex") { + do_pdftex(@ARGV); +} elsif ($what eq "xdvi") { + do_xdvi(@ARGV); +} else { + print "Unknown what $what!\n"; + exit 1; +} +exit 0; + +# +# pdftex pdftexconfig.tex format +# /--- pdftexconfig.tex --- +# |... +# |\pdfpagewidth=NNN true <unit> +# |\pdfpageheight=NNN true <unit> +# |... +# \------------------------ +# +# Reading is done via --progname=pdftex --format='tex' pdftexconfig.tex +# Writing is done to TEXMFSYSCONFIG/tex/generic/config/pdftexconfig.tex +# +sub do_pdftex { + my $newpaper = shift; + my $cf = `kpsewhich --progname=pdftex --format=tex pdftexconfig.tex`; + chomp($cf); + print STDERR "file used for pdftex: $cf\n"; + + open(FOO, "<$cf") or die("cannot open file: $!"); + my @lines = <FOO>; + close(FOO); + + my ($cpw, $cph); + my ($cpwidx, $cphidx); + my $endinputidx; + # read the lines and the last pdfpageswidth/height wins + for my $idx (0..$#lines) { + my $l = $lines[$idx]; + if ($l =~ m/^\s*\\pdfpagewidth\s*=?\s*(.+)\s*$/) { + $cpw = $1; + $cpwidx = $idx; + next; + } + if ($l =~ m/^\s*\\pdfpageheight\s*=?\s*(.+)\s*$/) { + $cph = $1; + $cphidx = $idx; + next; + } + if ($l =~ m/^\s*\\endinput\s*/) { + $endinputidx = $idx; + next; + } + } + # trying to find the right papersize + # + my $currentpaper; + if (defined($cpw) && defined($cph)) { + for my $pname (keys %pdftex_papersize) { + my ($w, $h) = @{$pdftex_papersize{$pname}}; + if (($w eq $cpw) && ($h eq $cph)) { + $currentpaper = $pname; + last; + } + } + } else { + $currentpaper = "(undefined)"; + } + $currentpaper || ($currentpaper = "$cpw x $cph"); + if (defined($newpaper)) { + if ($newpaper eq "--list") { + print "$currentpaper\n"; + for my $p (keys %pdftex_papersize) { + print "$p\n" unless ($p eq $currentpaper); + } + } else { + my $found = 0; + for my $p (keys %pdftex_papersize) { + if ($p eq $newpaper) { + $found = 1; + last; + } + } + if ($found) { + my $newwline = + '\pdfpagewidth=' . ${$pdftex_papersize{$newpaper}}[0] . "\n"; + my $newhline = + '\pdfpageheight=' . ${$pdftex_papersize{$newpaper}}[1] . "\n"; + if (defined($cpwidx)) { + $lines[$cpwidx] = $newwline; + } else { + if (defined($endinputidx)) { + $lines[$endinputidx] = $newwline . $lines[$endinputidx]; + } else { + $lines[$#lines] = $newwline; + } + } + if (defined($cphidx)) { + $lines[$cphidx] = $newhline; + } else { + if (defined($endinputidx)) { + $lines[$endinputidx] = $newhline . $lines[$endinputidx]; + } else { + $lines[$#lines] = $newwline; + } + } + for (@lines) { print; } + } else { + warn "Not a valid paper size for pdftex: $newpaper\n"; + } + } + } else { + # just print the current value + print "current pdftex paper size: $currentpaper\n"; + } +} + +############################################### +# +# dvips config.ps format: +# /--- config.ps --- +# |... +# |stuff not related to paper sizes +# |... +# | <empty line> +# |% some comments +# |% more comments +# |@ <NAME> <WIDTH> <HEIGHT> +# |@+ ...definition line +# |@+ ...definition line +# |... more definition lines +# |@+ %%EndPaperSize +# | +# |@ <NAME> <WIDTH> <HEIGHT> +# |... +# \------------ +# +# the first paper definition is the default +# selecting paper is done like with texconfig which used ed to move the +# selected part between @ $selected_paper .... @ /-1 (the line before the +# next @ line) to the line before the first @ line. +# (what a tricky ed invocation te created there, impressive!!!) +# +# Reading is done via --progname=dvips --format='dvips config' config.ps +# Writing is done to TEXMFSYSCONFIG/dvips/config/config.ps +# +sub do_dvips { + my $newpaper = shift; + my $configps = `kpsewhich --progname=dvips --format='dvips config' config.ps`; + chomp($configps); + print "file used: $configps\n"; + + open(FOO, "<$configps") or die("cannot open file: $!"); + my @lines = <FOO>; + close(FOO); + + my @papersizes; + my $firstpaperidx; + my %startidx; + my %endidx; + my $in_block = ""; + my $idx = 0; + for my $idx (0 .. $#lines) { + if ($lines[$idx] =~ m/^@ (\w+)/) { + $startidx{$1} = $idx; + $firstpaperidx || ($firstpaperidx = $idx-1); + $in_block = $1; + push @papersizes, $1; + next; + } + # empty lines or comments stop a block + if ($in_block) { + if ($lines[$idx] =~ m/^\s*(%.*)?\s*$/) { + $endidx{$in_block} = $idx-1; + $in_block = ""; + } + next; + } + } + + #for my $p (@papersizes) { + # print "papersize $p from idx $startidx{$p} to $endidx{$p}\n"; + #} + + if (defined($newpaper)) { + if ($newpaper eq "--list") { + for my $p (@papersizes) { + print "$p\n"; # first is already the selected one + } + } else { + my $found = 0; + for my $p (@papersizes) { + if ($p eq $newpaper) { + $found = 1; + last; + } + } + if ($found) { + my @newlines; + for my $idx (0..$#lines) { + if ($idx < $firstpaperidx) { + push @newlines, $lines[$idx]; + next; + } + if ($idx == $firstpaperidx) { + # insert the selected paper definition + push @newlines, @lines[$startidx{$newpaper}..$endidx{$newpaper}]; + push @newlines, $lines[$idx]; + next; + } + if ($idx >= $startidx{$newpaper} && $idx <= $endidx{$newpaper}) { + next; + } + push @newlines, $lines[$idx]; + } + # output should go to $texmfsysconfig/dvips/config/config.ps TODO TODO + for (@newlines) { print; } + } else { + warn "Not a valid paper size for dvips: $newpaper\n"; + } + } + } else { + # just print the current value + print "current dvips paper size: $papersizes[0]\n"; + } +} + +# +# context format: +# /--- cont-sys.{tex,rme} +# |... +# |\setuppapersize[letter][letter] +# |... +# \------------ +# +# Reading is done first via kpsewhich -prog=tex -format=tex cont-sys.tex +# if that fails via -prog=tex -format='other text files' cont-sys.rme +# +# Writing is done to TEXMFSYSCONFIG/tex/context/user/cont-sys.tex +# +sub do_context { + my $newpaper = shift; + # TODO TODO + # we should check whether context is installed at all!!!! + my $inp = `kpsewhich --progname=tex --format=tex cont-sys.tex`; + chomp($inp); + if (!$inp) { + # we didn't get anything, so retry with .rme + $inp = `kpsewhich --progname=tex --format='other text files' cont-sys.rme`; + chomp($inp); + } + my @bla = keys %pdftex_papersize; + do_simple($inp, "context", '^\s*\\setuppapersize\s*', + '^\s*\\setuppapersize\s*\[([^][]*)\]', + sub { + my ($ll,$np) = @_; + if ($ll =~ m/^\s*\\setuppapersize\s*/) { + return("\\setuppapersize[$np][$np]\n"); + } else { + return($ll); + } + }, "tex/context/user/cont-sys.rme", \@bla, + 'a4', '\setuppapersize[a4][a4]', $newpaper); +} + + +# +# xdvi format: +# /--- XDvi --- +# |... +# |*paper: <NAME> +# |... +# \------------ +# +# Reading is done via --progname=xdvi --format='other text files' XDvi +# Writing is done to TEXMFSYSCONFIG/xdvi/XDvi +# +sub do_xdvi { + my $newpaper = shift; + my $xdvi = `kpsewhich --progname=xdvi --format='other text files' XDvi`; + chomp($xdvi); + my @bla = keys %xdvi_papersize; + do_simple($xdvi, "xdvi", '^\*paper: ', '^\*paper:\s+(\w+)\s*$', + sub { + my ($ll,$np) = @_; + $ll =~ s/^\*paper:\s+(\w+)\s*$/\*paper: $np\n/; + return($ll); + }, "xdvi/XDvi", \@bla, + '(not defined)', '*paper: a4', $newpaper); +} + + +# +# dvipdfm(x) format: +# /--- dvipdfm/config, dvipdfmx/dvipdfmx.cfg --- +# |... +# |p <NAME> +# |... +# \------------ +# +# Reading is done +# for dvipdfm via --progname=dvipdfm --format='other text files' config +# for dvipdfmx via --progname=dvipdfmx --format='other text files' dvipdfmx.cfg +# Writing is done to TEXMFSYSCONFIG/dvipdfm/config/config +# and /dvipdfmx/dvipdfmx.cfg +# +# +sub do_dvipdfm_and_x { + my ($inp, $prog, $outp, $paplist, $newpaper) = @_; + do_simple($inp, $prog, '^p\s+', '^p\s+(\w+)\s*$', + sub { + my ($ll,$np) = @_; + $ll =~ s/^p\s+(\w+)\s*$/p $np\n/; + return($ll); + }, $outp, $paplist, '(not defined)', 'p a4', $newpaper); +} + +sub do_dvipdfm { + my $newpaper = shift; + my $inp = `kpsewhich --progname=dvipdfm --format='other text files' config`; + chomp($inp); + my @bla = keys %dvipdfm_papersize; + do_dvipdfm_and_x($inp, "dvipdfm", "dvipdfm/config/config", \@bla, $newpaper); +} +sub do_dvipdfmx { + my $newpaper = shift; + my $inp = `kpsewhich --progname=dvipdfmx --format='other text files' dvipdfmx.cfg`; + chomp($inp); + my @bla = keys %dvipdfm_papersize; + do_dvipdfm_and_x($inp, "dvipdfmx", "dvipdfmx/dvipdfmx.cfg", \@bla, $newpaper); +} + + + +# +# do_simple does the work for single line config files +# (xdvi, dvipdfm, dvipdfmx) +# arguments: +# $inp, $prog, $firstre, $secondre, $bl, $outp, $paplist, $newpaper +# with +# $inp .. input file location +# $prog .. program name +# $firstre .. re that searches for paper lines +# $secondre .. re that extracts the paper from a paper line +# $bl .. block/sub taking two args, one paper line and the new paper, and +# returns the line with the paper configured, only lines mathing +# $firstre are shipped over to $bl +# $outp .. location of the output file +# $paplist .. ref to an array with the list of admissible paper sizes +# $defaultpaper .. default papersize (arbitrary string) if the $firstre is +# not found in the config file +# $defaultline .. the line to be added at the bottom of the file if +# no line has been found +# $newpaper .. --list, new paper, or undef +sub do_simple { + my ($inp, $prog, $firstre, $secondre, $bl, $outp, $paplist, $defaultpaper, $defaultline, $newpaper) = @_; + + print STDERR "file used for $prog: $inp\n"; + + open(FOO, "<$inp") or die("cannot open file: $!"); + my @lines = <FOO>; + close(FOO); + + my $currentpaper; + my @paperlines = grep (m/$firstre/,@lines); + if (!@paperlines) { + $currentpaper = $defaultpaper; + } else { + if ($#paperlines > 0) { + warn "Strange, more than one paper definiton, using the first one!\n"; + } + $currentpaper = $paperlines[0]; + chomp($currentpaper); + $currentpaper =~ s/$secondre/$1/; + } + + # change value + if (defined($newpaper)) { + if ($newpaper eq "--list") { + print "$currentpaper\n"; + for my $p (@$paplist) { + print "$p\n" unless ($p eq $currentpaper); + } + } else { + my $found = 0; + for my $p (@$paplist) { + if ($p eq $newpaper) { + $found = 1; + last; + } + } + if ($found) { + my @newlines; + my $foundcfg = 0; + for my $l (@lines) { + if ($l =~ m/$firstre/) { + push @newlines, &$bl($l, $newpaper); + $foundcfg = 1; + } else { + push @newlines, $l; + } + } + # what to do if no default line found??? + if (!$foundcfg) { + push @newlines, &$bl($defaultline, $newpaper); + } + # should save to $outf + for (@newlines) { print; } + } else { + warn "Not a valid paper size for $prog: $newpaper\n"; + } + } + } else { + # just return the current value + print "Current paper setting for $prog: $currentpaper\n"; + } +} + + +### Local Variables: +### perl-indent-level: 2 +### tab-width: 2 +### indent-tabs-mode: nil +### End: +# vim:set tabstop=2 expandtab: # diff --git a/Master/tlpkg/dev/tlmgr1.pl-with-recreate-tlpdb b/Master/tlpkg/dev/tlmgr1.pl-with-recreate-tlpdb new file mode 100644 index 00000000000..261de5a4e71 --- /dev/null +++ b/Master/tlpkg/dev/tlmgr1.pl-with-recreate-tlpdb @@ -0,0 +1,3038 @@ +#!/usr/bin/env perl +# $Id: tlmgr.pl 11715 2008-12-25 09:27:37Z preining $ +# +# Copyright 2008 Norbert Preining +# This file is licensed under the GNU General Public License version 2 +# or any later version. +# + +my $svnrev = '$Revision: 11715 $'; +my $datrev = '$Date: 2008-12-25 10:27:37 +0100 (Thu, 25 Dec 2008) $'; +my $tlmgrrevision; +if ($svnrev =~ m/: ([0-9]+) /) { + $tlmgrrevision = $1; +} else { + $tlmgrrevision = "unknown"; +} +$datrev =~ s/^.*Date: //; +$datrev =~ s/ \(.*$//; +$tlmgrrevision .= " ($datrev)"; + + +my $Master; + +BEGIN { + $^W = 1; + $Master = `kpsewhich -var-value=SELFAUTOPARENT`; + chomp($Master); + unshift (@INC, "$Master/tlpkg"); +} + +use TeXLive::TLPOBJ; +use TeXLive::TLPDB; +use TeXLive::TLConfig; +use TeXLive::TLMedia; +use TeXLive::TLUtils; +use TeXLive::TLWinGoo; +use TeXLive::TLPostActions; +TeXLive::TLUtils->import(qw(member info give_ctan_mirror win32 + merge_into copy log debug)); +use Cwd qw/abs_path/; +use Pod::Usage; +use Getopt::Long qw(:config no_autoabbrev require_order); +use strict; + +# used variables +# the TLMedia from which we install/update +my $tlmediasrc; +# the tlpdb of the install media +my $tlpdb; +# the local tlpdb +my $localtlpdb; +# a hash for saving the options saved into the local tlpdb +my %options; +# the location from where we install +my $location; + +# each action uses this. +my @getopt_configure_subopts = qw(no_pass_through permute); + +# option variables +my $opt_location; +my $opt_help = 0; +my $opt_version = 0; +my $opt_gui = 0; +my $opt_gui_lang; +my $opt_packagelogfile; +my $opt_pause = 0; + +TeXLive::TLUtils::process_logging_options(); +GetOptions("location=s" => \$opt_location, + "gui" => \$opt_gui, + "gui-lang=s" => \$opt_gui_lang, + "package-logfile=s" => \$opt_packagelogfile, + "version" => \$opt_version, + "pause" => \$opt_pause, + "h|help|?" => \$opt_help) or pod2usage(2); + +my $action = shift; + +if (!defined($action) && $opt_gui) { + $action = "gui"; +} + +if ($opt_version || (defined $action && $action eq "version")) { + print "tlmgr revision $tlmgrrevision\n"; + print "tlmgr using installation: $Master\n"; + if (open (REL_TL, "$Master/release-texlive.txt")) { + # print first and last lines, which have the TL version info. + my @rel_tl = <REL_TL>; + print $rel_tl[0]; + print $rel_tl[$#rel_tl]; + close (REL_TL); + } + finish(0); +} + +if (defined($action) && ($action =~ m/^help/i)) { + $opt_help = 1; +} + +if (!defined($action) && !$opt_help) { + die "$0: missing action; try --help if you need it.\n"; +} + +if ($opt_help) { + # perldoc does ASCII emphasis on the output, so it's nice to use it. + # But not all Unix platforms have it, and on Windows our Config.pm + # can apparently interfere, so always skip it there. + my @noperldoc = (); + if (win32() || ! TeXLive::TLUtils::which("perldoc")) { + @noperldoc = ("-noperldoc", "1"); + } + pod2usage("-exitstatus" => 0, "-verbose" => 2, @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 +my $packagelogged = 0; # how many msgs we logged +chomp (my $texmfsysvar = `kpsewhich -var-value=TEXMFSYSVAR`); +my $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 = ""; +} + +my $loadmediasrcerror = "Cannot load TeX Live database from "; +my %ret; + +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/^(.*)\.(src|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 not existing 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 "src") { + $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/^generate$/i) { + merge_into(\%ret, action_generate()); +} elsif ($action =~ m/^gui$/i) { + action_gui(); +} elsif ($action =~ m/^arch$/i) { + merge_into(\%ret, action_arch()); +} elsif ($action =~ m/^option$/i) { + merge_into(\%ret, action_option()); + # should we do postinstall stuff? + finish(0); +} elsif ($action =~ m/^list$/i) { + merge_into(\%ret, action_list()); + # should we do postinstall stuff? + finish(0); +} elsif ($action =~ m/^check$/i) { + merge_into(\%ret, action_check()); +} elsif ($action =~ m/^install$/i) { + merge_into(\%ret, action_install()); +} elsif ($action =~ m/^update$/i) { + merge_into(\%ret, action_update()); +} elsif ($action =~ m/^backup$/i) { + merge_into(\%ret, action_backup()); +} elsif ($action =~ m/^restore$/i) { + merge_into(\%ret, action_restore()); +} elsif ($action =~ m/^search$/i) { + merge_into(\%ret, action_search()); + finish(0); +} elsif ($action =~ m/^show$/i) { + merge_into(\%ret, action_show()); + finish(0); +} elsif ($action =~ m/^remove$/i) { + merge_into(\%ret, action_remove()); +} elsif (($action eq "paper") || ($action eq "dvips") || ($action eq "xdvi") || + ($action eq "pdftex") || ($action eq "dvipdfm") || + ($action eq "dvipdfmx") || ($action eq "context")) { + if ($opt_gui) { + action_gui("config"); + } + init_local_db(); + my $TEXMFMAIN = `kpsewhich -var-value=TEXMFMAIN`; + chomp $TEXMFMAIN; + my @cmdline = ("texlua", "$TEXMFMAIN/scripts/texlive/texconf.tlu", "--prog", "tlmgr","--sys"); + if (!$localtlpdb->option_create_formats) { + push @cmdline, "--noformat"; + } + push @cmdline, $action, @ARGV; + system(@cmdline); +} elsif ($action =~ m/^uninstall$/i) { + merge_into(\%ret, action_uninstall()); + finish(0); +} elsif ($action =~ m/^recreate-tlpdb$/i) { + merge_into(\%ret, action_recreate_tlpdb()); + finish(0); +} else { + die "$0: unknown action: $action\n"; +} + +# main action is done, now clean up. + +# close the special log file +if ($packagelogfile) { + print "tlmgr: package log updated at $packagelogfile\n" if $packagelogged; + close(PACKAGELOG); +} + +# run external programs. +my $error_count = &handle_ret_hash(%ret); + +# done. +finish($error_count ? 1 : 0); # just in case there are 256 errors. + + +# +# run external programs based on the RET hash we've accumulated. +# We return the number of unsuccessful runs, zero if all went well. +# +sub handle_ret_hash +{ + my (%ret) = @_; + my $errors = 0; + + if (defined($ret{'mktexlsr'})) { + print "running mktexlsr\n"; + $errors += system("mktexlsr"); + } + + if (defined($ret{'map'})) { + foreach my $m (@{$ret{'map'}}) { + print "$m\n"; + $errors += system("updmap-sys --nomkmap --nohash --$m"); + } + $errors += system("mktexlsr"); + $errors += system("updmap-sys"); + } + + chomp (my $TEXMFSYSVAR = `kpsewhich -var-value=TEXMFSYSVAR`); + chomp (my $TEXMFLOCAL = `kpsewhich -var-value=TEXMFLOCAL`); + + if (defined($ret{'format'})) { + print "regenerating fmtutil.cnf\n"; + TeXLive::TLUtils::create_fmtutil($localtlpdb, + "$TEXMFSYSVAR/web2c/fmtutil.cnf", + "$TEXMFLOCAL/web2c/fmtutil-local.cnf"); + # run fmtutil-sys only if the option for building formats is set. + if ($localtlpdb->option_create_formats) { + print "running fmtutil-sys --missing\n"; + $errors += system("fmtutil-sys", "--missing"); + } + } + + if (defined($ret{'language'})) { + for my $ext ("dat", "def") { + my $lang = "language.$ext"; + print "regenerating $lang\n"; + + my $arg1 = "$TEXMFSYSVAR/tex/generic/config/language.$ext"; + my $arg2 = "$TEXMFLOCAL/tex/generic/config/language-local.dat"; + if ($ext eq "dat") { + TeXLive::TLUtils::create_language_dat($localtlpdb, $arg1, $arg2); + } else { + TeXLive::TLUtils::create_language_def($localtlpdb, $arg1, $arg2); + } + + if (! TeXLive::TLUtils::win32()) { + # Use full path for external command, except on Windows. + $lang = "$TEXMFSYSVAR/tex/generic/config/$lang"; + } + # run fmtutil-sys only if the option for building formats is set. + if ($localtlpdb->option_create_formats) { + print "running fmtutil-sys --byhyphen $lang\n"; + $errors += system("fmtutil-sys", "--byhyphen", $lang); + } + } + } + return $errors / 256; # we were accumulating wait statuses +} + +# +# remove_package removes a single package with all files (including the +# tlpobj files) and the entry from the tlpdb. +# +sub remove_package { + my ($pkg, $localtlpdb) = @_; + my $tlp = $localtlpdb->get_package($pkg); + my %ret; + if (!defined($tlp)) { + tlwarn ("$pkg: package not present, cannot remove\n"); + } else { + if ($pkg =~ m/^texlive\.infra/) { + log ("Not removing $pkg, it is essential!\n"); + return; + } + # we have to chdir to $localtlpdb->root + my $Master = $localtlpdb->root; + chdir ($Master) || die "chdir($Master) failed: $!"; + my @files = $tlp->all_files; + # also remove the .tlpobj file + push @files, "tlpkg/tlpobj/$pkg.tlpobj"; + # and the ones from src/doc splitting + if (-r "tlpkg/tlpobj/$pkg.src.tlpobj") { + push @files, "tlpkg/tlpobj/$pkg.src.tlpobj"; + } + if (-r "tlpkg/tlpobj/$pkg.doc.tlpobj") { + push @files, "tlpkg/tlpobj/$pkg.doc.tlpobj"; + } + # + # we want to check that a file is only listed in one package, so + # in case that a file to be removed is listed in another package + # we will warn and *not* remove it + my %allfiles; + for my $p ($localtlpdb->list_packages) { + next if ($p eq $pkg); # we have to skip the to be removed package + for my $f ($localtlpdb->get_package($p)->all_files) { + $allfiles{$f} = $p; + } + } + my @goodfiles = (); + my @badfiles = (); + for my $f (@files) { + if (defined($allfiles{$f})) { + # this file should be removed but is mentioned somewhere, too + push @badfiles, $f; + } else { + push @goodfiles, $f; + } + } + if ($#badfiles >= 0) { + # warn the user + tlwarn("The following files should be removed due to the removal of $pkg,\n"); + tlwarn("but are part of another package, too.\n"); + for my $f (@badfiles) { + tlwarn(" $f - $allfiles{$f}\n"); + } + } + my @removals = &removed_dirs (@goodfiles); + foreach my $entry (@goodfiles) { + unlink $entry; + } + foreach my $entry (@removals) { + rmdir $entry; + } + $localtlpdb->remove_package($pkg); + merge_into(\%ret, $tlp->make_return_hash_from_executes("disable")); + $ret{'mktexlsr'} = 1; + # should we save at each removal??? + # advantage: the tlpdb actually reflects what is installed + # disadvantage: removing a collection calls the save routine several times + # still I consider it better that the tlpdb is in a consistent state + $localtlpdb->save; + # do the post removal actions + if (defined($PostRemove{$pkg})) { + info("running post remove action for $pkg\n"); + &{$PostRemove{$pkg}}($localtlpdb->root); + } + } + return \%ret; +} + +# 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 +# willabsolutely only install foo bar baz not even taking .ARCH into +# account +# +sub action_remove { + if ($opt_gui) { + action_gui("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 + # + my %ret; + my $opt_nodepends = 0; + my $opt_reallynodepends = 0; + my $opt_force = 0; + my $opt_dry = 0; + Getopt::Long::Configure(@getopt_configure_subopts); + GetOptions("no-depends" => \$opt_nodepends, + "no-depends-at-all" => \$opt_reallynodepends, + "force" => \$opt_force, + "n|dry-run" => \$opt_dry) or pod2usage(2); + $opt_nodepends = 1 if $opt_reallynodepends; + my %already_removed; + my @more_removal; + init_local_db(); + info("remove: dry run, no changes will be made\n") if $opt_dry; + 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 $opt_reallynodepends + @packs = $localtlpdb->expand_dependencies("-only-arch", $localtlpdb, @packs) unless $opt_reallynodepends; + # remove deps unless $opt_nodepends + @packs = $localtlpdb->expand_dependencies("-no-collections", $localtlpdb, @packs) unless $opt_nodepends; + 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 $opt_reallynodepends; + 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 ($opt_force) { + info("tlmgr: $p is needed by " . join(" ", @needed) . "\n"); + info("tlmgr: still removing it 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)) { + print "$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; + info ("remove $pkg\n"); + if ($opt_dry) { + # we need to set $foo to something positive otherwise + # the rest will not be run in dry_run mode + $foo{'mktexlsr'} = 1; + } else { + merge_into(\%foo, &remove_package($pkg, $localtlpdb)); + logpackage("remove: $pkg"); + } + if (keys %foo) { + # removal was successful, so the return is at least 0x0001 mktexlsr + # remove dependencies, too + merge_into(\%ret, \%foo); + $already_removed{$pkg} = 1; + } + } else { + # save all the other packages into the @more_removal list to + # be removed at the second state. Note that if a package has + # already been removed due to a removal of a collection + # it will be marked as such in %already_removed and not tried again + push @more_removal, $pkg; + } + } + } + foreach my $pkg (sort @more_removal) { + if (!defined($already_removed{$pkg})) { + info ("remove $pkg\n"); + if (!$opt_dry) { + my %foo; + merge_into(\%foo, &remove_package($pkg, $localtlpdb)); + if (keys %foo) { + # removal was successful + logpackage("remove: $pkg"); + merge_into(\%ret, \%foo); + $already_removed{$pkg} = 1; + } + } + } + } + if ($opt_dry) { + # stop here, don't do any postinstall actions + return; + } else { + $localtlpdb->save; + my @foo = sort keys %already_removed; + if (@foo) { + print "tlmgr: actually removed these packages: @foo\n"; + } else { + print "tlmgr: no packages removed.\n"; + } + return(\%ret); + } +} + +sub action_show { + if ($opt_gui) { + action_gui("config"); + } + my $opt_list = 0; + Getopt::Long::Configure(@getopt_configure_subopts); + GetOptions("list" => \$opt_list) or pod2usage(2); + init_local_db(); + my $tlmediatlpdb; + foreach my $pkg (@ARGV) { + my $tlpdb = $localtlpdb; + my $tlp = $localtlpdb->get_package($pkg); + my $installed = 0; + if (!$tlp) { + if (!$tlmediatlpdb) { + init_tlmedia(); + $tlmediatlpdb = $tlmediasrc->tlpdb; + } + $tlp = $tlmediatlpdb->get_package($pkg); + $tlpdb = $tlmediatlpdb; + } else { + $installed = 1; + } + if ($tlp) { + my @colls; + if ($tlp->category ne "Collection" && $tlp->category ne "Scheme") { + @colls = $localtlpdb->needed_by($pkg); + if (!@colls) { + # not referenced in the local tlpdb, so try the remote here, too + if (!$tlmediatlpdb) { + init_tlmedia(); + $tlmediatlpdb = $tlmediasrc->tlpdb; + } + @colls = $tlmediatlpdb->needed_by($pkg); + } + } + print "Package: ", $tlp->name, "\n"; + print "Category: ", $tlp->category, "\n"; + print "ShortDesc: ", $tlp->shortdesc, "\n" if ($tlp->shortdesc); + print "LongDesc: ", $tlp->longdesc, "\n" if ($tlp->longdesc); + print "Installed: ", ($installed ? "Yes" : "No"), "\n"; + print "Revision: ", $tlp->revision, "\n" if ($installed); + print "Collection: ", @colls, "\n" if (@colls); + if ($opt_list) { + 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) { + warn "That should not happen, no such package here!"; + 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\n"; } + } + # in case we have them + if ($foo->allbinfiles) { + print "bin files (all architectures):\n"; + for my $f (sort $foo->allbinfiles) { print " $f\n"; } + } + } + } + print "\n"; + } else { + printf STDERR "tlmgr: cannot find $pkg\n"; + } + } + return; +} + +sub action_search { + my $opt_global = 0; + my $opt_file = 0; + Getopt::Long::Configure(@getopt_configure_subopts); + GetOptions("global" => \$opt_global, + "file" => \$opt_file) or pod2usage(2); + my $r = shift @ARGV; + my $ret = ""; + my $tlpdb; + init_local_db(); + if ($opt_global) { + init_tlmedia(); + $tlpdb = $tlmediasrc->tlpdb; + } else { + $tlpdb = $localtlpdb; + } + foreach my $pkg ($tlpdb->list_packages) { + if ($opt_file) { + my @ret = grep(m;$r;, $tlpdb->get_package($pkg)->all_files); + if (@ret) { + print "$pkg:\n"; + foreach (@ret) { + print "\t$_\n"; + } + } + } else { + next if ($pkg =~ m/\./); + my $t = $tlpdb->get_package($pkg)->shortdesc; + $t |= ""; + my $lt = $tlpdb->get_package($pkg)->longdesc; + $lt |= ""; + if (($pkg =~ m/$r/) || ($t =~ m/$r/) || ($lt =~ m/$r/)) { + $ret .= " $pkg - $t\n"; + } + } + } + print "$ret"; + return; +} + +# RESTORE +# +sub action_restore { + # 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 + my $opt_backupdir; + my $opt_force; + my $opt_dry = 0; + Getopt::Long::Configure(@getopt_configure_subopts); + GetOptions("backupdir=s" => \$opt_backupdir, + "n|dry-run" => \$opt_dry, + "force" => \$opt_force) or pod2usage(2); + # check the backup dir argument + if ($opt_backupdir) { + my $ob = abs_path($opt_backupdir); + $ob && ($opt_backupdir = $ob); + if (! -d $opt_backupdir) { + tlwarn ("backupdir argument $opt_backupdir is not a directory.\n"); + tlwarn ("Don't know from where to restore backups, terminating.\n"); + exit 1; + } + } else { + # no argument, check for presence in TLPDB + $opt_backupdir = $localtlpdb->option("backupdir"); + if (!$opt_backupdir) { + tlwarn ("Don't know from where to restore backups, terminating.\n"); + exit 1; + } + # we are stil here, there is something set in tlpdb + my $ob = abs_path($opt_backupdir); + $ob && ($opt_backupdir = $ob); + if (! -d $opt_backupdir) { + tlwarn ("backupdir as set in tlpdb $opt_backupdir is not a directory.\n"); + tlwarn ("Don't know from where to restore backups, terminating.\n"); + exit 1; + } + } + + info("restore: dry run, no changes will be made\n") if $opt_dry; + + # initialize the hash(packages) of hash(revisions) + my %backups; + opendir (DIR, $opt_backupdir) || die "opendir($opt_backupdir) failed: $!"; + my @dirents = readdir (DIR); + closedir (DIR) || warn "closedir($opt_backupdir) failed: $!"; + for my $dirent (@dirents) { + next if (-d $dirent); + next if ($dirent !~ m/^(.*)\.r([0-9]+)\.tar\.lzma$/); + $backups{$1}->{$2} = 1; + } + my $ret; + my ($pkg, $rev) = @ARGV; + if (!defined($pkg)) { + if (keys %backups) { + print "Available backups:\n"; + foreach my $p (sort keys %backups) { + print "$p: "; + my @rs = sort (keys %{$backups{$p}}); + print "@rs\n"; + } + } else { + print "No backups available in $opt_backupdir\n"; + } + finish(0); + } + if (!defined($rev)) { + print "Available backups for $pkg: "; + my @rs = sort (keys %{$backups{$pkg}}); + print "@rs\n"; + finish(0); + } + # we did arrive here, so we try to restore ... + if (defined($backups{$pkg}->{$rev})) { + if (!$opt_force) { + print "Do you really want to restore $pkg to revision $rev (y/N): "; + my $yesno = <STDIN>; + if ($yesno !~ m/^y(es)?$/i) { + print "Ok, cancelling the restore!\n"; + finish(0); + } + } + print "Restoring $pkg, $rev from $opt_backupdir/${pkg}.r${rev}.tar.lzma\n"; + if (!$opt_dry) { + init_local_db(1); + # first remove the package, then reinstall it + # this way we get rid of useless files + $opt_backupdir = abs_path($opt_backupdir); + merge_into(\%ret, &remove_package($pkg, $localtlpdb)); + TeXLive::TLMedia->_install_package("$opt_backupdir/${pkg}.r${rev}.tar.lzma" , [] ,$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); + $ret = $localtlpdb->get_package($pkg)->make_return_hash_from_executes("enable"); + $localtlpdb->save; + } + } else { + print "revision $rev for $pkg is not present in $opt_backupdir\n"; + } + return $ret; +} + +sub action_backup { + init_local_db(1); + my $opt_dry = 0; + my $opt_all = 0; + my $opt_backupdir; + # --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. But Getopt::Long with clean:i leaves 0 in $opt_clean if + # it is present. So + # !defined($opt_clean) -> no --clean given + # $opt_clean = "" -> --clean without argument given, check tlpdb + # $opt_clean = -1, 0, N -> --clean=N given, check argument + # + my $opt_clean; + Getopt::Long::Configure(@getopt_configure_subopts); + GetOptions("all" => \$opt_all, + "backupdir=s" => \$opt_backupdir, + "clean:s" => \$opt_clean, + "n|dry-run" => \$opt_dry, + ) or pod2usage(2); + my $clean_mode = 0; + $clean_mode = 1 if defined($opt_clean); + if ($clean_mode) { + if ($opt_clean eq "") { + # we need to check the tlpdb + $opt_clean = $localtlpdb->option("autobackup"); + if (!$opt_clean) { + tlwarn ("--clean given without an argument, but no default clean\n"); + tlwarn ("mode specified in the tlpdb, terminating.\n"); + exit 1; + } + } + # now $opt_clean is something, but maybe not a number, check for + # validity + if ($opt_clean =~ m/^(-1|[0-9]+)$/) { + # get rid of leading zeros etc etc + $opt_clean = $opt_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 + if ($opt_backupdir) { + my $ob = abs_path($opt_backupdir); + $ob && ($opt_backupdir = $ob); + if (! -d $opt_backupdir) { + tlwarn ("backupdir argument $opt_backupdir is not a directory.\n"); + if ($clean_mode) { + tlwarn ("Cannot clean a non existing directory, terminating.\n"); + } else { + tlwarn ("Don't know where to save backups, terminating.\n"); + } + exit 1; + } + } else { + # no argument, check for presence in TLPDB + $opt_backupdir = $localtlpdb->option("backupdir"); + if (!$opt_backupdir) { + if ($clean_mode) { + tlwarn ("Cannot clean an unknown directory, terminating.\n"); + } else { + tlwarn ("Don't know where to save backups, terminating.\n"); + } + exit 1; + } + # we are stil here, there is something set in tlpdb + my $ob = abs_path($opt_backupdir); + $ob && ($opt_backupdir = $ob); + if (! -d $opt_backupdir) { + tlwarn ("backupdir as set in tlpdb $opt_backupdir is not a directory.\n"); + if ($clean_mode) { + tlwarn ("Cannot clean a non existing directory, terminating.\n"); + } else { + tlwarn ("Don't know where to save backups, terminating.\n"); + } + exit 1; + } + } + + my %ret; + my @todo; + if ($opt_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, $opt_backupdir, $opt_clean, $opt_dry); + } else { + my $tlp = $localtlpdb->get_package($pkg); + info("saving current status of $pkg to $opt_backupdir${pkg}.r" . + $tlp->revision . ".\n"); + if (!$opt_dry) { + $tlp->make_container("lzma", $localtlpdb->root, + $opt_backupdir, "${pkg}.r" . $tlp->revision); + } + } + } + return(\%ret); +} + +sub open_w32_updater { + my $root = shift; + open UPDATER, ">" . $root . "/tlpkg/installer/updater.bat" + or die "Cannot create updater.bat: $!"; + print UPDATER <<'EOF'; +rem update program, can savely removed after it has been done +set tlupdate=%~dp0 +set tldrive=%~d0 + +%tldrive% +cd %tlupdate% +rem now we are in .../tlpkg/installer +rem create tar.exe backup +copy tar.exe tarsave.exe +cd .. +cd .. +rem now we are in the root + +EOF +; + $::UPDATER = \*UPDATER; +} + +$::updater_started = 0; + +# +sub add_w32_updater { + my ($tlp, $opt_dry) = @_; + sub do_one { + my $pkg = shift; + print $::UPDATER <<EOF; +tlpkg\\installer\\lzma\\lzmadec.win32.exe < temp\\$pkg.tar.lzma > temp\\$pkg.tar +tlpkg\\installer\\tarsave.exe -x -f temp\\$pkg.tar +call tlmgr _include_tlpobj tlpkg\\tlpobj\\$pkg.tlpobj +del temp\\$pkg.tar.lzma +del temp\\$pkg.tar +EOF +; + } + + my $pkg = $tlp->name; + # that could be more intelligent for decent $opt_dry output + my $root = $localtlpdb->root; + my $temp = "$root/temp"; + return if $opt_dry; + if (!$::updater_started) { + open_w32_updater($root); + $::updater_started = 1; + } + # these packages cannot be upgrade on w32 + # so we have to create a update program + my $media = $tlmediasrc->media; + my $mediatlpdb = $tlmediasrc->tlpdb; + my $remoteroot = $mediatlpdb->root; + # + # we have to download/copy also the src/doc files if necessary! + my $container_src_split = $mediatlpdb->config_src_container; + my $container_doc_split = $mediatlpdb->config_doc_container; + # get options about src/doc splitting from $totlpdb + my $opt_src = $localtlpdb->option_install_srcfiles; + my $opt_doc = $localtlpdb->option_install_docfiles; + my $real_opt_doc = $opt_doc; + if ($tlp->category =~ m/documentation/i) { + # we do install documenation files for category Documentation + # even if opt_doc is false + $real_opt_doc = 1; + } + my $do_src = 0; + my $do_doc = 0; + $do_src = 1 if ($container_src_split && $opt_src && $tlp->srcfiles); + $do_doc = 1 if ($container_doc_split && $real_opt_doc && $tlp->docfiles); + TeXLive::TLUtils::mkdirhier($temp); + if ($media eq 'DVD') { + tlwarn ("Creating updater from DVD currently not implemented!\n"); + tlwarn ("But it should not be necessary!\n"); + } else { + if ($media eq 'CD') { + copy("$remoteroot/$Archive/$pkg.tar.lzma", "$temp"); + copy("$remoteroot/$Archive/$pkg.doc.tar.lzma", "$temp") if $do_doc; + copy("$remoteroot/$Archive/$pkg.src.tar.lzma", "$temp") if $do_src; + } else { # net + TeXLive::TLUtils::download_file("$remoteroot/$Archive/$pkg.tar.lzma", "$temp/$pkg.tar.lzma"); + TeXLive::TLUtils::download_file("$remoteroot/$Archive/$pkg.doc.tar.lzma", "$temp/$pkg.doc.tar.lzma") if $do_doc; + TeXLive::TLUtils::download_file("$remoteroot/$Archive/$pkg.src.tar.lzma", "$temp/$pkg.src.tar.lzma") if $do_src; + } + # now we should have the file present + if (! -r "$temp/$pkg.tar.lzma") { + tlwarn ("Couldn't get $pkg.tar.lzma, that is bad\n"); + } else { + # add lines to the un-archiver + do_one ($pkg); + } + if ($do_src) { + if (! -r "$temp/$pkg.src.tar.lzma") { + tlwarn ("Couldn't get $pkg.src.tar.lzma, that is bad\n"); + } else { + # add lines to the un-archiver + do_one ("$pkg.src"); + } + } + if ($do_doc) { + if (! -r "$temp/$pkg.doc.tar.lzma") { + tlwarn ("Couldn't get $pkg.doc.tar.lzma, that is bad\n"); + } else { + # add lines to the un-archiver + do_one ("$pkg.doc"); + } + } + } +} + +sub close_w32_updater { + if ($::updater_started) { + my $desktop_path = TeXLive::TLWinGoo::desktop_path(); + $desktop_path =~ s!/!\\!g; + print $::UPDATER "del tlpkg\\installer\\tarsave.exe\n"; + print $::UPDATER "pause\n"; + print $::UPDATER "del \"$desktop_path\\TeX Live Updater Final Step.lnk\"\n"; + print $::UPDATER "rem del /s /q temp\n"; + print $::UPDATER "del \"\%~f0\""; + close ($::UPDATER); + # create a desktop shortcut + add_desktop_shortcut( + $localtlpdb->root, + 'TeX Live Updater Final Step', + '', # the icon + $localtlpdb->root.'/tlpkg/installer/updater.bat', + '', # no args + '', # any non-null value to hide command-prompt + ); + tlwarn("UPDATER has been created, please execute\n"); + tlwarn(" " . $localtlpdb->root . "\\tlpkg\\installer\\updater.bat\n"); + } +} + + +# UPDATE +# +# tlmgr update foo +# if foo is of type Package|Documentation it will update only foo +# and the respective .ARCH dependencies +# if foo is of type Collection|Scheme it will update itself AND +# will check all depending packs of type NOT(COllection|Scheme) +# for necessary updates +# +# tlmgr update --no-depends foo +# as above, but will not check for depends of Collections/Schemes +# but it will still update .ARCH deps +# +# tlmgr update --no-depends-at-all foo +# will absolutely only update foo not even taking .ARCH into account +# +# TLMedia->install_package does ONLY INSTALL ONE PACKAGE, no deps whatsoever +# anymore. That has all to be done by hand. +# +sub action_update { + if ($opt_gui) { + action_gui("update"); + } + init_local_db(1); + # initialize the TLMedia from $location + my $opt_nodepends = 0; + my $opt_reallynodepends = 0; + my $opt_dry = 0; + my $opt_all = 0; + my $opt_list = 0; + my $opt_noremove = 0; + my $opt_backupdir; + my $opt_backup = 0; + my $opt_force = 0; + Getopt::Long::Configure(@getopt_configure_subopts); + GetOptions("no-depends" => \$opt_nodepends, + "no-depends-at-all" => \$opt_reallynodepends, + "all" => \$opt_all, + "list" => \$opt_list, + "no-remove" => \$opt_noremove, + "force" => \$opt_force, + "backupdir=s" => \$opt_backupdir, + "backup" => \$opt_backup, + "n|dry-run" => \$opt_dry) or pod2usage(2); + $opt_nodepends = 1 if $opt_reallynodepends; + my $nrupdated = 0; + my %ret; + + init_tlmedia(); + my $mediatlpdb = $tlmediasrc->tlpdb; + info("update: dry run, no changes will be made\n") if $opt_dry; + + # if the update is not for one of the critical packages then do + # check for updates to tlmgr and die unless either --force or --list + # is given + my $other_updates_asked_for = 0; + if ($opt_all) { + $other_updates_asked_for = 1; + } else { + for my $p (@ARGV) { + my $matched = 0; + for my $cp (@CriticalPackagesList) { + # we match for initial package name, that shold be fine + if ($p =~ m/^$cp/) { + $matched = 1; + last; + } + } + if (!$matched) { + $other_updates_asked_for = 1; + last; + } + } + } + if ($other_updates_asked_for) { + if (check_for_critical_updates($localtlpdb, $mediatlpdb)) { + if ($opt_force) { + tlwarn("$0: Continuing due to --force.\n"); + } elsif ($opt_list) { + # do not warn here + } else { + die "$0: Exiting, please read above warning.\n"; + } + } + } + + # do backup dir checking now so that we don't run into troubles + # later, and exit if that doesn't work + if ($opt_backupdir) { + $opt_backupdir = abs_path($opt_backupdir); + if (! -d $opt_backupdir) { + tlwarn("Argument for --backupdir must be an existing directory. Terminating.\n"); + exit 1; + } + } + + my $autobackup = 0; + # check for the tlpdb option autobackup, and if present and true (!= 0) + # assume we are doing backups + if (!$opt_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 \\infty backups.\n"); + $opt_backup = 1; + } elsif ($autobackup eq "0") { + debug ("Automatic backups disabled.\n"); + } elsif ($autobackup =~ m/^[0-9]+$/) { + debug ("Automatic backups activated, keeping $autobackup backups.\n"); + $opt_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 ($opt_backup) { + my $diebackupdir = 0; + if (!$opt_backupdir) { + $opt_backupdir = $localtlpdb->option("backupdir"); + if ($opt_backupdir) { + # check again: + $opt_backupdir = abs_path($opt_backupdir); + $diebackupdir = 1 if (! -d $opt_backupdir); + } else { + # not set in the tlpdb, and not set on cmd line, but asked for + # --backup + $diebackupdir = 1; + } + } + # no else branch necessary, we already checked that --backupdir if + # given is ok, see above + if ($diebackupdir) { + tlwarn("You have asked for backups, but the backup directory as specified\n"); + tlwarn("in the local TLPDB or the cmd line does not exists, exiting.\n"); + exit 1; + } + } + + # finally, if we have --backupdir, but no --backup, just enable it + $opt_backup = 1 if $opt_backupdir; + + debug("Doing backups to $opt_backupdir\n") if $opt_backup; + + # 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 $opt_dry; + } + + my @todo; + my %removals; + my %forcermpkgs; + my %newpkgs; + # 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 @colls = (); + for my $p ($localtlpdb->collections) { + push @colls, $p + if defined($mediatlpdb->get_package($p)); + } + my @localexpansion = + $localtlpdb->expand_dependencies($localtlpdb, @colls); + my @remoteexpansion = + $mediatlpdb->expand_dependencies($localtlpdb, @colls); + for my $p (@remoteexpansion) { + $newpkgs{$p} = 1; + } + for my $p (@localexpansion) { + delete($newpkgs{$p}); + $removals{$p} = 1; + } + for my $p (@remoteexpansion) { + delete($removals{$p}); + } + for my $p (@localexpansion) { + # intersection, don't check A\B and B\A + next if $newpkgs{$p}; + next if $removals{$p}; + my $tlp = $localtlpdb->get_package($p); + if (!defined($tlp)) { + $forcermpkgs{$p} = 1; + } + } + 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"); + + if ($opt_all || $opt_list) { + @todo = $localtlpdb->list_packages; + } else { + @todo = @ARGV; + } + + # + # 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: + for my $p (keys %removals) { + if ($opt_noremove) { + info("not removing $p due to -no-remove (removed on server)\n"); + } else { + info("remove $p (removed on server)\n"); + } + if (!($opt_dry or $opt_list or $opt_noremove)) { + merge_into(\%ret, &remove_package($p, $localtlpdb)); + logpackage("remove: $p"); + } + } + + if (!@todo) { + tlwarn("tlmgr update: please specify a list of packages or --all.\n"); + } + + # update all .ARCH dependencies, too, unless $opt_reallynodepends: + @todo = $mediatlpdb->expand_dependencies("-only-arch", $localtlpdb, @todo) + unless $opt_reallynodepends; + # + # update general deps unless $opt_nodepends: + @todo = $mediatlpdb->expand_dependencies("-no-collections",$localtlpdb,@todo) + unless $opt_nodepends; + + foreach my $pkg (sort @todo) { + next if ($pkg =~ m/^00texlive/); + 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) { + 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 + next; + } else { + tlwarn("\ntlmgr: $pkg mentioned, neither new nor forcibly removed\n"); + next; + } + # install new packages + my $mediatlp = $mediatlpdb->get_package($pkg); + if (!defined($mediatlp)) { + tlwarn("\nShould not happen: $pkg not found in $location\n"); + next; + } + my $mediarev = $mediatlp->revision; + info("auto-install: $pkg\n"); + next if ($opt_dry || $opt_list); + my $foo = $tlmediasrc->install_package($pkg, $localtlpdb); + if (ref $foo) { + # installation succeeded because we got a reference + merge_into (\%ret, $foo); + logpackage("auto-install new: $pkg ($mediarev)"); + $nrupdated++; + } else { + tlwarn("$0: couldn't install new package $pkg\n"); + } + next; + } + + my $unwind_package; + my $remove_unwind_container = 0; + my $rev = $tlp->revision; + my $mediatlp = $mediatlpdb->get_package($pkg); + if (!defined($mediatlp)) { + debug("$pkg cannot be found in $location\n"); + next; + } + my $mediarev = $mediatlp->revision; + if ($rev < $mediarev) { + $nrupdated++; + if ($opt_list) { + info("$pkg: local: $rev, source: $mediarev\n"); + } else { + if ($opt_backup && !$opt_dry) { + $tlp->make_container("lzma", $root, + $opt_backupdir, "${pkg}.r" . $tlp->revision); + $unwind_package = + "$opt_backupdir/${pkg}.r" . $tlp->revision . ".tar.lzma"; + + if ($autobackup) { + # in case we do auto backups we remove older backups + clear_old_backups($pkg, $opt_backupdir, $autobackup); + } + } + + if (win32() && ($pkg =~ m/$WinSpecialUpdatePackagesRegexp/)) { + add_w32_updater($mediatlp, $opt_dry); + } else { + info("update: $pkg ($rev -> $mediarev)"); + if ($opt_dry) { + info("\n"); + next; + } else { + info(" ... "); # 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); + if ($s <= 0) { + tlwarn("\n$0: Creation of backup container of $pkg failed.\n"); + tlwarn("Continuing to update other packages, please retry...\n"); + # we should try to update other packages at least + next; + } + $remove_unwind_container = 1; + $unwind_package = "$fullname"; + } + # first remove the package, then reinstall it + # this way we get rid of useless files + # force the deinstallation since we will reinstall it + # + # the remove_package should also remove empty dirs in case + # a dir is changed into a file + merge_into(\%ret, &remove_package($pkg, $localtlpdb)); + my $foo = $tlmediasrc->install_package($pkg, $localtlpdb); + if (ref $foo) { + # installation succeeded because we got a reference + merge_into (\%ret, $foo); + logpackage("update: $pkg ($rev -> $mediarev)"); + unlink($unwind_package) if $remove_unwind_container; + } else { + # install_package returned a scalar, so error. + # now in fact we should do some cleanup, removing files and + # dirs from the new package before re-installing the old one. + # TODO + logpackage("failed update: $pkg ($rev -> $mediarev)"); + tlwarn("\n\nInstallation of new version of $pkg did fail, trying to unwind.\n"); + if (win32()) { + # w32 is notorious for not releasing a file immediately + # we experienced permission denied errors + my $newname = $unwind_package; + $newname =~ s/__BACKUP/___BACKUP/; + copy ("-f", $unwind_package, $newname); + # try to remove the file if has been created by us + unlink($unwind_package) if $remove_unwind_container; + # and make sure that the temporary file is removed in any case + $remove_unwind_container = 1; + $unwind_package = $newname; + } + my $instret = TeXLive::TLMedia->_install_package("$unwind_package", + [], $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("restore failed: $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"); + } + } + } elsif ($rev > $mediarev) { + print "$pkg: local revision ($rev) is newer than revision in $location" + . " ($mediarev), not updating.\n"; + next; + } + } + # that already checks whether we actually have to do something + close_w32_updater(); + if (($nrupdated == 0) && ($tlmediasrc->media ne "NET") && $opt_all) { + # for all but net updates we warn if nothing is updated + tlwarn("\nNothing to update.\n"); + tlwarn("\nYour installation is set up to look on the disk for updates.\n"); + tlwarn("To install from the Internet for this one time only, run\n"); + tlwarn(" tlmgr -location $TeXLiveURL\n"); + tlwarn("\nTo change the default for all future updates, run\n"); + tlwarn(" tlmgr option location $TeXLiveURL\n\n"); + } + return(\%ret); +} + +# INSTALL +# +# tlmgr install foo bar baz +# will create the closure under dependencies of {foo,bar,baz}, i.e. all +# dependencies recursively down to the last package, and install all +# the packages that are necessary +# +# tlmgr install --no-depends foo bar baz +# will *only* install these three packages (if they are not already installed +# but it will STILL INSTALL foo.ARCH if they are necessary. +# +# tlmgr install --no-depends-at-all foo bar baz +# will absolutely only install these three packages, and will not even +# take .ARCH deps into account +# +# tlmgr install --reinstall ... +# behaves exactely like without --reinstall BUT the following two +# differences: +# . dependencies are not expanded from collection to collection, so +# if you reinstall a collection then all its dependencies of type +# Package will be reinstalled, too, but not the dependencies on +# other collection, because that would force the full reinstallation +# of the whole installation +# . it does not care for whether a package seems to be installed or +# not (that is the --reinstall) +# +# TLMedia->install_package does ONLY INSTALL ONE PACKAGE, no deps whatsoever +# anymore! That has all to be done by hand. +# +sub action_install { + if ($opt_gui) { + action_gui("install"); + } + init_local_db(1); + # initialize the TLMedia from $location + my $opt_nodepends = 0; + my $opt_reallynodepends = 0; + my $opt_dry = 0; + my $opt_reinstall = 0; + my $opt_force = 0; + Getopt::Long::Configure(@getopt_configure_subopts); + GetOptions("no-depends" => \$opt_nodepends, + "no-depends-at-all" => \$opt_reallynodepends, + "reinstall" => \$opt_reinstall, + "force" => \$opt_force, + "n|dry-run" => \$opt_dry) or pod2usage(2); + my %ret; + init_tlmedia(); + my $tlmediatlpdb = $tlmediasrc->tlpdb; + + # check for updates to tlmgr itself, and die unless --force is given + if(check_for_critical_updates( $localtlpdb, $tlmediatlpdb, !$opt_force)) { + die "not continuing, please see warning above!"; + } + + $opt_nodepends = 1 if $opt_reallynodepends; + info("install: dry run, no changes will be made\n") if $opt_dry; + + my @packs = @ARGV; + # first expand the .ARCH dependencies unless $opt_reallynodepends + @packs = $tlmediatlpdb->expand_dependencies("-only-arch", $localtlpdb, @ARGV) unless $opt_reallynodepends; + # now expand all others unless $opt_nodepends + # if $opt_reinstall do not collection->collection dependencies + if ($opt_reinstall) { + @packs = $tlmediatlpdb->expand_dependencies("-no-collections", $localtlpdb, @packs) unless $opt_nodepends; + } else { + @packs = $tlmediatlpdb->expand_dependencies($localtlpdb, @packs) unless $opt_nodepends; + } + foreach my $pkg (sort @packs) { + my $re = ""; + if (defined($localtlpdb->get_package($pkg))) { + if ($opt_reinstall) { + $re = "re"; + } else { + debug("already installed: $pkg\n"); + next; + } + } + info("${re}install: $pkg\n"); + if (!$opt_dry) { + merge_into(\%ret, $tlmediasrc->install_package($pkg, $localtlpdb)); + logpackage("${re}install: $pkg"); + } + } + if ($opt_dry) { + # stop here, don't do any postinstall actions + return(0); + } + return(\%ret); +} + +sub action_list { + init_local_db(); + # make sure that the @ARGV hash is not changed in case we switch to + # show mode + my ($what) = @ARGV; + if ($what) { + # if the argument to list is either 'collection' or 'scheme' + # we list them, otherwise we go direct into tlmgr show $pkg mode + if ($what !~ m/^(collection|scheme)/i) { + tlwarn("(switching to show mode)\n"); + action_show(); + return; + } + } else { + $what = ""; + } + init_tlmedia(); + my @whattolist; + if ($what =~ m/^collection/i) { + @whattolist = $tlmediasrc->tlpdb->collections; + } elsif ($what =~ m/^scheme/i) { + @whattolist = $tlmediasrc->tlpdb->schemes; + } else { + @whattolist = $tlmediasrc->tlpdb->list_packages; + } + foreach (@whattolist) { + if (defined($localtlpdb->get_package($_))) { + print "i "; + } else { + print " "; + } + my $foo = $tlmediasrc->tlpdb->get_package($_)->shortdesc; + print "$_: ", defined($foo) ? $foo : "(shortdesc missing)" , "\n"; + } + return; +} + + +sub action_option { + if ($opt_gui) { + action_gui("config"); + } + my $what = shift @ARGV; + $what = "show" unless defined($what); + init_local_db(); + if ($what =~ m/^location$/i) { + # changes the default location + my $loc = shift @ARGV; + if ($loc) { + # support "ctan" on the cmd line, and don't abs_path it! + if ($loc =~ m/^ctan$/i) { + $loc = "$TeXLive::TLConfig::TeXLiveURL"; + } + if ($loc !~ m!^(http|ftp)://!i) { + # seems to be a local path, try to normalize it + my $testloc = abs_path($loc); + # 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. + $loc = $testloc if $testloc; + } + info("tlmgr: setting default installation location to $loc\n"); + $localtlpdb->option_location($loc); + $localtlpdb->save; + } else { + info("Default installation location: " + . $localtlpdb->option_location . "\n"); + } + } elsif ($what =~ m/^docfiles$/i) { + # changes the default docfiles + my $loc = shift @ARGV; + if (defined($loc)) { + print "Defaulting to", ($loc ? "" : " not"), + " installing documentation files.\n"; + $localtlpdb->option_install_docfiles($loc); + $localtlpdb->save; + } else { + print "Install documentation files: ", + $localtlpdb->option_install_docfiles, "\n"; + } + } elsif ($what =~ m/^srcfiles$/i) { + # changes the default srcfiles + my $loc = shift @ARGV; + if (defined($loc)) { + print "Defaulting to", ($loc ? "" : " not"), + " installing source files.\n"; + $localtlpdb->option_install_srcfiles($loc); + $localtlpdb->save; + } else { + print "Install source files: ", + $localtlpdb->option_install_srcfiles, "\n"; + } + } elsif ($what =~ m/^formats$/i) { + # changes the default formats + my $loc = shift @ARGV; + if (defined($loc)) { + print "Defaulting to", ($loc ? "" : " not"), + " generating format files on installation.\n"; + $localtlpdb->option_create_formats($loc); + $localtlpdb->save; + } else { + print "Create formats on installation: ", + $localtlpdb->option_create_formats, "\n"; + } + } elsif ($what =~ m/^show$/i) { + print "Default installation location (location): ", + $localtlpdb->option_location, "\n"; + print "Create formats on installation (formats): ", + ($localtlpdb->option_create_formats ? "yes" : "no"), "\n"; + print "Install documentation files (docfiles): ", + ($localtlpdb->option_install_docfiles ? "yes": "no"), "\n"; + print "Install source files (srcfiles): ", + ($localtlpdb->option_install_srcfiles ? "yes": "no"), "\n"; + print "Directory for backups (backupdir): ", + $localtlpdb->option("backupdir"), "\n" + if $localtlpdb->option("backupdir"); + print "Number of backups to keep (autobackup): ", + $localtlpdb->option("autobackup"), "\n" + if $localtlpdb->option("autobackup"); + } else { + my $val = shift @ARGV; + if (defined($val)) { + print "Setting option $what to $val.\n"; + $localtlpdb->option($what,$val); + $localtlpdb->save; + } else { + print "Option $what = ", $localtlpdb->option($what), "\n";; + } + #warn "Setting other options currently not supported, please edit texlive.tlpdb!"; + } + return; +} + + +# ARCH +# +sub action_arch { + if ($^O=~/^MSWin(32|64)$/i) { + printf STDERR "action `arch' not supported on Windows\n"; + } + if ($opt_gui) { + action_gui("arch"); + } + my %ret; + my $opt_dry = 0; + Getopt::Long::Configure(@getopt_configure_subopts); + GetOptions("n|dry-run" => \$opt_dry) or pod2usage(2); + my $what = shift @ARGV; + init_local_db(1); + info("arch: dry run, no changes will be made\n") if $opt_dry; + $what || ($what = "list"); + if ($what =~ m/^list$/i) { + # list the available architectures + # initialize the TLMedia from $location + init_tlmedia(); + my $mediatlpdb = $tlmediasrc->tlpdb; + my @already_installed_arch = $localtlpdb->available_architectures; + print "Available architectures:\n"; + foreach my $a ($mediatlpdb->available_architectures) { + if (member($a,@already_installed_arch)) { + print "(i) $a\n"; + } else { + print " $a\n"; + } + } + print "Already installed architectures are marked with (i)\n"; + print "You can add new architectures with tlmgr arch add arch1 arch2\n"; + finish(0); + } elsif ($what =~ m/^add$/i) { + init_tlmedia(); + my $mediatlpdb = $tlmediasrc->tlpdb; + my @already_installed_arch = $localtlpdb->available_architectures; + my @available_arch = $mediatlpdb->available_architectures; + my @todoarchs; + foreach my $a (@ARGV) { + if (TeXLive::TLUtils::member($a, @already_installed_arch)) { + print "Arch $a is already installed\n"; + next; + } + if (!TeXLive::TLUtils::member($a, @available_arch)) { + print "Arch $a not available, use 'tlmgr available_archs'!\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 ($opt_dry) { + print "Installing $pkg.$a\n"; + } else { + info("install: $pkg.$a\n"); + merge_into(\%ret, $tlmediasrc->install_package("$pkg.$a", $localtlpdb)); + } + } + } + } + } + if (TeXLive::TLUtils::member('win32', @todoarchs)) { + # install the necessary w32 stuff + info("install: bin-tlperl.win32\n"); + merge_into (\%ret, $tlmediasrc->install_package("bin-tlperl.win32", $localtlpdb)); + info("install: bin-tlgs.win32\n"); + merge_into (\%ret, $tlmediasrc->install_package("bin-tlgs.win32", $localtlpdb)); + info("install: bin-tlpsv.win32\n"); + merge_into (\%ret, $tlmediasrc->install_package("bin-tlpsv.win32", $localtlpdb)); + } + # update the option_archs list of installed archs + my @larchs = $localtlpdb->option_available_architectures; + push @larchs, @todoarchs; + $localtlpdb->option_available_architectures(@larchs); + $localtlpdb->save; + } else { + die "Unknown option for arch: $what"; + } + return(\%ret); +} + +sub action_generate { + my $localconf = ""; + my $dest = ""; + Getopt::Long::Configure(@getopt_configure_subopts); + GetOptions("localcfg=s" => \$localconf, "dest=s" => \$dest,) or pod2usage(2); + my $what = shift @ARGV; + init_local_db(); + + chomp (my $TEXMFSYSVAR = `kpsewhich -var-value=TEXMFSYSVAR`); + chomp (my $TEXMFLOCAL = `kpsewhich -var-value=TEXMFLOCAL`); + + if ($what =~ m/^language(\.dat|\.def)?$/i) { + if ($what =~ m/^language(\.dat)?$/i) { + $dest ||= "$TEXMFSYSVAR/tex/generic/config/language.dat"; + $localconf ||= "$TEXMFLOCAL/tex/generic/config/language-local.dat"; + debug ("$0: writing language.dat data to $dest\n"); + TeXLive::TLUtils::create_language_dat($localtlpdb, $dest, $localconf); + $dest .= ".def"; + } + if ($what =~ m/^language(\.def)?$/i) { + $dest ||= "$TEXMFSYSVAR/tex/generic/config/language.def"; + $localconf ||= "$TEXMFLOCAL/tex/generic/config/language-local.def"; + debug("$0: writing language.def data to $dest\n"); + TeXLive::TLUtils::create_language_def($localtlpdb, $dest, $localconf); + } + + } elsif ($what =~ m/^fmtutil$/i) { + $dest ||= "$TEXMFSYSVAR/web2c/fmtutil.cnf"; + $localconf ||= "$TEXMFLOCAL/web2c/fmtutil-local.cnf"; + debug("$0: writing new fmtutil.cnf to $dest\n"); + TeXLive::TLUtils::create_fmtutil($localtlpdb, $dest, $localconf); + + } elsif ($what =~ m/^updmap$/i) { + $dest ||= "$TEXMFSYSVAR/web2c/updmap.cfg"; + $localconf ||= "$TEXMFLOCAL/web2c/updmap-local.cfg"; + debug("$0: writing new updmap.cfg to $dest\n"); + TeXLive::TLUtils::create_updmap($localtlpdb, $dest, $localconf); + + } else { + die "$0: Unknown option for generate: $what; try --help if you need it.\n"; + } + + return; +} + +# set global $location variable. +# if we cannot read tlpdb, die if arg SHOULD_I_DIE is true. +# +sub init_local_db { + my ($should_i_die) = @_; + $localtlpdb = TeXLive::TLPDB->new ("root" => $Master); + die("cannot find tlpdb in $Master") unless (defined($localtlpdb)); + # setup the programs, for w32 we need the shipped wget/lzma etc, so we + # pass the location of these files to setup_programs. + my $ret = setup_programs("$Master/tlpkg/installer", + $localtlpdb->option_platform); + if ($ret == -1) { + tlwarn("no binary of lzmadec for $::_platform_ detected.\n"); + if (defined($should_i_die) && $should_i_die) { + exit 1; + } else { + tlwarn("Continuing anyway ...\n"); + } + } + if (!$ret) { + 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 = $localtlpdb->option_location; + if (defined($loc)) { + $location = $loc; + } + if (defined($opt_location)) { + $location = $opt_location; + } + if (!defined($location)) { + die("No installation source found, nor in the texlive.tlpdb nor on the cmd line.\nPlease specify one!"); + } + if ($location =~ m/^ctan$/i) { + $location = "$TeXLive::TLConfig::TeXLiveURL"; + } + # we normalize the path only if it is + # - neither a URL starting with http or ftp + # - if we are on windows it does not start with Drive: + if (! ( $location =~ m!^(http|ftp)://!i || + (win32() && $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; + } +} + +sub action_gui { + my ($guiscreen) = @_; + my $perlbin = "perl"; + my $perlbinquote = "perl"; + my $perlscript = "$Master/texmf/scripts/texlive/tlmgrgui/tlmgrgui.pl"; + if ($^O=~/^MSWin(32|64)$/i) { + $ENV{'PERL5LIB'} = "$Master/tlpkg/tlperl/lib"; + $perlbin = "$Master/tlpkg/tlperl/bin/perl.exe"; + $perlbinquote = "\"$Master/tlpkg/tlperl/bin/perl.exe\""; + $perlbinquote =~ s!/!\\!g; + $perlscript = "\"$perlscript\""; + $perlscript =~ s!/!\\!g; + } + my @cmdline; + push @cmdline, $perlbinquote, $perlscript; + push @cmdline, "--lang", "$opt_gui_lang" + if (defined($opt_gui_lang)); + push @cmdline, "--location", "$opt_location" + if (defined($opt_location)); + push @cmdline, "--logfile", "$::LOGFILENAME" + if (defined($::LOGFILENAME)); + push @cmdline, "-v" if ($::opt_verbosity > 0); + push @cmdline, "-v" if ($::opt_verbosity > 1); + push @cmdline, "-q" if ($::opt_quiet > 0); + if (defined($guiscreen)) { + if (($guiscreen eq "install") || ($guiscreen eq "update")) { + push @cmdline, "--load"; + } + push @cmdline, "--guiscreen", $guiscreen; + } + exec $perlbin @cmdline; + die("Cannot execute the GUI script\n"); +} + +sub action_uninstall { + if (win32()) { + printf STDERR "Please use \"Add/Remove Programs\" from the Control Panel to removing TeX Live!\n"; + finish(1); + } + if ($opt_gui) { + action_gui("uninstall"); + } + my $force = 0; + Getopt::Long::Configure(@getopt_configure_subopts); + GetOptions("force" => \$force) or pod2usage(2); + if (!$force) { + print("If you answer yes here the whole TeX Live installation will be removed!\n"); + print "Remove TeX Live (y/N): "; + my $yesno = <STDIN>; + if ($yesno !~ m/^y(es)?$/i) { + print "Ok, cancelling the removal!\n"; + finish(0); + } + } + print ("Ok, removing the whole installation:\n"); + init_local_db; + my $tlpdb = $localtlpdb; + my $opt_symlinks = 0; + my ($sys_bin,$sys_info,$sys_man); + my ($texdir,$texmflocal,$texmfhome,$texmfsysvar); + if ($tlpdb) { + $sys_bin = $tlpdb->option_sys_bin; + $sys_man = $tlpdb->option_sys_man; + $sys_info= $tlpdb->option_sys_info; + $opt_symlinks = $tlpdb->option_create_symlinks; + $texdir = $Master; + $texmfhome = `kpsewhich -var-value=TEXMFHOME`; chomp($texmfhome); + $texmfsysvar = `kpsewhich -var-value=TEXMFSYSVAR`; chomp($texmfsysvar); + $texmflocal = `kpsewhich -var-value=TEXMFLOCAL`; chomp($texmflocal); + } else { + die("cannot find tlpdb in $Master"); + } + # remove the links missing + if ($opt_symlinks) { + my @files; + if ((-d "$sys_bin") && (-w "$sys_bin")) { + my $plat_bindir; + if (-l "$sys_bin/pdftex") { + my $fullpath = readlink("$sys_bin/pdftex"); + if ($fullpath =~ m;^$Master/bin/(.*)/[^/]*$;) { + $plat_bindir = $1; + } + } else { + warn "$sys_bin/pdftex not present or not a link, not removing any link of binaries!\n"; + } + if ($plat_bindir) { + @files = `ls "$Master/bin/$plat_bindir"`; + chomp(@files); + foreach my $f (@files) { + next if (! -r "$sys_bin/$f"); + if ((-l "$sys_bin/$f") && + (readlink("$sys_bin/$f") =~ m;^$Master/bin/$plat_bindir/;)) { + unlink("$sys_bin/$f"); + } else { + warn ("not removing $sys_bin/$f, not a link or wrong destination!\n"); + } + } + } + `rmdir "$sys_bin" 2>/dev/null`; + } else { + warn "destination of bin symlink $sys_bin not writable, no removal of links of bin files done!\n"; + } + # info files + if (-w $sys_info) { + @files = `ls "$Master/texmf/doc/info"`; + chomp(@files); + foreach my $f (@files) { + next if (! -r "$sys_info/$f"); + if ((-l "$sys_info/$f") && + (readlink("$sys_info/$f") =~ m;^$Master/texmf/doc/info/;)) { + unlink("$sys_info/$f"); + } else { + warn ("not removing $sys_info/$f, not a link or wrong destination!\n"); + } + } + `rmdir "$sys_info" 2>/dev/null`; + } else { + warn "destination of info symlink $sys_info not writable, no removal of links of info files done!\n"; + } + # man files + if (-w $sys_man) { + my $foo = `(cd "$Master/texmf/doc/man" && echo *)`; + my @mans = split ' ', $foo; + chomp(@mans); + foreach my $m (@mans) { + my $mandir = "$Master/texmf/doc/man/$m"; + next unless -d $mandir; + next unless -d "$sys_man/$m"; + @files = `ls "$mandir"`; + chomp(@files); + foreach my $f (@files) { + next if (! -r "$sys_man/$m/$f"); + if ((-l "$sys_man/$m/$f") && + (readlink("$sys_man/$m/$f") =~ m;^$Master/texmf/doc/man/$m/;)) { + unlink("$sys_man/$m/$f"); + } else { + warn ("not removing $sys_man/$m/$f, not a link or wrong destination!\n"); + } + } + # ignore errors, it might be not empty + `rmdir "$sys_man/$m" 2>/dev/null`; + } + `rmdir "$sys_man" 2>/dev/null`; + } else { + warn "destination of man symlink $sys_man not writable, no removal of links of man files done!\n"; + } + } + # + # now do remove the rest + system("rm", "-rf", "$texdir/texmf-dist"); + system("rm", "-rf", "$texdir/texmf-doc"); + system("rm", "-rf", "$texdir/texmf-var"); + system("rm", "-rf", "$texdir/texmf"); + system("rm", "-rf", "$texdir/tlpkg"); + system("rm", "-rf", "$texdir/bin"); + if (-d "$texdir/temp") { + system("rmdir", "--ignore-fail-on-non-empty", "$texdir/temp"); + } + unlink("$texdir/install-tl.log"); + # should we do that???? + system("rm", "-rf", "$texdir/texmf-config"); + system("rmdir", "--ignore-fail-on-non-empty", "$texdir"); +} + + +sub action_check { + my $svn = 0; + Getopt::Long::Configure(@getopt_configure_subopts); + GetOptions("use-svn" => \$svn) or pod2usage(2); + my $what = shift @ARGV; + $what || ($what = "all"); + init_local_db(); + my $ret = 0; + if ($what =~ m/^all/i) { + $ret ||= check_files($svn); + $ret ||= check_collections($svn); + } elsif ($what =~ m/^files/i) { + $ret ||= check_files($svn); + } elsif ($what =~ m/^collections/i) { + $ret ||= check_collections($svn); + } else { + print "No idea how to check that: $what\n"; + } + finish($ret); +} + +# +# check file coverage, roughly equivalent to tlpkg/bin/check-file-coverage +# +sub check_files { + my $svn = 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); + for my $f ($tlp->all_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 "Multiple included files (relative to $Master):\n"; + for (sort @multiple) { + my @foo = @{$filetopacks{$_}}; + print " $_ (@foo)\n"; + } + print "\n"; + } + if ($#missing >= 0) { + $ret = 1; + print "Files mentioned in tlpdb but missing (relative to $Master):\n"; + for my $m (@missing) { + print "\t$m\n"; + } + print "\n"; + } + + # if we are on Win32 or MacOS we return, they currently do not allow + # find -wholename (missing find on w32, or bsd find on darwin). + # we need the -use-svn version only for the check-file-coverage + # replacement anyway, so it will be used on tug, which is neither w32 nor + # darwin. + my $arch = $localtlpdb->option_platform; + return $ret if ($arch eq "win32" || $arch eq "universal-darwin"); + + # + # do check that all files in the trees are covered + # + my @IgnorePatterns = qw! + .mkisofsrc$ autorun.inf$ + support/ source/ setuptl/ + texmf-dist/ls-R$ texmf-doc/ls-R$ texmf/ls-R$ + tlpkg/tlpsrc/ + tlpkg/bin + tlpkg/lib/ tlpkg/tests/ tlpkg/etc/ + tlpkg/texlive.tlpdb + tlpkg/tlpobj + texmf-var/ + texmf-config/ + texmf.cnf + install-tl.log + tlpkg/texlive.profile + tlpkg/installer + install-tl$ + install-tl.bat$ + install-tl.bat.manifest$ + !; + 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; + } + 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 "Files present but not covered (relative to $Master):\n"; + for my $f (sort @nohit) { + print " $f\n"; + } + print "\n"; + } + return($ret); +} + +# +# check collections +# +sub check_collections { + my @missing = (); + for my $p ($localtlpdb->collections()) { + my $col = $localtlpdb->get_package($p); + for my $d ($col->depends) { + push @missing, "$d ($p)" if (!defined($localtlpdb->get_package($d))); + } + } + return(0) if (!@missing); + print "Packages listed in collections but not present:\n"; + for my $m (@missing) { + print "\t$m\n"; + } + print "\n"; +} + + +# +# initialize the global $tlmediasrc object, or die. +# uses the global $location. +# +sub init_tlmedia { + if ($location =~ m/^($TeXLiveServerURL|ctan$)/) { + $location = give_ctan_mirror(); + } + info("tlmgr: installation location $location\n"); + # $tlmediasrc is a global variable + $tlmediasrc = TeXLive::TLMedia->new($location); + die($loadmediasrcerror . $location) unless defined($tlmediasrc); +} + +# +# the logpackage function +# if the packagelog variable is set then write to PACKAGELOG filehandle +# +sub logpackage { + if ($packagelogfile) { + $packagelogged++; + my $tim = localtime(); + print PACKAGELOG "[$tim] @_\n"; + } +} + +# +# finish # evaluates only the -pause option, and then exits. +sub finish { + my ($ret) = @_; + + if ($ret > 0) { + print "tlmgr: exiting unsuccessfully (status $ret).\n"; + } + + if ($opt_pause) { + print "Please press <ENTER> to exit the program.\n"; + <STDIN>; + } + + exit($ret); +} + +# RECREATE TLPDB +# +sub action_recreate_tlpdb { + my $opt_arch; + Getopt::Long::Configure(@getopt_configure_subopts); + GetOptions("arch=s" => \$opt_arch) or pod2usage(2); + my $tlpdb = TeXLive::TLPDB->new; + $tlpdb->root($Master); + my $inst = TeXLive::TLPOBJ->new; + $inst->name("00texlive-installation.config"); + $inst->category("TLCore"); + my @deps; + push @deps, "location:$TeXLive::TLConfig::TeXLiveURL"; + push @deps, "opt_create_formats:0"; + push @deps, "opt_create_symlinks:0"; + push @deps, "opt_sys_bin:/usr/local/bin"; + push @deps, "opt_sys_info:/usr/local/info"; + push @deps, "opt_sys_man:/usr/local/man"; + push @deps, "opt_install_docfiles:0"; + push @deps, "opt_install_srcfiles:0"; + # 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, "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 ($^O =~ /^MSWin(32|64)$/i) { + push @deps, "platform:win32"; + } else { + if ($#archs == 0) { + # only one arch available, fine, use it as default + push @deps, "platform:$archs[0]"; + } else { + if (defined($opt_arch)) { + if (member($opt_arch, @archs)) { + push @deps, "platform:" . $opt_arch; + } else { + tlwarn("The architecture you passed in with --arch is not present in $Master/bin\n"); + tlwarn("Please specify one from the available ones: @archs\n"); + exit(1); + } + } else { + tlwarn("More than one architecture available: @archs\n"); + tlwarn("Please pass one as the default you are running on with --arch=...\n"); + exit(1); + } + } + } + $inst->depends(@deps); + # now we have all the stuff for 00texlive-installation.config done + $tlpdb->add_tlpobj($inst); + # 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; +} + +# 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, $opt_backupdir, $autobackup, $opt_dry) = @_; + + my $dryrun = 0; + $dryrun = 1 if ($opt_dry); + # keep arbitrary many backups + return if ($autobackup == -1); + + opendir (DIR, $opt_backupdir) || die "opendir($opt_backupdir) failed: $!"; + my @dirents = readdir (DIR); + closedir (DIR) || warn "closedir($opt_backupdir) failed: $!"; + my @backups; + for my $dirent (@dirents) { + next if (-d $dirent); + next if ($dirent !~ m/^$pkg\.r([0-9]+)\.tar\.lzma$/); + push @backups, $1; + } + my $i = 1; + for my $e (reverse sort {$a <=> $b} @backups) { + if ($i > $autobackup) { + info ("Removing backup $opt_backupdir/$pkg.r$e.tar.lzma\n"); + unlink("$opt_backupdir/$pkg.r$e.tar.lzma") 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); + 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("Fundamental package $pkg not present, that is sooo bad.\n"); + die "Serious error, $pkg not found"; + } + my $localrev = $tlp->revision; + my $mtlp = $mediatlpdb->get_package($pkg); + if (!defined($mtlp)) { + tlwarn("Very suprising, $pkg is not present in the remote tlpdb.\n"); + next; + } + my $remoterev = $mtlp->revision; + if ($remoterev > $localrev) { + $criticalupdate = 1; + last; + } + } + if ($criticalupdate) { + tlwarn("Updates for tlmgr itself are present.\n"); + tlwarn("==========================================================\n"); + tlwarn("Please update the package bin-texlive and texlive.infra first,"); + tlwarn("e.g., by calling\n"); + tlwarn(" tlmgr update bin-texlive texlive.infra\n"); + tlwarn("Or get the latest updater from\nfor Unix-ish systems:\n"); + tlwarn(" $TeXLiveURL/update-tlmgr-latest.sh\n"); + tlwarn("for Windows systems:\n"); + tlwarn(" $TeXLiveURL/update-tlmgr-latest.exe\n"); + tlwarn("Then continue with other updates.\n"); + tlwarn("==========================================================\n"); + } + return($criticalupdate); +} + + +# +# return all the directories from which all content will be removed +# +# idea: +# - create a hashes by_dir listing all files that should be removed +# by directory, i.e., key = dir, value is list of files +# - for each of the dirs (keys of by_dir and ordered deepest first) +# check that all actually contained files are removed +# and all the contained dirs are in the removal list. If this is the +# case put that directory into the removal list +# - return this removal list +# +sub removed_dirs +{ + my (@files) = @_; + my %removed_dirs; + my %by_dir; + + # construct hash of all directories mentioned, values are lists of the + # files/dirs in that directory. + for my $f (@files) { + # what should we do with not existing entries???? + next if (! -r "$f"); + my $abs_f = abs_path ($f); + if (!$abs_f) { + warn ("oops, no abs_path($f) from " . `pwd`); + next; + } + (my $d = $abs_f) =~ s,/[^/]*$,,; + my @a = exists $by_dir{$d} ? @{$by_dir{$d}} : (); + push (@a, $abs_f); + $by_dir{$d} = \@a; + } + + # for each of our directories, see if we are removing everything in + # the directory. if so, return the directory; else return the + # individual files. + for my $d (reverse sort keys %by_dir) { + opendir (DIR, $d) || die "opendir($d) failed: $!"; + my @dirents = readdir (DIR); + closedir (DIR) || warn "closedir($d) failed: $!"; + + # initialize test hash with all the files we saw in this dir. + # (These idioms are due to "Finding Elements in One Array and Not + # Another" in the Perl Cookbook.) + my %seen; + my @rmfiles = @{$by_dir{$d}}; + @seen{@rmfiles} = (); + + # see if everything is the same. + my $cleandir = 1; + for my $dirent (@dirents) { + next if $dirent =~ /^\.(\.|svn)?$/; # ignore . .. .svn + my $item = "$d/$dirent"; # prepend directory for comparison + if ( + ((-d $item) && (defined($removed_dirs{$item}))) + || + (exists $seen{$item}) + ) { + # do nothing + } else { + $cleandir = 0; + last; + } + } + if ($cleandir) { + $removed_dirs{$d} = 1; + } + } + return keys %removed_dirs; +} + +__END__ + +=head1 NAME + +tlmgr - the TeX Live Manager + +=head1 SYNOPSIS + +tlmgr [I<option>]... I<action> [I<option>]... [I<operand>]... + +=head1 DESCRIPTION + +B<tlmgr> manages an existing TeX Live installation, both packages and +configuration options. It performs many of the same actions as +B<texconfig>(1), and more besides. (C<texconfig> continues to be +included and supported, but C<tlmgr> is now preferred.) + +The most up-to-date version of this documentation is available at +L<http://tug.org/texlive/tlmgr.html>, along with procedures for updating +tlmgr itself, disaster recovery when it's broken, and test versions. + +=head1 OPTIONS + +The following options to C<tlmgr> have to be given I<before> you specify +the main action to perform. In all cases, C<-->I<option> and +C<->I<option> are equivalent, and an C<=> is optional between an option +name and its value. + +=over 4 + +=item B<--location> I<location> + +Specifies the location from which packages should be installed or +updated, overriding the location found in the installation's TeX Live +Package Database (TLPDB). + +=item B<--gui> [I<action>] + +You can give this option together with an action to be brought directly +into the respective screen of the GUI. For example, running + + tlmgr --gui update + +starts you directly at the update screen. + +=item B<--gui-lang> I<llcode> + +Normally the GUI tries to deduce your language from the environment (on +Windows via the registry, on Unix via C<LC_MESSAGES>). If that fails you +can select a different language by giving this option a two-letter +language code. + +=item B<--package-logfile> I<file> + +B<tlmgr> logs all package actions (install, remove, update, failed +updates, failed restores) to a separate log file, by default +C<TEXMFSYSVAR/web2c/tlmgr.log>. This option allows you to select a +different file for that. This is separate from normal logging; for +that, see the option C<-v> below, and TeXLive::TLUtils. + +=item B<--pause> + +This option make B<tlmgr> wait for user input before exiting. Useful on +Windows to avoid command windows disappearing. + +=back + +The standard options for TeX Live programs are also accepted: +C<--help/-h/-?>, C<--version>, C<-q> (no informational messages), C<-v> +(debugging messages, can be repeated). For the details about these, see +the TeXLive::TLUtils documentation. + +The C<--version> option shows version information about the TeX Live +release as well as the C<tlmgr> script itself. + +=head1 ACTIONS + +=head2 help + +Gives this help information (same as C<--help>). + +=head2 version + +Gives version information (same as C<--version>). + +=head2 gui + +Start the graphical user interface. + + +=head2 install [I<option>]... I<pkg>... + +Install each I<pkg> given on the command line. By default this installs +all packages on which the given I<pkg>s are dependent, also. Options: + +=over 4 + +=item B<--reinstall> + +Reinstall a package (including dependencies for collections) even if it +seems to be already installed (i.e, is present in the TLPDB). This is +useful to recover from accidental removal of files in the hierarchy. + +When re-installing, only dependencies on normal packages are followed +(not those of category Scheme or Collection). + +=item B<--no-depends> + +Do not install dependencies. (By default, installing a package ensures +that all dependencies of this package are fulfilled.) + +=item B<--no-depends-at-all> + +When you install a package which ships binary files the respective binary +package will also be installed. (So for package bin-foobar also the package +bin-foobar.i386-linux will be installed on an i386-linux system.) +This switch supresses this behaviour, and also implies C<--no-depends>. +Don't use it unless you are sure of what you are doing. + +=item B<--dry-run> + +Nothing is actually installed; instead, the actions to be performed are +written to the terminal. + +=item B<--force> + +If updates to the tlmgr itself (and the underlying infrastructure) are +present tlmgr will bail out and not perform the installation unless this option +is given. + +=back + + +=head2 update [I<option>]... [I<pkg>]... + +Updates the packages given as arguments to the latest version available +at the installation source. Either C<--all> or at least one I<pkg> name +must be specified. Options: + +=over 4 + +=item B<--all> + +Update all installed packages. For packages contained in an installed +collection, also install new packages and remove deleted packages in +those collections. Packages not contained in an installed collection +are not considered for addition or removal. + +More precisely, if both the server and the local installation have a +collection C, and C on the server contains a package A that is not +present locally, A will be added. Conversely, if the local collection C +contains a package B, but B is no longer in the server's C, then B will +be deleted. + +=item B<--list> + +Concisely list the packages which would be updated, newly installed, or +removed, without actually changing anything. + +=item B<--dry-run> + +Nothing is actually installed; instead, the actions to be performed are +written to the terminal. This is a more detailed report than C<--list>. + +=item B<--force> + +If updates to tlmgr itself (that is, the infrastructure packages) are +present, tlmgr will not perform the update unless this option (or +C<--list>) is given. + +=item B<--no-remove> + +Under normal circumstances tlmgr tries to remove packages which have +disappeared on the server when called with C<--all>, as described +above. This prevents any such removals. + +=item B<--backup> and B<--backupdir> I<directory> + +These two options control the creation of backups of packages before an +update is started; that is, the backup is of the package as it's +installed. If neither of the two are given, no backup package will be +saved. If B<--backupdir> is given and specifies a writable directory +then a backup will be made in that location. If only B<--backup> is +given, then a backup will be made to the directory previously set via +the C<option> action (see below). If both are given then a backup will +be made to the specified I<directory>. + +You can set options via the C<option> action to automatically create +backups for all packages, and/or keep only a certain number of +backups. Please see the C<option> action for details. + +C<tlmgr> always makes a temporary backup when updating packages, in case +of download or other failure during an update. The purpose of this +B<--backup> option is to allow you to save a persistent backup in case +the actual I<content> of the update causes problems, e.g., introduces an +incompatibility. + +The C<restore> action explains how to restore from a backup. + +=item B<--no-depends> + +If you call for updating a package normally all depending packages +will also be checked for updates and updated if necessary. This switch +supresses this behaviour. + +=item B<--no-depends-at-all> + +By default, when you update a package which ships binary files the +respective binary package will also be updated. That is, for an update +of a package C<foo>, the package C<foobar.i386-linux> will also be +updated if the i386-linux platform is installed. This option suppresses +this behaviour, and implies C<--no-depends>. Don't use it unless you +are sure of what you are doing. + +=back + +If the package on the server is older than the package already installed +(e.g., if the selected mirror is out of date), C<tlmgr> does not +downgrade. Also, packages for uninstalled platforms are not installed. + + +=head2 backup [--clean[=I<N>]] [--backupdir I<dir>] [--all] [I<pkg>]... + +If the B<--clean> option is not specified, this action makes a backup of +the given packages, or all packages given C<--all>. These backups are +saved to the value of B<--backupdir> option if that is an existing and +writable directory; if B<--backupdir> is not given, the C<backupdir> +option setting in the TLPDB is used, if present. If both are missing, +no backups are made. + +If the B<--clean> option is specified, backups are cleaned (pruned) +instead of made. If the value for the B<--clean> switch is not given, +the value of the C<autobackup> option is used. If both are missing, +an error is issued. For the details of backup cleaning, see the +C<option> action below. + +Options: + +=over 4 + +=item B<--backupdir> I<directory> + +The I<directory> argument is required and must specify an existing +directory where backups are to be placed. + +=item B<--all> + +Make a backup of all packages in the TeX Live installation. This will +take quite a lot of space and time. + +=item B<--clean>[=I<N>] + +Instead of making backups, prune the backup directory of old backups. + +=item B<--dry-run> + +Nothing is actually backed up or removed; instead, the actions to be +performed are written to the terminal. + +=back + + +=head2 restore --backupdir I<dir> [I<pkg> [I<rev>] + +Restore a package from a previously-made backup. + +If neither I<pkg> nor I<rev> are given, list the available backup +revisions for all packages. + +With I<pkg> given but no I<rev>, list all available backup revisions of +I<pkg>. + +With both I<pkg> and I<rev>, tries to restore the package from the +specified backup. + +Options: + +=over 4 + +=item B<--dry-run> + +Nothing is actually restored; instead, the actions to be performed are +written to the terminal. + +=item B<--backupdir> I<directory> + +Specify the directory where the backups are to be found. + +=back + + +=head2 remove [I<option>]... I<pkg>... + +Remove each I<pkg> specified. Removing a collection removes all package +dependencies, but not collection dependencies, in that collection +(unless C<--no-depends> is specified). However, when removing a +package, dependencies are never removed. Options: + +=over 4 + +=item B<--no-depends> + +Do not remove dependent packages. + +=item B<--no-depends-at-all> + +See above under B<action> (and beware). + +=item B<--force> + +By default, removal of a package or collection that is a dependency of +another collection or scheme is not allowed. With this option, the +package will be removed unconditionally. Use with care. + +=item B<--dry-run> + +Nothing is actually removed; instead, the actions to be performed are +written to the terminal. + +=back + + +=head2 option + +=over 4 + +=item B<option [show]> + +=item B<option I<key> [I<value>]> + +=back + +The first form shows the global TeX Live settings currently saved in the +TLPDB. + +In the second form, if I<value> is not given, the setting for I<key> is +displayed. If I<value> is present, I<key> is set to I<value>. + +Possible values for I<key> are: + C<location> (default installation location), + C<formats> (create formats at installation time), + C<docfiles> (install documentation files), + C<srcfiles> (install source files), + C<backupdir> (default directory for backups), + C<autobackup> (number of backups to keep). + +Perhaps the most common use of C<option> is if you originally installed from +DVD, and want to permanently change the installation to get further +updates from the Internet. To do this, you can run + + tlmgr option location http://mirror.ctan.org/systems/texlive/tlnet/YYYY + +where YYYY is the TeX Live release year. + +The two options C<autobackup> and C<backupdir> determine defaults for +the C<update>, C<backup> and C<restore> actions. These three actions +need a directory to write to or read from the backups. If +C<--backupdir> is not specified on the command line, the C<backupdir> +option value is used (if set). + +The C<autobackup> option (de)activates the automatic generation of +backups. Its value is an integer. If C<autobackup> is C<-1>, backups +will not be removed by default. If C<autobackup> is 0 or positive, it +specifies the number of backups to be kept. Thus, backups are disabled +with 0 as the values. + +In the C<--clean> mode of the C<backup> action this option also +specifies the number to be kept. + + +=head2 paper + +=over 4 + +=item B<paper a4> + +=item B<paper letter> + +=item B<[xdvi|dvips|pdftex|dvipdfm|dvipdfmx|context] paper [help|I<papersize>|--list]> + +=back + +Configures the system wide paper settings, either for all programs at +once (first two forms), or just for the specified program (third +form). The third form with C<--list> outputs all known papersizes for the +specified program. + + +=head2 arch list|add I<arch>... + +C<arch list> lists the TeX Live names of all the architectures +(C<i386-linux>, ...) available at the default install location. + +C<arch add> I<arch> adds the executables for each given architecture +I<arch> to the installation. Options: + +=over 4 + +=item B<--dry-run> + +Nothing is actually installed; instead, the actions to be performed are +written to the terminal. + +=back + + +=head2 search [I<option>]... I<what> + +By default searches the names, short and long descriptions of all +locally installed packages for the given argument (interpreted as +regexp). Options: + +=over 4 + +=item B<--file> + +List all filenames containing I<what>. + +=item B<--global> + +Search the TeX Live Database of the installation medium, instead of the +local installation. + +=back + + +=head2 show [--list] I<pkg>... + +Shows information about I<pkg>: the name, category, installation status, +short and long description. Searches in the remote installation source +for the package if it is not locally installed. + +If the option B<--list> is given the list of contained files is also +shown, including those for architecture specific dependencies. + + +=head2 list [collections|schemes|I<pkg>...] + +With no argument, lists all packages available at the default install +location, prefixing those already installed with C<i>. + +With the single word C<collections> or C<schemes> as the argument, lists +the request type. + +With anything else as arguments, operates as the C<show> action. + + +=head2 check [I<option>]... [files|collections|all] + +Executes one (or all) check(s) on the consistency of the installation. + +=over 4 + +=item B<files> + +Checks that all files listed in the TeX Live Database (C<texlive.tlpdb>) +are actually present, and lists those missing. + +=item B<collections> + +Lists those packages which occur as dependencies in an installed collections, +but are themselves not installed. Options: + +=back + +=over 4 + +=item B<--use-svn> + +Use svn status instead of listing the files, for checking the +development repository. + +=back + + +=head2 uninstall + +Uninstalls the entire TeX Live installation. Options: + +=over 4 + +=item B<--force> + +Do not ask for confirmation, remove immediately. + +=back + + +=head2 generate + +=over 4 + +=item B<generate language> + +=item B<generate language.dat> + +=item B<generate language.def> + +=item B<generate fmtutil> + +=item B<generate updmap> + +=back + +The I<generate> action overwrites any manual changes made in the +respective files: it recreates them from scratch. + +For fmtutil and the language files, this is normal, and both the TeX +Live installer and C<tlmgr> routinely call I<generate> for them. + +For updmap, however, neither the installer nor C<tlmgr> use I<generate>, +because the result would be to disable all maps which have been manually +installed via C<updmap-sys --enable>, e.g., for proprietary or local +fonts. Only the changes in the C<--localcfg> file mentioned below are +incorporated by I<generate>. + +On the other hand, if you only use the fonts and font packages within +TeX Live, there is nothing wrong with using I<generate updmap>. Indeed, +we use it to generate the C<updmap.cfg> file that is maintained in the +live source repository. + +In more detail: I<generate> remakes any of the four config files +C<language.dat>, C<language.def>, C<fmtutil.cnf>, and C<updmap.cfg> from +the information present in the local TLPDB. If the files +C<language-local.dat>, C<language-local.def>, C<fmtutil-local.cnf>, or +C<updmap-local.cfg> are present under C<TEXMFLOCAL> in the respective +directories, their contents will be simply merged into the final files, +with no error checking of any kind. + +The form C<generate language> recreates both the C<language.dat> and the +C<language.def> files, while the forms with extension only recreates +the given file. + +Options: + +=over 4 + +=item B<--dest> I<output file> + +specifies the output file (defaults to the respective location in +C<TEXMFSYSVAR>). If B<--dest> is given to C<generate language>, its +value will be used for the C<language.dat> output, and C<.def> will be +appended to the value for the name of the C<language.def> output file. +(This is just to avoid overwriting; if you want a specific name for each +output file, we recommend invoking C<tlmgr> twice.) + +=item B<--localcfg> I<local conf file> + +specifies the (optional) local additions (defaults to the respective +location in C<TEXMFSYSVAR>). + +=back + +The respective locations are as follows: + C<tex/generic/config/language.dat> (and C<language-local.dat>) + C<tex/generic/config/language.def> (and C<language-local.def>) + C<web2c/fmtutil.cnf> (and C<fmtutil-local.cnf>) + C<web2c/updmap.cfg> (and C<updmap-local.cfg>) + + +=head1 AUTHORS AND COPYRIGHT + +This script and its documentation were written for the TeX Live +distribution (L<http://tug.org/texlive>) and both are licensed under the +GNU General Public License Version 2 or later. + +=cut + +# to remake HTML version: pod2html --cachedir=/tmp tlmgr.pl >/foo/tlmgr.html. + +### Local Variables: +### perl-indent-level: 2 +### tab-width: 2 +### indent-tabs-mode: nil +### End: +# vim:set tabstop=2 expandtab: # diff --git a/Master/tlpkg/dev/tlnet-disabled-packages.txt b/Master/tlpkg/dev/tlnet-disabled-packages.txt new file mode 100644 index 00000000000..479bc6f25b5 --- /dev/null +++ b/Master/tlpkg/dev/tlnet-disabled-packages.txt @@ -0,0 +1,2 @@ +# this file specifies (non-critical) packages which should not be +# updated on tlnet for whatever reason. It's used by tl-update-containers. diff --git a/Master/tlpkg/dev/tlrepo-from-minimals.pl b/Master/tlpkg/dev/tlrepo-from-minimals.pl new file mode 100644 index 00000000000..b4c33855c8c --- /dev/null +++ b/Master/tlpkg/dev/tlrepo-from-minimals.pl @@ -0,0 +1,322 @@ +#!/usr/bin/env perl +# +# tlrepo-from-minimals.pl +# Copyright 2008, 2009 Norbert Preining +# Licensed under GPLv2 or higher + +my $mydir; + +BEGIN { + $^W = 1; + chomp ($mydir = `dirname $0`); + unshift (@INC, "$mydir/.."); +} + +use strict; + +use Cwd qw/abs_path/; +use TeXLive::TLConfig; +use TeXLive::TLPSRC; +use TeXLive::TLPOBJ; +use TeXLive::TLPDB; +use TeXLive::TLTREE; +use TeXLive::TLUtils; +use Getopt::Long; +use Pod::Usage; + +my %min_to_tl_arch = qw/freebsd i386-freebsd + freebsd-amd64 amd64-freebsd + linux i386-linux + linux-64 x86_64-linux + linux-ppc powerpc-linux + osx-universal universal-darwin + mswin win32 + solaris-intel i386-solaris + solaris-sparc sparc-solaris/; + +my @scripts_to_link = qw/luatools mtxrun/; + +my $help = 0; +my $dist = "current"; +my $dest = "web"; +my $opt_luatex = 0; +my $opt_context = 1; +my $opt_all = 0; +my $opt_dontclean = 0; +my $source = "."; +my $opt_recreate = 0; +my $opt_nocontainers = 0; +my $texliveminimals = abs_path("$mydir/../../.."); +my $upstreamsource = "$texliveminimals/upstream"; +my $upstreamoverride = "$texliveminimals/override"; +my $upstreamaddons = "$texliveminimals/addons"; + +TeXLive::TLUtils::process_logging_options(); +GetOptions( + "source=s" => \$source, + "dist=s" => \$dist, + "dest=s" => \$dest, + "luatex" => \$opt_luatex, + "context" => \$opt_context, + "all" => \$opt_all, + "dont-clean" => \$opt_dontclean, + "recreate" => \$opt_recreate, + "nocontainers" => \$opt_nocontainers, + "upstream-source" => \$upstreamsource, + "upstream-override" => \$upstreamoverride, + "upstream-addons" => \$upstreamaddons, + "help|?" => \$help) or pod2usage(1); +pod2usage(-exitstatus => 0, -verbose => 2) if $help; + +print "location of context minimals: ", abs_path($source), "\n"; +print "location of upstream code: ", abs_path($upstreamsource), "\n"; +print "location of upstream overides: ", abs_path($upstreamoverride), "\n"; +print "location of upstream addons: ", abs_path($upstreamaddons), "\n"; + +# +if ($opt_all) { + $opt_luatex = $opt_context = 1; +} + +# +# create a temporary directory for storage of the various files +my $tmp = `mktemp -d`; +chomp($tmp); + +info("Using tmp dir $tmp\n"); + +# copy context files +if ($opt_context) { + info("Copying files for ConTeXt ... "); + system("mkdir -p $tmp/texmf-dist"); + system("cp -a $source/current/context/$dist/* $tmp/texmf-dist"); + system("mkdir -p $tmp/tlpkg/tlpsrc"); + get_from_ups("tlpkg/tlpsrc/context.tlpsrc", "$tmp/tlpkg/tlpsrc"); + get_from_ups("tlpkg/tlpsrc/bin-context.tlpsrc", "$tmp/tlpkg/tlpsrc"); + for my $i (keys %min_to_tl_arch) { + system("mkdir -p $tmp/bin/$min_to_tl_arch{$i}"); + system("cp -a $source/current/bin/context/$i/bin/* $tmp/bin/$min_to_tl_arch{$i}"); + # we have to fix the scripts to be links on unix systems + if ($i ne "mswin") { + for my $s (@scripts_to_link) { + system("ln -sf ../../texmf-dist/scripts/context/lua/$s.lua $tmp/bin/$min_to_tl_arch{$i}/$s"); + system("chmod 0755 $tmp/bin/$min_to_tl_arch{$i}/$s"); + } + } + # metafun is not needed anymore, according to Mojca on the train + # to BachoTeX 2009 + #if ($i eq "mswin") { + # system("cp -a $tmp/bin/$min_to_tl_arch{$i}/mpost.exe $tmp/bin/$min_to_tl_arch{$i}/metafun.exe"); + #} else { + # system("ln -s mpost $tmp/bin/$min_to_tl_arch{$i}/metafun"); + #} + # that is needed otherwise the dangling symlink is ignored + #system("touch $tmp/bin/$min_to_tl_arch{$i}/mpost"); + } + get_from_ups("texmf-dist/tex/mptopdf/config/mptopdf.ini", + "$tmp/texmf-dist/tex/mptopdf/config/mptopdf.ini"); + get_from_ups("texmf/fmtutil/format.context.cnf", + "$tmp/texmf/fmtutil/format.context.cnf"); + # + # we want to create the .ini files for the used languages. We could + # just copy from tug the files there, but some have been renamed, so + # we create them on the spot. + # + # Copying would be: + #for my $l (qw/cz de en fr it nl ro uk/) { + # get_from_ups("texmf-dist/tex/context/config/cont-$l.ini" + # "$tmp/texmf-dist/tex/context/config/cont-$l.ini"); + #} + for my $l (glob "$source/current/context/$dist/tex/context/base/cont-??.tex") { + my $bn = TeXLive::TLUtils::basename($l); + my $ll = $bn; + $ll =~ s/cont-(..)\.tex/$1/; + system('printf \'\\\\input ' . "$bn" . '\n\\\\endinput\n\' > ' . "$tmp/texmf-dist/tex/context/config/cont-$ll.ini"); + } + if (-d $upstreamaddons) { + if (-d "$upstreamaddons/context") { + system("cp -a $upstreamaddons/context/* $tmp"); + } + } + info("done\n"); +} + +if ($opt_luatex) { + info("Copying files for luatex ... "); + for my $i (keys %min_to_tl_arch) { + system("mkdir -p $tmp/bin/$min_to_tl_arch{$i}"); + system("cp -a $source/current/bin/luatex/$i/bin/* $tmp/bin/$min_to_tl_arch{$i}"); + if ($i eq "mswin") { + system("cp -a $tmp/bin/$min_to_tl_arch{$i}/luatex.exe $tmp/bin/$min_to_tl_arch{$i}/pdfluatex.exe"); + } else { + system("ln -s luatex $tmp/bin/$min_to_tl_arch{$i}/pdfluatex"); + } + } + system("mkdir -p $tmp/tlpkg/tlpsrc"); + get_from_ups("tlpkg/tlpsrc/luatex.tlpsrc", "$tmp/tlpkg/tlpsrc"); + get_from_ups("texmf/fmtutil/format.luatex.cnf", + "$tmp/texmf/fmtutil/format.luatex.cnf"); + if (-d $upstreamaddons) { + if (-d "$upstreamaddons/luatex") { + system("cp -a $upstreamaddons/luatex/* $tmp"); + } + } + info("done\n"); +} + +# copy the tlpkg stuff +info("Copying files for infrastructure ... "); + +get_from_ups("tlpkg/TeXLive", "$tmp/tlpkg/TeXLive"); +get_from_ups("tlpkg/bin", "$tmp/tlpkg/bin"); +get_from_ups("tlpkg/tlpsrc/00texlive.config.tlpsrc", "$tmp/tlpkg/tlpsrc"); +# should we get that from TUG, too? +get_from_ups("tlpkg/tlpsrc/00texlive.autopatterns.tlpsrc", + "$tmp/tlpkg/tlpsrc/00texlive.autopatterns.tlpsrc"); +info("done\n"); + +# create new tlpdb +info("Creating the texlive.tlpdb ... \n"); +system("perl $tmp/tlpkg/bin/tlpsrc2tlpdb -from-files -with-win-pattern-warning -all"); +info("done\n"); + +# get the current texlive.tlpdb from the tug server +get_from_ups("tlpkg/texlive.tlpdb", "$tmp/texlive-dist.tlpdb"); +my $tltlpdb = TeXLive::TLPDB->new; +$tltlpdb->from_file("$tmp/texlive-dist.tlpdb"); +die("Cannot read original tlpdb from $tmp/texlive-dist.tlpdb") unless defined($tltlpdb); + +my $mitlpdb = TeXLive::TLPDB->new(root => "$tmp"); +die("Cannot read minimals tlpdb from $tmp/tlpkg/texlive.tlpdb") unless defined($mitlpdb); + +my $webtlpdb; +if (!$opt_recreate) { + $webtlpdb = TeXLive::TLPDB->new(root => "$dest"); +} +$webtlpdb = $mitlpdb unless defined($webtlpdb); + +for my $p ($mitlpdb->list_packages) { + next if ($p =~ m/^00texlive/); + my $mitlp = $mitlpdb->get_package($p); + die "Cannot get $p from minimals tlpdb" unless defined($mitlp); + my $tltlp = $tltlpdb->get_package($p); + die "Cannot get $p from original tlpdb" unless defined($tltlp); + my $webtlp = $webtlpdb->get_package($p); + die "Cannot get $p from web tlpdb" unless defined($webtlp); + # fix the revision by adding 1 to the revision as currently shipped by TL + # or my increasing the version from the minimals tlpdb + if ($webtlp->revision > $tltlp->revision) { + $mitlp->revision($webtlp->revision + 1); + } else { + $mitlp->revision($tltlp->revision + 1); + } + # the following is actually replacing the original tlpobj + $mitlpdb->add_tlpobj($mitlp); + + # now make basic comparision of the file lists + my @tlrun = $tltlp->runfiles; + my @mirun = $mitlp->runfiles; + my @diffrun = compare_lists(\@tlrun, \@mirun); + my @tlsrc = $tltlp->srcfiles; + my @misrc = $mitlp->srcfiles; + my @diffsrc = compare_lists(\@tlsrc, \@misrc); + my @tldoc = $tltlp->docfiles; + my @midoc = $mitlp->docfiles; + my @diffdoc = compare_lists(\@tldoc, \@midoc); + my %tlbin = %{$tltlp->binfiles}; + my %mibin = %{$mitlp->binfiles}; + my @tlbin = (); + my @mibin = (); + for my $a (values %min_to_tl_arch) { + push @tlbin, @{$tlbin{$a}} if defined($tlbin{$a}); + push @mibin, @{$mibin{$a}} if defined($mibin{$a}); + } + my @diffbin = compare_lists(\@tlbin, \@mibin); + if (@diffrun || @diffbin || @diffdoc || @diffsrc) { + print "\nDifferences for $p found:\n(- removed from texlive for minimals)\n(+ added to minimals (not present in texlive))\n"; + print "runfiles:\n" if (@diffrun); + for my $l (@diffrun) { + print "$l\n"; + } + print "binfiles:\n" if (@diffbin); + for my $l (@diffbin) { + print "$l\n"; + } + print "docfiles:\n" if (@diffdoc); + for my $l (@diffdoc) { + print "$l\n"; + } + print "srcfiles:\n" if (@diffsrc); + for my $l (@diffsrc) { + print "$l\n"; + } + print "\n"; + } +} + +$mitlpdb->save; + +if (!$opt_nocontainers) { + # create the containers + info("Creating containers ... "); + my $cmd = "perl $tmp/tlpkg/bin/tl-update-containers -location $dest -no-setup"; + $cmd .= " -recreate" if $opt_recreate; + system($cmd); + info("done\ņ"); + if (!$opt_dontclean) { + info("Cleaning tmp directory (probably dangerous!!!) ... "); + system("rm -rf \"$tmp\""); + info ("done\n"); + } +} + +info("Finished!\n"); + + +sub compare_lists { + my ($aref, $bref) = @_; + my @aa = @$aref; + my @bb = @$bref; + my @added = (); + my @removed = (); + my @diff = (); + AA: for my $a (@aa) { + for my $b (@bb) { + next AA if ($a eq $b); + } + # next AA was not called, so it was not found under $b, thus it is removed + push @removed, $a; + } + BB: for my $b (@bb) { + for my $a (@aa) { + next BB if ($a eq $b); + } + # next BB was not called, so it was not found under $a, thus it is added + push @added, $b; + } + for my $a (sort @removed) { + push @diff, "-$a"; + } + for my $a (sort @added) { + push @diff, "+$a"; + } + return(@diff); +} + +sub get_from_ups { + my ($what, $dest) = @_; + my $dn = TeXLive::TLUtils::dirname($dest); + system("mkdir -p $dn"); + if (-r "$upstreamoverride/$what") { + system("cp -a $upstreamoverride/$what $dest"); + } else { + system("cp -a $upstreamsource/$what $dest"); + } +} + +### Local Variables: +### perl-indent-level: 2 +### tab-width: 2 +### indent-tabs-mode: nil +### End: +# vim:set tabstop=2 expandtab: # |