summaryrefslogtreecommitdiff
path: root/Build/source/texk/texlive/linked_scripts/texlive/tlmgr.pl
diff options
context:
space:
mode:
authorDenis Bitouzé <dbitouze@wanadoo.fr>2021-02-25 18:23:07 +0000
committerDenis Bitouzé <dbitouze@wanadoo.fr>2021-02-25 18:23:07 +0000
commitc6101f91d071883b48b1b4b51e5eba0f36d9a78d (patch)
tree1bf7f5a881d7a4f5c5bf59d0b2821943dd822372 /Build/source/texk/texlive/linked_scripts/texlive/tlmgr.pl
parent07ee7222e389b0777456b427a55c22d0e6ffd267 (diff)
French translation for tlmgr updated
git-svn-id: svn://tug.org/texlive/trunk@57912 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/texk/texlive/linked_scripts/texlive/tlmgr.pl')
-rwxr-xr-xBuild/source/texk/texlive/linked_scripts/texlive/tlmgr.pl10130
1 files changed, 0 insertions, 10130 deletions
diff --git a/Build/source/texk/texlive/linked_scripts/texlive/tlmgr.pl b/Build/source/texk/texlive/linked_scripts/texlive/tlmgr.pl
deleted file mode 100755
index 13d5e83641f..00000000000
--- a/Build/source/texk/texlive/linked_scripts/texlive/tlmgr.pl
+++ /dev/null
@@ -1,10130 +0,0 @@
-#!/usr/bin/env perl
-# $Id: tlmgr.pl 57772 2021-02-17 19:01:36Z karl $
-#
-# Copyright 2008-2021 Norbert Preining
-# This file is licensed under the GNU General Public License version 2
-# or any later version.
-
-my $svnrev = '$Revision: 57772 $';
-my $datrev = '$Date: 2021-02-17 20:01:36 +0100 (Wed, 17 Feb 2021) $';
-my $tlmgrrevision;
-my $tlmgrversion;
-my $prg;
-my $bindir;
-if ($svnrev =~ m/: ([0-9]+) /) {
- $tlmgrrevision = $1;
-} else {
- $tlmgrrevision = "unknown";
-}
-$datrev =~ s/^.*Date: //;
-$datrev =~ s/ \(.*$//;
-$tlmgrversion = "$tlmgrrevision ($datrev)";
-
-our $Master;
-our $ismain;
-our $loadmediasrcerror;
-our $packagelogfile;
-our $packagelogged;
-our $commandlogfile;
-our $tlmgr_config_file;
-our $pinfile;
-our $action; # for the pod2usage -sections call
-our %opts;
-our $allowed_verify_args_regex = qr/^(none|main|all)$/i;
-
-END {
- if ($opts{"pause"}) {
- print "Press Enter to exit the program.\n";
- <STDIN>;
- }
-}
-
-BEGIN {
- $^W = 1;
- $ismain = (__FILE__ eq $0);
- # WARNING
- # The only use anticipated for tlmgr.pl as library for the 2009 release
- # is the Windows w32client prototype script.
- # Unix-specific problems with use as library will probably go undetected.
-
- # make subprograms (including kpsewhich) have the right path:
- my $kpsewhichname;
- if ($^O =~ /^MSWin/i) {
- # on w32 $0 and __FILE__ point directly to tlmgr.pl; they can be relative
- $Master = __FILE__;
- $Master =~ s!\\!/!g;
- $Master =~ s![^/]*$!../../..!
- unless ($Master =~ s!/texmf-dist/scripts/texlive/tlmgr\.pl$!!i);
- $bindir = "$Master/bin/win32";
- $kpsewhichname = "kpsewhich.exe";
- # path already set by wrapper batchfile
- } else {
- $Master = __FILE__;
- $Master =~ s,/*[^/]*$,,;
- if ($ismain) {
- $bindir = $Master;
- $Master = "$Master/../..";
- } else {
- # for the time being, this code will not be used or tested
- $Master = "$Master/../../..";
- # no code yet for $bindir; would have to detect platform
- }
- # make subprograms (including kpsewhich) have the right path:
- $ENV{"PATH"} = "$bindir:$ENV{PATH}";
- $kpsewhichname = "kpsewhich";
- }
- if (-r "$bindir/$kpsewhichname") {
- # if not in bootstrapping mode => kpsewhich exists, so use it to get $Master
- chomp($Master = `kpsewhich -var-value=SELFAUTOPARENT`);
- }
-
- # if we have no directory in which to find our modules,
- # no point in going on.
- if (! $Master) {
- die ("Could not determine directory of tlmgr executable, "
- . "maybe shared library woes?\nCheck for error messages above");
- }
-
- $::installerdir = $Master; # for config.guess et al., see TLUtils.pm
-
- # make Perl find our packages first:
- unshift (@INC, "$Master/tlpkg");
- unshift (@INC, "$Master/texmf-dist/scripts/texlive");
-}
-
-use Cwd qw/abs_path/;
-use File::Find;
-use File::Spec;
-use Pod::Usage;
-use Getopt::Long qw(:config no_autoabbrev permute);
-use strict;
-
-use TeXLive::TLConfig;
-use TeXLive::TLPDB;
-use TeXLive::TLPOBJ;
-use TeXLive::TLUtils;
-use TeXLive::TLWinGoo;
-use TeXLive::TLDownload;
-use TeXLive::TLConfFile;
-use TeXLive::TLCrypto;
-TeXLive::TLUtils->import(qw(member info give_ctan_mirror win32 dirname
- mkdirhier copy debug tlcmp repository_to_array));
-use TeXLive::TLPaper;
-
-#
-# set up $prg for warning messages
-$prg = TeXLive::TLUtils::basename($0);
-# for usage in various Perl modules
-$::prg = $prg;
-
-binmode(STDOUT, ":utf8");
-binmode(STDERR, ":utf8");
-
-our %config; # hash of config settings from config file
-our $remotetlpdb;
-our $location; # location from which the new packages come
-our $localtlpdb; # local installation which we are munging
-
-# flags for machine-readable form
-our $FLAG_REMOVE = "d";
-our $FLAG_FORCIBLE_REMOVED = "f";
-our $FLAG_UPDATE = "u";
-our $FLAG_REVERSED_UPDATE = "r";
-our $FLAG_AUTOINSTALL = "a";
-our $FLAG_INSTALL = "i";
-our $FLAG_REINSTALL = "I";
-
-# keep in sync with install-tl.
-our $common_fmtutil_args =
- "--no-error-if-no-engine=$TeXLive::TLConfig::PartialEngineSupport";
-
-# option variables
-$::gui_mode = 0;
-$::machinereadable = 0;
-
-my %action_specification = (
- '_include_tlpobj' => {
- "run-post" => 0,
- "function" => \&action_include_tlpobj
- },
- "backup" => {
- "options" => {
- "all" => 1,
- "backupdir" => "=s",
- "clean" => ":-99",
- "dry-run|n" => 1
- },
- "run-post" => 1,
- "function" => \&action_backup
- },
- "candidates" => {
- "run-post" => 0,
- "function" => \&action_candidates
- },
- "check" => {
- "options" => { "use-svn" => 1 },
- "run-post" => 1,
- "function" => \&action_check
- },
- "conf" => {
- "options" => {
- "conffile" => "=s",
- "delete" => 1,
- },
- "run-post" => 0,
- "function" => \&action_conf
- },
- "dump-tlpdb" => {
- "options" => { local => 1, remote => 1 },
- "run-post" => 0,
- "function" => \&action_dumptlpdb
- },
- "generate" => {
- "options" => {
- "dest" => "=s",
- "localcfg" => "=s",
- "rebuild-sys" => 1
- },
- "run-post" => 1,
- "function" => \&action_generate
- },
- "get-mirror" => {
- "run-post" => 0,
- "function" => \&action_get_mirror
- },
- "gui" => {
- "options" => {
- "load" => 1,
- # Tk::CmdLine options
- "background" => "=s",
- "class" => "=s",
- "display" => "=s",
- "font" => "=s",
- "foreground" => "=s",
- "geometry" => "=s",
- "iconic" => 1,
- "motif" => 1,
- "name" => "=s",
- "screen" => "=s",
- "synchronous" => 1,
- "title" => "=s",
- "xrm" => "=s",
- },
- "run-post" => 1,
- "function" => \&action_gui
- },
- "info" => {
- "options" => {
- "data" => "=s",
- "all" => 1,
- "list" => 1,
- "only-installed" => 1,
- "only-remote" => 1
- },
- "run-post" => 0,
- "function" => \&action_info
- },
- "init-usertree" => {
- "run-post" => 0,
- "function" => \&action_init_usertree
- },
- "install" => {
- "options" => {
- "dry-run|n" => 1,
- "file" => 1,
- "force" => 1,
- "no-depends" => 1,
- "no-depends-at-all" => 1,
- "reinstall" => 1,
- "with-doc" => 1,
- "with-src" => 1,
- },
- "run-post" => 1,
- "function" => \&action_install
- },
- "key" => {
- "run-post" => 0,
- "function" => \&action_key
- },
- "option" => {
- "run-post" => 1,
- "function" => \&action_option
- },
- "paper" => {
- "options" => { "list" => 1 },
- "run-post" => 1,
- "function" => \&action_paper
- },
- "path" => {
- "options" => { "w32mode" => "=s" },
- "run-post" => 0,
- "function" => \&action_path
- },
- "pinning" => {
- "options" => { "all" => 1 },
- "run-post" => 1,
- "function" => \&action_pinning
- },
- "platform" => {
- "options" => { "dry-run|n" => 1 },
- "run-post" => 1,
- "function" => \&action_platform
- },
- "postaction" => {
- "options" => {
- "all" => 1,
- "fileassocmode" => "=i",
- "w32mode" => "=s",
- },
- "run-post" => 0,
- "function" => \&action_postaction
- },
- "recreate-tlpdb" => {
- "options" => { "platform|arch" => "=s" },
- "run-post" => 0,
- "function" => \&action_recreate_tlpdb
- },
- "remove" => {
- "options" => {
- "all" => 1,
- "backup" => 1,
- "backupdir" => "=s",
- "dry-run|n" => 1,
- "force" => 1,
- "no-depends" => 1,
- "no-depends-at-all" => 1,
- },
- "run-post" => 1,
- "function" => \&action_remove
- },
- repository => {
- "options" => { "with-platforms" => 1 },
- "run-post" => 1,
- "function" => \&action_repository
- },
- "restore" => {
- "options" => {
- "all" => 1,
- "backupdir" => "=s",
- "dry-run|n" => 1,
- "force" => 1
- },
- "run-post" => 1,
- "function" => \&action_restore
- },
- "search" => {
- "options" => {
- "all" => 1,
- "file" => 1,
- "global" => 1,
- "word" => 1,
- },
- "run-post" => 1,
- "function" => \&action_search
- },
- "shell" => {
- "function" => \&action_shell
- },
- "update" => {
- "options" => {
- "all" => 1,
- "backup" => 1,
- "backupdir" => "=s",
- "dry-run|n" => 1,
- "exclude" => "=s@",
- "force" => 1,
- "list" => 1,
- "no-auto-install" => 1,
- "no-auto-remove" => 1,
- "no-depends" => 1,
- "no-depends-at-all" => 1,
- "no-restart" => 1,
- "reinstall-forcibly-removed" => 1,
- "self" => 1,
- },
- "run-post" => 1,
- "function" => \&action_update
- },
- "version" => { }, # handled separately
-);
-
-my %globaloptions = (
- "gui" => 1,
- "gui-lang" => "=s",
- "debug-json-timing" => 1,
- "debug-translation" => 1,
- "h|?" => 1,
- "help" => 1,
- "json" => 1,
- "location|repository|repo" => "=s",
- "machine-readable" => 1,
- "no-execute-actions" => 1,
- "package-logfile" => "=s",
- "command-logfile" => "=s",
- "persistent-downloads" => "!",
- "pause" => 1,
- "pin-file" => "=s",
- "print-platform|print-arch" => 1,
- "print-platform-info" => 1,
- "usermode|user-mode" => 1,
- "usertree|user-tree" => "=s",
- "verify-repo" => "=s",
- "verify-downloads" => "!",
- "require-verification" => "!",
- "version" => 1,
-);
-
-main() if $ismain;
-
-
-### main ##################################################################
-
-sub main {
- my %options; # TL options from local tlpdb
-
- my %optarg;
- for my $k (keys %globaloptions) {
- if ($globaloptions{$k} eq "1") {
- $optarg{$k} = 1;
- } else {
- $optarg{"$k" . $globaloptions{$k}} = 1;
- }
- }
- for my $v (values %action_specification) {
- if (defined($v->{'options'})) {
- my %opts = %{$v->{'options'}};
- for my $k (keys %opts) {
- if ($opts{$k} eq "1") {
- $optarg{$k} = 1;
- } else {
- $optarg{"$k" . $opts{$k}} = 1;
- }
- }
- }
- }
-
- # save command line options for later restart, if necessary
- @::SAVEDARGV = @ARGV;
-
- TeXLive::TLUtils::process_logging_options();
-
- GetOptions(\%opts, keys(%optarg)) or pod2usage(2);
-
- $::debug_translation = 0;
- $::debug_translation = 1 if $opts{"debug-translation"};
-
- $::machinereadable = $opts{"machine-readable"}
- if (defined($opts{"machine-readable"}));
-
- $action = shift @ARGV;
- if (!defined($action)) {
- if ($opts{"gui"}) { # -gui = gui
- $action = "gui";
- } elsif ($opts{"print-platform"}) {
- $action = "print-platform";
- } elsif ($opts{"print-platform-info"}) {
- $action = "print-platform-info";
- } else {
- $action = "";
- }
- }
- $action = lc($action);
-
- $action = "platform" if ($action eq "arch");
-
- ddebug("action = $action\n");
- for my $k (keys %opts) {
- ddebug("$k => $opts{$k}\n");
- }
- ddebug("arguments: @ARGV\n") if @ARGV;
-
- if ($opts{"version"} || (defined $action && $action eq "version")) {
- if ($::machinereadable) {
- # give_version already is machinereadable aware
- print give_version();
- } else {
- info(give_version());
- }
- exit(0);
- }
-
- if (defined($action) && $action eq "help") {
- $opts{"help"} = 1;
- $action = undef; # an option not an action
- }
-
- if (defined($action) && $action eq "print-platform") {
- print TeXLive::TLUtils::platform(), "\n";
- exit 0;
- }
-
- if (defined($action) && $action eq "print-platform-info") {
- print "config.guess ", `$::installerdir/tlpkg/installer/config.guess`;
- my $plat = TeXLive::TLUtils::platform();
- print "platform ", $plat, "\n";
- print "platform_desc ", TeXLive::TLUtils::platform_desc($plat), "\n";
- exit 0;
- }
-
- # ACTION massaging
- # for backward compatibility and usability
-
- # unify arguments so that the $action contains paper in all cases
- # and push the first arg back to @ARGV for action_paper processing
- if (defined $action
- && $action =~ /^(paper|xdvi|psutils|pdftex|dvips|dvipdfmx?|context)$/) {
- unshift(@ARGV, $action);
- $action = "paper";
- }
-
- # backward compatibility with action "show" and "list" from before
- if (defined $action && $action =~ /^(show|list)$/) {
- $action = "info";
- }
- # merge actions remove and uninstall
- if (defined $action && $action eq "uninstall") {
- $action = "remove";
- }
-
- #
- # check for correctness of verify-repo argument
- if (defined($opts{"verify-repo"}) &&
- ($opts{"verify-repo"} !~ m/$allowed_verify_args_regex/)) {
- tldie("$prg: unknown value for --verify-repo: $opts{'verify-repo'}\n");
- }
- # convert command line crypto options
- $opts{"verify-repo"}
- = convert_crypto_options($opts{"verify-downloads"},
- $opts{"require-verification"},
- $opts{"verify-repo"});
- if (defined($opts{"verify-downloads"})
- || defined($opts{"require-verification"})) {
- tlwarn("$prg: please use -verify-repo options instead of verify-downloads/require-verification\n" .
- "$prg: adjusting to --verify-repo=$opts{'verify-repo'}\n");
- }
- delete $opts{"require-verification"};
- delete $opts{"verify-downloads"};
-
- # now $action should be part of %actionoptions, otherwise this is
- # an error
- if (defined($action) && $action && !exists $action_specification{$action}) {
- die "$prg: unknown action: $action; try --help if you need it.\n";
- }
-
- if ((!defined($action) || !$action) && !$opts{"help"} && !$opts{"h"}) {
- die "$prg: no action given; try --help if you need it.\n";
- }
-
- if ($opts{"help"} || $opts{"h"}) {
- # perldoc does ASCII emphasis on the output, and runs it through
- # $PAGER, so people want it. But not all Unix platforms have it,
- # and on Windows our Config.pm can apparently interfere, so always
- # skip it there. Or if users have NOPERLDOC set in the environment.
- my @noperldoc = ();
- if (win32() || $ENV{"NOPERLDOC"}) {
- @noperldoc = ("-noperldoc", "1");
- } else {
- if (!TeXLive::TLUtils::which("perldoc")) {
- @noperldoc = ("-noperldoc", "1");
- } else {
- # checking only for the existence of perldoc is not enough
- # because Debian/Ubuntu unfortunately ship a stub that does nothing;
- # try to check for that, too.
- my $ret = system("perldoc -V >/dev/null 2>&1");
- if ($ret == 0) {
- debug("working perldoc found, using it\n");
- } else {
- tlwarn("$prg: perldoc seems to be non-functional, not using it.\n");
- @noperldoc = ("-noperldoc", "1");
- }
- }
- }
- # less can break control characters and thus the output of pod2usage
- # is broken. We add/set LESS=-R in the environment and unset
- # LESSPIPE and LESSOPEN to try to help.
- #
- if (defined($ENV{'LESS'})) {
- $ENV{'LESS'} .= " -R";
- } else {
- $ENV{'LESS'} = "-R";
- }
- delete $ENV{'LESSPIPE'};
- delete $ENV{'LESSOPEN'};
- if ($action && ($action ne "help")) {
- # 1) Must use [...] form for -sections arg because otherwise the
- # /$action subsection selector applies to all sections.
- # https://rt.cpan.org/Public/Bug/Display.html?id=102116
- # 2) Must use "..." for that so the $action value is interpolated.
- pod2usage(-exitstatus => 0, -verbose => 99,
- -sections => [ 'NAME', 'SYNOPSIS', "ACTIONS/$::action.*" ],
- @noperldoc);
- } else {
- if ($opts{"help"}) {
- pod2usage(-exitstatus => 0, -verbose => 2, @noperldoc);
- } else {
- # give a short message about usage
- print "
-tlmgr revision $tlmgrversion
-usage: tlmgr OPTION... ACTION ARGUMENT...
-where ACTION is one of:\n";
- for my $k (sort keys %action_specification) {
- # don't print internal options
- next if ($k =~ m/^_/);
- print " $k\n";
- }
- print "\nUse\n tlmgr ACTION --help
-for more details on a specific option, and
- tlmgr --help
-for the full story.\n";
- exit 0;
- }
- }
- }
-
- # --machine-readable is only supported by update.
- if ($::machinereadable &&
- $action ne "update" && $action ne "install" && $action ne "option" && $action ne "shell" && $action ne "remove") {
- tlwarn("$prg: --machine-readable output not supported for $action\n");
- }
-
- #
- # bail out of it is unknown action
- if (!defined($action_specification{$action})) {
- tlwarn("$prg: action unknown: $action\n");
- exit ($F_ERROR);
- }
-
- # check on supported arguments
- #
- my %suppargs;
- %suppargs = %{$action_specification{$action}{'options'}}
- if defined($action_specification{$action}{'options'});
- my @notvalidargs;
- for my $k (keys %opts) {
- my @allargs = keys %suppargs;
- push @allargs, keys %globaloptions;
- my $found = 0;
- for my $ok (@allargs) {
- my @variants = split '\|', $ok;
- if (TeXLive::TLUtils::member($k, @variants)) {
- $found = 1;
- last;
- }
- }
- push @notvalidargs, $k if !$found;
- }
- if (@notvalidargs) {
- my $msg = "The action $action does not support the following option(s):\n";
- for my $c (@notvalidargs) {
- $msg .= " $c";
- }
- tlwarn("$prg: $msg\n");
- tldie("$prg: Try --help if you need it.\n");
- }
-
- #
- # 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"};
- 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")) {
- debug("Cannot open package log file for appending: $packagelogfile\n");
- debug("Will not log package installation/removal/update for this run\n");
- $packagelogfile = "";
- } else {
- debug("appending to package log file: $packagelogfile\n");
- }
-
- # output of executed commands are put into -command-logfile
- $commandlogfile = $opts{"command-logfile"};
- if ($opts{"usermode"}) {
- $commandlogfile ||= "$::maintree/web2c/tlmgr-commands.log";
- } else {
- $commandlogfile ||= "$texmfsysvar/web2c/tlmgr-commands.log";
- }
- # Try to open the packagelog file, but do NOT die when that does not work
- if (!open(COMMANDLOG, ">>$commandlogfile")) {
- debug("Cannot open command log file for appending: $commandlogfile\n");
- debug("Will not log output of executed commands for this run\n");
- $commandlogfile = "";
- } else {
- debug("appending to command log file: $commandlogfile\n");
- }
-
- $loadmediasrcerror = "Cannot load TeX Live database from ";
-
- # load the config file and set the config options
- # load it BEFORE starting downloads as we set persistent-downloads there!
- load_config_file();
-
- # in system mode verify that the selected action is allowed
- if (!$opts{"usermode"} && $config{'allowed-actions'}) {
- if (!TeXLive::TLUtils::member($action, @{$config{'allowed-actions'}})) {
- tlwarn("$prg: action not allowed in system mode: $action\n");
- exit ($F_ERROR);
- }
- }
-
- # set global variable if execute actions should be suppressed
- $::no_execute_actions = 1 if (defined($opts{'no-execute-actions'}));
-
- # if we are asked to use persistent connections try to start it here
- ddebug("tlmgr:main: do persistent downloads = $opts{'persistent-downloads'}\n");
- if ($opts{'persistent-downloads'}) {
- TeXLive::TLUtils::setup_persistent_downloads() ;
- }
- if (!defined($::tldownload_server)) {
- debug("tlmgr:main: ::tldownload_server not defined\n");
- } else {
- if ($::opt_verbosity >= 1) {
- debug(debug_hash_str("$prg:main: ::tldownload_server hash:",
- $::tldownload_server));
- }
- }
-
- my $ret = execute_action($action, @ARGV);
-
- # close the special log file
- if ($packagelogfile && !$::gui_mode) {
- info("$prg: package log updated: $packagelogfile\n") if $packagelogged;
- close(PACKAGELOG);
- }
-
- # F_ERROR stops processing immediately, and prevents postactions from
- # being run (e.g., untar fails). F_WARNING continues on, including
- # postactions (e.g., user tries to install 10 packages and the
- # checksum fails for one, but the others are ok), but still ends the
- # program by exiting unsuccessfully. So call them both "errors"
- # as far as the user is concerned.
- if ($ret & ($F_ERROR | $F_WARNING)) {
- tlwarn("$prg: An error has occurred. See above messages. Exiting.\n");
- }
-
- # end of main program, returns also error codes
- exit ($ret);
-
-} # end main
-
-sub give_version {
- if (!defined($::version_string)) {
- $::version_string = "";
- $::mrversion = "";
- $::version_string .= "tlmgr revision $tlmgrversion\n";
- $::mrversion .= "revision $tlmgrrevision\n";
- $::version_string .= "tlmgr using installation: $Master\n";
- $::mrversion .= "installation $Master\n";
- if (open (REL_TL, "$Master/release-texlive.txt")) {
- # print first, which has the TL version info.
- my $rel_tl = <REL_TL>;
- $::version_string .= $rel_tl;
- # for machine readable we only want the last word which is the version
- my @foo = split(' ', $rel_tl);
- $::mrversion .= "tlversion $foo[$#foo]\n";
- close (REL_TL);
- }
- #
- # add the list of revisions
- if ($::opt_verbosity > 0) {
- $::version_string .= "Revisions of TeXLive:: modules:";
- $::version_string .= "\nTLConfig: " . TeXLive::TLConfig->module_revision();
- $::version_string .= "\nTLUtils: " . TeXLive::TLUtils->module_revision();
- $::version_string .= "\nTLPOBJ: " . TeXLive::TLPOBJ->module_revision();
- $::version_string .= "\nTLPDB: " . TeXLive::TLPDB->module_revision();
- $::version_string .= "\nTLPaper: " . TeXLive::TLPaper->module_revision();
- $::version_string .= "\nTLWinGoo: " . TeXLive::TLWinGoo->module_revision();
- $::version_string .= "\n";
- }
- $::mrversion .= "TLConfig " . TeXLive::TLConfig->module_revision();
- $::mrversion .= "\nTLUtils " . TeXLive::TLUtils->module_revision();
- $::mrversion .= "\nTLPOBJ " . TeXLive::TLPOBJ->module_revision();
- $::mrversion .= "\nTLPDB " . TeXLive::TLPDB->module_revision();
- $::mrversion .= "\nTLPaper " . TeXLive::TLPaper->module_revision();
- $::mrversion .= "\nTLWinGoo " . TeXLive::TLWinGoo->module_revision();
- $::mrversion .= "\n";
- }
- if ($::machinereadable) {
- return $::mrversion;
- } else {
- return $::version_string;
- }
-}
-
-
-sub execute_action {
- my ($action, @argv) = @_;
-
- # we have to set @ARGV to the @argv since many of the action_* subs
- # use GetOption
- @ARGV = @argv;
-
- # actions which shouldn't have any lasting effects, such as search or
- # list, end by calling finish(0), which skips postinstall actions.
- if (!defined($action_specification{$action})) {
- tlwarn ("$prg: unknown action: $action; try --help if you need it.\n");
- return ($F_ERROR);
- }
-
- if (!defined($action_specification{$action}{"function"})) {
- tlwarn ("$prg: action $action defined, but no way to execute it.\n");
- return $F_ERROR;
- }
-
- my $ret = $F_OK;
- my $foo = &{$action_specification{$action}{"function"}}();
- if (defined($foo)) {
- if ($foo & $F_ERROR) {
- # report of bad messages are given at the top level.
- return $foo;
- }
- if ($foo & $F_WARNING) {
- tlwarn("$prg: action $action returned an error; continuing.\n");
- $ret = $foo;
- }
- } else {
- $ret = $F_OK;
- tlwarn("$prg: no value returned from action $action, assuming ok.\n");
- }
- my $run_post = 1;
- if ($ret & $F_NOPOSTACTION) {
- # clear the postaction bit
- $ret ^= $F_NOPOSTACTION;
- $run_post = 0;
- }
- if (!$action_specification{$action}{"run-post"}) {
- $run_post = 0;
- }
-
- return ($ret) if (!$run_post);
-
- # run external programs.
- $ret |= &handle_execute_actions();
-
- return $ret;
-}
-
-
-
-# run CMD with notice to the user and if exit status is nonzero, complain.
-# return exit status.
-#
-sub do_cmd_and_check {
- my $cmd = shift;
- # we output the pre-running notice on a separate line so that
- # tlmgr front ends (MacOSX's TeX Live Utility) can read it
- # and show it to the user before the possibly long delay.
- info("running $cmd ...\n");
- logcommand("running $cmd");
- my ($out, $ret);
- if ($opts{"dry-run"}) {
- $ret = $F_OK;
- $out = "";
- } elsif (win32() && (! -r "$Master/bin/win32/luatex.dll")) {
- # deal with the case where only scheme-infrastructure is installed
- # on Windows, thus no luatex.dll is available and the wrapper cannot be started
- tlwarn("Cannot run wrapper due to missing luatex.dll\n");
- $ret = $F_OK;
- $out = "";
- } else {
- ($out, $ret) = TeXLive::TLUtils::run_cmd("$cmd 2>&1");
- }
- $out =~ s/\n+$//; # trailing newlines don't seem interesting
- my $outmsg = "output:\n$out\n--end of output of $cmd.\n";
- if ($ret == $F_OK) {
- info("done running $cmd.\n");
- logcommand("success, $outmsg");
- ddebug("$cmd $outmsg");
- return ($F_OK);
- } else {
- info("\n");
- tlwarn("$prg: $cmd failed (status $ret), output:\n$out\n");
- logcommand("error, status: $ret, $outmsg");
- return ($F_ERROR);
- }
-}
-
-# run external programs (mktexlsr, updmap-sys, etc.) as specified by the
-# keys in the RET hash. We return the number of unsuccessful runs, zero
-# if all ok.
-#
-# If the "map" key is specified, the value may be a reference to a list
-# of map command strings to pass to updmap, e.g., "enable Map=ascii.map".
-#
-sub handle_execute_actions {
- my $errors = 0;
-
- my $sysmode = ($opts{"usermode"} ? "-user" : "-sys");
- my $invoke_fmtutil = "fmtutil$sysmode $common_fmtutil_args";
-
- if ($::files_changed) {
- $errors += do_cmd_and_check("mktexlsr");
- if (defined($localtlpdb->get_package('context'))
- && (-x "$bindir/texlua" || -x "$bindir/texlua.exe")) {
- $errors += do_cmd_and_check("mtxrun --generate");
- }
- $::files_changed = 0;
- }
-
- chomp(my $TEXMFSYSVAR = `kpsewhich -var-value=TEXMFSYSVAR`);
- chomp(my $TEXMFSYSCONFIG = `kpsewhich -var-value=TEXMFSYSCONFIG`);
- chomp(my $TEXMFLOCAL = `kpsewhich -var-value=TEXMFLOCAL`);
- chomp(my $TEXMFDIST = `kpsewhich -var-value=TEXMFDIST`);
-
- # maps handling
- {
- my $updmap_run_needed = 0;
- for my $m (keys %{$::execute_actions{'enable'}{'maps'}}) {
- $updmap_run_needed = 1;
- }
- for my $m (keys %{$::execute_actions{'disable'}{'maps'}}) {
- $updmap_run_needed = 1;
- }
- my $dest = $opts{"usermode"} ? "$::maintree/web2c/updmap.cfg"
- : "$TEXMFDIST/web2c/updmap.cfg";
- if ($updmap_run_needed) {
- TeXLive::TLUtils::create_updmap($localtlpdb, $dest);
- }
- $errors += do_cmd_and_check("updmap$sysmode") if $updmap_run_needed;
- }
-
- # format relevant things
- # we first have to check if the config files, that is fmtutil.cnf
- # or one of the language* files have changed, regenerate them
- # if necessary, and then run the necessary fmtutil calls.
- {
- # first check for language* files
- my $regenerate_language = 0;
- for my $m (keys %{$::execute_actions{'enable'}{'hyphens'}}) {
- $regenerate_language = 1;
- last;
- }
- for my $m (keys %{$::execute_actions{'disable'}{'hyphens'}}) {
- $regenerate_language = 1;
- last;
- }
- if ($regenerate_language) {
- for my $ext ("dat", "def", "dat.lua") {
- my $lang = "language.$ext";
- info("regenerating $lang\n");
- my $arg1 = "$TEXMFSYSVAR/tex/generic/config/language.$ext";
- my $arg2 = "$TEXMFLOCAL/tex/generic/config/language-local.$ext";
- if ($ext eq "dat") {
- TeXLive::TLUtils::create_language_dat($localtlpdb, $arg1, $arg2);
- } elsif ($ext eq "def") {
- TeXLive::TLUtils::create_language_def($localtlpdb, $arg1, $arg2);
- } else {
- TeXLive::TLUtils::create_language_lua($localtlpdb, $arg1, $arg2);
- }
- }
- }
-
- # format-regenerate is used when the paper size changes. In that
- # case, if option("create_formats") is set, we simply want to generate
- # all formats
- #
- my %done_formats;
- my %updated_engines;
- my %format_to_engine;
- my %do_enable;
- my $do_full = 0;
- for my $m (keys %{$::execute_actions{'enable'}{'formats'}}) {
- $do_full = 1;
- $do_enable{$m} = 1;
- # here we check whether an engine is updated
- my %foo = %{$::execute_actions{'enable'}{'formats'}{$m}};
- if (!defined($foo{'name'}) || !defined($foo{'engine'})) {
- tlwarn("$prg: Very strange error, please report ", %foo);
- } else {
- $format_to_engine{$m} = $foo{'engine'};
- if ($foo{'name'} eq $foo{'engine'}) {
- $updated_engines{$m} = 1;
- }
- }
- }
- for my $m (keys %{$::execute_actions{'disable'}{'formats'}}) {
- $do_full = 1;
- }
- my $opt_fmt = $localtlpdb->option("create_formats");
- if ($do_full) {
- info("regenerating fmtutil.cnf in $TEXMFDIST\n");
- TeXLive::TLUtils::create_fmtutil($localtlpdb,
- "$TEXMFDIST/web2c/fmtutil.cnf");
- }
- if ($opt_fmt && !$::regenerate_all_formats) {
- # first regenerate all formats --byengine
- for my $e (keys %updated_engines) {
- debug ("updating formats based on $e\n");
- $errors += do_cmd_and_check
- ("$invoke_fmtutil --no-error-if-no-format --byengine $e");
- }
- # now rebuild all other formats
- for my $f (keys %do_enable) {
- next if defined($updated_engines{$format_to_engine{$f}});
- # ignore disabled formats
- next if !$::execute_actions{'enable'}{'formats'}{$f}{'mode'};
- debug ("(re)creating format dump $f\n");
- $errors += do_cmd_and_check ("$invoke_fmtutil --byfmt $f");
- $done_formats{$f} = 1;
- }
- }
-
- # now go back to the hyphenation patterns and regenerate formats
- # based on the various language files
- # this of course will in some cases duplicate fmtutil calls,
- # but it is much easier than actually checking which formats
- # don't need to be updated
-
- if ($regenerate_language) {
- for my $ext ("dat", "def", "dat.lua") {
- my $lang = "language.$ext";
- if (! TeXLive::TLUtils::win32()) {
- # Use full path for external command, except on Windows.
- $lang = "$TEXMFSYSVAR/tex/generic/config/$lang";
- }
- if ($localtlpdb->option("create_formats")
- && !$::regenerate_all_formats) {
- $errors += do_cmd_and_check ("$invoke_fmtutil --byhyphen \"$lang\"");
- }
- }
- }
- }
-
- #
- if ($::regenerate_all_formats) {
- info("Regenerating all formats, this may take some time ...");
- $errors += do_cmd_and_check("$invoke_fmtutil --all");
- info("done\n");
- $::regenerate_all_formats = 0;
- }
-
- # undefine the global var, otherwise in GUI mode the actions
- # are accumulating
- undef %::execute_actions;
-
- if ($errors > 0) {
- # should we return warning here?
- return $F_ERROR;
- } else {
- return $F_OK;
- }
-}
-
-
-# GET_MIRROR
-#
-# just return a mirror
-sub action_get_mirror {
- my $loc = give_ctan_mirror();
- print "$loc\n";
- return ($F_OK | $F_NOPOSTACTION);
-}
-
-#
-# includes a .tlpobj in the db, also searchers for sub-tlpobj
-# for doc and source files
-#
-
-# _INCLUDE_TLPOBJ
-#
-# includes a .tlpobj in the db, also searchers for sub-tlpobj
-# for doc and source files
-#
-sub action_include_tlpobj {
- # this is an internal function that should not be used outside
- init_local_db();
- for my $f (@ARGV) {
- my $tlpobj = TeXLive::TLPOBJ->new;
- $tlpobj->from_file($f);
- # we now have to check whether that is a .doc or .src package, so shipping
- # src or doc files from a different package.
- # We should have that package already installed ...
- my $pkg = $tlpobj->name;
- if ($pkg =~ m/^(.*)\.(source|doc)$/) {
- # got a .src or .doc package
- my $type = $2;
- my $mothership = $1;
- my $mothertlp = $localtlpdb->get_package($mothership);
- if (!defined($mothertlp)) {
- tlwarn("$prg: We are trying to add ${type} files to a nonexistent package $mothership!\n");
- tlwarn("$prg: Trying to continue!\n");
- # the best we can do is rename that package to $mothername and add it!
- $tlpobj->name($mothership);
- # add the src/docfiles tlpobj under the mothership name
- $localtlpdb->add_tlpobj($tlpobj);
- } else {
- if ($type eq "source") {
- $mothertlp->srcfiles($tlpobj->srcfiles);
- $mothertlp->srcsize($tlpobj->srcsize);
- } else {
- # must be "doc"
- $mothertlp->docfiles($tlpobj->docfiles);
- $mothertlp->docsize($tlpobj->docsize);
- }
- # that make sure that the original entry is overwritten
- $localtlpdb->add_tlpobj($mothertlp);
- }
- } else {
- # completely normal package, just add it
- $localtlpdb->add_tlpobj($tlpobj);
- }
- $localtlpdb->save;
- }
- # no error checking here for now
- return ($F_OK);
-}
-
-
-# REMOVE
-#
-# tlmgr remove foo bar baz
-# will remove the packages foo bar baz itself
-# and will remove all .ARCH dependencies, too
-# and if some of them are collections it will also remove the
-# depending packages which are NOT Collections|Schemes.
-# if some of them are referenced somewhere they will not be removed
-# unless --force given
-#
-# tlmgr remove --no-depends foo bar baz
-# will remove the packages foo bar baz itself without any dependencies
-# but it will still remove all .ARCH dependency
-# if some of them are referenced somewhere they will not be removed
-# unless --force given
-#
-# tlmgr remove --no-depends-at-all foo bar baz
-# will absolutely only install foo bar baz not even taking .ARCH into
-# account
-#
-
-sub backup_and_remove_package {
- my ($pkg, $autobackup) = @_;
- my $tlp = $localtlpdb->get_package($pkg);
- if (!defined($tlp)) {
- info("$pkg: package not present, cannot remove\n");
- return($F_WARNING);
- }
- if ($opts{"backup"}) {
- $tlp->make_container($::progs{'compressor'}, $localtlpdb->root,
- destdir => $opts{"backupdir"},
- relative => $tlp->relocated,
- user => 1);
- if ($autobackup) {
- # in case we do auto backups we remove older backups
- clear_old_backups($pkg, $opts{"backupdir"}, $autobackup);
- }
- }
- return($localtlpdb->remove_package($pkg));
-}
-
-sub action_remove {
- # if --all is given, pass on to uninstall_texlive
- if ($opts{'all'}) {
- if (@ARGV) {
- tlwarn("$prg: No additional arguments allowed with --all: @ARGV\n");
- return($F_ERROR);
- }
- exit(uninstall_texlive());
- }
- # we do the following:
- # - (not implemented) order collections such that those depending on
- # other collections are first removed, and then those which only
- # depend on packages. Otherwise
- # remove collection-latex collection-latexrecommended
- # will not succeed
- # - first loop over all cmd line args and consider only the collections
- # - for each to be removed collection:
- # . check that no other collections/scheme asks for that collection
- # . remove the collection
- # . remove all dependencies
- # - for each normal package not already removed (via the above)
- # . check that no collection/scheme still depends on this package
- # . remove the package
- #
- $opts{"no-depends"} = 1 if $opts{"no-depends-at-all"};
- my %already_removed;
- my @more_removal;
- init_local_db();
- return($F_ERROR) if !check_on_writable();
- info("$prg remove: dry run, no changes will be made\n") if $opts{"dry-run"};
-
- my ($ret, $autobackup) = setup_backup_directory();
- return ($ret) if ($ret != $F_OK);
-
- my @packs = @ARGV;
- #
- # we have to be careful not to remove too many packages. The idea is
- # as follows:
- # - let A be the set of all packages to be removed from the cmd line
- # - let A* be the set of A with all dependencies expanded
- # - let B be the set of all packages
- # - let C = B \ A*, ie the set of all packages without those packages
- # in the set of A*
- # - let C* be the set of C with all dependencies expanded
- # - let D = A* \ C*, ie the set of all packages to be removed (A*)
- # without all the package that are still needed (C*)
- # - remove all package in D
- # - for any package in A (not in A*, in A, ie on the cmd line) that is
- # also in C* (so a package that was asked for to be removed on the
- # cmd line, but it isn't because someone else asks for it), warn the
- # user that it is still needed
- #
- # remove all .ARCH dependencies, too, unless $opts{"no-depends-at-all"}
- @packs = $localtlpdb->expand_dependencies("-only-arch", $localtlpdb, @packs)
- unless $opts{"no-depends-at-all"};
- # remove deps unless $opts{"no-depends"}
- @packs = $localtlpdb->expand_dependencies("-no-collections", $localtlpdb, @packs) unless $opts{"no-depends"};
- my %allpacks;
- for my $p ($localtlpdb->list_packages) { $allpacks{$p} = 1; }
- for my $p (@packs) { delete($allpacks{$p}); }
- my @neededpacks = $localtlpdb->expand_dependencies($localtlpdb, keys %allpacks);
- my %packs;
- my %origpacks;
- my @origpacks = $localtlpdb->expand_dependencies("-only-arch", $localtlpdb, @ARGV) unless $opts{"no-depends-at-all"};
- for my $p (@origpacks) { $origpacks{$p} = 1; }
- for my $p (@packs) { $packs{$p} = 1; }
- for my $p (@neededpacks) {
- if (defined($origpacks{$p})) {
- # that package was asked for to be removed on the cmd line
- my @needed = $localtlpdb->needed_by($p);
- if ($opts{"force"}) {
- info("$prg: $p is needed by " . join(" ", @needed) . "\n");
- info("$prg: removing it anyway, due to --force\n");
- } else {
- delete($packs{$p});
- tlwarn("$prg: not removing $p, needed by " .
- join(" ", @needed) . "\n");
- $ret |= $F_WARNING;
- }
- } else {
- delete($packs{$p});
- }
- }
- @packs = keys %packs;
-
- my %sizes = %{$localtlpdb->sizes_of_packages(
- $localtlpdb->option("install_srcfiles"),
- $localtlpdb->option("install_docfiles"), undef, @packs)};
- defined($sizes{'__TOTAL__'}) || ($sizes{'__TOTAL__'} = 0);
- my $totalsize = $sizes{'__TOTAL__'};
- my $totalnr = $#packs;
- my $currnr = 1;
- my $starttime = time();
- my $donesize = 0;
-
- print "total-bytes\t$sizes{'__TOTAL__'}\n" if $::machinereadable;
- print "end-of-header\n" if $::machinereadable;
-
- foreach my $pkg (sort @packs) {
- my $tlp = $localtlpdb->get_package($pkg);
- next if defined($already_removed{$pkg});
- if (!defined($tlp)) {
- info("$pkg: package not present, cannot remove\n");
- $ret |= $F_WARNING;
- } else {
- my ($estrem, $esttot) = TeXLive::TLUtils::time_estimate($totalsize,
- $donesize, $starttime);
-
- # in the first round we only remove collections, nothing else
- # but removing collections will remove all dependencies, too
- # save the information of which packages have already been removed
- # into %already_removed.
- if ($tlp->category eq "Collection") {
- my $foo = 0;
- if ($::machinereadable) {
- machine_line($pkg, "d", $tlp->revision, "-", $sizes{$pkg}, $estrem, $esttot);
- } else {
- # info ("$prg: removing $pkg\n");
- info("[$currnr/$totalnr, $estrem/$esttot] remove: $pkg\n");
- }
- if (!$opts{"dry-run"}) {
- $foo = backup_and_remove_package($pkg, $autobackup);
- logpackage("remove: $pkg");
- }
- $currnr++;
- $donesize += $sizes{$pkg};
- if ($foo) {
- # removal was successful, so the return is at least 0x0001 mktexlsr
- # remove dependencies, too
- $already_removed{$pkg} = 1;
- }
- } else {
- # save all the other packages into the @more_removal list to
- # be removed at the second state. Note that if a package has
- # already been removed due to a removal of a collection
- # it will be marked as such in %already_removed and not tried again
- push (@more_removal, $pkg);
- }
- }
- }
- foreach my $pkg (sort @more_removal) {
- my $tlp = $localtlpdb->get_package($pkg);
- if (!defined($already_removed{$pkg})) {
- my ($estrem, $esttot) = TeXLive::TLUtils::time_estimate($totalsize,
- $donesize, $starttime);
- # info ("$prg: removing package $pkg\n");
- if ($::machinereadable) {
- machine_line($pkg, "d", $tlp->revision, "-", $sizes{$pkg}, $estrem, $esttot);
- } else {
- # info ("$prg: removing $pkg\n");
- info("[$currnr/$totalnr, $estrem/$esttot] remove: $pkg\n");
- }
- $currnr++;
- $donesize += $sizes{$pkg};
- if (!$opts{"dry-run"}) {
- if (backup_and_remove_package($pkg, $autobackup)) {
- # removal was successful
- logpackage("remove: $pkg");
- $already_removed{$pkg} = 1;
- }
- }
- }
- }
- print "end-of-updates\n" if $::machinereadable;
- if ($opts{"dry-run"}) {
- # stop here, don't do any postinstall actions
- return ($ret | $F_NOPOSTACTION);
- } else {
- $localtlpdb->save;
- my @foo = sort keys %already_removed;
- if (@foo) {
- info("$prg: ultimately removed these packages: @foo\n")
- if (!$::machinereadable);
- } else {
- info("$prg: no packages removed.\n")
- if (!$::machinereadable);
- }
- }
- return ($ret);
-}
-
-
-# PAPER
-#
-# ARGV can look like:
-# paper a4
-# paper letter
-# [xdvi|...|context] paper [help|papersize|--list]
-#
-sub action_paper {
- init_local_db();
- my $texmfconfig;
- if ($opts{"usermode"}) {
- tlwarn("$prg: action `paper' not supported in usermode\n");
- return ($F_ERROR);
- }
- chomp($texmfconfig = `kpsewhich -var-value=TEXMFSYSCONFIG`);
- $ENV{"TEXMFCONFIG"} = $texmfconfig;
-
- my $action = shift @ARGV;
- if (!$action) {
- # can only happen in shell mode, because otherwise we push paper onto the stack before
- # going into the action_paper
- $action = "paper";
- }
-
- if ($action =~ m/^paper$/i) { # generic paper
- my $newpaper = shift @ARGV;
- if ($opts{"list"}) { # tlmgr paper --list => complain.
- tlwarn("$prg: ignoring paper setting to $newpaper with --list\n")
- if $newpaper; # complain if they tried to set, too.
- tlwarn("$prg: please specify a program before paper --list, ",
- "as in: tlmgr pdftex paper --list\n");
- return($F_ERROR)
-
- } elsif (!defined($newpaper)) { # tlmgr paper => show all current sizes.
- my $ret = $F_OK;
- if ($opts{'json'}) {
- my @foo;
- for my $prog (keys %TeXLive::TLPaper::paper) {
- my $pkg = $TeXLive::TLPaper::paper{$prog}{'pkg'};
- if ($localtlpdb->get_package($pkg)) {
- my $val = TeXLive::TLPaper::do_paper($prog,$texmfconfig,"--json");
- push @foo, $val;
- }
- }
- my $json = TeXLive::TLUtils::encode_json(\@foo);
- print "$json\n";
- return $ret;
- }
- for my $prog (sort keys %TeXLive::TLPaper::paper) {
- my $pkg = $TeXLive::TLPaper::paper{$prog}{'pkg'};
- if ($localtlpdb->get_package($pkg)) {
- $ret |= TeXLive::TLPaper::do_paper($prog,$texmfconfig,undef);
- }
- }
- return($ret);
- # return TeXLive::TLPaper::paper_all($texmfconfig,undef);
-
- } elsif ($newpaper !~ /^(a4|letter)$/) { # tlmgr paper junk => complain.
- $newpaper = "the empty string" if !defined($newpaper);
- tlwarn("$prg: expected `a4' or `letter' after paper, not $newpaper\n");
- return($F_ERROR);
-
- } else { # tlmgr paper {a4|letter} => do it.
- return ($F_ERROR) if !check_on_writable();
- if ($opts{'json'}) {
- tlwarn("$prg: option --json not supported with other arguments\n");
- return ($F_ERROR);
- }
- my $ret = $F_OK;
- for my $prog (sort keys %TeXLive::TLPaper::paper) {
- my $pkg = $TeXLive::TLPaper::paper{$prog}{'pkg'};
- if ($localtlpdb->get_package($pkg)) {
- $ret |= TeXLive::TLPaper::do_paper($prog,$texmfconfig,$newpaper);
- }
- }
- return($ret);
- # return (TeXLive::TLPaper::paper_all($texmfconfig,$newpaper));
- }
-
- } else { # program-specific paper
- if ($opts{'json'}) {
- tlwarn("$prg: option --json not supported with other arguments\n");
- return ($F_ERROR);
- }
- my $prog = $action; # first argument is the program to change
- my $pkg = $TeXLive::TLPaper::paper{$prog}{'pkg'};
- if (!$pkg) {
- tlwarn("Unknown paper configuration program $prog!\n");
- return ($F_ERROR);
- }
- if (!$localtlpdb->get_package($pkg)) {
- tlwarn("$prg: package $prog is not installed - cannot adjust paper size!\n");
- return ($F_ERROR);
- }
- my $arg = shift @ARGV; # get "paper" argument
- if (!defined($arg) || $arg ne "paper") {
- $arg = "the empty string." if ! $arg;
- tlwarn("$prg: expected `paper' after $prog, not $arg\n");
- return ($F_ERROR);
- }
- # the do_paper progs check for the argument --list, so if given
- # restore it to the cmd line.
- if (@ARGV) {
- return ($F_ERROR) if !check_on_writable();
- }
- unshift(@ARGV, "--list") if $opts{"list"};
- return(TeXLive::TLPaper::do_paper($prog,$texmfconfig,@ARGV));
- }
- # we should not come here anyway
- return($F_OK);
-}
-
-
-# PATH
-#
-sub action_path {
- if ($opts{"usermode"}) {
- tlwarn("$prg: action `path' not supported in usermode!\n");
- exit 1;
- }
- my $what = shift @ARGV;
- if (!defined($what) || ($what !~ m/^(add|remove)$/i)) {
- $what = "" if ! $what;
- tlwarn("$prg: action path requires add or remove, not: $what\n");
- return ($F_ERROR);
- }
- init_local_db();
- my $winadminmode = 0;
- if (win32()) {
- #
- # for w32 we do system wide vs user setting detection as follows:
- # - if --w32mode is NOT given,
- # - if admin
- # --> honor opt_w32_multi_user setting in tlpdb
- # - if not admin
- # - if opt_w32_multi_user == NO
- # --> do user path adjustment
- # - if opt_w32_multi_user == YES
- # --> do nothing, warn that the setting is on, suggest --w32mode user
- # - if --w32mode admin
- # - if admin
- # --> ignore opt_w32_multi_user and do system path adjustment
- # - if non-admin
- # --> do nothing but warn that user does not have privileges
- # - if --w32mode user
- # - if admin
- # --> ignore opt_w32_multi_user and do user path adjustment
- # - if non-admin
- # --> ignore opt_w32_multi_user and do user path adjustment
- if (!$opts{"w32mode"}) {
- $winadminmode = $localtlpdb->option("w32_multi_user");
- if (!TeXLive::TLWinGoo::admin()) {
- if ($winadminmode) {
- tlwarn("The TLPDB specifies system wide path adjustments\nbut you don't have admin privileges.\nFor user path adjustment please use\n\t--w32mode user\n");
- # and do nothing
- return ($F_ERROR);
- }
- }
- } else {
- # we are in the block where a --w32mode argument is given
- # we reverse the tests:
- if (TeXLive::TLWinGoo::admin()) {
- # in admin mode we simply use what is given on the cmd line
- if ($opts{"w32mode"} eq "user") {
- $winadminmode = 0;
- } elsif ($opts{"w32mode"} eq "admin") {
- $winadminmode = 1;
- } else {
- tlwarn("$prg: unknown --w32admin mode: $opts{w32mode}, should be 'admin' or 'user'\n");
- return ($F_ERROR);
- }
- } else {
- # we are non-admin
- if ($opts{"w32mode"} eq "user") {
- $winadminmode = 0;
- } elsif ($opts{"w32mode"} eq "admin") {
- tlwarn("$prg: You don't have the privileges to work in --w32mode admin\n");
- return ($F_ERROR);
- } else {
- tlwarn("$prg: unknown --w32admin mode: $opts{w32mode}, should be 'admin' or 'user'\n");
- return ($F_ERROR);
- }
- }
- }
- }
- my $ret = $F_OK;
- if ($what =~ m/^add$/i) {
- if (win32()) {
- $ret |= TeXLive::TLUtils::w32_add_to_path(
- $localtlpdb->root . "/bin/win32",
- $winadminmode);
- # ignore this return value, since broadcase_env might return
- # nothing in case of errors, and there is no way around it.
- # $ret |= TeXLive::TLWinGoo::broadcast_env();
- } else {
- $ret |= TeXLive::TLUtils::add_symlinks($localtlpdb->root,
- $localtlpdb->platform(),
- $localtlpdb->option("sys_bin"),
- $localtlpdb->option("sys_man"),
- $localtlpdb->option("sys_info"));
- }
- } elsif ($what =~ m/^remove$/i) {
- if (win32()) {
- $ret |= TeXLive::TLUtils::w32_remove_from_path(
- $localtlpdb->root . "/bin/win32",
- $winadminmode);
- # ignore this return value, since broadcase_env might return
- # nothing in case of errors, and there is no way around it.
- # $ret |= TeXLive::TLWinGoo::broadcast_env();
- } else {
- # remove symlinks
- $ret |= TeXLive::TLUtils::remove_symlinks($localtlpdb->root,
- $localtlpdb->platform(),
- $localtlpdb->option("sys_bin"),
- $localtlpdb->option("sys_man"),
- $localtlpdb->option("sys_info"));
- }
- } else {
- tlwarn("\n$prg: Should not happen, action_path what=$what\n");
- return ($F_ERROR);
- }
- # we should not need to run any post actions here, since
- # that changes only integrations, but no rebuild of formats etc etc
- # is needed
- return ($ret | $F_NOPOSTACTION);
-}
-
-# DUMP-TLPDB
-#
-sub action_dumptlpdb {
- init_local_db();
-
- # we are basically doing machine-readable output.
- my $savemr = $::machinereadable;
- $::machinereadable = 1;
-
- if ($opts{"local"} && !$opts{"remote"}) {
- if ($opts{"json"}) {
- print $localtlpdb->as_json;
- } else {
- # for consistency we write out the location of the installation,
- # too, in the same format as when dumping the remote tlpdb
- print "location-url\t", $localtlpdb->root, "\n";
- $localtlpdb->writeout;
- }
-
- } elsif ($opts{"remote"} && !$opts{"local"}) {
- init_tlmedia_or_die(1);
- if ($opts{"json"}) {
- print $remotetlpdb->as_json;
- } else {
- $remotetlpdb->writeout;
- }
-
- } else {
- tlwarn("$prg dump-tlpdb: need exactly one of --local and --remote.\n");
- return ($F_ERROR);
- }
-
- $::machinereadable = $savemr;
- return ($F_OK | $F_NOPOSTACTION);
-}
-
-# INFO
-#
-sub action_info {
- if ($opts{'only-installed'} && $opts{'only-remote'}) {
- tlwarn("Are you joking? --only-installed and --only-remote cannot both be specified!\n");
- return($F_ERROR);
- }
- init_local_db();
- my ($what,@todo) = @ARGV;
- my $ret = $F_OK | $F_NOPOSTACTION;
- my @datafields;
- my $fmt = "list";
- if ($opts{'data'} && $opts{'json'}) {
- tlwarn("Preferring json output over data output!\n");
- delete($opts{'data'});
- }
- if ($opts{'json'}) {
- $fmt = 'json';
- # the 1 is the silent mode!
- init_tlmedia_or_die(1);
- } elsif ($opts{'data'}) {
- # output format is changed to csv with " as quotes
- # we need to determine the fields
- @datafields = split(',', $opts{'data'});
- # check for correctness of data fields and whether remote is necessary
- my $load_remote = 0;
- for my $d (@datafields) {
- $load_remote = 1 if ($d eq "remoterev");
- if ($d !~ m/^(name|category|localrev|remoterev|shortdesc|longdesc|size|installed|relocatable|depends|[lr]?cat-version|[lr]?cat-date|[lr]?cat-license|[lr]?cat-contact-.*)$/) {
- tlwarn("unknown data field: $d\n");
- return($F_ERROR);
- }
- }
- $fmt = "csv";
- if ($load_remote) {
- if ($opts{"only-installed"}) {
- tlwarn("requesting only-installed with data field remoterev, loading remote anyway!\n");
- $opts{"only-installed"} = 0;
- }
- # loading of tlpdb is done below
- }
- } elsif (!$what || $what =~ m/^(collections|schemes)$/i) {
- $fmt = "list";
- } else {
- $fmt = "detail";
- }
- my $tlm;
- if ($opts{"only-installed"}) {
- $tlm = $localtlpdb;
- } else {
- # silent mode
- init_tlmedia_or_die(1);
- $tlm = $remotetlpdb;
- }
-
- #
- # tlmgr info
- # tlmgr info collection
- # tlmgr info scheme
- # these commands just list the packages/collections/schemes installed with
- # a short list
- my @whattolist;
- $what = ($what || "-all");
- if ($what =~ m/^collections$/i) {
- @whattolist = $tlm->collections;
- } elsif ($what =~ m/^schemes$/i) {
- @whattolist = $tlm->schemes;
- } elsif ($what =~ m/^-all$/i) {
- if ($tlm->is_virtual) {
- @whattolist = $tlm->list_packages("-all");
- } else {
- @whattolist = $tlm->list_packages;
- }
- if (!$opts{'only-remote'}) {
- # add also the local packages
- TeXLive::TLUtils::push_uniq(\@whattolist, $localtlpdb->list_packages);
- }
- } else {
- @whattolist = ($what, @todo);
- }
- my @adds;
- if ($opts{'data'}) {
- @adds = @datafields;
- }
- # TIMING OF JSON IMPLEMENTATIONS
- my ($startsec, $startmsec);
- if ($opts{'debug-json-timing'}) {
- require Time::HiRes;
- ($startsec, $startmsec) = Time::HiRes::gettimeofday();
- }
- print "[" if ($fmt eq "json");
- my $first = 1;
- foreach my $ppp (@whattolist) {
- next if ($ppp =~ m/^00texlive\./);
- print "," if ($fmt eq "json" && !$first);
- $first = 0;
- $ret |= show_one_package($ppp, $fmt, @adds);
- }
- print "]\n" if ($fmt eq "json");
- if ($opts{'debug-json-timing'}) {
- my ($endsec, $endmsec) = Time::HiRes::gettimeofday();
- if ($endmsec < $startmsec) {
- $endsec -= 1;
- $endmsec += 1000000;
- }
- print STDERR "JSON (", $TeXLive::TLUtils::jsonmode, ") generation took ", $endsec - $startsec, ".", substr($endmsec - $startmsec,0,2), " sec\n";
- }
- return ($ret);
-}
-
-
-# SEARCH
-#
-sub action_search {
- my ($r) = @ARGV;
- my $tlpdb;
- # check the arguments
- my $search_type_nr = 0;
- $search_type_nr++ if $opts{"file"};
- $search_type_nr++ if $opts{"all"};
- if ($search_type_nr > 1) {
- tlwarn("$prg: please specify only one thing to search for\n");
- return ($F_ERROR);
- }
- #
- if (!defined($r) || !$r) {
- tlwarn("$prg: nothing to search for.\n");
- return ($F_ERROR);
- }
-
- init_local_db();
- if ($opts{"global"}) {
- init_tlmedia_or_die();
- $tlpdb = $remotetlpdb;
- } else {
- $tlpdb = $localtlpdb;
- }
-
- my ($foundfile, $founddesc) = search_tlpdb($tlpdb, $r,
- $opts{'file'} || $opts{'all'},
- (!$opts{'file'} || $opts{'all'}),
- $opts{'word'});
-
- print $founddesc;
- print $foundfile;
-
- return ($F_OK | $F_NOPOSTACTION);
-}
-
-sub search_tlpdb {
- my ($tlpdb, $what, $dofile, $dodesc, $inword) = @_;
- my $retfile = '';
- my $retdesc = '';
- foreach my $pkg ($tlpdb->list_packages) {
- my $tlp = $tlpdb->get_package($pkg);
-
- # --file or --all -> search (full) file names
- if ($dofile) {
- my @ret = search_pkg_files($tlp, $what);
- if (@ret) {
- $retfile .= "$pkg:\n";
- foreach (@ret) {
- $retfile .= "\t$_\n";
- }
- }
- }
- #
- # no options or --all -> search package names/descriptions
- if ($dodesc) {
- next if ($pkg =~ m/\./);
- my $matched = search_pkg_desc($tlp, $what, $inword);
- $retdesc .= "$matched\n" if ($matched);
- }
- }
- return($retfile, $retdesc);
-}
-
-sub search_pkg_desc {
- my ($tlp, $what, $inword) = @_;
- my $pkg = $tlp->name;
- my $t = "$pkg\n";
- $t = $t . $tlp->shortdesc . "\n" if (defined($tlp->shortdesc));
- $t = $t . $tlp->longdesc . "\n" if (defined($tlp->longdesc));
- $t = $t . $tlp->cataloguedata->{'topics'} . "\n" if (defined($tlp->cataloguedata->{'topics'}));
- my $pat = $what;
- $pat = '\W' . $what . '\W' if ($inword);
- my $matched = "";
- if ($t =~ m/$pat/i) {
- my $shortdesc = $tlp->shortdesc || "";
- $matched .= "$pkg - $shortdesc";
- }
- return $matched;
-}
-
-sub search_pkg_files {
- my ($tlp, $what) = @_;
- my @files = $tlp->all_files;
- if ($tlp->relocated) {
- for (@files) { s:^$RelocPrefix/:$RelocTree/:; }
- }
- my @ret = grep(m;$what;, @files);
- return @ret;
-}
-
-# RESTORE
-#
-# read the directory and check what files/package/rev are available
-# for restore
-sub get_available_backups {
- my $bd = shift;
- my $do_stat = shift;
- # initialize the hash(packages) of hash(revisions)
- my %backups;
- opendir (DIR, $bd) || die "opendir($bd) failed: $!";
- my @dirents = readdir (DIR);
- closedir (DIR) || warn "closedir($bd) failed: $!";
- #
- # see below for explanation, this has effects only on W32
- my $oldwsloppy = ${^WIN32_SLOPPY_STAT};
- ${^WIN32_SLOPPY_STAT} = 1;
- #
- my $pkg;
- my $rev;
- my $ext;
- for my $dirent (@dirents) {
- $pkg = "";
- $rev = "";
- $ext = "";
- next if (-d $dirent);
- if ($dirent =~ m/^(.*)\.r([0-9]+)\.tar\.$CompressorExtRegexp$/) {
- $pkg = $1;
- $rev = $2;
- $ext = $3;
- } else {
- next;
- }
- if (!$do_stat) {
- $backups{$pkg}->{$rev} = 1;
- next;
- }
- my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
- $atime,$mtime,$ctime,$blksize,$blocks) = stat("$bd/$dirent");
- # times: as we want to be portable we try the following times:
- # - first choice is ctime which hopefully works nicely
- # - on UFS (OSX) ctime is not supported, so use mtime
- # furthermore, if we are on W32 we want to be fast and make only
- # a sloppy stat
- # for more on that please see man perlport
- my $usedt = $ctime;
- if (!$usedt) {
- # can happen on
- $usedt = $mtime;
- }
- if (!$usedt) {
- # stat failed, set key to -1 as a sign that there is a backup
- # but we cannot stat it
- $backups{$pkg}->{$rev} = -1;
- } else {
- $backups{$pkg}->{$rev} = $usedt;
- }
- }
- # reset the original value of the w32 sloppy mode for stating files
- ${^WIN32_SLOPPY_STAT} = $oldwsloppy;
- return %backups;
-}
-
-sub restore_one_package {
- my ($pkg, $rev, $bd) = @_;
- # first remove the package, then reinstall it
- # this way we get rid of useless files
- my $restore_file;
- for my $ext (map {$Compressors{$_}{'extension'}}
- sort {$Compressors{$a}{'priority'} <=> $Compressors{$a}{'priority'}}
- keys %Compressors) {
- if (-r "$bd/${pkg}.r${rev}.tar.$ext") {
- $restore_file = "$bd/${pkg}.r${rev}.tar.$ext";
- last;
- }
- }
- if (!$restore_file) {
- tlwarn("$prg: cannot find restore file $bd/${pkg}.r${rev}.tar.*, no action taken\n");
- return ($F_ERROR);
- }
- $localtlpdb->remove_package($pkg);
- # the -1 force the TLUtils::unpack to NOT warn about missing checksum/sizes
- TeXLive::TLPDB->_install_data($restore_file , 0, [], $localtlpdb, "-1", "-1");
- logpackage("restore: $pkg ($rev)");
- # now we have to read the .tlpobj file and add it to the DB
- my $tlpobj = TeXLive::TLPOBJ->new;
- $tlpobj->from_file($localtlpdb->root . "/tlpkg/tlpobj/$pkg.tlpobj");
- $localtlpdb->add_tlpobj($tlpobj);
- TeXLive::TLUtils::announce_execute_actions("enable",
- $localtlpdb->get_package($pkg));
- check_announce_format_triggers($pkg);
- $localtlpdb->save;
- # TODO_ERRORCHECKING we should check the return values of the
- # various calls above
- return ($F_OK);
-}
-
-sub setup_backup_directory {
- my $ret = $F_OK;
- my $autobackup = 0;
- # check for the tlpdb option autobackup, and if present and true (!= 0)
- # assume we are doing backups
- if (!$opts{"backup"}) {
- $autobackup = $localtlpdb->option("autobackup");
- if ($autobackup) {
- # check the format, we currently allow only natural numbers, and -1
- if ($autobackup eq "-1") {
- debug ("Automatic backups activated, keeping all backups.\n");
- $opts{"backup"} = 1;
- } elsif ($autobackup eq "0") {
- debug ("Automatic backups disabled.\n");
- } elsif ($autobackup =~ m/^[0-9]+$/) {
- debug ("Automatic backups activated, keeping $autobackup backups.\n");
- $opts{"backup"} = 1;
- } else {
- tlwarn ("$prg: Option autobackup value can only be an integer >= -1.\n");
- tlwarn ("$prg: Disabling auto backups.\n");
- $localtlpdb->option("autobackup", 0);
- $autobackup = 0;
- $ret |= $F_WARNING;
- }
- }
- }
-
- # cmd line --backup, we check for --backupdir, and if that is not given
- # we try to get the default from the tlpdb. If that doesn't work, exit.
- if ($opts{"backup"}) {
- my ($a, $b) = check_backupdir_selection();
- if ($a & $F_ERROR) {
- # in all these cases we want to terminate in the non-gui mode
- tlwarn($b);
- return ($F_ERROR, $autobackup);
- }
- }
-
- # finally, if we have --backupdir, but no --backup, just enable it
- $opts{"backup"} = 1 if $opts{"backupdir"};
-
- my $saving_verb = $opts{"dry-run"} || $opts{"list"} ? "would save" :"saving";
- info("$prg: $saving_verb backups to $opts{'backupdir'}\n")
- if $opts{"backup"} && !$::machinereadable;
-
- return ($ret, $autobackup);
-}
-
-sub check_backupdir_selection {
- my $warntext = "";
- if ($opts{"backupdir"}) {
- my $ob = abs_path($opts{"backupdir"});
- $ob && ($opts{"backupdir"} = $ob);
- if (! -d $opts{"backupdir"}) {
- $warntext .= "$prg: backupdir argument\n";
- $warntext .= " $opts{'backupdir'}\n";
- $warntext .= "is not a directory.\n";
- return ($F_ERROR, $warntext);
- }
- } else {
- # no argument, check for presence in TLPDB
- init_local_db(1);
- $opts{"backupdir"} = norm_tlpdb_path($localtlpdb->option("backupdir"));
- if (!$opts{"backupdir"}) {
- return (0, "$prg: cannot determine backupdir.\n");
- }
- # we are still here, there is something set in tlpdb
- my $ob = abs_path($opts{"backupdir"});
- $ob && ($opts{"backupdir"} = $ob);
- if (! -d $opts{"backupdir"}) {
- $warntext = "$prg: backupdir as set in tlpdb\n";
- $warntext .= " $opts{'backupdir'}\n";
- $warntext .= "is not a directory.\n";
- return ($F_ERROR, $warntext);
- }
- }
- return $F_OK;
-}
-
-sub action_restore {
- # tlmgr restore [--backupdir dir] --all
- # restores of all packages found in backupdir the latest version
- # tlmgr restore --backupdir dir
- # lists all packages with all revisions
- # tlmgr restore --backupdir dir pkg
- # lists all revisions of pkg
- # tlmgr restore --backupdir dir pkg rev
- # restores pkg to revision rev
- # check the backup dir argument
-
- {
- my ($a, $b) = check_backupdir_selection();
- if ($a & $F_ERROR) {
- # in all these cases we want to terminate in the non-gui mode
- tlwarn($b);
- return ($F_ERROR);
- }
- }
- info("$prg restore: dry run, no changes will be made\n") if $opts{"dry-run"};
-
- # initialize the hash(packages) of hash(revisions), do stat files! (the 1)
- my %backups = get_available_backups($opts{"backupdir"}, 1);
- my ($pkg, $rev) = @ARGV;
- if (defined($pkg) && $opts{"all"}) {
- tlwarn("$prg: Specify either --all or individual package(s) ($pkg)\n");
- tlwarn("$prg: to restore, not both. Terminating.\n");
- return ($F_ERROR);
- }
- if ($opts{"all"}) {
- init_local_db(1);
- return ($F_ERROR) if !check_on_writable();
- if (!$opts{"force"}) {
- print "Do you really want to restore all packages to the latest revision found in\n\t$opts{'backupdir'}\n===> (y/N): ";
- my $yesno = <STDIN>;
- if ($yesno !~ m/^y(es)?$/i) {
- print "Ok, cancelling the restore!\n";
- return ($F_OK | $F_NOPOSTACTION);
- }
- }
- for my $p (sort keys %backups) {
- my @tmp = sort {$b <=> $a} (keys %{$backups{$p}});
- my $rev = $tmp[0];
- print "Restoring $p, $rev from $opts{'backupdir'}/${p}.r${rev}.tar.*\n";
- if (!$opts{"dry-run"}) {
- # first remove the package, then reinstall it
- # this way we get rid of useless files
- # TODO_ERRORCHECK needs check for return values!!
- restore_one_package($p, $rev, $opts{"backupdir"});
- }
- }
- # localtlpdb already saved, so we are finished
- return ($F_OK);
- }
- #
- # intermediate sub
- sub report_backup_revdate {
- my $p = shift;
- my $mode = shift;
- my %revs = @_;
- my @rs = sort {$b <=> $a} (keys %revs);
- my @outarr;
- for my $rs (@rs) {
- my %jsonkeys;
- $jsonkeys{'name'} = $p;
- my $dstr;
- if ($revs{$rs} == -1) {
- $dstr = "unknown";
- } else {
- my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
- localtime($revs{$rs});
- # localtime returns dates starting from 1900, and the month is 0..11
- $dstr = sprintf "%04d-%02d-%02d %02d:%02d",
- $year+1900, $mon+1, $mday, $hour, $min;
- }
- if ($mode eq "json") {
- $jsonkeys{'rev'} = "$rs";
- $jsonkeys{'date'} = $dstr;
- push @outarr, \%jsonkeys;
- } else {
- push @outarr, "$rs ($dstr)";
- }
- }
- if ($mode eq "json") {
- return @outarr;
- } else {
- return ( join(" ", @outarr));
- }
- }
- # end sub
- if (!defined($pkg)) {
- if (keys %backups) {
- if ($opts{'json'}) {
- my @bla = map { report_backup_revdate($_, "json", %{$backups{$_}}) } keys %backups;
- my $str = TeXLive::TLUtils::encode_json(\@bla);
- print "$str\n";
- } else {
- print "Available backups:\n";
- foreach my $p (sort keys %backups) {
- print "$p: ";
- print(report_backup_revdate($p, "text", %{$backups{$p}}));
- print "\n";
- }
- }
- } else {
- if ($opts{'json'}) {
- print "[]\n";
- } else {
- print "No backups available in $opts{'backupdir'}\n";
- }
- }
- return ($F_OK | $F_NOPOSTACTION);
- }
- if (!defined($rev)) {
- if ($opts{'json'}) {
- my @bla = report_backup_revdate($pkg, "json", %{$backups{$pkg}});
- my $str = TeXLive::TLUtils::encode_json(\@bla);
- print "$str\n";
- } else {
- print "Available backups for $pkg: ";
- print(report_backup_revdate($pkg, "text", %{$backups{$pkg}}));
- print "\n";
- }
- return ($F_OK | $F_NOPOSTACTION);
- }
- # we did arrive here, so we try to restore ...
- if (defined($backups{$pkg}->{$rev})) {
- return if !check_on_writable();
- if (!$opts{"force"}) {
- print "Do you really want to restore $pkg to revision $rev (y/N): ";
- my $yesno = <STDIN>;
- if ($yesno !~ m/^y(es)?$/i) {
- print "Ok, cancelling the restore!\n";
- return ($F_OK | $F_NOPOSTACTION);
- }
- }
- print "Restoring $pkg, $rev from $opts{'backupdir'}/${pkg}.r${rev}.tar.xz\n";
- if (!$opts{"dry-run"}) {
- init_local_db(1);
- # first remove the package, then reinstall it
- # this way we get rid of useless files
- restore_one_package($pkg, $rev, $opts{"backupdir"});
- }
- # TODO_ERRORCHECKING check return value of restore_one_package
- return ($F_OK);
- } else {
- print "revision $rev for $pkg is not present in $opts{'backupdir'}\n";
- return ($F_ERROR);
- }
-}
-
-sub action_backup {
- init_local_db(1);
- # --clean argument
- # can be either -1 ... don't clean
- # 0 ... remove all backups
- # N ... keep only N backups
- # that parallels the value of autoclean in the configuration
- # we have to be careful, because if simply --clean is given, we should
- # check for the value saved in the tlpdb, and if that is not present
- # do nothing.
- # We have set clean to clean:-99 which makes -99 the default value
- # if only --clean is given without any argument
- # !defined($opts{"clean"}) -> no --clean given
- # $opts{"clean"} = -99 -> --clean without argument given, check tlpdb
- # $opts{"clean"} = -1, 0, N -> --clean=N given, check argument
- #
- my $clean_mode = 0;
- $clean_mode = 1 if defined($opts{"clean"});
- if ($clean_mode) {
- if ($opts{"clean"} == -99) {
- # we need to check the tlpdb
- my $tlpdb_option = $localtlpdb->option("autobackup");
- if (!defined($tlpdb_option)) {
- tlwarn ("$prg: --clean given without an argument, but no default clean\n");
- tlwarn ("$prg: mode specified in the tlpdb.\n");
- return ($F_ERROR);
- }
- $opts{"clean"} = $tlpdb_option;
- }
- # now $opts{"clean"} is something, but maybe not a number, check for
- # validity
- if ($opts{"clean"} =~ m/^(-1|[0-9]+)$/) {
- # get rid of leading zeros etc etc
- $opts{"clean"} = $opts{"clean"} + 0;
- } else {
- tlwarn ("$prg: clean mode as specified on the command line or as given by default\n");
- tlwarn ("$prg: must be an integer larger or equal than -1, terminating.\n");
- return($F_ERROR);
- }
- }
- # check the backup dir argument
- {
- my ($a, $b) = check_backupdir_selection();
- if ($a & $F_ERROR) {
- # in all these cases we want to terminate in the non-gui mode
- tlwarn($b);
- return($F_ERROR);
- }
- }
-
- # if we do --clean --all we also want to remove packages that
- # are not present anymore in the tlpdb, so use the readdir mode
- # to determine backups
- if ($opts{"all"} && $clean_mode) {
- # initialize the hash(packages) of hash(revisions)
- # no need to stat the files
- my %backups = get_available_backups($opts{"backupdir"}, 0);
- init_local_db(1);
- for my $p (sort keys %backups) {
- clear_old_backups ($p, $opts{"backupdir"}, $opts{"clean"}, $opts{"dry-run"}, 1);
- }
- info("no action taken due to --dry-run\n") if $opts{"dry-run"};
- return ($F_OK | $F_NOPOSTACTION);
- }
-
- # in case we are not cleaning or cleaning only specific packages
- # use the one-by-one mode
- my @todo;
- if ($opts{"all"}) {
- @todo = $localtlpdb->list_packages;
- } else {
- @todo = @ARGV;
- @todo = $localtlpdb->expand_dependencies("-only-arch", $localtlpdb, @todo);
- }
- if (!@todo) {
- printf "tlmgr backup takes either a list of packages or --all\n";
- return ($F_ERROR);
- }
- foreach my $pkg (@todo) {
- if ($clean_mode) {
- clear_old_backups ($pkg, $opts{"backupdir"}, $opts{"clean"}, $opts{"dry-run"}, 1);
- } else {
- # for now default to xz and allow overriding with env var
- my $compressorextension = $Compressors{$::progs{'compressor'}}{'extension'};
- my $tlp = $localtlpdb->get_package($pkg);
- my $saving_verb = $opts{"dry-run"} ? "would save" : "saving";
- info("$saving_verb current status of $pkg to $opts{'backupdir'}/${pkg}.r"
- . $tlp->revision . ".tar.$compressorextension\n");
- if (!$opts{"dry-run"}) {
- $tlp->make_container($::progs{'compressor'}, $localtlpdb->root,
- destdir => $opts{"backupdir"},
- user => 1);
- }
- }
- }
- info("no action taken due to --dry-run\n") if $opts{"dry-run"};
- # TODO_ERRORCHECKING needs checking of the above
- return ($F_OK);
-}
-
-# =====================================================================
-# INFRASTRUCTURE UPDATE ON WINDOWS
-# =====================================================================
-# Infrastructure files cannot be updated directly from the
-# tlmgr.pl script due to file locking problem on Windows - files that
-# are in use (either open or executing) cannot be removed or replaced.
-# For that reason the update process is performed by a batch script
-# outside of tlmgr.pl.
-# There are three pieces involved in the update: tlmgr.bat
-# launcher, write_w32_updater subroutine below and a batch
-# updater script. Their roles are as follows:
-# * tlmgr.bat is a watchdog, it launches tlmgr.pl and watches for
-# the updater script that is to be executed. If the updater script
-# exists before tlmgr.pl is launched, it will be removed or
-# tlmgr.bat will abort if it fails to do so. This means that the
-# updater script has to be created by the current invocation of
-# tlmgr.pl. Furthermore, the updater script is renamed from
-# updater-w32 to updater-w32.bat just before it is run, and thus
-# it can be executed only once.
-# * write_w32_updater subroutine in tlmgr.pl prepares the update
-# and writes the updater script. Packages in .xz archives are
-# downloaded/copied and uncompressed to .tar files. Also .tar
-# backups of the current packages are made. If everything is
-# successful, the update script is created from the template.
-# Otherwise the update is aborted.
-# * updater-w32[.bat] batch script, triggers and executes the actual
-# update. It first restarts itself in a separate instance of cmd.exe
-# (and in a new console window in gui mode) and runs the update
-# from there. The update is run with echo on and all output is
-# logged to a file (or stderr in verbose mode). After successful
-# infrastructure update, tlmgr is optionally restarted if update
-# of other packages is asked for.
-# The infrastructure update itself proceeds as follows:
-# (1) untar all package archives
-# (2) include .tlpobj files into tlpdb
-# (3) print update info to console
-# Any error during (1) or (2) triggers the rollback sequence:
-# (1) print failed update info to console
-# (2) untar all package backups
-# (3) include .tlpobj files (from backup) into tlpdb
-# (4) print restore info to console
-# Any error during (2) or (3) and we go into panic state. At this
-# point there is no guarantee that the installation is still working.
-# There is not much we can do but to print failed restore info and
-# give instructions to download and run 'update-tlmgr-latest.exe'
-# to repair the installation.
-# =====================================================================
-#
-sub write_w32_updater {
- my ($restart_tlmgr, $ref_files_to_be_removed, @w32_updated) = @_;
- my @infra_files_to_be_removed = @$ref_files_to_be_removed;
- # TODO do something with these files TODO
- my $media = $remotetlpdb->media;
- # we have to download/copy also the src/doc files if necessary!
- my $container_src_split = $remotetlpdb->config_src_container;
- my $container_doc_split = $remotetlpdb->config_doc_container;
- # get options about src/doc splitting from $totlpdb
- # TT: should we use local options to decide about install of doc & src?
- my $opt_src = $localtlpdb->option("install_srcfiles");
- my $opt_doc = $localtlpdb->option("install_docfiles");
- my $root = $localtlpdb->root;
- my $temp = "$root/temp";
- TeXLive::TLUtils::mkdirhier($temp);
- tlwarn("$prg: Backup option not implemented for infrastructure update.\n") if ($opts{"backup"});
- if ($media eq 'local_uncompressed') {
- tlwarn("$prg: Creating updater from local_uncompressed currently not implemented!\n");
- tlwarn("$prg: But it should not be necessary!\n");
- return 1; # abort
- }
- my (@upd_tar, @upd_tlpobj, @upd_info, @rst_tar, @rst_tlpobj, @rst_info);
- foreach my $pkg (@w32_updated) {
- my $repo;
- my $mediatlp;
- # need to update the media type to the original, as it is
- # reset below
- $media = $remotetlpdb->media;
- if ($media eq "virtual") {
- my $maxtlpdb;
- (undef, undef, $mediatlp, $maxtlpdb) =
- $remotetlpdb->virtual_candidate($pkg);
- $repo = $maxtlpdb->root . "/$Archive";
- # update the media type of the used tlpdb
- # otherwise later on we stumble when preparing the updater
- $media = $maxtlpdb->media;
- } else {
- $mediatlp = $remotetlpdb->get_package($pkg);
- $repo = $remotetlpdb->root . "/$Archive";
- }
- my $localtlp = $localtlpdb->get_package($pkg);
- my $oldrev = $localtlp->revision;
- my $newrev = $mediatlp->revision;
- # we do install documentation files for category Documentation even if
- # option("install_docfiles") is false
- my $opt_real_doc = ($mediatlp->category =~ m/documentation/i) ? 1 : $opt_doc;
- my @pkg_parts = ($pkg);
- push(@pkg_parts, "$pkg.source") if ($container_src_split && $opt_src && $mediatlp->srcfiles);
- push(@pkg_parts, "$pkg.doc") if ($container_doc_split && $opt_real_doc && $mediatlp->docfiles);
- foreach my $pkg_part (@pkg_parts) {
- push (@upd_tar, "$pkg_part.tar");
- push (@upd_tlpobj, "tlpkg\\tlpobj\\$pkg_part.tlpobj");
- }
- push (@upd_info, "$pkg ^($oldrev -^> $newrev^)");
- push (@rst_tar, "__BACKUP_$pkg.r$oldrev.tar");
- push (@rst_tlpobj, "tlpkg\\tlpobj\\$pkg.tlpobj");
- push (@rst_info, "$pkg ^($oldrev^)");
- next if ($opts{"dry-run"});
- # create backup; make_container expects filename in format:
- # some-name.r[0-9]+
- my ($size, undef, $fullname) = $localtlp->make_container("tar", $root,
- destdir => $temp,
- containername => "__BACKUP_$pkg",
- user => 1);
- if ($size <= 0) {
- tlwarn("$prg: creation of backup container failed for: $pkg\n");
- return 1; # backup failed? abort
- }
- my $decompressor = $::progs{$DefaultCompressorFormat};
- my $compressorextension = $Compressors{$DefaultCompressorFormat}{'extension'};
- my @decompressorArgs = @{$Compressors{$DefaultCompressorFormat}{'decompress_args'}};
- foreach my $pkg_part (@pkg_parts) {
- my $dlcontainer = "$temp/$pkg_part.tar.$compressorextension";
- if ($media eq 'local_compressed') {
- copy("$repo/$pkg_part.tar.$compressorextension", "$temp");
- } else { # net
- TeXLive::TLUtils::download_file("$repo/$pkg_part.tar.$compressorextension", $dlcontainer);
- }
- # now we should have the file present
- if (!-r $dlcontainer) {
- tlwarn("$prg: couldn't get $pkg_part.tar.$compressorextension, that is bad\n");
- return 1; # abort
- }
- # unpack xz archive
- my $sysret = system("$decompressor @decompressorArgs < \"$dlcontainer\" > \"$temp/$pkg_part.tar\"");
- if ($sysret) {
- tlwarn("$prg: couldn't unpack $pkg_part.tar.$compressorextension\n");
- return 1; # unpack failed? abort
- }
- unlink($dlcontainer); # we don't need that archive anymore
- }
- }
-
- # prepare updater script
- my $respawn_cmd = "cmd.exe /e:on/v:off/d/c";
- $respawn_cmd = "start /wait $respawn_cmd" if ($::gui_mode);
- my $gui_pause = ($::gui_mode ? "pause" : "rem");
- my $upd_log = ($::opt_verbosity ? "STDERR" : '"%~dp0update-self.log"');
- my $std_handles_redir = ($::opt_verbosity ? "1^>^&2" : "2^>$upd_log 1^>^&2");
- my $pkg_log = ($packagelogfile ? "\"$packagelogfile\"" : "nul");
- my $post_update_msg = "You may now close this window.";
- my $rerun_tlmgr = "rem";
- if ($restart_tlmgr) {
- $post_update_msg = "About to restart tlmgr to complete update ...";
- # quote all arguments for tlmgr restart in case of spaces
- $rerun_tlmgr = join (" ", map ("\"$_\"", @::SAVEDARGV) );
- $rerun_tlmgr = "if not errorlevel 1 tlmgr.bat $rerun_tlmgr";
- }
- my $batch_script = <<"EOF";
-:: This file is part of an automated update process of
-:: infrastructure files and should not be run standalone.
-:: For more details about the update process see comments
-:: in tlmgr.pl (subroutine write_w32_updater).
-
- if [%1]==[:doit] goto :doit
- if not exist "%~dp0tar.exe" goto :notar
- $respawn_cmd call "%~f0" :doit $std_handles_redir
- $rerun_tlmgr
- goto :eof
-
-:notar
- echo %~nx0: cannot run without "%~dp0tar.exe"
- findstr "^::" <"%~f0"
- exit /b 1
-
-:doit
- set prompt=TL\$G
- title TeX Live Manager $TeXLive::TLConfig::ReleaseYear Update
- set PERL5LIB=$root/tlpkg/tlperl/lib
- >con echo DO NOT CLOSE THIS WINDOW!
- >con echo TeX Live infrastructure update in progress ...
- >con echo Detailed command logging to $upd_log
- pushd "%~dp0.."
- if not errorlevel 1 goto :update
- >con echo Could not change working directory to "%~dp0.."
- >con echo Aborting infrastructure update, no changes have been made.
- >con $gui_pause
- popd
- exit /b 1
-
-:update
- for %%I in (@upd_tar) do (
- temp\\tar.exe -xmf temp\\%%I
- if errorlevel 1 goto :rollback
- )
- tlpkg\\tlperl\\bin\\perl.exe .\\texmf-dist\\scripts\\texlive\\tlmgr.pl _include_tlpobj @upd_tlpobj
- if errorlevel 1 goto :rollback
- >>$pkg_log echo [%date% %time%] self update: @upd_info
- >con echo self update: @upd_info
- del "%~dp0*.tar" "%~dp0tar.exe"
- >con echo Infrastructure update finished successfully.
- >con echo $post_update_msg
- >con $gui_pause
- popd
- exit /b 0
-
-:rollback
- >>$pkg_log echo [%date% %time%] failed self update: @upd_info
- >con echo failed self update: @upd_info
- >con echo Rolling back to previous version ...
- for %%I in (@rst_tar) do (
- temp\\tar.exe -xmf temp\\%%I
- if errorlevel 1 goto :panic
- )
- tlpkg\\tlperl\\bin\\perl.exe .\\texmf-dist\\scripts\\texlive\\tlmgr.pl _include_tlpobj @rst_tlpobj
- if errorlevel 1 goto :panic
- >>$pkg_log echo [%date% %time%] self restore: @rst_info
- >con echo self restore: @rst_info
- >con echo Infrastructure update failed. Previous version has been restored.
- >con $gui_pause
- popd
- exit /b 1
-
-:panic
- >>$pkg_log echo [%date% %time%] failed self restore: @rst_info
- >con echo failed self restore: @rst_info
- >con echo FATAL ERROR:
- >con echo Infrastructure update failed and backup recovery failed too.
- >con echo To repair your TeX Live installation download and run:
- >con echo $TeXLive::TLConfig::TeXLiveURL/update-tlmgr-latest.exe
- >con $gui_pause
- popd
- exit /b 666
-EOF
-
- ddebug("\n:: UPDATER BATCH SCRIPT ::\n$batch_script\n:: END OF FILE ::\n");
- if ($opts{"dry-run"}) {
- my $upd_info = "self update: @upd_info";
- $upd_info =~ s/\^//g;
- info($upd_info);
- } else {
- copy("$root/tlpkg/installer/tar.exe", "$temp");
- # make sure copied tar is working
- if (system("\"$temp/tar.exe\" --version >nul")) {
- tlwarn("$prg: could not copy tar.exe, that is bad.\n");
- return 1; # abort
- }
- open UPDATER, ">$temp/updater-w32" or die "Cannot create updater script: $!";
- print UPDATER $batch_script;
- close UPDATER;
- }
- return 0;
-}
-
-
-# UPDATE
-
-# compute the list of auto-install, auto-remove, forcibly-removed
-# packages from the list of packages to be installed
-# the list of packages passed in is already expanded
-sub auto_remove_install_force_packages {
- my @todo = @_;
- my %removals_full;
- my %forcermpkgs_full;
- my %newpkgs_full;
- my %new_pkgs_due_forcerm_coll;
- # check for new/removed/forcibly removed packages.
- # we start from the list of installed collections in the local tlpdb
- # which are also present in the remote database
- # and expand this list once with expand_dependencies in the local tlpdb
- # and once in the tlmedia tlpdb. Then we compare the lists
- # let A = set of local expansions
- # B = set of remote expansions
- # then we should(?) have
- # B \ A set of new packages
- # A \ B set of packages removed on the server
- # A \cup B set of packages which should be checked for forcible removal
- #
- my @all_schmscolls = ();
- for my $p ($localtlpdb->schemes) {
- push (@all_schmscolls, $p) if defined($remotetlpdb->get_package($p));
- }
- for my $p ($localtlpdb->collections) {
- push (@all_schmscolls, $p) if defined($remotetlpdb->get_package($p));
- }
- my @localexpansion_full =
- $localtlpdb->expand_dependencies($localtlpdb, @all_schmscolls);
- my @remoteexpansion_full =
- $remotetlpdb->expand_dependencies($localtlpdb, @all_schmscolls);
-
- # compute new/remove/forcerm based on the full expansions
- for my $p (@remoteexpansion_full) {
- $newpkgs_full{$p} = 1;
- }
- for my $p (@localexpansion_full) {
- delete($newpkgs_full{$p});
- $removals_full{$p} = 1;
- }
- for my $p (@remoteexpansion_full) {
- delete($removals_full{$p});
- }
- # in a first round we check only for forcibly removed collections
- # this is necessary to NOT declare a package that is contained
- # in a forcibly removed collections as auto-install since it appears
- # in the @remoteexpansion_full, but not in @localexpansion_full.
- for my $p (@localexpansion_full) {
- # intersection, don't check A\B and B\A
- next if $newpkgs_full{$p};
- next if $removals_full{$p};
- my $remotetlp = $remotetlpdb->get_package($p);
- if (!defined($remotetlp)) {
- tlwarn("$prg: Strange, $p mentioned but not found anywhere!\n");
- next;
- }
- next if ($remotetlp->category ne "Collection");
- my $tlp = $localtlpdb->get_package($p);
- if (!defined($tlp)) {
- if ($opts{"reinstall-forcibly-removed"}) {
- $newpkgs_full{$p} = 1;
- } else {
- $forcermpkgs_full{$p} = 1;
- }
- }
- }
- # now we have in %forcermpkgs_full only collections that have been
- # forcibly removed. Again, expand those against the remote tlpdb
- # and remove the expanded packages from the list of localexpansion.
- my @pkgs_from_forcerm_colls =
- $remotetlpdb->expand_dependencies($localtlpdb, keys %forcermpkgs_full);
- #
- # the package in @pkgs_from_forcerm_colls would be auto-installed, so
- # check for that:
- for my $p (keys %newpkgs_full) {
- if (member($p, @pkgs_from_forcerm_colls)) {
- delete $newpkgs_full{$p};
- $new_pkgs_due_forcerm_coll{$p} = 1;
- }
- }
- #
- # now create the final list of forcerm packages by checking against
- # all packages
- for my $p (@localexpansion_full) {
- # intersection, don't check A\B and B\A
- next if $newpkgs_full{$p};
- next if $removals_full{$p};
- my $tlp = $localtlpdb->get_package($p);
- if (!defined($tlp)) {
- if ($opts{"reinstall-forcibly-removed"}) {
- $newpkgs_full{$p} = 1;
- } else {
- $forcermpkgs_full{$p} = 1;
- }
- }
- }
- #
- # for some packages (texworks, psview, ...) we only have w32 packages
- # in the repository, but it is possible that alternative repositories
- # ship binaries for some platforms (like texworks for GNU/Linux on tlcontrib)
- # currently updating from tlnet will remove these alternative .ARCH
- # packages because they are not listed anywhere locally, so they
- # are considered as disappearing.
- # We remove here packages PKG.ARCH if the main package PKG is found
- # here and is *not* disappearing, from the removal hash
- for my $p (keys %removals_full) {
- if ($p =~ m/^([^.]*)\./) {
- my $mpkg = $1;
- if (!defined($removals_full{$mpkg})) {
- delete($removals_full{$p});
- }
- }
- }
- #
- # now take only the subset of packages that is in @todo
- # note that @todo is already expanded in action_update according
- # to the --no-depends and --no-depends-at-all options
- #
- my %removals;
- my %forcermpkgs;
- my %newpkgs;
- for my $p (@todo) {
- $removals{$p} = 1 if defined($removals_full{$p});
- $forcermpkgs{$p} = 1 if defined($forcermpkgs_full{$p});
- $newpkgs{$p} = 1 if defined($newpkgs_full{$p});
- }
- debug ("$prg: new pkgs: " . join("\n\t",keys %newpkgs) . "\n");
- debug ("$prg: deleted : " . join("\n\t",keys %removals) . "\n");
- debug ("$prg: forced : " . join("\n\t",keys %forcermpkgs) . "\n");
-
- return (\%removals, \%newpkgs, \%forcermpkgs, \%new_pkgs_due_forcerm_coll);
-}
-
-# tlmgr update foo
-# if foo is of type Package|Documentation it will update only foo
-# and the respective .ARCH dependencies
-# if foo is of type Collection|Scheme it will update itself AND
-# will check all depending packs of type NOT(Collection|Scheme)
-# for necessary updates
-#
-# tlmgr update --no-depends foo
-# as above, but will not check for depends of Collections/Schemes
-# but it will still update .ARCH deps
-#
-# tlmgr update --no-depends-at-all foo
-# will absolutely only update foo not even taking .ARCH into account
-#
-# TLPDB->install_package INSTALLS ONLY ONE PACKAGE, no deps whatsoever
-# anymore. That has all to be done by hand.
-#
-sub machine_line {
- my ($flag1) = @_;
- my $ret = 0;
- if ($flag1 eq "-ret") {
- $ret = 1;
- shift;
- }
- my ($pkg, $flag, $lrev, $rrev, $size, $runtime, $esttot, $tag, $lcv, $rcv) = @_;
- $lrev ||= "-";
- $rrev ||= "-";
- $flag ||= "?";
- $size ||= "-";
- $runtime ||= "-";
- $esttot ||= "-";
- $tag ||= "-";
- $lcv ||= "-";
- $rcv ||= "-";
- my $str = join("\t", $pkg, $flag, $lrev, $rrev, $size, $runtime, $esttot, $tag, $lcv, $rcv);
- $str .= "\n";
- return($str) if $ret;
- print $str;
-}
-
-sub upd_info {
- my ($pkg, $kb, $lrev, $mrev, $txt) = @_;
- my $flen = 25;
- my $kbstr = ($kb >= 0 ? " [${kb}k]" : "");
- my $kbstrlen = length($kbstr);
- my $pkglen = length($pkg);
- my $is = sprintf("%-9s ", "$txt:");
- if ($pkglen + $kbstrlen > $flen) {
- $is .= "$pkg$kbstr: ";
- } else {
- $is .= sprintf ('%*2$s', $pkg, -($flen-$kbstrlen));
- $is .= "$kbstr: ";
- }
- $is .= sprintf("local: %8s, source: %8s",
- $lrev, $mrev);
- info("$is\n");
-}
-
-sub action_update {
- init_local_db(1);
- $opts{"no-depends"} = 1 if $opts{"no-depends-at-all"};
-
- # make a quick check on command line arguments to avoid loading
- # the remote db uselessly.
- # we require:
- # if no --list is given: either --self or --all or <pkgs>
- # if --list is given: nothing
- # other options just change the behavior
- if (!($opts{"list"} || @ARGV || $opts{"all"} || $opts{"self"})) {
- if ($opts{"dry-run"}) {
- $opts{"list"} = 1; # update -n same as update -n --list
- } else {
- tlwarn("$prg update: specify --list, --all, --self, or a list of package names.\n");
- return ($F_ERROR);
- }
- }
-
- init_tlmedia_or_die();
- info("$prg update: dry run, no changes will be made\n") if $opts{"dry-run"};
-
- my @excluded_pkgs = ();
- if ($opts{"exclude"}) {
- @excluded_pkgs = @{$opts{"exclude"}};
- } elsif ($config{'update-exclude'}) {
- @excluded_pkgs = @{$config{'update-exclude'}};
- }
-
- if (!$opts{"list"}) {
- return ($F_ERROR) if !check_on_writable();
- }
-
- # check for updates to tlmgr and die unless either --force or --list or --self
- # is given
- my @critical;
- if (!$opts{"usermode"}) {
- @critical = check_for_critical_updates($localtlpdb, $remotetlpdb);
- }
- my $dry_run_cont = $opts{"dry-run"} && ($opts{"dry-run"} < 0);
- if ( !$dry_run_cont && !$opts{"self"} && @critical) {
- critical_updates_warning() if (!$::machinereadable);
- if ($opts{"force"}) {
- tlwarn("$prg: Continuing due to --force.\n");
- } elsif ($opts{"list"}) {
- # do not warn here
- } else {
- return($F_ERROR);
- }
- }
-
- my ($ret, $autobackup) = setup_backup_directory();
- return ($ret) if ($ret != $F_OK);
-
- # these two variables are used throughout this function
- my $root = $localtlpdb->root;
- my $temp = TeXLive::TLUtils::tl_tmpdir();
-
- # remove old _BACKUP packages that have piled up in temp
- # they can be recognized by their name starting with __BACKUP_
- for my $f (<$temp/__BACKUP_*>) {
- unlink($f) unless $opts{"dry-run"};
- }
-
-
- my @todo;
- if ($opts{"list"}) {
- if ($opts{"all"}) {
- @todo = $localtlpdb->list_packages;
- } elsif ($opts{"self"}) {
- @todo = @critical;
- } else {
- if (@ARGV) {
- @todo = @ARGV;
- } else {
- @todo = $localtlpdb->list_packages;
- }
- }
- } elsif ($opts{"self"} && @critical) {
- @todo = @critical;
- } elsif ($opts{"all"}) {
- @todo = $localtlpdb->list_packages;
- } else {
- @todo = @ARGV;
- }
- if ($opts{"self"} && !@critical) {
- info("$prg: no self-updates for tlmgr available\n");
- }
- # don't do anything if we have been invoked in a strange way
- if (!@todo && !$opts{"self"}) {
- tlwarn("$prg update: please specify a list of packages, --all, or --self.\n");
- return ($F_ERROR);
- }
-
- if (!($opts{"self"} && @critical) || ($opts{"self"} && $opts{"list"})) {
- # update all .ARCH dependencies, too, unless $opts{"no-depends-at-all"}:
- @todo = $remotetlpdb->expand_dependencies("-only-arch", $localtlpdb, @todo)
- unless $opts{"no-depends-at-all"};
- #
- # update general deps unless $opts{"no-depends"}:
- @todo = $remotetlpdb->expand_dependencies("-no-collections",$localtlpdb,@todo)
- unless $opts{"no-depends"};
- #
- # filter out critical packages
- @todo = grep (!m/$CriticalPackagesRegexp/, @todo)
- unless $opts{"list"};
- }
-
- my ($remref, $newref, $forref, $new_due_to_forcerm_coll_ref) =
- auto_remove_install_force_packages(@todo);
- my %removals = %$remref;
- my %forcermpkgs = %$forref;
- my %newpkgs = %$newref;
- my %new_due_to_forcerm_coll = %$new_due_to_forcerm_coll_ref;
-
- # check that the --exclude options do not conflict with the
- # options --no-auto-remove, --no-auto-install, --reinstall-forcibly-removed
- my @option_conflict_lines = ();
- my $in_conflict = 0;
- if (!$opts{"no-auto-remove"} && $config{"auto-remove"}) {
- for my $pkg (keys %removals) {
- for my $ep (@excluded_pkgs) {
- if ($pkg eq $ep || $pkg =~ m/^$ep\./) {
- push @option_conflict_lines, "$pkg: excluded but scheduled for auto-removal\n";
- $in_conflict = 1;
- last; # of the --exclude for loop
- }
- }
- }
- }
- if (!$opts{"no-auto-install"}) {
- for my $pkg (keys %newpkgs) {
- for my $ep (@excluded_pkgs) {
- if ($pkg eq $ep || $pkg =~ m/^$ep\./) {
- push @option_conflict_lines, "$pkg: excluded but scheduled for auto-install\n";
- $in_conflict = 1;
- last; # of the --exclude for loop
- }
- }
- }
- }
- if ($opts{"reinstall-forcibly-removed"}) {
- for my $pkg (keys %forcermpkgs) {
- for my $ep (@excluded_pkgs) {
- if ($pkg eq $ep || $pkg =~ m/^$ep\./) {
- push @option_conflict_lines, "$pkg: excluded but scheduled for reinstall\n";
- $in_conflict = 1;
- last; # of the --exclude for loop
- }
- }
- }
- }
- if ($in_conflict) {
- tlwarn("$prg: Conflicts have been found:\n");
- for (@option_conflict_lines) { tlwarn(" $_"); }
- tlwarn("$prg: Please resolve these conflicts!\n");
- return ($F_ERROR);
- }
-
- #
- # we first collect the list of packages to be actually updated or installed
- my %updated;
- my @new;
- my @addlines;
-
- TODO: foreach my $pkg (sort @todo) {
- next if ($pkg =~ m/^00texlive/);
- for my $ep (@excluded_pkgs) {
- if ($pkg eq $ep || $pkg =~ m/^$ep\./) {
- info("$prg: skipping excluded package: $pkg\n");
- next TODO;
- }
- }
- my $tlp = $localtlpdb->get_package($pkg);
- if (!defined($tlp)) {
- # if the user has forcibly removed (say) bin-makeindex, then the
- # loop above has no way to add bin-makeindex.ARCH into the
- # %forcermpkgs hash, but the .ARCH will still be in the dependency
- # expansion. So try both with and without the .ARCH extension.
- (my $pkg_noarch = $pkg) =~ s/\.[^.]*$//;
- my $forcerm_coll = $forcermpkgs{$pkg} || $forcermpkgs{$pkg_noarch};
-
- # similarly for new packages. If latexmk is new, latexmk.ARCH
- # will be in the dependency expansion, and we want it.
- my $newpkg_coll = $newpkgs{$pkg} || $newpkgs{$pkg_noarch};
- if ($forcerm_coll) {
- if ($::machinereadable) {
- # TODO should we add a revision number
- push @addlines,
- # $pkg, $flag, $lrev, $rrev, $size, $runtime, $esttot, $tag, $lcv, $rcv
- machine_line("-ret", $pkg, $FLAG_FORCIBLE_REMOVED);
- } else {
- info("$prg: skipping forcibly removed package: $pkg\n");
- }
- next;
- } elsif ($newpkg_coll) {
- # do nothing here, it will be reported below.
- } elsif (defined($removals{$pkg})) {
- # skipping this package, it has been removed due to server removal
- # and has already been removed
- next;
- } elsif (defined($new_due_to_forcerm_coll{$pkg})) {
- debug("$prg: $pkg seems to be contained in a forcibly removed" .
- " collection, not auto-installing it!\n");
- next;
- } else {
- tlwarn("\n$prg: $pkg mentioned, but neither new nor forcibly removed");
- tlwarn("\n$prg: perhaps try tlmgr search or tlmgr info.\n");
- next;
- }
- # install new packages
- my $mediatlp = $remotetlpdb->get_package($pkg);
- if (!defined($mediatlp)) {
- tlwarn("\n$prg: Should not happen: $pkg not found in $location\n");
- $ret |= $F_WARNING;
- next;
- }
- my $mediarev = $mediatlp->revision;
- push @new, $pkg;
- next;
- }
- my $rev = $tlp->revision;
- my $lctanvers = $tlp->cataloguedata->{'version'};
- my $mediatlp;
- my $maxtag;
- if ($remotetlpdb->is_virtual) {
- ($maxtag, undef, $mediatlp, undef) =
- $remotetlpdb->virtual_candidate($pkg);
- } else {
- $mediatlp = $remotetlpdb->get_package($pkg);
- }
- if (!defined($mediatlp)) {
- ddebug("$pkg cannot be found in $location\n");
- next;
- }
- my $rctanvers = $mediatlp->cataloguedata->{'version'};
- my $mediarev = $mediatlp->revision;
- my $mediarevstr = $mediarev;
- my @addargs = ();
- if ($remotetlpdb->is_virtual) {
- push @addargs, $maxtag;
- $mediarevstr .= "\@$maxtag";
- } else {
- push @addargs, undef;
- }
- push @addargs, $lctanvers, $rctanvers;
- if ($rev < $mediarev) {
- $updated{$pkg} = 0; # will be changed to one on successful update
- } elsif ($rev > $mediarev) {
- if ($::machinereadable) {
- # $pkg, $flag, $lrev, $rrev, $size, $runtime, $esttot, $tag, $lcv, $rcv
- push @addlines,
- machine_line("-ret", $pkg, $FLAG_REVERSED_UPDATE, $rev, $mediarev, "-", "-", "-", @addargs);
- } else {
- if ($opts{"list"}) {
- # not issuing anything if we keep a package
- upd_info($pkg, -1, $rev, $mediarevstr, "keep");
- }
- }
- }
- }
- my @updated = sort keys %updated;
- for my $i (sort @new) {
- debug("$i new package\n");
- }
- for my $i (@updated) {
- debug("$i upd package\n");
- }
-
- # number calculation
- # without w32 special packages, those are dealt with in the updater batch
- # script
- my $totalnr = $#updated + 1;
- my @alltodo = @updated;
- my $nrupdated = 0;
- my $currnr = 1;
-
- # we have to remove all the stuff before we install other packages
- # to support moving of files from one package to another.
- # remove the packages that have disappeared:
- # we add that only to the list of total packages do be worked on
- # when --all is given, because we remove packages only on --all
- if (!$opts{"no-auto-remove"} && $config{"auto-remove"}) {
- my @foo = keys %removals;
- $totalnr += $#foo + 1;
- }
- if (!$opts{"no-auto-install"}) {
- $totalnr += $#new + 1;
- push @alltodo, @new;
- }
-
- # sizes_of_packages returns the sizes of *all* packages if nothing
- # is passed over, so if @new and @updated both are empty we will
- # get something wrong back, namely the total size of all packages
- # the third argument is undef to compute *all* platforms
- my %sizes;
- if (@alltodo) {
- %sizes = %{$remotetlpdb->sizes_of_packages(
- $localtlpdb->option("install_srcfiles"),
- $localtlpdb->option("install_docfiles"), undef, @alltodo)};
- } else {
- $sizes{'__TOTAL__'} = 0;
- }
-
- print "total-bytes\t$sizes{'__TOTAL__'}\n" if $::machinereadable;
- print "end-of-header\n" if $::machinereadable;
-
- # print deferred machine-readable lines after the header
- for (@addlines) { print; }
-
- #
- # compute the list of moved files from %removals, @new, @updated
- #
- my %do_warn_on_move;
- {
- # keep all these vars local to this block
- my @removals = keys %removals;
- my %old_files_to_pkgs;
- my %new_files_to_pkgs;
- # first save for each file in the currently installed packages
- # to be updated the packages it is contained it (might be more!)
- #
- # TODO WHY WHY is there the next so that all the file move checks
- # are actually disabled?!?!?!
- for my $p (@updated, @removals) {
- my $pkg = $localtlpdb->get_package($p);
- tlwarn("$prg: Should not happen: $p not found in local tlpdb\n") if (!$pkg);
- next;
- for my $f ($pkg->all_files) {
- push @{$old_files_to_pkgs{$f}}, $p;
- }
- }
- for my $p (@updated, @new) {
- my $pkg = $remotetlpdb->get_package($p);
- tlwarn("$prg: Should not happen: $p not found in $location\n") if (!$pkg);
- next;
- for my $f ($pkg->all_files) {
- if ($pkg->relocated) {
- $f =~ s:^$RelocPrefix/:$RelocTree/:;
- }
- push @{$new_files_to_pkgs{$f}}, $p;
- }
- }
- #
- # the idea of suppressing warnings is simply that if a file is present
- # in more than one package either in the beginning or after a full
- # update then this should give a warning. In all other cases
- # the warning should be suppressed.
- for my $f (keys %old_files_to_pkgs) {
- my @a = @{$old_files_to_pkgs{$f}};
- $do_warn_on_move{$f} = 1 if ($#a > 0)
- }
- for my $f (keys %new_files_to_pkgs) {
- my @a = @{$new_files_to_pkgs{$f}};
- $do_warn_on_move{$f} = 1 if ($#a > 0)
- }
- }
-
- # parameters for field width
- my $totalnrdigits = length("$totalnr");
-
- #
- # ORDER OF PACKAGE ACTIONS
- # 1. removals
- # 2. updates
- # 3. auto-install
- # that way if a file has been moved from one to another package it
- # removing the old version after the new package has been installed
- # will not give a warning about files being included somewhere else
- #
-
- #
- # REMOVALS
- #
- for my $p (keys %removals) {
- if ($opts{"no-auto-remove"} || !$config{"auto-remove"}) {
- info("not removing $p due to -no-auto-remove or config file option (removed on server)\n");
- } else {
- &ddebug("removing package $p\n");
- my $pkg = $localtlpdb->get_package($p);
- if (! $pkg) {
- # This happened when a collection was removed by the user,
- # and then renamed on the server, e.g., collection-langarab ->
- # collection-langarabic; Luecking report 20 July 2009.
- &ddebug(" get_package($p) failed, ignoring");
- next;
- }
- my $rev = $pkg->revision;
- my $lctanvers = $pkg->cataloguedata->{'version'};
- if ($opts{"list"}) {
- if ($::machinereadable) {
- # $pkg, $flag, $lrev, $rrev, $size, $runtime, $esttot, $tag, $lcv, $rcv
- machine_line($p, $FLAG_REMOVE, $rev, "-", "-", "-", "-", "-", $lctanvers);
- } else {
- upd_info($p, -1, $rev, "<absent>", "autorm");
- }
- $currnr++;
- } else {
- # new we are sure that:
- # - $opts{"no-auto-remove"} is *not* set
- # - $opts{"list"} is *not* set
- # we have to check in addition that
- # - $opts{"dry-run"} is not set
- if ($::machinereadable) {
- # $pkg, $flag, $lrev, $rrev, $size, $runtime, $esttot, $tag, $lcv, $rcv
- machine_line($p, $FLAG_REMOVE, $rev, "-", "-", "-", "-", "-", $lctanvers);
- } else {
- info("[" . sprintf ('%*2$s', $currnr, $totalnrdigits) .
- "/$totalnr] auto-remove: $p ... ");
- }
- if (!$opts{"dry-run"}) {
- # older tlmgr forgot to clear the relocated bit when saving a tlpobj
- # into the local tlpdb, although the paths were rewritten.
- # We have to clear this bit otherwise the make_container calls below
- # for creating the backup will create some rubbish!
- # Same as further down in the update part!
- if ($pkg->relocated) {
- debug("$prg: warn, relocated bit set for $p, but that is wrong!\n");
- $pkg->relocated(0);
- }
- # TODO we do not check return value here!
- backup_and_remove_package($p, $autobackup);
- logpackage("remove: $p");
- }
- info("done\n") unless $::machinereadable;
- $currnr++;
- }
- }
- }
-
-
- my $starttime = time();
- my $donesize = 0;
- my $totalsize = $sizes{'__TOTAL__'};
-
-
- #
- # UPDATES AND NEW PACKAGES
- #
- # order:
- # - update normal packages
- # - install new normal packages
- # - update collections
- # - install new collections
- # - update schemes
- # - install new schemes (? will not happen?)
- #
- # this makes sure that only if all depending packages are installed
- # the collection is updated, which in turn makes sure that
- # if the installation of a new package does break it will not be
- # counted as forcibly removed later on.
- #
- my @inst_packs;
- my @inst_colls;
- my @inst_schemes;
- for my $pkg (@updated) {
- # we do name checking here, not to load all tlpobj again and again
- if ($pkg =~ m/^scheme-/) {
- push @inst_schemes, $pkg;
- } elsif ($pkg =~ m/^collection-/) {
- push @inst_colls, $pkg;
- } else {
- push @inst_packs, $pkg;
- }
- }
- @inst_packs = sort packagecmp @inst_packs;
-
- my @new_packs;
- my @new_colls;
- my @new_schemes;
- for my $pkg (sort @new) {
- # we do name checking here, not to load all tlpobj again and again
- if ($pkg =~ m/^scheme-/) {
- push @new_schemes, $pkg;
- } elsif ($pkg =~ m/^collection-/) {
- push @new_colls, $pkg;
- } else {
- push @new_packs, $pkg;
- }
- }
- @new_packs = sort packagecmp @new_packs;
- my %is_new;
- for my $pkg (@new_packs, @new_colls, @new_schemes) {
- $is_new{$pkg} = 1;
- }
-
- #
- # TODO idea
- # currently this big loop contains a long if then for new packages
- # and updated package. That *could* be merged into one so that
- # some things like the logging has not been written two times.
- # OTOH, the control flow in the "new package" part is much simpler
- # and following it after the change would make it much harder
- #
- foreach my $pkg (@inst_packs, @new_packs, @inst_colls, @new_colls, @inst_schemes, @new_schemes) {
-
- if (!$is_new{$pkg}) {
- # skip this loop if infra update on w32
- next if ($pkg =~ m/^00texlive/);
- my $tlp = $localtlpdb->get_package($pkg);
- # we checked already that this package is present!
- # but our checks seem to be wrong, no idea why
- # ahhh, it seems that it can happen due to a stupid incident, a bug
- # on the server:
- # - remove a package from a collection
- # - at the same time increase its version number
- # then what happens is:
- # - first the package is removed (auto-remove!)
- # - then it is tried to be updated here, which is not working!
- # report that and ask for report
- if (!defined($tlp)) {
- my %servers = repository_to_array($location);
- my $servers = join("\n ", values(%servers));
- tlwarn("$prg: inconsistency on (one of) the server(s): $servers\n");
- tlwarn("$prg: tlp for package $pkg cannot be found, please report.\n");
- $ret |= $F_WARNING;
- next;
- }
- my $unwind_package;
- my $remove_unwind_container = 0;
- my $rev = $tlp->revision;
- my $lctanvers = $tlp->cataloguedata->{'version'};
- my $mediatlp;
- my $maxtag;
- if ($remotetlpdb->is_virtual) {
- ($maxtag, undef, $mediatlp, undef) =
- $remotetlpdb->virtual_candidate($pkg);
- } else {
- $mediatlp = $remotetlpdb->get_package($pkg);
- }
- if (!defined($mediatlp)) {
- debug("$pkg cannot be found in $location\n");
- next;
- }
- my $rctanvers = $mediatlp->cataloguedata->{'version'};
- my $mediarev = $mediatlp->revision;
- my $mediarevstr = $mediarev;
- my @addargs = ();
- if ($remotetlpdb->is_virtual) {
- push @addargs, $maxtag;
- $mediarevstr .= "\@$maxtag";
- } else {
- push @addargs, undef;
- }
- push @addargs, $lctanvers, $rctanvers;
- $nrupdated++;
- if ($opts{"list"}) {
- if ($::machinereadable) {
- # $pkg, $flag, $lrev, $rrev, $size, $runtime, $esttot, $tag, $lcv, $rcv
- machine_line($pkg, $FLAG_UPDATE, $rev, $mediarev, $sizes{$pkg}, "-", "-", @addargs);
- } else {
- my $kb = int($sizes{$pkg} / 1024) + 1;
- upd_info($pkg, $kb, $rev, $mediarevstr, "update");
- if ($remotetlpdb->is_virtual) {
- my @cand = $remotetlpdb->candidates($pkg);
- shift @cand; # remove the top element
- if (@cand) {
- print "\tother candidates: ";
- for my $a (@cand) {
- my ($t,$r) = split(/\//, $a, 2);
- print $r . '@' . $t . " ";
- }
- print "\n";
- }
- }
- }
- $updated{$pkg} = 1;
- next;
- } elsif (win32() && ($pkg =~ m/$CriticalPackagesRegexp/)) {
- # we pretend that the update happened
- # in order to calculate file changes properly
- $updated{$pkg} = 1;
- next;
- }
-
- # older tlmgr forgot to clear the relocated bit when saving a tlpobj
- # into the local tlpdb, although the paths were rewritten.
- # We have to clear this bit otherwise the make_container calls below
- # for creating an unwind container will create some rubbish
- # TODO for user mode we should NOT clear this bit!
- if ($tlp->relocated) {
- debug("$prg: warn, relocated bit set for $pkg, but that is wrong!\n");
- $tlp->relocated(0);
- }
-
- if ($opts{"backup"} && !$opts{"dry-run"}) {
- my $compressorextension = $Compressors{$::progs{'compressor'}}{'extension'};
- $tlp->make_container($::progs{'compressor'}, $root,
- destdir => $opts{"backupdir"},
- relative => $tlp->relocated,
- user => 1);
- $unwind_package =
- "$opts{'backupdir'}/${pkg}.r" . $tlp->revision . ".tar.$compressorextension";
-
- if ($autobackup) {
- # in case we do auto backups we remove older backups
- clear_old_backups($pkg, $opts{"backupdir"}, $autobackup);
- }
- }
-
- my ($estrem, $esttot);
- if (!$opts{"list"}) {
- ($estrem, $esttot) = TeXLive::TLUtils::time_estimate($totalsize,
- $donesize, $starttime);
- }
-
- if ($::machinereadable) {
- machine_line($pkg, $FLAG_UPDATE, $rev, $mediarev, $sizes{$pkg}, $estrem, $esttot, @addargs);
- } else {
- my $kb = int ($sizes{$pkg} / 1024) + 1;
- info("[" . sprintf ('%*2$s', $currnr, $totalnrdigits) .
- "/$totalnr, $estrem/$esttot] update: $pkg [${kb}k] ($rev -> $mediarevstr)");
- }
- $donesize += $sizes{$pkg};
- $currnr++;
-
- if ($opts{"dry-run"}) {
- info("\n") unless $::machinereadable;
- $updated{$pkg} = 1;
- next;
- } else {
- info(" ... ") unless $::machinereadable; # more to come
- }
-
- if (!$unwind_package) {
- # no backup was made, so let us create a temporary .tar file
- # of the package
- my $tlp = $localtlpdb->get_package($pkg);
- my ($s, undef, $fullname) = $tlp->make_container("tar", $root,
- destdir => $temp,
- containername => "__BACKUP_${pkg}",
- relative => $tlp->relocated,
- user => 1);
- if ($s <= 0) {
- tlwarn("\n$prg: creation of backup container failed for: $pkg\n");
- tlwarn("$prg: continuing to update other packages, please retry...\n");
- $ret |= $F_WARNING;
- # we should try to update other packages at least
- next;
- }
- $remove_unwind_container = 1;
- $unwind_package = "$fullname";
- }
- # first remove the package, then reinstall it
- # this way we get rid of useless files
- # force the deinstallation since we will reinstall it
- #
- # the remove_package should also remove empty dirs in case
- # a dir is changed into a file
- if ($pkg =~ m/$CriticalPackagesRegexp/) {
- debug("Not removing critical package $pkg\n");
- } else {
- $localtlpdb->remove_package($pkg,
- "remove-warn-files" => \%do_warn_on_move);
- }
- if ($remotetlpdb->install_package($pkg, $localtlpdb)) {
- # installation succeeded because we got a reference
- logpackage("update: $pkg ($rev -> $mediarevstr)");
- unlink($unwind_package) if $remove_unwind_container;
- # remember successful update
- $updated{$pkg} = 1;
- #
- # if we updated a .ARCH package we have to announce the postactions
- # of the parent package so that formats are rebuild
- if ($pkg =~ m/^([^.]*)\./) {
- my $parent = $1;
- if (!TeXLive::TLUtils::member($parent, @inst_packs, @new_packs, @inst_colls, @new_colls, @inst_schemes, @new_schemes)) {
- # ok, nothing happens with the parent package, so we have to
- # find it and execute the postactions
- my $parentobj = $localtlpdb->get_package($parent);
- if (!defined($parentobj)) {
- # well, in this case we might have hit a package that only
- # has .ARCH package, like psv.win32, so do nothing
- debug("$prg: .ARCH package without parent, not announcing postaction\n");
- } else {
- debug("$prg: announcing parent execute action for $pkg\n");
- TeXLive::TLUtils::announce_execute_actions("enable", $parentobj);
- }
- }
- }
- } else {
- # install_package returned a scalar, so error.
- # now in fact we should do some cleanup, removing files and
- # dirs from the new package before re-installing the old one.
- # TODO
- logpackage("failed update: $pkg ($rev -> $mediarevstr)");
- tlwarn("$prg: Installation of new version of $pkg failed, 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;
- }
-
- # the -1 force the TLUtils::unpack to NOT warn about missing checksum/sizes
- my ($instret, $msg) = TeXLive::TLUtils::unpack("$unwind_package",
- $localtlpdb->root, checksum => "-1", checksize => "-1");
- 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)");
- $ret |= $F_WARNING;
- tlwarn("$prg: Restoring old package state succeeded.\n");
- } else {
- logpackage("failed restore: $pkg ($rev)");
- tlwarn("$prg: Restoring of old package did NOT succeed.\n");
- tlwarn("$prg: Error message from unpack: $msg\n");
- tlwarn("$prg: Most likely repair: run tlmgr install $pkg and hope.\n");
- # TODO_ERRORCHECKING
- # should we return F_ERROR here??? If we would do this, then
- # no postactions at all would run? Maybe better only to give
- # a warning
- $ret |= $F_WARNING;
- }
- unlink($unwind_package) if $remove_unwind_container;
- }
- info("done\n") unless $::machinereadable;
- } else { # $is_new{$pkg} is true!!!
- #
- # NEW PACKAGES
- #
- if ($opts{"no-auto-install"}) {
- info("not auto-installing $pkg due to -no-auto-install (new on server)\n")
- unless $::machinereadable;
- } else {
- # install new packages
- my $mediatlp;
- my $maxtag;
- if ($remotetlpdb->is_virtual) {
- ($maxtag, undef, $mediatlp, undef) =
- $remotetlpdb->virtual_candidate($pkg);
- } else {
- $mediatlp = $remotetlpdb->get_package($pkg);
- }
- if (!defined($mediatlp)) {
- tlwarn("\n$prg: Should not happen: $pkg not found in $location\n");
- $ret |= $F_WARNING;
- next;
- }
- my $mediarev = $mediatlp->revision;
- my $mediarevstr = $mediarev;
- my @addargs;
- if ($remotetlpdb->is_virtual) {
- $mediarevstr .= "\@$maxtag";
- push @addargs, $maxtag;
- }
- my ($estrem, $esttot);
- if (!$opts{"list"}) {
- ($estrem, $esttot) = TeXLive::TLUtils::time_estimate($totalsize,
- $donesize, $starttime);
- }
- if ($::machinereadable) {
- my @maargs = ($pkg, $FLAG_AUTOINSTALL, "-", $mediatlp->revision, $sizes{$pkg});
- if (!$opts{"list"}) {
- push @maargs, $estrem, $esttot;
- } else {
- push @maargs, undef, undef;
- }
- machine_line(@maargs, @addargs);
- } else {
- my $kb = int($sizes{$pkg} / 1024) + 1;
- if ($opts{"list"}) {
- upd_info($pkg, $kb, "<absent>", $mediarevstr, "autoinst");
- } else {
- info("[" . sprintf ('%*2$s', $currnr, $totalnrdigits) .
- "/$totalnr, $estrem/$esttot] auto-install: $pkg ($mediarevstr) [${kb}k] ... ");
- }
- }
- $currnr++;
- $donesize += $sizes{$pkg};
- next if ($opts{"dry-run"} || $opts{"list"});
- if ($remotetlpdb->install_package($pkg, $localtlpdb)) {
- # installation succeeded because we got a reference
- logpackage("auto-install new: $pkg ($mediarevstr)");
- $nrupdated++;
- info("done\n") unless $::machinereadable;
- } else {
- tlwarn("$prg: couldn't install new package $pkg\n");
- }
- }
- }
- }
-
- #
- # special check for depending format updates:
- # if one of latex or tex has been updated, we rebuild the formats
- # defined in packages *depending* on these packages.
- check_announce_format_triggers(@inst_packs, @new_packs)
- if (!$opts{"list"});
-
- print "end-of-updates\n" if $::machinereadable;
-
- #
- # check that if updates to the critical packages are present all of
- # them have been successfully updated
- my $infra_update_done = 1;
- my @infra_files_to_be_removed;
- if ($opts{"list"}) {
- $infra_update_done = 0;
- } else {
- for my $pkg (@critical) {
- next unless (defined($updated{$pkg}));
- $infra_update_done &&= $updated{$pkg};
- my $oldtlp;
- my $newtlp;
- if ($updated{$pkg}) {
- $oldtlp = $localtlpdb->get_package($pkg);
- $newtlp = $remotetlpdb->get_package($pkg);
- } else {
- # update failed but we could introduce new files, that
- # should be removed now as a part of restoring backup
- $oldtlp = $remotetlpdb->get_package($pkg);
- $newtlp = $localtlpdb->get_package($pkg);
- }
- die ("That shouldn't happen: $pkg not found in tlpdb") if !defined($newtlp);
- die ("That shouldn't happen: $pkg not found in tlpdb") if !defined($oldtlp);
- my @old_infra_files = $oldtlp->all_files;
- my @new_infra_files = $newtlp->all_files;
- my %del_files;
- @del_files{@old_infra_files} = ();
- delete @del_files{@new_infra_files};
- for my $k (keys %del_files) {
- my @found_pkgs = $localtlpdb->find_file($k);
- if ($#found_pkgs >= 0) {
- my $bad_file = 1;
- if (win32()) {
- # on w32 the packages have not been removed already,
- # so we check that the only package listed in @found_pkgs
- # is the one we are working on ($pkg)
- if ($#found_pkgs == 0 && $found_pkgs[0] =~ m/^$pkg:/) {
- # only one package has been returned and it
- # matches the current package followed by a colon
- # remember the TLPDB->find_file returns
- # $pkg:$file
- # in this case we can ignore it
- $bad_file = 0;
- }
- }
- if ($bad_file) {
- tlwarn("$prg: The file $k has disappeared from the critical" .
- " package $pkg but is still present in @found_pkgs\n");
- $ret |= $F_WARNING;
- } else {
- push @infra_files_to_be_removed, $k;
- }
- } else {
- push @infra_files_to_be_removed, $k;
- }
- }
- }
-
- if (!win32()) {
- for my $f (@infra_files_to_be_removed) {
- # TODO actually unlink the stuff
- #unlink("$Master/$f");
- debug("removing disappearing file $f\n");
- }
- }
- } # end of if ($opts{"list"}) ... else part
-
- # check if any additional updates are asked for
- my $other_updates_asked_for = 0;
- if ($opts{"all"}) {
- $other_updates_asked_for = 1;
- } else {
- foreach my $p (@ARGV) {
- if ($p !~ m/$CriticalPackagesRegexp/) {
- $other_updates_asked_for = 1;
- last;
- }
- }
- }
-
- my $restart_tlmgr = 0;
- if ($opts{"self"} && @critical && !$opts{'no-restart'} &&
- $infra_update_done && $other_updates_asked_for) {
- # weed out the --self argument from the saved arguments
- @::SAVEDARGV = grep (!m/^-?-self$/, @::SAVEDARGV);
- $restart_tlmgr = 1;
- }
-
- # infra update and tlmgr restart on w32 is done by the updater batch script
- if (win32() && $opts{'self'} && !$opts{"list"} && @critical) {
- info("$prg: Preparing TeX Live infrastructure update...\n");
- for my $f (@infra_files_to_be_removed) {
- debug("file scheduled for removal $f\n");
- }
- my $ret = write_w32_updater($restart_tlmgr,
- \@infra_files_to_be_removed, @critical);
- if ($ret) {
- tlwarn ("$prg: Aborting infrastructure update.\n");
- $ret |= $F_ERROR;
- $restart_tlmgr = 0 if ($opts{"dry-run"});
- }
- }
-
- # only when we are not dry-running we restart the program
- if (!win32() && $restart_tlmgr && !$opts{"dry-run"} && !$opts{"list"}) {
- info("$prg: Restarting to complete update ...\n");
- debug("restarting tlmgr @::SAVEDARGV\n");
- # cleanup temp files before re-exec-ing tlmgr
- File::Temp::cleanup();
- exec("tlmgr", @::SAVEDARGV);
- # we need warn here, otherwise perl gives warnings!
- warn("$prg: cannot restart tlmgr, please retry update\n");
- return($F_ERROR);
- }
-
- # for --dry-run we cannot restart tlmgr (no way to fake successful
- # infra update) instead we call action_update() again and signal this
- # by $opts{"dry-run"} = -1
- if ($opts{"dry-run"} && !$opts{"list"} && $restart_tlmgr) {
- $opts{"self"} = 0;
- $opts{"dry-run"} = -1;
- $localtlpdb = undef;
- $remotetlpdb = undef;
- info ("$prg --dry-run: would restart tlmgr to complete update ...\n");
- $ret |= action_update();
- return ($ret);
- }
-
- # if a real update from default disk location didn't find anything,
- # warn if nothing is updated. Unless they said --self, in which case
- # we've already reported it.
- # But if --self --all was given, and *no* update available for
- # critical packages, then we should report it, too!
- if (!(@new || @updated) && ( !$opts{"self"} || @todo )) {
- if (!$::machinereadable) {
- info("$prg: no updates available\n");
- if ($remotetlpdb->media ne "NET"
- && $remotetlpdb->media ne "virtual"
- && !$opts{"dry-run"}
- && !$opts{"repository"}
- && !$ENV{"TEXLIVE_INSTALL_ENV_NOCHECK"}
- ) {
- tlwarn(<<END_DISK_WARN);
-$prg: Your installation is set up to look on the disk for updates.
-To install from the Internet for this one time only, run:
- tlmgr -repository $TeXLiveURL ACTION ARG...
-where ACTION is install, update, etc.; see tlmgr -help if needed.
-
-To change the default for all future updates, run:
- tlmgr option repository $TeXLiveURL
-END_DISK_WARN
- }
- }
- }
- return ($ret);
-}
-
-
-sub check_announce_format_triggers {
- # we treat new and updated packages the same as updated
- # when it comes to triggers
- my %updpacks = map { $_ => 1 } @_;
-
- # search all format definitions in the tlpdb
- FMTDEF: for my $fmtdef ($localtlpdb->format_definitions) {
- # if by default they are activated, check the whether the
- # trigger packages appear in the list of updated/new packages
- if (($fmtdef->{'mode'} == 1) && $fmtdef->{'fmttriggers'}) {
- for my $trigger (@{$fmtdef->{'fmttriggers'}}) {
- if ($updpacks{$trigger}) {
- TeXLive::TLUtils::announce_execute_actions("rebuild-format",
- 0, $fmtdef);
- next FMTDEF;
- }
- }
- }
- }
-}
-
-# 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 exactly 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)
-#
-# TLPDB->install_package does ONLY INSTALL ONE PACKAGE, no deps whatsoever
-# anymore! That has all to be done by the caller.
-#
-sub action_install {
- init_local_db(1);
- my $ret = $F_OK;
- return ($F_ERROR) if !check_on_writable();
-
- # installation from a .tar.xz
- if ($opts{"file"}) {
- if ($localtlpdb->install_package_files(@ARGV)) {
- return ($ret);
- } else {
- return ($F_ERROR);
- }
- }
-
- # if we are still here, we are installing from some repository
- # initialize the TLPDB from $location
- $opts{"no-depends"} = 1 if $opts{"no-depends-at-all"};
- init_tlmedia_or_die();
-
- # check for updates to tlmgr itself, and die unless --force is given
- if (!$opts{"usermode"}) {
- if (check_for_critical_updates( $localtlpdb, $remotetlpdb)) {
- critical_updates_warning() if (!$::machinereadable);
- if ($opts{"force"}) {
- tlwarn("$prg: Continuing due to --force\n");
- } else {
- if ($::gui_mode) {
- # return here and don't do any updates
- return ($F_ERROR);
- } else {
- die "$prg: Terminating; please see warning above!\n";
- }
- }
- }
- }
-
- $opts{"no-depends"} = 1 if $opts{"no-depends-at-all"};
- info("$prg install: dry run, no changes will be made\n") if $opts{"dry-run"};
-
- my @packs = @ARGV;
- # first expand the .ARCH dependencies unless $opts{"no-depends-at-all"}
- @packs = $remotetlpdb->expand_dependencies("-only-arch", $localtlpdb, @ARGV)
- unless $opts{"no-depends-at-all"};
- #
- # if no-depends, we're done; else get rest of deps.
- unless ($opts{"no-depends"}) {
- if ($opts{"reinstall"} || $opts{"usermode"}) {
- # if reinstall or usermode, omit collection->collection deps
- @packs = $remotetlpdb->expand_dependencies("-no-collections",
- $localtlpdb, @packs);
- } else {
- @packs = $remotetlpdb->expand_dependencies($localtlpdb, @packs);
- }
- }
- #
- # expand dependencies returns a list pkg@tag in case of a virtual
- # remote db.
- my %packs;
- for my $p (@packs) {
- my ($pp, $aa) = split('@', $p);
- $packs{$pp} = (defined($aa) ? $aa : 0);
- }
- #
- # installation order of packages:
- # first all normal packages, then collections, then schemes
- # isn't already installed, but the collection already updated, it will
- # be reported as forcibly removed.
- my @inst_packs;
- my @inst_colls;
- my @inst_schemes;
- for my $pkg (sort keys %packs) {
- # we do name checking here, not to load all tlpobj again and again
- if ($pkg =~ m/^scheme-/) {
- push @inst_schemes, $pkg;
- } elsif ($pkg =~ m/^collection-/) {
- push @inst_colls, $pkg;
- } else {
- push @inst_packs, $pkg;
- }
- }
- @inst_packs = sort packagecmp @inst_packs;
-
- my $starttime = time();
- # count packages
- my $totalnr = 0;
- my %revs;
- my @todo;
- for my $pkg (@inst_packs, @inst_colls, @inst_schemes) {
- my $pkgrev = 0;
- # if the package name is asked from a specific repository, use
- # that one, otherwise leave the decision to $remotetlpdb by not
- # giving a final argument
- my $mediatlp = $remotetlpdb->get_package($pkg,
- ($packs{$pkg} ? $packs{$pkg} : undef));
- if (!defined($mediatlp)) {
- tlwarn("$prg install: package $pkg not present in repository.\n");
- $ret |= $F_WARNING;
- next;
- }
- if (defined($localtlpdb->get_package($pkg))) {
- if ($opts{"reinstall"}) {
- $totalnr++;
- $revs{$pkg} = $mediatlp->revision;
- push @todo, $pkg;
- } else {
- # debug msg that we have this one.
- debug("already installed: $pkg\n");
- # if explicitly requested by user (not a dep), tell them.
- info("$prg install: package already present: $pkg\n")
- if grep { $_ eq $pkg } @ARGV;
- }
- } else {
- $totalnr++;
- $revs{$pkg} = $mediatlp->revision;
- push (@todo, $pkg);
- }
- }
- # return if there is nothing to install!
- return ($ret) if (!@todo);
-
- my $orig_do_src = $localtlpdb->option("install_srcfiles");
- my $orig_do_doc = $localtlpdb->option("install_docfiles");
- if (!$opts{"dry-run"}) {
- $localtlpdb->option("install_srcfiles", 1) if $opts{'with-src'};
- $localtlpdb->option("install_docfiles", 1) if $opts{'with-doc'};
- }
-
- my $currnr = 1;
- # undef here is a ref to array of platforms, if undef all are used
- my %sizes = %{$remotetlpdb->sizes_of_packages(
- $localtlpdb->option("install_srcfiles"),
- $localtlpdb->option("install_docfiles"), undef, @todo)};
- defined($sizes{'__TOTAL__'}) || ($sizes{'__TOTAL__'} = 0);
- my $totalsize = $sizes{'__TOTAL__'};
- my $donesize = 0;
-
- print "total-bytes\t$sizes{'__TOTAL__'}\n" if $::machinereadable;
- print "end-of-header\n" if $::machinereadable;
-
- foreach my $pkg (@todo) {
- my $flag = $FLAG_INSTALL;
- my $re = "";
- my $tlp = $remotetlpdb->get_package($pkg);
- my $rctanvers = $tlp->cataloguedata->{'version'};
- if (!defined($tlp)) {
- info("$prg: unknown package: $pkg\n");
- next;
- }
- if (!$tlp->relocated && $opts{"usermode"}) {
- info("$prg: package $pkg is not relocatable, cannot install it in user mode!\n");
- next;
- }
- my $lctanvers;
- if (defined($localtlpdb->get_package($pkg))) {
- my $lctanvers = $localtlpdb->get_package($pkg)->cataloguedata->{'version'};
- if ($opts{"reinstall"}) {
- $re = "re";
- $flag = $FLAG_REINSTALL;
- } else {
- debug("already installed (but didn't we say that already?): $pkg\n");
- next;
- }
- }
- my ($estrem, $esttot) = TeXLive::TLUtils::time_estimate($totalsize,
- $donesize, $starttime);
- my $kb = int($sizes{$pkg} / 1024) + 1;
- my @addargs = ();
- my $tagstr = "";
- if ($remotetlpdb->is_virtual) {
- if ($packs{$pkg} ne "0") {
- push @addargs, $packs{$pkg};
- $tagstr = " \@" . $packs{$pkg};
- } else {
- my ($maxtag,undef,undef,undef) = $remotetlpdb->virtual_candidate($pkg);
- push @addargs, $maxtag;
- $tagstr = " \@" . $maxtag;
- }
- }
- push @addargs, $lctanvers, $rctanvers;
- if ($::machinereadable) {
- machine_line($pkg, $flag, "-", $revs{$pkg}, $sizes{$pkg}, $estrem, $esttot, @addargs);
- } else {
- info("[$currnr/$totalnr, $estrem/$esttot] ${re}install: $pkg$tagstr [${kb}k]\n");
- }
- if (!$opts{"dry-run"}) {
- if ($remotetlpdb->install_package($pkg, $localtlpdb,
- ($packs{$pkg} ? $packs{$pkg} : undef) )) {
- logpackage("${re}install: $pkg$tagstr");
- } else {
- logpackage("failed ${re}install: $pkg$tagstr");
- }
- }
- $donesize += $sizes{$pkg};
- $currnr++;
- }
- print "end-of-updates\n" if $::machinereadable;
-
-
- if ($opts{"dry-run"}) {
- # stop here, don't do any postinstall actions
- return($ret | $F_NOPOSTACTION);
- } else {
- # reset option if --with-src argument was given
- $localtlpdb->option("install_srcfiles", $orig_do_src) if $opts{'with-src'};
- $localtlpdb->option("install_docfiles", $orig_do_doc) if $opts{'with-doc'};
- $localtlpdb->save if ($opts{'with-src'} || $opts{'with-doc'});
- }
- return ($ret);
-}
-
-sub show_one_package {
- my ($pkg, $fmt, @rest) = @_;
- my $ret;
- if ($fmt eq "list") {
- $ret = show_one_package_list($pkg, @rest);
- } elsif ($fmt eq "detail") {
- $ret = show_one_package_detail($pkg, @rest);
- } elsif ($fmt eq "csv") {
- $ret = show_one_package_csv($pkg, @rest);
- } elsif ($fmt eq "json") {
- $ret = show_one_package_json($pkg);
- } else {
- tlwarn("$prg: show_one_package: unknown format: $fmt\n");
- return($F_ERROR);
- }
- return($ret);
-}
-
-sub show_one_package_json {
- my ($p) = @_;
- my @out;
- my $loctlp = $localtlpdb->get_package($p);
- my $remtlp = $remotetlpdb->get_package($p);
- my $is_installed = (defined($loctlp) ? 1 : 0);
- my $is_available = (defined($remtlp) ? 1 : 0);
- if (!($is_installed || $is_available)) {
- # output proper JSON for unavailable packages
- print "{ \"name\":\"$p\", \"available\":false }";
- #tlwarn("$prg: package $p not found neither locally nor remote!\n");
- #return($F_WARNING);
- return($F_OK);
- }
- # prefer local TLPs as they have RELOC replaced by proper paths
- my $tlp = ($is_installed ? $loctlp : $remtlp);
- #my $tlp = ($is_available ? $remtlp : $loctlp);
- # add available, installed, lrev, rrev fields and remove revision field
- my $str = $tlp->as_json(available => ($is_available ? TeXLive::TLUtils::True() : TeXLive::TLUtils::False()),
- installed => ($is_installed ? TeXLive::TLUtils::True() : TeXLive::TLUtils::False()),
- lrev => ($is_installed ? $loctlp->revision : 0),
- rrev => ($is_available ? $remtlp->revision : 0),
- rcataloguedata => ($is_available ? $remtlp->cataloguedata : {}),
- revision => undef);
- print $str;
- return($F_OK);
-}
-
-
-sub show_one_package_csv {
- my ($p, @datafields) = @_;
- my @out;
- my $loctlp = $localtlpdb->get_package($p);
- my $remtlp = $remotetlpdb->get_package($p) unless ($opts{'only-installed'});
- my $is_installed = (defined($loctlp) ? 1 : 0);
- my $is_available = (defined($remtlp) ? 1 : 0);
- if (!($is_installed || $is_available)) {
- if ($opts{'only-installed'}) {
- tlwarn("$prg: package $p not locally!\n");
- } else {
- tlwarn("$prg: package $p not found neither locally nor remote!\n");
- }
- return($F_WARNING);
- }
- my $tlp = ($is_installed ? $loctlp : $remtlp);
- for my $d (@datafields) {
- if ($d eq "name") {
- push @out, $p;
- } elsif ($d eq "category") {
- push @out, $tlp->category || "";
- } elsif ($d eq "shortdesc") {
- my $str = $tlp->shortdesc;
- if (defined $tlp->shortdesc) {
- $str =~ s/"/\\"/g;
- push @out, "\"$str\"";
- } else {
- push @out, "";
- }
- } elsif ($d eq "longdesc") {
- my $str = $tlp->longdesc;
- if (defined $tlp->shortdesc) {
- $str =~ s/"/\\"/g;
- $str =~ s/\n/\\n/g;
- push @out, "\"$str\"";
- } else {
- push @out, "";
- }
- } elsif ($d eq "installed") {
- push @out, $is_installed;
- } elsif ($d eq "relocatable") {
- push @out, ($tlp->relocated ? 1 : 0);
- } elsif ($d eq "cat-version") {
- push @out, ($tlp->cataloguedata->{'version'} || "");
- } elsif ($d eq "lcat-version") {
- push @out, ($is_installed ? ($loctlp->cataloguedata->{'version'} || "") : "");
- } elsif ($d eq "rcat-version") {
- push @out, ($is_available ? ($remtlp->cataloguedata->{'version'} || "") : "");
- } elsif ($d eq "cat-date") {
- push @out, ($tlp->cataloguedata->{'date'} || "");
- } elsif ($d eq "lcat-date") {
- push @out, ($is_installed ? ($loctlp->cataloguedata->{'date'} || "") : "");
- } elsif ($d eq "rcat-date") {
- push @out, ($is_available ? ($remtlp->cataloguedata->{'date'} || "") : "");
- } elsif ($d eq "cat-license") {
- push @out, ($tlp->cataloguedata->{'license'} || "");
- } elsif ($d eq "lcat-license") {
- push @out, ($is_installed ? ($loctlp->cataloguedata->{'license'} || "") : "");
- } elsif ($d eq "rcat-license") {
- push @out, ($is_available ? ($remtlp->cataloguedata->{'license'} || "") : "");
- } elsif ($d =~ m/^cat-(contact-.*)$/) {
- push @out, ($tlp->cataloguedata->{$1} || "");
- } elsif ($d =~ m/^lcat-(contact-.*)$/) {
- push @out, ($is_installed ? ($loctlp->cataloguedata->{$1} || "") : "");
- } elsif ($d =~ m/^rcat-(contact-.*)$/) {
- push @out, ($is_available ? ($remtlp->cataloguedata->{$1} || "") : "");
- } elsif ($d eq "localrev") {
- push @out, ($is_installed ? $loctlp->revision : 0);
- } elsif ($d eq "remoterev") {
- push @out, ($is_available ? $remtlp->revision : 0);
- } elsif ($d eq "depends") {
- push @out, (join(":", $tlp->depends));
- } elsif ($d eq "size") {
- # tlp->*size is in 4k blocks!
- my $srcsize = $tlp->srcsize * $TeXLive::TLConfig::BlockSize;
- my $docsize = $tlp->docsize * $TeXLive::TLConfig::BlockSize;
- my $runsize = $tlp->runsize * $TeXLive::TLConfig::BlockSize;
- my $binsize = 0;
- my $binsizes = $tlp->binsize;
- for my $a (keys %$binsizes) { $binsize += $binsizes->{$a} ; }
- $binsize *= $TeXLive::TLConfig::BlockSize;
- my $totalsize = $srcsize + $docsize + $runsize + $binsize;
- push @out, $totalsize;
- } else {
- tlwarn("$prg: unknown data field $d\n");
- return($F_WARNING);
- }
- }
- print join(",", @out), "\n";
- return($F_OK);
-}
-
-sub show_one_package_list {
- my ($p, @rest) = @_;
- my @out;
- my $loctlp = $localtlpdb->get_package($p);
- my $remtlp = $remotetlpdb->get_package($p) unless ($opts{'only-installed'});
- my $is_installed = (defined($loctlp) ? 1 : 0);
- my $is_available = (defined($remtlp) ? 1 : 0);
- if (!($is_installed || $is_available)) {
- if ($opts{'only-installed'}) {
- tlwarn("$prg: package $p not locally!\n");
- } else {
- tlwarn("$prg: package $p not found neither locally nor remote!\n");
- }
- return($F_WARNING);
- }
- my $tlp = ($is_installed ? $loctlp : $remtlp);
- my $tlm;
- if ($opts{"only-installed"}) {
- $tlm = $localtlpdb;
- } else {
- $tlm = $remotetlpdb;
- }
- if ($is_installed) {
- print "i ";
- } else {
- print " ";
- }
- if (!$tlp) {
- if ($remotetlpdb->is_virtual) {
- # we might have the case that a package is present in a
- # subsidiary repository, but not pinned, so it will
- # not be found by ->get_package
- # In this case we list all repositories shipping it,
- # but warn that it is not pinned and thus not reachable.
- my @cand = $remotetlpdb->candidates($p);
- if (@cand) {
- my $first = shift @cand;
- if (defined($first)) {
- tlwarn("$prg: strange, we have a first candidate but no tlp: $p\n");
- return($F_WARNING);
- }
- # already shifted away the first element
- if ($#cand >= 0) {
- print "$p: --- no installable candidate found, \n";
- print " but present in subsidiary repositories without a pin.\n";
- print " This package is not reachable without pinning.\n";
- print " Repositories containing this package:\n";
- for my $a (@cand) {
- my ($t,$r) = split(/\//, $a, 2);
- my $tlp = $remotetlpdb->get_package($p, $t);
- my $foo = $tlp->shortdesc;
- print " $t: ", defined($foo) ? $foo : "(shortdesc missing)" , "\n";
- }
- return($F_WARNING);
- } else {
- tlwarn("$prg: strange, package listed but no residual candidates: $p\n");
- return($F_WARNING);
- }
- } else {
- tlwarn("$prg: strange, package listed but no candidates: $p\n");
- return($F_WARNING);
- }
- } else {
- tlwarn("$prg: strange, package cannot be found in remote tlpdb: $p\n");
- return($F_WARNING);
- }
- }
- my $foo = $tlp->shortdesc;
- print "$p: ", defined($foo) ? $foo : "(shortdesc missing)" , "\n";
- return($F_OK);
-}
-
-sub show_one_package_detail {
- my ($ppp, @rest) = @_;
- my $ret = $F_OK;
- my ($pkg, $tag) = split ('@', $ppp, 2);
- my $tlpdb = $localtlpdb;
- my $source_found;
- my $tlp = $localtlpdb->get_package($pkg);
- my $installed = 0;
- if (!$tlp) {
- if ($opts{"only-installed"}) {
- print "package: $pkg\n";
- print "installed: No\n";
- return($F_OK);
- }
- if (!$remotetlpdb) {
- init_tlmedia_or_die(1);
- }
- if (defined($tag)) {
- if (!$remotetlpdb->is_virtual) {
- tlwarn("$prg: specifying implicit tags not allowed for non-virtual databases!\n");
- return($F_WARNING);
- } else {
- if (!$remotetlpdb->is_repository($tag)) {
- tlwarn("$prg: no such repository tag defined: $tag\n");
- return($F_WARNING);
- }
- }
- }
- $tlp = $remotetlpdb->get_package($pkg, $tag);
- if (!$tlp) {
- if (defined($tag)) {
- # we already searched for the package in a specific tag, don't retry
- # all candidates!
- tlwarn("$prg: cannot find package $pkg in repository $tag\n");
- return($F_WARNING);
- }
- my @cand = $remotetlpdb->candidates($pkg);
- if (@cand) {
- # if @cand is not empty, then we have a virtual database
- # we might have a package that is available in a
- # subsidiary repository, but not installable
- # because it is not pinned
- # we will list it but warn about this fact
- # useless test, @cand will always be defined because $remotetlpdb is virtual
- my $first = shift @cand;
- if (defined($first)) {
- tlwarn("$prg: strange, we have a first candidate but no tlp: $pkg\n");
- return($F_WARNING);
- }
- # already shifted away the first element
- if ($#cand >= 0) {
- # recursively showing all tags, but warn
- print "package: ", $pkg, "\n";
- print "WARNING: This package is not pinned but present in subsidiary repositories\n";
- print "WARNING: As long as it is not pinned it is not installable.\n";
- print "WARNING: Listing all available copies of the package.\n";
- my @aaa;
- for my $a (@cand) {
- my ($t,$r) = split(/\//, $a, 2);
- push @aaa, "$pkg" . '@' . $t;
- }
- $ret |= action_info(@aaa);
- return($ret);
- }
- }
- # we didn't find a package like this, so use search
- info("$prg: cannot find package $pkg, searching for other matches:\n");
- my ($foundfile, $founddesc) = search_tlpdb($remotetlpdb,$pkg,1,1,0);
- print "\nPackages containing \`$pkg\' in their title/description:\n";
- print $founddesc;
- print "\nPackages containing files matching \`$pkg\':\n";
- print $foundfile;
- return($ret);
- }
- # we want to also show the source if it is known
- if (defined($tag)) {
- $source_found = $tag;
- } else {
- if ($remotetlpdb->is_virtual) {
- my ($firsttag, @cand) = $remotetlpdb->candidates($pkg);
- $source_found = $firsttag;
- } else {
- # might be single user repository, don't mention anything
- }
- }
- $tlpdb = $remotetlpdb;
- } else {
- $installed = 1;
- }
- my @colls;
- if ($tlp->category ne "Collection" && $tlp->category ne "Scheme") {
- @colls = $localtlpdb->needed_by($pkg);
- if (!@colls) {
- if (!$opts{"only-installed"}) {
- # not referenced in the local tlpdb, so try the remote here, too
- init_tlmedia_or_die() if (!$remotetlpdb);
- @colls = $remotetlpdb->needed_by($pkg);
- }
- }
- }
- # {
- # require Data::Dumper;
- # print Data::Dumper->Dump([\$tlp], [qw(tlp)]);
- # }
- # some packages might depend on other packages, so do not
- # include arbitrary packages in the list of collections, but
- # only collections:
- @colls = grep {m;^collection-;} @colls;
- print "package: ", $tlp->name, "\n";
- print "repository: ", $source_found, "\n" if (defined($source_found));
- print "category: ", $tlp->category, "\n";
- print "shortdesc: ", $tlp->shortdesc, "\n" if ($tlp->shortdesc);
- print "longdesc: ", $tlp->longdesc, "\n" if ($tlp->longdesc);
- print "installed: ", ($installed ? "Yes" : "No"), "\n";
- print "revision: ", $tlp->revision, "\n" if ($installed);
- #
- # size computation: for normal packages give src/run/doc/bin sizes
- # for scheme/collection accumulated (including deps) sizes
- my $sizestr = "";
- if ($tlp->category ne "Collection" && $tlp->category ne "Scheme") {
- my $srcsize = $tlp->srcsize * $TeXLive::TLConfig::BlockSize;
- $sizestr = sprintf("%ssrc: %dk", $sizestr, int($srcsize / 1024) + 1)
- if ($srcsize > 0);
- my $docsize = $tlp->docsize * $TeXLive::TLConfig::BlockSize;
- $sizestr .= sprintf("%sdoc: %dk",
- ($sizestr ? ", " : ""), int($docsize / 1024) + 1)
- if ($docsize > 0);
- my $runsize = $tlp->runsize * $TeXLive::TLConfig::BlockSize;
- $sizestr .= sprintf("%srun: %dk",
- ($sizestr ? ", " : ""), int($runsize / 1024) + 1)
- if ($runsize > 0);
- # check for .ARCH expansions
- my $do_archs = 0;
- for my $d ($tlp->depends) {
- if ($d =~ m/^(.*)\.ARCH$/) {
- $do_archs = 1;
- last;
- }
- }
- if ($do_archs) {
- my @a = $localtlpdb->available_architectures;
- my %binsz = %{$tlp->binsize};
- my $binsize = 0;
- for my $a (@a) {
- $binsize += $binsz{$a} if defined($binsz{$a});
- my $atlp = $tlpdb->get_package($tlp->name . ".$a");
- if (!$atlp) {
- tlwarn("$prg: cannot find depending package" . $tlp->name . ".$a\n");
- return($F_WARNING);
- }
- my %abinsz = %{$atlp->binsize};
- $binsize += $abinsz{$a} if defined($abinsz{$a});
- }
- $binsize *= $TeXLive::TLConfig::BlockSize;
- $sizestr .= sprintf("%sbin: %dk",
- ($sizestr ? ", " : ""), int($binsize / 1024) + 1)
- if ($binsize > 0);
- }
- } else {
- # case of collection or scheme
- my $foo = $tlpdb->sizes_of_packages_with_deps ( 1, 1, undef, $pkg);
- if (defined($foo->{$pkg})) {
- $sizestr = sprintf("%dk", int($foo->{$pkg} / 1024) + 1);
- }
- }
- print "sizes: ", $sizestr, "\n";
- print "relocatable: ", ($tlp->relocated ? "Yes" : "No"), "\n";
- print "cat-version: ", $tlp->cataloguedata->{'version'}, "\n"
- if $tlp->cataloguedata->{'version'};
- print "cat-date: ", $tlp->cataloguedata->{'date'}, "\n"
- if $tlp->cataloguedata->{'date'};
- print "cat-license: ", $tlp->cataloguedata->{'license'}, "\n"
- if $tlp->cataloguedata->{'license'};
- print "cat-topics: ", $tlp->cataloguedata->{'topics'}, "\n"
- if $tlp->cataloguedata->{'topics'};
- print "cat-related: ", $tlp->cataloguedata->{'also'}, "\n"
- if $tlp->cataloguedata->{'also'};
- # print all the contact-* keys
- for my $k (keys %{$tlp->cataloguedata}) {
- if ($k =~ m/^contact-/) {
- print "cat-$k: ", $tlp->cataloguedata->{$k}, "\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
- my @deps = $tlp->depends;
- if (@deps) {
- print "depends:\n";
- for my $d (@deps) {
- print "\t$d\n";
- }
- }
- }
- print "Included files, by type:\n";
- # if the package has a .ARCH dependency we also list the files for
- # those packages
- my @todo = $tlpdb->expand_dependencies("-only-arch", $tlpdb, ($pkg));
- for my $d (sort @todo) {
- my $foo = $tlpdb->get_package($d);
- if (!$foo) {
- tlwarn ("$prg: Should not happen, no dependent package $d\n");
- return($F_WARNING);
- }
- if ($d ne $pkg) {
- print "depending package $d:\n";
- }
- if ($foo->runfiles) {
- print "run files:\n";
- for my $f (sort $foo->runfiles) { print " $f\n"; }
- }
- if ($foo->srcfiles) {
- print "source files:\n";
- for my $f (sort $foo->srcfiles) { print " $f\n"; }
- }
- if ($foo->docfiles) {
- print "doc files:\n";
- for my $f (sort $foo->docfiles) {
- print " $f";
- my $dfd = $foo->docfiledata;
- if (defined($dfd->{$f})) {
- for my $k (keys %{$dfd->{$f}}) {
- print " $k=\"", $dfd->{$f}->{$k}, '"';
- }
- }
- print "\n";
- }
- }
- # in case we have them
- if ($foo->allbinfiles) {
- print "bin files (all platforms):\n";
- for my $f (sort $foo->allbinfiles) { print " $f\n"; }
- }
- }
- }
- print "\n";
- return($ret);
-}
-
-# PINNING
-#
-# this action manages the pinning file
-# of course it can be edited by hand, but we want to make this
-# easier for people to use
-# tlmgr pinning show
-# tlmgr pinning check
-# tlmgr pinning add <repo> <pkgglob> [<pkgglob> ...]
-# tlmgr pinning remove <repo> <pkgglob> [<pkgglob> ...]
-# tlmgr pinning remove <repo> --all
-sub action_pinning {
- my $what = shift @ARGV;
- $what || ($what = 'show');
- init_local_db();
- init_tlmedia_or_die();
- if (!$remotetlpdb->is_virtual) {
- tlwarn("$prg: only one repository configured, "
- . "pinning actions not supported.\n");
- return $F_WARNING;
- }
- my $pinref = $remotetlpdb->virtual_pindata();
- my $pf = $remotetlpdb->virtual_pinning();
-
- if ($what =~ m/^show$/i) {
- my @pins = @$pinref;
- if (!@pins) {
- tlwarn("$prg: no pinning data present.\n");
- return $F_OK;
- }
- info("$prg: this pinning data is defined:\n");
- for my $p (@pins) {
- info(" ", $p->{'repo'}, ":", $p->{'glob'}, "\n");
- }
- return $F_OK;
-
- } elsif ($what =~ m/^check$/i) {
- tlwarn("$prg: not implemented yet, sorry!\n");
- return $F_WARNING;
-
- } elsif ($what =~ m/^add$/i) {
- # we need at least two more arguments
- if (@ARGV < 2) {
- tlwarn("$prg: need at least two arguments to pinning add\n");
- return $F_ERROR;
- }
- my $repo = shift @ARGV;
- my @new = ();
- my @ov = $pf->value($repo);
- for my $n (@ARGV) {
- if (member($n, @ov)) {
- info("$prg: already pinned to $repo: $n\n");
- } else {
- push (@ov, $n);
- push (@new, $n);
- }
- }
- $pf->value($repo, @ov);
- $remotetlpdb->virtual_update_pins();
- $pf->save;
- info("$prg: new pinning data for $repo: @new\n") if @new;
- return $F_OK;
-
- } elsif ($what =~ m/^remove$/i) {
- my $repo = shift @ARGV;
- if (!defined($repo)) {
- tlwarn("$prg: missing repository argument to pinning remove\n");
- return $F_ERROR;
- }
- if ($opts{'all'}) {
- if (@ARGV) {
- tlwarn("$prg: additional argument(s) not allowed with --all: @ARGV\n");
- return $F_ERROR;
- }
- $pf->delete_key($repo);
- $remotetlpdb->virtual_update_pins();
- $pf->save;
- info("$prg: all pinning data removed for repository $repo\n");
- return $F_OK;
- }
- # complicated case, we want to remove only one setting
- my @ov = $pf->value($repo);
- my @nv;
- for my $pf (@ov) {
- push (@nv, $pf) if (!member($pf, @ARGV));
- }
- if ($#ov == $#nv) {
- info("$prg: no changes in pinning data for $repo\n");
- return $F_OK;
- }
- if (@nv) {
- $pf->value($repo, @nv);
- } else {
- $pf->delete_key($repo);
- }
- $remotetlpdb->virtual_update_pins();
- $pf->save;
- info("$prg: removed pinning data for repository $repo: @ARGV\n");
- return $F_OK;
-
- } else {
- tlwarn("$prg: unknown argument for pinning action: $what\n");
- return $F_ERROR;
- }
- # $pin{'repo'} = $repo;
- # $pin{'glob'} = $glob;
- # $pin{'re'} = $re;
- # $pin{'line'} = $line; # for debug/warning purpose
- return $F_ERROR;
-}
-
-# REPOSITORY
-#
-# this action manages the list of repositories
-# tlmgr repository list -> lists repositories
-# tlmgr repository list path|tag -> lists content of repo path|tag
-# tlmgr repository add path [tag] -> add repository with optional tag
-# tlmgr repository remove [path|tag] -> removes repository or tag
-# tlmgr repository set path[#tag] [path[#tag] ...] -> sets the list
-#
-
-sub array_to_repository {
- my %r = @_;
- my @ret;
- my @k = keys %r;
- if ($#k == 0) {
- # only one repo, don't write any tag
- return $r{$k[0]};
- }
- for my $k (keys %r) {
- my $v = $r{$k};
- if ($k ne $v) {
- $v = "$v#$k";
- }
- # encode spaces and % in the path and tags
- $v =~ s/%/%25/g;
- $v =~ s/ /%20/g;
- push @ret, $v;
- }
- return "@ret";
-}
-sub merge_sub_packages {
- my %pkgs;
- for my $p (@_) {
- if ($p =~ m/^(.*)\.([^.]*)$/) {
- my $n = $1;
- my $a = $2;
- if ($p eq "texlive.infra") {
- push @{$pkgs{$p}}, "all";
- } else {
- push @{$pkgs{$n}}, $a;
- }
- } else {
- push @{$pkgs{$p}}, "all";
- }
- }
- return %pkgs;
-}
-sub action_repository {
- init_local_db();
- my $what = shift @ARGV;
- $what = "list" if !defined($what);
- my %repos = repository_to_array($localtlpdb->option("location"));
- if ($what =~ m/^list$/i) {
- if (@ARGV) {
- # list what is in a repository
- for my $repo (@ARGV) {
- my $loc = $repo;
- if (defined($repos{$repo})) {
- $loc = $repos{$repo};
- }
- my ($tlpdb, $errormsg) = setup_one_remotetlpdb($loc);
- if (!defined($tlpdb)) {
- tlwarn("$prg: cannot get TLPDB from location $loc\n\n");
- } else {
- print "Packages at $loc:\n";
- my %pkgs = merge_sub_packages($tlpdb->list_packages);
- for my $p (sort keys %pkgs) {
- next if ($p =~ m/00texlive/);
- print " $p";
- if (!$opts{'with-platforms'}) {
- print "\n";
- } else {
- my @a = @{$pkgs{$p}};
- if ($#a == 0) {
- if ($a[0] eq "all") {
- # no further information necessary
- print "\n";
- } else {
- print ".$a[0]\n";
- }
- } else {
- print " (@{$pkgs{$p}})\n";
- }
- }
- }
- }
- }
- } else {
- print "List of repositories (with tags if set):\n";
- for my $k (keys %repos) {
- my $v = $repos{$k};
- print "\t$v";
- if ($k ne $v) {
- print " ($k)";
- }
- print "\n";
- }
- }
- return ($F_OK);
- }
- if ($what eq "add") {
- my $p = shift @ARGV;
- if (!defined($p)) {
- tlwarn("$prg: no repository given (to add)\n");
- return ($F_ERROR);
- }
- # check if it is either url or absolute path
- if (($p !~ m!^(https?|ftp)://!i) && ($p !~ m!$TeXLive::TLUtils::SshURIRegex!) &&
- !File::Spec->file_name_is_absolute($p)) {
- tlwarn("$prg: neither https?/ftp/ssh/scp/file URI nor absolute path, no action: $p\n");
- return ($F_ERROR);
- }
- my $t = shift @ARGV;
- $t = $p if (!defined($t));
- if (defined($repos{$t})) {
- tlwarn("$prg: repository or its tag already defined, no action: $p\n");
- return ($F_ERROR);
- }
- # TODO more checks needed?
- # if there was till now only *one* repository and that without
- # a tag, we give that one the "main" tag which is necessary
- # for proper operation!
- my @tags = keys %repos;
- if ($#tags == 0) {
- # we have only one repository, check if it has the main tag
- my $maintag = $tags[0];
- if ($maintag ne 'main') {
- $repos{'main'} = $repos{$maintag};
- delete $repos{$maintag};
- }
- }
- $repos{$t} = $p;
- $localtlpdb->option("location", array_to_repository(%repos));
- $localtlpdb->save;
- if ($t eq $p) {
- print "$prg: added repository: $p\n";
- } else {
- print "$prg: added repository with tag $t: $p\n";
- }
- return ($F_OK);
- }
- if ($what eq "remove") {
- my $p = shift @ARGV;
- if (!defined($p)) {
- tlwarn("$prg: no repository given (to remove)\n");
- return ($F_ERROR);
- }
- my $found = 0;
- for my $k (keys %repos) {
- if ($k eq $p || $repos{$k} eq $p) {
- $found = 1;
- delete $repos{$k};
- }
- }
- if (!$found) {
- tlwarn("$prg: repository not defined, cannot remove: $p\n");
- return ($F_ERROR);
- } else {
- $localtlpdb->option("location", array_to_repository(%repos));
- $localtlpdb->save;
- print "$prg: removed repository: $p\n";
- return ($F_OK);
- }
- # no reached
- return ($F_OK);
- }
- if ($what eq "set") {
- # TODO TODO
- # we have to make sure that there is ONE main repository!!!
- %repos = repository_to_array("@ARGV");
- $localtlpdb->option("location", array_to_repository(%repos));
- $localtlpdb->save;
- return ($F_OK);
- }
- if ($what eq "status") {
- if (!defined($remotetlpdb)) {
- init_tlmedia_or_die();
- }
- if (!$remotetlpdb->is_virtual) {
- my $verstat = $remotetlpdb->verification_status;
- print "main ", $remotetlpdb->location, " ",
- ($::machinereadable ? "$verstat " : ""),
- $VerificationStatusDescription{$verstat}, "\n";
- return ($F_OK);
- } else {
- for my $t ($remotetlpdb->virtual_get_tags()) {
- my $tlpdb = $remotetlpdb->virtual_get_tlpdb($t);
- my $verstat = $tlpdb->verification_status;
- print "$t ", $tlpdb->location, " ",
- ($::machinereadable ? "$verstat " : ""),
- $VerificationStatusDescription{$verstat}, "\n";
- }
- return($F_OK);
- }
- }
- # we are still here, unknown command to repository
- tlwarn("$prg: unknown subaction for tlmgr repository: $what\n");
- return ($F_ERROR);
-}
-
-sub action_candidates {
- my $what = shift @ARGV;
- if (!defined($what)) {
- tlwarn("$prg: candidates needs a package name as argument\n");
- return ($F_ERROR);
- }
- init_local_db();
- init_tlmedia_or_die();
- my @cand = $remotetlpdb->candidates($what);
- if (@cand) {
- my $first = shift @cand;
- if (defined($first)) {
- my ($t,$r) = split(/\//, $first, 2);
- print "Install candidate for $what from $t ($r)\n";
- } else {
- print "No install candidate for $what found.\n";
- }
- # already shifted away the first element
- if ($#cand >= 0) {
- print "Other repositories providing this package:\n";
- for my $a (@cand) {
- my ($t,$r) = split(/\//, $a, 2);
- print "$t ($r)\n";
- }
- }
- } else {
- print "Package $what not found.\n";
- return ($F_WARNING);
- }
- return ($F_OK);;
-}
-
-# OPTION
-#
-sub action_option {
- my $what = shift @ARGV;
- $what = "show" unless defined($what);
- init_local_db();
- my $ret = $F_OK;
- my %json;
- if ($what =~ m/^show$/i) {
- if ($opts{'json'}) {
- my $json = $localtlpdb->options_as_json();
- print("$json\n");
- return($ret);
- }
- for my $o (sort keys %{$localtlpdb->options}) {
- # ignore generate_update which is no longer used or needed.
- next if ($o eq "generate_updmap");
- # ignore some things which are w32 specific
- next if ($o eq "desktop_integration" && !win32());
- next if ($o eq "file_assocs" && !win32());
- next if ($o eq "w32_multi_user" && !win32());
- #
- if (win32()) {
- next if ($o =~ m/^sys_/);
- }
- if (defined $TLPDBOptions{$o}) {
- if ($::machinereadable) {
- print "$TLPDBOptions{$o}->[2]\t", $localtlpdb->option($o), "\n";
- } else {
- info("$TLPDBOptions{$o}->[3] ($TLPDBOptions{$o}->[2]): " .
- $localtlpdb->option($o) . "\n");
- }
- } else {
- tlwarn ("$prg: option $o not supported\n");
- $ret |= $F_WARNING;
- }
- }
- } elsif ($what =~ m/^(showall|help)$/i) {
- if ($opts{'json'}) {
- my $json = $localtlpdb->options_as_json();
- print("$json\n");
- return($ret);
- }
- my %loc = %{$localtlpdb->options};
- for my $o (sort keys %TLPDBOptions) {
- if ($::machinereadable) {
- print "$TLPDBOptions{$o}->[2]\t",
- (defined($loc{$o}) ? $loc{$o} : "(not set)"), "\n";
- } else {
- info("$TLPDBOptions{$o}->[3] ($TLPDBOptions{$o}->[2]): " .
- (defined($loc{$o}) ? $loc{$o} : "(not set)") . "\n");
- }
- }
- } else {
- if ($what eq "location" || $what eq "repo") {
- # silently rewrite location|repo -> repository
- $what = "repository";
- }
- my $found = 0;
- for my $opt (keys %TLPDBOptions) {
- if (($what eq $TLPDBOptions{$opt}->[2]) || ($what eq $opt)) {
- $found = 1;
- # the option argument matches the name
- my $val = shift @ARGV;
- if (defined($val)) {
- return ($F_ERROR) if !check_on_writable();
- # set new value
- # here we have to care for some special cases
- if ($what eq $TLPDBOptions{"location"}->[2]) {
- # support "ctan" on the cmd line
- if ($val =~ m/^ctan$/i) {
- $val = "$TeXLive::TLConfig::TeXLiveURL";
- }
- info("$prg: setting default package repository to $val\n");
- $localtlpdb->option($opt, $val);
- } elsif ($what eq $TLPDBOptions{"backupdir"}->[2]) {
- info("$prg: setting option $what to $val.\n");
- if (! -d $val) {
- info("$prg: the directory $val does not exists, it has to be created\n");
- info("$prg: before backups can be done automatically.\n");
- }
- $localtlpdb->option($opt, $val);
- } elsif ($what eq $TLPDBOptions{"w32_multi_user"}->[2]) {
- # when running w32 do not allow that a non-admin users sets
- # this from false to true
- my $do_it = 0;
- if (win32()) {
- if (admin()) {
- $do_it = 1;
- } else {
- if ($val) {
- # non admin and tries to set to true, warn
- tlwarn("$prg: non-admin user cannot set $TLPDBOptions{'w32_multi_user'}->[2] option to true\n");
- } else {
- $do_it = 1;
- }
- }
- } else {
- $do_it = 1;
- }
- if ($do_it) {
- if ($val) {
- info("$prg: setting option $what to 1.\n");
- $localtlpdb->option($opt, 1);
- } else {
- info("$prg: setting option $what to 0.\n");
- $localtlpdb->option($opt, 0);
- }
- }
- } else {
- # default case, switch for different types
- if ($TLPDBOptions{$opt}->[0] eq "b") {
- if ($val) {
- info("$prg: setting option $what to 1.\n");
- $localtlpdb->option($opt, 1);
- } else {
- info("$prg: setting option $what to 0.\n");
- $localtlpdb->option($opt, 0);
- }
- } elsif ($TLPDBOptions{$opt}->[0] eq "p") {
- info("$prg: setting option $what to $val.\n");
- $localtlpdb->option($opt, $val);
- } elsif ($TLPDBOptions{$opt}->[0] eq "u") {
- info("$prg: setting option $what to $val.\n");
- $localtlpdb->option($opt, $val);
- } elsif ($TLPDBOptions{$opt}->[0] =~ m/^n(:((-)?\d+)?..((-)?\d+)?)?$/) {
- my $isgood = 1;
- my $n = int($val);
- my $low;
- my $up;
- if (defined($1)) {
- # range given
- if (defined($2)) {
- # lower border
- if ($2 > $n) {
- tlwarn("$prg: value $n for $what out of range ($TLPDBOptions{$opt}->[0])\n");
- $isgood = 0;
- }
- }
- if (defined($4)) {
- # upper border
- if ($4 < $n) {
- tlwarn("$prg: value $n for $what out of range ($TLPDBOptions{$opt}->[0])\n");
- $isgood = 0;
- }
- }
- }
- if ($isgood) {
- info("$prg: setting option $what to $n.\n");
- $localtlpdb->option($opt, $n);
- }
- } else {
- tlwarn ("$prg: unknown type of option $opt: $TLPDBOptions{$opt}->[0]\n");
- return ($F_ERROR);
- }
- }
- my $local_location = $localtlpdb->location;
- info("$prg: updating $local_location\n");
- $localtlpdb->save;
- # now also save the TLPOBJ of 00texlive.installation
- my $tlpo = $localtlpdb->get_package("00texlive.installation");
- if ($tlpo) {
- if (open(TOFD, ">$::maintree/tlpkg/tlpobj/00texlive.installation.tlpobj")) {
- $tlpo->writeout(\*TOFD);
- close(TOFD);
- } else {
- tlwarn("$prg: Cannot save 00texlive.installation to $::maintree/tlpkg/tlpobj/00texlive.installation.tlpobj\n");
- $ret |= $F_WARNING;
- }
- }
- } else {
- # show current value
- if ($::machinereadable) {
- print "$TLPDBOptions{$opt}->[2]\t", $localtlpdb->option($opt), "\n";
- } else {
- info ("$TLPDBOptions{$opt}->[3] ($TLPDBOptions{$opt}->[2]): " .
- $localtlpdb->option($opt) . "\n");
- }
- }
- last;
- }
- }
- if (!$found) {
- tlwarn("$prg: option $what not supported!\n");
- return ($F_ERROR);
- }
- }
- return ($ret);
-}
-
-
-# PLATFORM (was ARCH)
-#
-sub action_platform {
- my $ret = $F_OK;
- my @extra_w32_packs = qw/tlperl.win32 tlgs.win32 tlpsv.win32
- collection-wintools
- dviout.win32 wintools.win32/;
- if ($^O =~ /^MSWin/i) {
- warn("action `platform' not supported on Windows\n");
- # return an error here so that we don't go into post-actions
- return ($F_ERROR);
- }
- if ($opts{"usermode"}) {
- tlwarn("$prg: action `platform' not supported in usermode\n");
- return ($F_ERROR);
- }
- my $what = shift @ARGV;
- init_local_db(1);
- info("$prg platform: dry run, no changes will be made\n") if $opts{"dry-run"};
- $what || ($what = "list");
- if ($what =~ m/^list$/i) {
- # list the available platforms
- # initialize the TLPDB from $location
- init_tlmedia_or_die();
- my @already_installed_arch = $localtlpdb->available_architectures;
- print "Available platforms:\n";
- foreach my $a ($remotetlpdb->available_architectures) {
- if (member($a,@already_installed_arch)) {
- print "(i) $a\n";
- } else {
- print " $a\n";
- }
- }
- print "Already installed platforms are marked with (i)\n";
- print "You can add new platforms with: tlmgr platform add PLAT1 PLAT2...\n";
- print "You can remove platforms with: tlmgr platform remove PLAT1 PLAT2...\n";
- print "You can set the active platform with: tlmgr platform set PLAT\n";
- return ($F_OK | $F_NOPOSTACTION);
-
- } elsif ($what =~ m/^add$/i) {
- return ($F_ERROR) if !check_on_writable();
- init_tlmedia_or_die();
- my @already_installed_arch = $localtlpdb->available_architectures;
- my @available_arch = $remotetlpdb->available_architectures;
- my @todoarchs;
- foreach my $a (@ARGV) {
- if (TeXLive::TLUtils::member($a, @already_installed_arch)) {
- info("$prg: platform already installed: $a\n");
- next;
- }
- if (!TeXLive::TLUtils::member($a, @available_arch)) {
- info("$prg: platform `$a' not available; see tlmgr platform list\n");
- next;
- }
- push @todoarchs, $a;
- }
- foreach my $pkg ($localtlpdb->list_packages) {
- next if ($pkg =~ m/^00texlive/);
- my $tlp = $localtlpdb->get_package($pkg);
- foreach my $dep ($tlp->depends) {
- if ($dep =~ m/^(.*)\.ARCH$/) {
- # we have to install something
- foreach my $a (@todoarchs) {
- if ($remotetlpdb->get_package("$pkg.$a")) {
- info("install: $pkg.$a\n");
- if (!$opts{'dry-run'}) {
- if (! $remotetlpdb->install_package("$pkg.$a", $localtlpdb)) {
- $ret |= $F_ERROR;
- }
- }
- } else {
- tlwarn("$prg: action platform add, cannot find package $pkg.$a\n");
- $ret |= $F_WARNING;
- }
- }
- }
- }
- }
- if (TeXLive::TLUtils::member('win32', @todoarchs)) {
- # install the necessary w32 stuff
- for my $p (@extra_w32_packs) {
- info("install: $p\n");
- if (!$opts{'dry-run'}) {
- if (! $remotetlpdb->install_package($p, $localtlpdb)) {
- $ret |= $F_ERROR;
- }
- }
- }
- }
- # update the option("available_architectures") list of installed archs
- if (!$opts{"dry-run"}) {
- my @larchs = $localtlpdb->setting("available_architectures");
- push @larchs, @todoarchs;
- $localtlpdb->setting("available_architectures",@larchs);
- $localtlpdb->save;
- }
-
- } elsif ($what =~ m/^remove$/i) {
- return ($F_ERROR) if !check_on_writable();
- my @already_installed_arch = $localtlpdb->available_architectures;
- my @todoarchs;
- my $currentarch = $localtlpdb->platform();
- foreach my $a (@ARGV) {
- if (!TeXLive::TLUtils::member($a, @already_installed_arch)) {
- tlwarn("$prg: Platform $a not installed, use 'tlmgr platform list'!\n");
- $ret |= $F_WARNING;
- next;
- }
- if ($currentarch eq $a) {
- info("$prg: You are running on platform $a, you cannot remove that one!\n");
- $ret |= $F_WARNING;
- next;
- }
- push @todoarchs, $a;
- }
- foreach my $pkg ($localtlpdb->list_packages) {
- next if ($pkg =~ m/^00texlive/);
- my $tlp = $localtlpdb->get_package($pkg);
- if (!$tlp) {
- # that is a package foobar.$a that has already been remove but
- # is still in the list above, so ignore that
- next;
- }
- foreach my $dep ($tlp->depends) {
- if ($dep =~ m/^(.*)\.ARCH$/) {
- # we have to install something
- foreach my $a (@todoarchs) {
- if ($localtlpdb->get_package("$pkg.$a")) {
- info("remove: $pkg.$a\n");
- # assume that this works out
- $localtlpdb->remove_package("$pkg.$a") if (!$opts{"dry-run"});
- }
- }
- }
- }
- }
- if (TeXLive::TLUtils::member('win32', @todoarchs)) {
- for my $p (@extra_w32_packs) {
- info("remove: $p\n");
- $localtlpdb->remove_package($p) if (!$opts{"dry-run"});
- }
- }
- if (!$opts{"dry-run"}) {
- # try to remove bin/$a dirs
- for my $a (@todoarchs) {
- if (!rmdir("$Master/bin/$a")) {
- tlwarn("$prg: failed to rmdir $Master/bin/$a: $!\n");
- $ret |= $F_WARNING;
- }
- }
- # update the option("available_architectures") list of installed archs
- my @larchs = $localtlpdb->setting("available_architectures");
- my @newarchs;
- for my $a (@larchs) {
- push @newarchs, $a if !member($a, @todoarchs);
- }
- $localtlpdb->setting("available_architectures",@newarchs);
- $localtlpdb->save;
- }
-
- } elsif ($what =~ m/^set$/i) {
- return if !check_on_writable();
- my $arg = shift @ARGV;
- die "Missing argument to platform set" unless defined($arg);
- my @already_installed_arch = $localtlpdb->available_architectures;
- if ($arg =~ m/^auto$/i) {
- info("Setting platform detection to auto mode.\n");
- $localtlpdb->setting('-clear', 'platform');
- $localtlpdb->save;
- } else {
- if (!TeXLive::TLUtils::member($arg, @already_installed_arch)) {
- tlwarn("$prg: cannot set platform to a not installed one.\n");
- return ($F_ERROR);
- }
- $localtlpdb->setting('platform', $arg);
- $localtlpdb->save;
- }
- } else {
- tlwarn("$prg: Unknown option for platform: $what\n");
- $ret |= $F_ERROR;
- }
- return ($ret);
-}
-
-
-# GENERATE
-#
-sub action_generate {
- if ($opts{"usermode"}) {
- tlwarn("$prg: action `generate' not supported in usermode!\n");
- return $F_ERROR;
- }
- my $what = shift @ARGV;
- if (!defined($what)) {
- tlwarn("$prg: action `generate' requires an argument!\n");
- return ($F_ERROR);
- }
- init_local_db();
-
- # we create fmtutil.cnf, language.dat, language.def in TEXMFSYSVAR and
- # updmap.cfg in TEXMFDIST. The reason is that we are now using an
- # implementation of updmap that supports multiple updmap files.
- # Local adaptions should not be made there, but only in TEXMFLOCAL
- # or TEXMF(SYS)CONFIG updmap.cfg
- #
- chomp (my $TEXMFSYSVAR = `kpsewhich -var-value=TEXMFSYSVAR`);
- chomp (my $TEXMFSYSCONFIG = `kpsewhich -var-value=TEXMFSYSCONFIG`);
- chomp (my $TEXMFLOCAL = `kpsewhich -var-value=TEXMFLOCAL`);
- chomp (my $TEXMFDIST = `kpsewhich -var-value=TEXMFDIST`);
-
- # we do generate all config files, treat $opts{"dest"} as pattern
- # and make it append the respective extensions
- my $append_extension = (($opts{"dest"} && ($what eq "language")) ? 1 : 0);
-
- if ($what =~ m/^language(\.dat|\.def|\.dat\.lua)?$/i) {
- #
- # if --rebuild-sys is given *and* --dest we warn that this might not
- # work if the destination is not the default one
- if ($opts{"rebuild-sys"} && $opts{"dest"}) {
- tlwarn("$prg generate $what: warning: both --rebuild-sys and --dest\n",
- "given; the call to fmtutil-sys can fail if the given\n",
- "destination is different from the default.\n");
- }
- #
- # we have to set TEXMFVAR, TEXMFCONFIG in the environment so that
- # searching for language.(dat/def) does search in the right place
- if ($what =~ m/^language(\.dat\.lua)?$/i) {
- my $dest = $opts{"dest"} ||
- "$TEXMFSYSVAR/tex/generic/config/language.dat.lua";
- $dest .= ".dat.lua" if $append_extension;
- my $localcfg = $opts{"localcfg"} ||
- "$TEXMFLOCAL/tex/generic/config/language-local.dat.lua";
- debug("$prg: writing language.dat.lua data to $dest\n");
- TeXLive::TLUtils::create_language_lua($localtlpdb, $dest, $localcfg);
- if ($opts{"rebuild-sys"}) {
- do_cmd_and_check
- ("fmtutil-sys $common_fmtutil_args --byhyphen \"$dest\"");
- } else {
- info("To make the newly-generated language.dat.lua take effect,"
- . " run fmtutil-sys --byhyphen $dest.\n");
- }
- }
- if ($what =~ m/^language(\.dat)?$/i) {
- my $dest = $opts{"dest"} ||
- "$TEXMFSYSVAR/tex/generic/config/language.dat";
- $dest .= ".dat" if $append_extension;
- my $localcfg = $opts{"localcfg"} ||
- "$TEXMFLOCAL/tex/generic/config/language-local.dat";
- debug ("$prg: writing language.dat data to $dest\n");
- TeXLive::TLUtils::create_language_dat($localtlpdb, $dest, $localcfg);
- if ($opts{"rebuild-sys"}) {
- do_cmd_and_check
- ("fmtutil-sys $common_fmtutil_args --byhyphen \"$dest\"");
- } else {
- info("To make the newly-generated language.dat take effect,"
- . " run fmtutil-sys --byhyphen $dest.\n");
- }
- }
- if ($what =~ m/^language(\.def)?$/i) {
- my $dest = $opts{"dest"} ||
- "$TEXMFSYSVAR/tex/generic/config/language.def";
- $dest .= ".def" if $append_extension;
- my $localcfg = $opts{"localcfg"} ||
- "$TEXMFLOCAL/tex/generic/config/language-local.def";
- debug("$prg: writing language.def data to $dest\n");
- TeXLive::TLUtils::create_language_def($localtlpdb, $dest, $localcfg);
- if ($opts{"rebuild-sys"}) {
- do_cmd_and_check
- ("fmtutil-sys $common_fmtutil_args --byhyphen \"$dest\"");
- } else {
- info("To make the newly-generated language.def take effect,"
- . " run fmtutil-sys --byhyphen $dest.\n");
- }
- }
-
- } elsif ($what =~ m/^fmtutil$/i) {
- tlwarn("$prg: generate fmtutil is no longer needed or supported.\n");
- tlwarn("$prg: Please read the documentation of the `fmtutil' program.\n");
- tlwarn("$prg: Goodbye.\n");
- return $F_ERROR;
-
- } elsif ($what =~ m/^_fmtutil$/i) {
- my $dest = $opts{"dest"} || "$TEXMFDIST/web2c/fmtutil.cnf";
- debug("$prg: writing new fmtutil.cnf to $dest\n");
- TeXLive::TLUtils::create_fmtutil($localtlpdb, $dest);
-
- if ($opts{"rebuild-sys"}) {
- do_cmd_and_check("fmtutil-sys $common_fmtutil_args --all");
- } else {
- info("To make the newly-generated fmtutil.cnf take effect,"
- . " run fmtutil-sys --all.\n");
- }
-
- } elsif ($what =~ m/^updmap$/i) {
- tlwarn("$prg: generate updmap is no longer needed or supported.\n");
- tlwarn("$prg: Please read the documentation of the `updmap' program.\n");
- tlwarn("$prg: Goodbye.\n");
- return $F_ERROR;
-
- } elsif ($what =~ m/^_updmap$/i) {
- my $dest = $opts{"dest"} || "$TEXMFDIST/web2c/updmap.cfg";
- debug("$prg: writing new updmap.cfg to $dest\n");
- TeXLive::TLUtils::create_updmap($localtlpdb, $dest);
-
- if ($opts{"rebuild-sys"}) {
- do_cmd_and_check("updmap-sys");
- } else {
- info("To make the newly-generated updmap.cfg take effect,"
- . " run updmap-sys.\n");
- }
-
- } else {
- tlwarn("$prg: Unknown option for generate: $what; try --help if you need it.\n");
- return $F_ERROR;
- }
-
- return $F_OK;
-}
-
-
-# GUI
-#
-sub action_gui {
- eval { require Tk; };
- if ($@) {
- # that didn't work out, give some usefull error message and stop
- my $tkmissing = 0;
- if ($@ =~ /^Can\'t locate Tk\.pm/) {
- $tkmissing = 1;
- }
- if ($tkmissing) {
- if ($^O =~ /^MSWin/i) {
- # that should not happen, we are shipping Tk!!
- require Win32;
- my $msg = "Cannot load Tk, that should not happen as we ship it!\nHow did you start tlmgrgui??\n(Error message: $@)\n";
- Win32::MsgBox($msg, 1|Win32::MB_ICONSTOP(), "Warning");
- } else {
- printf STDERR "
-$prg: Cannot load Tk, thus the GUI cannot be started!
-The Perl/Tk module is not shipped with the TeX Live installation.
-You have to install it to get the tlmgr GUI working.
-(INC = @INC)
-
-See https://tug.org/texlive/distro.html#perltk for more details.
-Goodbye.
-";
- }
- } else {
- printf STDERR "$prg: unexpected problem loading Tk: $@\n";
- }
- exit 1;
- }
-
- # now check that we can actually create a top level window,
- # on darwin the X server might not be started, or on unix we are working
- # on a console, or whatever.
- eval { my $foo = Tk::MainWindow->new; $foo->destroy; };
- if ($@) {
- printf STDERR "perl/Tk unusable, cannot create main windows.
-That could be a consequence of not having X Windows installed or started!
-Error message from creating MainWindow:
- $@
-";
- exit 1;
- }
-
- # be sure that sub actions do *not* finish
- $::gui_mode = 1;
- # also unset the $opts{"gui"} to make recursive calls to action_* not starting
- # another GUI instance (or better trying to ...)
- $opts{"gui"} = 0;
-
- require("tlmgrgui.pl");
- # should not be reached
- exit(1);
-}
-
-
-# UNINSTALL
-# Return zero if successful, nonzero if failure.
-#
-sub uninstall_texlive {
- if (win32()) {
- printf STDERR "Please use \"Add/Remove Programs\" from the Control Panel to removing TeX Live!\n";
- return ($F_ERROR);
- }
- return if !check_on_writable();
-
- init_local_db(0);
- my $force = defined($opts{"force"}) ? $opts{"force"} : 0;
- if (!$force) {
- print("If you answer yes here the whole TeX Live installation here,\n",
- "under ", $localtlpdb->root, ", 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";
- return ($F_OK | $F_NOPOSTACTION);
- }
- }
- print ("Ok, removing the whole installation:\n");
- TeXLive::TLUtils::remove_symlinks($localtlpdb->root,
- $localtlpdb->platform(),
- $localtlpdb->option("sys_bin"),
- $localtlpdb->option("sys_man"),
- $localtlpdb->option("sys_info"));
- # now remove the rest
- system("rm", "-rf", "$Master/texmf-dist");
- system("rm", "-rf", "$Master/texmf-doc");
- system("rm", "-rf", "$Master/texmf-var");
- system("rm", "-rf", "$Master/tlpkg");
- system("rm", "-rf", "$Master/bin");
- system("rm", "-rf", "$Master/readme-html.dir");
- system("rm", "-rf", "$Master/readme-txt.dir");
- for my $f (qw/doc.html index.html install-tl
- LICENSE.CTAN LICENSE.TL README README.usergroups
- release-texlive.txt texmf.cnf texmfcnf.lua/) {
- system("rm", "-f", "$Master/$f");
- }
- if (-d "$Master/temp") {
- finddepth(sub { rmdir; }, "$Master/temp");
- }
- unlink("$Master/install-tl.log");
- # if they want removal, give them removal. Hopefully they know how to
- # regenerate any changed config files.
- system("rm", "-rf", "$Master/texmf-config");
- finddepth(sub { rmdir; }, "$Master");
-
- return -d "$Master";
-}
-
-
-# RECREATE-TLPDB
-#
-sub action_recreate_tlpdb {
- return if !check_on_writable();
- my $tlpdb = TeXLive::TLPDB->new;
- $tlpdb->root($Master);
- my $inst = TeXLive::TLPOBJ->new;
- $inst->name("00texlive.installation");
- $inst->category("TLCore");
- my @deps;
- # options are done further down with $tlpdb->reset_options()
- #for my $k (keys %TeXLive::TLConfig::TLPDBOptions) {
- # push @deps, "opt_$k:" . $TeXLive::TLConfig::TLPDBOptions{k}->[1];
- #}
- # find list of available archs
- my @archs;
- opendir (DIR, "$Master/bin") || die "opendir($Master/bin) failed: $!";
- my @dirents = readdir (DIR);
- closedir (DIR) || warn "closedir($Master/bin) failed: $!";
- for my $dirent (@dirents) {
- next if $dirent eq ".";
- next if $dirent eq "..";
- next unless -d "$Master/bin/$dirent";
- if (-r "$Master/bin/$dirent/kpsewhich" || -r "$Master/bin/$dirent/kpsewhich.exe") {
- push @archs, $dirent;
- debug("$prg: skipping directory $Master/bin/$dirent, no kpsewhich there\n");
- }
- }
- push @deps, "setting_available_architectures:" . join(" ",@archs);
- # we have to find out the default arch
- # if there is only one dir in $Master/bin then we are settled,
- # otherwise we expect the user to pass a correct arch string
- if (!TeXLive::TLUtils::member(TeXLive::TLUtils::platform(), @archs)) {
- # hmm that is bad, the platform as detected is not in the list
- # of installed platforms, so the option --arch has to be given
- # if only one is installed use that one
- if ($#archs == 0) {
- # only one arch available, fine, use it as default
- push @deps, "setting_platform:$archs[0]";
- } else {
- if (defined($opts{"platform"})) {
- if (member($opts{"platform"}, @archs)) {
- push @deps, "setting_platform:" . $opts{"platform"};
- } else {
- tlwarn("$prg: The platform you passed in with --platform is not present in $Master/bin\n");
- tlwarn("$prg: Please specify one of the available ones: @archs\n");
- exit(1);
- }
- } else {
- tlwarn("$prg: More than one platform available: @archs\n");
- tlwarn("$prg: Please pass one as the default you are running on with --platform=...\n");
- exit(1);
- }
- }
- }
- $inst->depends(@deps);
- # now we have all the stuff for 00texlive.installation done
- $tlpdb->add_tlpobj($inst);
- # reset the options to default values
- $tlpdb->add_default_options();
- # check for location == _MASTER_
- if ($tlpdb->option("location") eq "__MASTER__") {
- $tlpdb->option("location", $TeXLive::TLConfig::TeXLiveURL);
- }
- # add the other stuff in $Master/tlpkg/tlpobj/*.tlpobj
- # we can ignore *.{source,doc}.tlpobj because they are already
- # included in the *.tlpobj parent one at install time
- # (TODO: we should actually REMOVE the *.{source,doc}.tlpobj files
- # at package install time)
- opendir (DIR, "$Master/tlpkg/tlpobj") or die "opendir($Master/tlpkg/tlpobj) failed: $!";
- my @tlps = readdir(DIR);
- closedir (DIR) || warn "closedir($Master/tlpkg/tlpobj) failed: $!";
- for my $t (@tlps) {
- next if -d $t; # also does . and ..
- next if ($t !~ m/\.tlpobj$/i);
- # ignore .source and .doc tlpobjs
- next if ($t =~ m/\.(source|doc)\.tlpobj$/i);
- my $tlp = TeXLive::TLPOBJ->new;
- $tlp->from_file("$Master/tlpkg/tlpobj/$t");
- $tlpdb->add_tlpobj($tlp);
- }
- # writeout the re-created tlpdb to stdout
- $tlpdb->writeout;
- return;
-}
-
-
-# CHECK
-#
-sub init_tltree {
- my ($svn) = @_;
-
- # if we are on W32, die (no find).
- my $arch = $localtlpdb->platform();
- if ($arch eq "win32") {
- tldie("$prg: sorry, cannot check this on Windows.\n");
- }
-
- my $Master = $localtlpdb->root;
- my $tltree = TeXLive::TLTREE->new ("svnroot" => $Master);
- if ($svn) {
- debug("Initializing TLTREE from svn\n");
- $tltree->init_from_svn;
- } else {
- debug("Initializing TLTREE from find\n");
- $tltree->init_from_files;
- }
- return($tltree);
-}
-
-sub action_check {
- my $svn = defined($opts{"use-svn"}) ? $opts{"use-svn"} : 0;
- my $what = shift @ARGV;
- $what || ($what = "all");
- $what =~ s/^ *//;
- $what =~ s/ *$//;
- init_local_db();
- my $ret = 0;
- if ($what =~ m/^all$/i) {
- my $tltree = init_tltree($svn);
- print "Running check files:\n"; $ret |= check_files($tltree);
- print "Running check depends:\n"; $ret |= check_depends();
- print "Running check executes:\n"; $ret |= check_executes();
- print "Running check runfiles:\n"; $ret |= check_runfiles();
- print "Running check texmfdbs\n"; $ret |= check_texmfdbs();
- } elsif ($what =~ m/^files$/i) {
- my $tltree = init_tltree($svn);
- $ret |= check_files($tltree);
- } elsif ($what =~ m/^collections$/i) {
- tlwarn("$prg: \"collections\" check has been replaced by \"depends\".\n");
- $ret |= check_depends();
- } elsif ($what =~ m/^depends$/i) {
- $ret |= check_depends();
- } elsif ($what =~ m/^runfiles$/i) {
- $ret |= check_runfiles();
- } elsif ($what =~ m/^executes$/i) {
- $ret |= check_executes();
- } elsif ($what =~ m/^texmfdbs$/i) {
- $ret |= check_texmfdbs();
- } else {
- tlwarn("$prg: No idea how to check: $what\n");
- $ret = 1;
- }
- if ($ret) {
- return ($F_ERROR);
- } else {
- return ($F_OK);
- }
-}
-
-# check file coverage in both direction.
-#
-sub check_files {
- my $tltree = shift;
- my $ret = 0;
- my %filetopacks;
- my $Master = $localtlpdb->root;
- debug("Collecting all files of all packages\n");
- for my $p ($localtlpdb->list_packages()) {
- # ignore files in the installer
- next if ($p eq "00texlive.installer");
- my $tlp = $localtlpdb->get_package($p);
- my @files = $tlp->all_files;
- if ($tlp->relocated) {
- for (@files) { s:^$RelocPrefix/:$RelocTree/:; }
- }
- for my $f (@files) {
- push @{$filetopacks{$f}}, $p;
- }
- }
- my @multiple = ();
- my @missing = ();
- debug("Checking for occurrences and existence of all files\n");
- for (keys %filetopacks) {
- push @missing, $_ if (! -r "$Master/$_");
- my @foo = @{$filetopacks{$_}};
- if ($#foo < 0) {
- warn "that shouldn't happen #foo < 0: $_";
- } elsif ($#foo > 0) {
- push @multiple, $_;
- }
- }
- if ($#multiple >= 0) {
- $ret = 1;
- print "\f Multiple included files (relative to $Master):\n";
- for (sort @multiple) {
- my @foo = @{$filetopacks{$_}};
- print " $_ (@foo)\n";
- }
- print "\n";
- }
- if ($#missing >= 0) {
- $ret = 1;
- print "\f Files mentioned in tlpdb but missing (relative to $Master):\n";
- for my $m (@missing) {
- print "\t$m\n";
- }
- print "\n";
- }
-
- # check that all files in the trees are covered, along with
- # 00texlive.image, q.v. The ones here are not included in the
- # archival source/ tarball;
- my @IgnorePatterns = qw!
- release-texlive.txt source/
- texmf-dist/ls-R$ texmf-doc/ls-R$
- tlpkg/archive tlpkg/backups tlpkg/installer
- tlpkg/texlive.tlpdb tlpkg/tlpobj tlpkg/texlive.profile
- texmf-config/ texmf-var/
- texmf.cnf texmfcnf.lua install-tl.log
- !;
- my %tltreefiles = %{$tltree->{'_allfiles'}};
- my @tlpdbfiles = keys %filetopacks;
- my @nohit;
- for my $f (keys %tltreefiles) {
- # if it is mentioned in the tlpdb or is ignored it is considered
- # as covered, thus, otherwise we push it onto the nothit list
- if (!defined($filetopacks{$f})) {
- my $ignored = 0;
- for my $p (@IgnorePatterns) {
- if ($f =~ m/^$p/) {
- $ignored = 1;
- last;
- }
- }
- if (!$ignored) {
- push @nohit, $f;
- }
- }
- }
- if (@nohit) {
- $ret = 1;
- print "\f Files present but not covered (relative to $Master):\n";
- for my $f (sort @nohit) {
- print " $f\n";
- }
- print "\n";
- }
- return($ret);
-}
-
-# Check for runtime files with the same name but different contents.
-#
-sub check_runfiles {
- my $Master = $localtlpdb->root;
-
- # build a list of all runtime files associated with normal packages.
- (my $omit_pkgs = `ls "$Master/bin"`) =~ s/\n/\$|/g; # binaries
- $omit_pkgs .= '^0+texlive|^bin-|^collection-|^scheme-|^texlive-|^texworks';
- $omit_pkgs .= '|^pgf$'; # intentionally duplicated .lua
- $omit_pkgs .= '|^latex-.*-dev$'; # intentionally duplicated base latex
- my @runtime_files = ();
- #
- foreach my $tlpn ($localtlpdb->list_packages) {
- next if $tlpn =~ /$omit_pkgs/;
- #
- my $tlp = $localtlpdb->get_package($tlpn);
- my @files = $tlp->runfiles;
- if ($tlp->relocated) {
- for (@files) {
- s!^$TeXLive::TLConfig::RelocPrefix/!$TeXLive::TLConfig::RelocTree/!;
- }
- }
- # special case for koma-script where doc/src files are in runfiles section
- if ($tlpn eq "koma-script") {
- @files = grep {!m;^texmf-dist/source/latex/koma-script/;} @files;
- @files = grep {!m;^texmf-dist/doc/latex/koma-script/;} @files;
- }
- push @runtime_files, @files;
- }
-
- # build the duplicates list.
- my @duplicates = (""); # just to use $duplicates[-1] freely
- my $prev = "";
- foreach my $f (sort map { TeXLive::TLUtils::basename($_) } @runtime_files) {
- push (@duplicates, $f) if (($f eq $prev) and not ($f eq $duplicates[-1]));
- $prev = $f;
- }
- shift @duplicates; # get rid of the fake 1st value
-
- # check if duplicates are different files.
- foreach my $f (@duplicates) {
- # assume tex4ht, xdy, afm stuff is ok, and don't worry about
- # Changes, README et al. Other per-format versions, etc.
- next if $f =~ /\.(afm|cfg|dll|exe|4hf|htf|pm|xdy)$/;
- next if $f
- =~ /^((czech|slovak)\.sty
- |Changes
- |LICENSE.*
- |Makefile
- |README.*
- |a_.*\.enc
- |cid2code\.txt
- |context\.json
- |etex\.src
- |fithesis.*
- |u?kinsoku\.tex
- |language\.dat
- |language\.def
- |local\.mf
- |m-tex4ht\.tex
- |metatex\.tex
- |.*-noEmbed\.map
- |ps2mfbas\.mf
- |pstricks\.con
- |readme.*
- |sample\.bib
- |tex4ht\.env
- |test\.mf
- |texutil\.rb
- |tlmgrgui\.pl
- )$/x;
- # For the a_.* line above: source*pro has .enc files which differ
- # only in comments, hence the otftotfm-hashed name is the same.
- # Seems like it could happen more or at random with other fonts too.
- # Most font packagers nowadays insert a prefix to avoid this,
- # but older ones are not getting changed.
-
- # the files here were supposedly being dealt with in the next
- # release of their respective packages, but the releases are not
- # forthcoming, so just ignore them. They apparently make no
- # practical difference (maybe because of kpse's optimization of
- # preferring same-directory results). Latest report:
- # https://tug.org/pipermail/tex-live/2019-December/044530.html
- next if $f
- =~ /^( afoot\.sty
- |gamma\.mf
- |lexer\.lua
- |ligature\.mf
- |md-utrma\.pfb
- |ot1\.cmap
- |t1\.cmap
- )$/x;
-
- my @copies = grep (/\/$f$/, @runtime_files);
- # map files can be duplicated between (but not within) formats.
- if ($f =~ /\.map$/) {
- my $need_check = 0;
- my $prev_dir = "";
- my @cop = @copies; # don't break the outside list
- map { s#^texmf-dist/fonts/map/(.*?)/.*#$1# } @cop;
- foreach my $dir (sort @cop ) {
- last if ($need_check = ($dir eq $prev_dir));
- $prev_dir = $dir;
- }
- next unless $need_check;
- }
- # if all copies are identical, ok, else, complain
- my $diff = 0;
- for (my $i = 1; $i < scalar(@copies); $i++) {
- if ($diff = tlcmp("$Master/$copies[$i-1]", "$Master/$copies[$i]")) {
- print "# $f\ndiff $Master/$copies[$i-1] $Master/$copies[$i]\n";
- last;
- }
- }
- print join ("\n", @copies), "\n" if ($diff and (scalar(@copies) > 2));
- }
-}
-
-# check executes
-#
-sub check_executes {
- my $Master = $localtlpdb->root;
- my (%maps,%langcodes,%fmtlines);
- for my $pkg ($localtlpdb->list_packages) {
- for my $e ($localtlpdb->get_package($pkg)->executes) {
- if ($e =~ m/add(Mixed|Kanji)?Map\s+(.*)$/) {
- my $foo = $2;
- chomp($foo);
- if ($foo !~ m/\@(kanji|ja|tc|sc|ko)Embed@/) {
- push @{$maps{$foo}}, $pkg;
- }
- } elsif ($e =~ m/AddFormat\s+(.*)$/) {
- my $foo = $1;
- chomp($foo);
- push @{$fmtlines{$foo}}, $pkg;
- } elsif ($e =~ m/AddHyphen\s+.*\s+file=(\S+)(\s*$|\s+.*)/) {
- my $foo = $1;
- chomp($foo);
- push @{$langcodes{$foo}}, $pkg;
- } else {
- tlwarn("$prg: unmatched execute in $pkg: $e\n");
- }
- }
- }
- my %badmaps;
- foreach my $mf (keys %maps) {
- my @pkgsfound = @{$maps{$mf}};
- if ($#pkgsfound > 0) {
- tlwarn("$prg: map file $mf is referenced in the executes of @pkgsfound\n");
- } else {
- # less then 1 occurrences is not possible, so we have only one
- # package that contains the reference to that map file
- my $pkgfoundexecute = $pkgsfound[0];
- my @found = $localtlpdb->find_file($mf);
- if ($#found < 0) {
- $badmaps{$mf} = $maps{$mf};
- } elsif ($#found > 0) {
- # we want to check for multiple inclusions
- my %mapfn;
- foreach my $foo (@found) {
- $foo =~ m/^(.*):(.*)$/;
- push @{$mapfn{$2}}, $1;
- }
- foreach my $k (keys %mapfn) {
- my @bla = @{$mapfn{$k}};
- if ($#bla > 0) {
- tlwarn("$prg: map file $mf occurs multiple times (in pkgs: @bla)!\n");
- }
- }
- } else {
- # only one occurrence found, we check that the map is also contained
- # in the right package!
- my ($pkgcontained) = ( $found[0] =~ m/^(.*):.*$/ );
- if ($pkgcontained ne $pkgfoundexecute) {
- tlwarn("$prg: map file $mf: execute in $pkgfoundexecute, map file in $pkgcontained\n");
- }
- }
- }
- }
- if (keys %badmaps) {
- tlwarn("$prg: mentioned map file not present in any package:\n");
- foreach my $mf (keys %badmaps) {
- print "\t$mf (execute in @{$badmaps{$mf}})\n";
- }
- }
- my %badhyphcodes;
- my %problemhyphen;
- foreach my $lc (keys %langcodes) {
- next if ($lc eq "zerohyph.tex");
- my @found = $localtlpdb->find_file("texmf-dist/tex/generic/hyph-utf8/loadhyph/$lc");
- if ($#found < 0) {
- # try again this time search all packages
- my @found = $localtlpdb->find_file("$lc");
- if ($#found < 0) {
- $badhyphcodes{$lc} = $langcodes{$lc};
- } else {
- $problemhyphen{$lc} = [ @found ];
- }
- }
- }
- if (keys %badhyphcodes) {
- print "\f mentioned hyphen loaders without file:\n";
- foreach my $mf (keys %badhyphcodes) {
- print "\t$mf (execute in @{$badhyphcodes{$mf}})\n";
- }
- }
- # disable the echoing of problematic hyphens
- #if (keys %problemhyphen) {
- # print "hyphen files with possible problematic location:\n";
- # foreach my $mf (keys %problemhyphen) {
- # print "\t$mf (@{$problemhyphen{$mf}})\n";
- # }
- #}
- #
- # what should be checked for the executes? we could check
- # - the existence of the engine in bin/i386-linux or all $arch
- # - the existence of the format name link/bat
- # - parse the options parameter and check for the inifile
- # - rework the format definition that we have inifile=pdflatex.ini
- # isn't the * unnecessary?
- my %missingbins;
- my %missingengines;
- my %missinginis;
- my @archs_to_check = $localtlpdb->available_architectures;
- for (keys %fmtlines) {
- my %r = TeXLive::TLUtils::parse_AddFormat_line("$_");
- if (defined($r{"error"})) {
- die "$r{'error'}, parsing $_, package(s) @{$fmtlines{$_}}";
- }
- my $opt = $r{"options"};
- my $engine = $r{"engine"};
- my $name = $r{"name"};
- my $mode = $r{"mode"};
- ddebug("check_executes: fmtline name=$name engine=$engine"
- . " mode=$mode opt=$opt\n");
- next if ($name eq "cont-en"); # too confusing
- # we check that the name exist in bin/$arch
- if (",$TeXLive::TLConfig::PartialEngineSupport," =~ /,$engine,/) {
- # luajit[hb]tex is special since it is not available on all architectures
- # due to inherent reasons (machine code);
- #
- # We do not want to have error messages here, so we do the following:
- # * if tlpkg/tlpsrc/luajittex.tlpsrc is available, then load it
- # and filter away those archs that are excluded with f/!...
- # * similarly for the other partial-support engines; too much
- # trouble to reverse-map to package names, so just hardwire;
- # * if tlpkg/tlpsrc/<engine>.tlpsrc is *not* available (i.e., not
- # the development tree) we just ignore it completely.
- my $pkg;
- if ($engine =~ /luajit(hb)?tex/) {
- $pkg = "luajittex";
- } elsif ($engine eq "mfluajit") {
- $pkg = "mflua";
- } else {
- die "unknown partial engine $engine, goodbye"; # should not happen
- }
- my $tlpsrc_file = $localtlpdb->root . "/tlpkg/tlpsrc/$pkg.tlpsrc";
- if (-r $tlpsrc_file) {
- ddebug("check_executes: found $tlpsrc_file\n");
- require TeXLive::TLPSRC;
- my $tlpsrc = new TeXLive::TLPSRC;
- $tlpsrc->from_file($tlpsrc_file);
- my @binpats = $tlpsrc->binpatterns;
- my @negarchs = ();
- for my $p (@binpats) {
- if ($p =~ m%^(\w+)/(!?[-_a-z0-9,]+)\s+(.*)$%) {
- my $pt = $1;
- my $aa = $2;
- my $pr = $3;
- if ($pr =~ m!/$engine$!) {
- # bingo, get the negative patterns
- if ($aa =~ m/^!(.*)$/) {
- @negarchs = split(/,/,$1);
- ddebug("check_executes: negative arches: @negarchs\n");
- }
- }
- }
- }
- my @new_archs = ();
- for my $a (@archs_to_check) {
- push (@new_archs, $a) unless grep { $a eq $_ } @negarchs;
- }
- @archs_to_check = @new_archs;
- } else {
- @archs_to_check = (); # no tlpsrc, check nothing.
- }
- ddebug("check_executes: final arches to check: @archs_to_check\n");
- }
- for my $a (@archs_to_check) {
- my $f = "$Master/bin/$a/$name";
- if (!check_file($a, $f)) {
- push @{$missingbins{$_}}, "bin/$a/${name}[engine=$engine]" if $mode;
-# # unfortunately there are too many exceptions to this check:
-# # cygwin symlinks pointing to .exe names, pdcsplain extras, mptopdf,
-# # *latex-dev pointing to *latex instead of the binary. Instead of
-# # writing all those error-prone tests, just give up.
-# } elsif (-l $f) {
-# my $linktarget = readlink($f);
-# #ddebug("check_executes: linktarget $linktarget, $name->$engine\n");
-# if ($linktarget ne $engine) {
-# print "\t bin/$a/$name: symlink to $linktarget, not $engine\n";
-# }
-# } else {
-# ; #ddebug("check_executes: $f readable file, not a link\n");
- }
- if (!check_file($a, "$Master/bin/$a/$engine")) {
- push @{$missingengines{$_}}, "bin/$a/${engine}[fmt=$name]" if $mode;
- }
- }
- # check for the existence of the .ini file
- # by using the last word in the options value
- my $inifile = $opt;
- # $inifile now contains "bla bla bla *file.ini"
- # strip initial and trailing "
- $inifile =~ s/^"(.*)"$/$1/;
- # remove everything before the last space
- $inifile =~ s/^.* ([^ ]*)$/$1/;
- # remove the optional leading *
- $inifile =~ s/^\*//;
- my @found = $localtlpdb->find_file("$inifile");
- if ($#found < 0) {
- $missinginis{$_} = "$inifile";
- }
- }
- if (keys %missinginis) {
- print "\f mentioned ini files that cannot be found:\n";
- for my $i (sort keys %missinginis) {
- print "\t $missinginis{$i} (execute: $i)\n";
- }
- }
- if (keys %missingengines) {
- print "\f mentioned engine files that cannot be found:\n";
- for my $i (sort keys %missingengines) {
- print "\t @{$missingengines{$i}}\n";
- }
- }
- if (keys %missingbins) {
- print "\f mentioned bin files that cannot be found:\n";
- for my $i (sort keys %missingbins) {
- print "\t @{$missingbins{$i}}\n";
- }
- }
-}
-
-sub check_file {
- my ($a, $f) = @_;
- if (-r $f) {
- return 1;
- } else {
- # not -r, so check for the extensions .bat and .exe on windoze-ish.
- if ($a =~ /win[0-9]|.*-cygwin/) {
- if (-r "$f.exe" || -r "$f.bat") {
- return 1;
- }
- }
- return 0;
- }
-}
-
-# check depends
-#
-sub check_depends {
- my $ret = 0;
- my $Master = $localtlpdb->root;
- my %presentpkg;
- for my $pkg ($localtlpdb->list_packages) {
- $presentpkg{$pkg} = 1;
- }
- # list of collections.
- my @colls = $localtlpdb->collections;
- my @coll_deps
- = $localtlpdb->expand_dependencies("-no-collections", $localtlpdb, @colls);
- my %coll_deps;
- @coll_deps{@coll_deps} = (); # initialize hash with keys from list
-
- my (%wrong_dep, @no_dep);
- for my $pkg ($localtlpdb->list_packages) {
- # do not check any package starting with 00texlive.
- next if $pkg =~ m/^00texlive/;
-
- # For each package, check that it is a dependency of some collection.
- if (! exists $coll_deps{$pkg}) {
- # Except that schemes and our ugly Windows packages are ok.
- push (@no_dep, $pkg) unless $pkg =~/^scheme-|\.win32$/;
- }
-
- # For each dependency, check that we have a package.
- for my $d ($localtlpdb->get_package($pkg)->depends) {
- next if ($d =~ m/\.ARCH$/);
- if (!defined($presentpkg{$d})) {
- push (@{$wrong_dep{$d}}, $pkg);
- }
- }
- }
-
- # check whether packages are included more than one time in a collection
- my %pkg2mother;
- for my $c (@colls) {
- for my $p ($localtlpdb->get_package($c)->depends) {
- next if ($p =~ /^collection-/);
- push @{$pkg2mother{$p}}, $c;
- }
- }
- my @double_inc_pkgs;
- for my $k (keys %pkg2mother) {
- if (@{$pkg2mother{$k}} > 1) {
- push @double_inc_pkgs, $k;
- }
- }
-
- if (keys %wrong_dep) {
- $ret++;
- print "\f DEPENDS WITHOUT PACKAGES:\n";
- for my $d (keys %wrong_dep) {
- print "$d in: @{$wrong_dep{$d}}\n";
- }
- }
-
- if (@no_dep) {
- $ret++;
- print "\f PACKAGES NOT IN ANY COLLECTION: @no_dep\n";
- }
-
- if (@double_inc_pkgs) {
- $ret++;
- print "\f PACKAGES IN MORE THAN ONE COLLECTION: @double_inc_pkgs\n";
- }
-
- return $ret;
-}
-
-# check texmfdbs -- that !! paths have an ls-R and are in TEXMFDBS,
-# and that trees with an ls-R specify !!.
-#
-sub check_texmfdbs {
- my $texmfdbs = `kpsewhich -var-value TEXMFDBS`;
- my @tfmdbs = glob $texmfdbs;
- my $tfms = `kpsewhich -var-value TEXMF`;
- my @tfms = glob $tfms;
- my %tfmdbs;
- my $ret = 0;
-
- debug("Checking TEXMFDBS\n");
- for my $p (@tfmdbs) {
- debug(" $p\n");
- if ($p !~ m/^!!/) {
- tlwarn("$prg: item $p in TEXMFDBS does not have leading !!\n");
- $ret++;
- }
- $p =~ s/^!!//;
- $tfmdbs{$p} = 1;
- if (-d $p && ! -r "$p/ls-R") {
- tlwarn("$prg: item $p in TEXMFDBS does not have an associated ls-R file\n");
- $ret++;
- }
- }
-
- debug("Checking TEXMF\n");
- for my $p (@tfms) {
- debug(" $p\n");
- my $pnobang = $p;
- $pnobang =~ s/^!!//;
- if (! $tfmdbs{$pnobang}) {
- if ($p =~ m/^!!/) {
- tlwarn("$prg: tree $p in TEXMF not in TEXMFDBS, but has !!\n");
- $ret++;
- }
- if (-r "$pnobang/ls-R") {
- tlwarn("$prg: tree $p in TEXMF not in TEXMFDBS, but has ls-R file\n");
- $ret++;
- }
- }
- }
- return($ret);
-}
-
-# POSTACTION
-#
-# explicitly run the various post actions, e.g.,
-# on a client system or overriding global settings.
-#
-# tlmgr postaction [--w32mode=user|admin] [--fileassocmode=1|2] [--all]
-# [install|remove] [shortcut|fileassoc|script] [<pkg>...]
-
-sub action_postaction {
- my $how = shift @ARGV;
- if (!defined($how) || ($how !~ m/^(install|remove)$/i)) {
- tlwarn("$prg: action postaction needs at least two arguments, first being either 'install' or 'remove'\n");
- return;
- }
- my $type = shift @ARGV;
- my $badtype = 0;
- if (!defined($type)) {
- $badtype = 1;
- } elsif ($type !~ m/^(shortcut|fileassoc|script)$/i) {
- $badtype = 1;
- }
- if ($badtype) {
- tlwarn("$prg: action postaction needs as second argument one from 'shortcut', 'fileassoc', 'script'\n");
- return;
- }
- if (win32()) {
- if ($opts{"w32mode"}) {
- if ($opts{"w32mode"} eq "user") {
- if (TeXLive::TLWinGoo::admin()) {
- debug("Switching to user mode on user request\n");
- TeXLive::TLWinGoo::non_admin();
- }
- # in user mode we also switch TEXMFSYSVAR to TEXMFVAR since
- # xetex.pl, but maybe others are writing to TEXMFSYSVAR
- chomp($ENV{"TEXMFSYSVAR"} = `kpsewhich -var-value TEXMFVAR`);
- } elsif ($opts{"w32mode"} eq "admin") {
- if (!TeXLive::TLWinGoo::admin()) {
- tlwarn("$prg: you don't have the permissions for --w32mode=admin\n");
- return;
- }
- } else {
- tlwarn("$prg: action postaction --w32mode can only be 'admin' or 'user'\n");
- return;
- }
- }
- }
- my @todo;
- if ($opts{"all"}) {
- init_local_db();
- @todo = $localtlpdb->list_packages;
- } else {
- if ($#ARGV < 0) {
- tlwarn("$prg: action postaction: need either --all or a list of packages\n");
- return;
- }
- init_local_db();
- @todo = @ARGV;
- @todo = $localtlpdb->expand_dependencies("-only-arch", $localtlpdb, @todo);
- }
- if ($type =~ m/^shortcut$/i) {
- if (!win32()) {
- tlwarn("$prg: action postaction shortcut only works on windows.\n");
- return;
- }
- for my $p (@todo) {
- my $tlp = $localtlpdb->get_package($p);
- if (!defined($tlp)) {
- tlwarn("$prg: $p is not installed, ignoring it.\n");
- } else {
- # run all shortcut actions, desktop and menu integration
- TeXLive::TLUtils::do_postaction($how, $tlp, 0, 1, 1, 0);
- }
- }
- } elsif ($type =~ m/^fileassoc$/i) {
- if (!win32()) {
- tlwarn("$prg: action postaction fileassoc only works on windows.\n");
- return;
- }
- my $fa = $localtlpdb->option("file_assocs");
- if ($opts{"fileassocmode"}) {
- if ($opts{"fileassocmode"} < 1 || $opts{"fileassocmode"} > 2) {
- tlwarn("$prg: action postaction: value of --fileassocmode can only be 1 or 2\n");
- return;
- }
- $fa = $opts{"fileassocmode"};
- }
- for my $p (@todo) {
- my $tlp = $localtlpdb->get_package($p);
- if (!defined($tlp)) {
- tlwarn("$prg: $p is not installed, ignoring it.\n");
- } else {
- TeXLive::TLUtils::do_postaction($how, $tlp, $fa, 0, 0, 0);
- }
- }
- } elsif ($type =~ m/^script$/i) {
- for my $p (@todo) {
- my $tlp = $localtlpdb->get_package($p);
- if (!defined($tlp)) {
- tlwarn("$prg: $p is not installed, ignoring it.\n");
- } else {
- TeXLive::TLUtils::do_postaction($how, $tlp, 0, 0, 0, 1);
- }
- }
- } else {
- tlwarn("$prg: action postaction needs one of 'shortcut', 'fileassoc', 'script'\n");
- return;
- }
-}
-
-
-# INIT-USERTREE
-#
-# sets up the user tree for tlmgr in user mode
-sub action_init_usertree {
- # init_local_db but do not die if localtlpdb is not found!
- init_local_db(2);
- my $tlpdb = TeXLive::TLPDB->new;
- my $usertree;
- if ($opts{"usertree"}) {
- $usertree = $opts{"usertree"};
- } else {
- chomp($usertree = `kpsewhich -var-value TEXMFHOME`);
- }
- if (-r "$usertree/$InfraLocation/$DatabaseName") {
- tldie("$prg: user mode database already set up in\n$prg: $usertree/$InfraLocation/$DatabaseName\n$prg: not overwriting it.\n");
- }
- $tlpdb->root($usertree);
- # copy values from main installation over
- my $maininsttlp;
- my $inst;
- if (defined($localtlpdb)) {
- $maininsttlp = $localtlpdb->get_package("00texlive.installation");
- $inst = $maininsttlp->copy;
- } else {
- $inst = TeXLive::TLPOBJ->new;
- $inst->name("00texlive.installation");
- $inst->category("TLCore");
- }
- $tlpdb->add_tlpobj($inst);
- # remove all available architectures
- $tlpdb->setting( "available_architectures", "");
- $tlpdb->option( "location", $TeXLive::TLConfig::TeXLiveURL);
- # specify that we are in user mode
- $tlpdb->setting( "usertree", 1 );
- $tlpdb->save;
- #
- # we need to create web2c dir for TLMedia to succeed setting up
- # and for tlmgr.log file
- mkdir ("$usertree/web2c");
- mkdir ("$usertree/tlpkg/tlpobj");
- return ($F_OK);
-}
-
-
-# CONF
-#
-# tries to mimic texconfig conf but can also set values for both tlmgr
-# and texmf conf files.
-#
-sub action_conf {
- my $arg = shift @ARGV;
- my $ret = $F_OK;
-
- if (!defined($arg)) {
- texconfig_conf_mimic();
-
- } elsif ($arg !~ /^(tlmgr|texmf|updmap|auxtrees)$/) {
- warn "$prg: unknown conf arg: $arg (try tlmgr or texmf or updmap or auxtrees)\n";
- return($F_ERROR);
-
- } else {
- my ($fn,$cf);
- if ($opts{'conffile'}) {
- $fn = $opts{'conffile'} ;
- }
- if ($arg eq "tlmgr") {
- chomp (my $TEXMFCONFIG = `kpsewhich -var-value=TEXMFCONFIG`);
- $fn || ( $fn = "$TEXMFCONFIG/tlmgr/config" ) ;
- $cf = TeXLive::TLConfFile->new($fn, "#", "=");
- } elsif ($arg eq "texmf" || $arg eq "auxtrees") {
- $fn || ( $fn = "$Master/texmf.cnf" ) ;
- $cf = TeXLive::TLConfFile->new($fn, "[%#]", "=");
- } elsif ($arg eq "updmap") {
- $fn || ( chomp ($fn = `kpsewhich updmap.cfg`) ) ;
- $cf = TeXLive::TLConfFile->new($fn, '(#|(Mixed)?Map)', ' ');
- } else {
- die "Should not happen, conf arg=$arg";
- }
- my ($key,$val) = @ARGV;
- # make sure that in case of tlmgr conf auxtrees (without anything else)
- # we add the "show" argument which makes it easier to do stuff.
- $key = "show" if ($arg eq "auxtrees" && !defined($key));
-
- if (!defined($key)) {
- # show all settings (not auxtrees case!)
- if ($cf) {
- info("$arg configuration values (from $fn):\n");
- for my $k ($cf->keys) {
- info("$k = " . $cf->value($k) . "\n");
- }
- } else {
- info("$prg: $arg config file $fn not present\n");
- return($F_WARNING);
- }
- } else {
- if ($arg eq "auxtrees") {
- my $tmfa = 'TEXMFAUXTREES';
- my $tv = $cf->value($tmfa);
- if (!$key || $key eq "show") {
- if (defined($tv)) {
- $tv =~ s/^\s*//;
- $tv =~ s/\s*$//;
- $tv =~ s/,$//;
- my @foo = split(',', $tv);
- print "List of auxiliary texmf trees:\n" if (@foo);
- for my $f (@foo) {
- print " $f\n";
- }
- return($F_OK);
- } else {
- print "$prg: no auxiliary texmf trees defined.\n";
- return($F_OK);
- }
- } elsif ($key eq "add") {
- if (defined($val)) {
- if (defined($tv)) {
- $tv =~ s/^\s*//;
- $tv =~ s/\s*$//;
- $tv =~ s/,$//;
- my @foo = split(',', $tv);
- my @new;
- my $already = 0;
- for my $f (@foo) {
- if ($f eq $val) {
- tlwarn("$prg: already registered auxtree: $val\n");
- return ($F_WARNING);
- } else {
- push @new, $f;
- }
- }
- push @new, $val;
- $cf->value($tmfa, join(',', @new) . ',');
- } else {
- $cf->value($tmfa, $val . ',');
- }
- } else {
- tlwarn("$prg: missing argument for auxtrees add\n");
- return($F_ERROR);
- }
- } elsif ($key eq "remove") {
- if (defined($val)) {
- if (defined($tv)) {
- $tv =~ s/^\s*//;
- $tv =~ s/\s*$//;
- $tv =~ s/,$//;
- my @foo = split(',', $tv);
- my @new;
- my $removed = 0;
- for my $f (@foo) {
- if ($f ne $val) {
- push @new, $f;
- } else {
- $removed = 1;
- }
- }
- if ($removed) {
- if ($#new >= 0) {
- $cf->value($tmfa, join(',', @new) . ',');
- } else {
- $cf->delete_key($tmfa);
- }
- } else {
- tlwarn("$prg: not defined as auxiliary texmf tree: $val\n");
- return($F_WARNING);
- }
- } else {
- tlwarn("$prg: no auxiliary texmf trees defined, "
- . "so nothing removed\n");
- return($F_WARNING);
- }
- } else {
- tlwarn("$prg: missing argument for auxtrees remove\n");
- return($F_ERROR);
- }
- } else {
- tlwarn("$prg: unknown auxtrees operation: $key\n");
- return($F_ERROR);
- }
- } elsif (!defined($val)) {
- if (defined($opts{'delete'})) {
- if (defined($cf->value($key))) {
- info("$prg: removing setting $arg $key value: " . $cf->value($key)
- . "from $fn\n");
- $cf->delete_key($key);
- } else {
- info("$prg: $arg $key not defined, cannot remove ($fn)\n");
- $ret = $F_WARNING;
- }
- } else {
- if (defined($cf->value($key))) {
- info("$prg: $arg $key value: " . $cf->value($key) . " ($fn)\n");
- } else {
- info("$prg: $key not defined in $arg config file ($fn)\n");
- if ($arg eq "texmf") {
- # not in user-specific file, show anything kpsewhich gives us.
- chomp (my $defval = `kpsewhich -var-value $key`);
- if ($? != 0) {
- info("$prg: $arg $key default value is unknown");
- } else {
- info("$prg: $arg $key default value: $defval");
- }
- info(" (from kpsewhich -var-value)\n");
- }
- }
- }
- } else {
- if (defined($opts{'delete'})) {
- tlwarn("$arg --delete and value for key $key given, don't know what to do!\n");
- $ret = $F_ERROR;
- } else {
- info("$prg: setting $arg $key to $val (in $fn)\n");
- $cf->value($key, $val);
- }
- }
- }
- if ($cf->is_changed) {
- $cf->save;
- }
- }
- return($ret);
-}
-
-# output various values in same form as texconfig conf.
-sub texconfig_conf_mimic {
- my $PATH = $ENV{'PATH'};
- info("=========================== version information ==========================\n");
- info(give_version());
- info("==================== executables found by searching PATH =================\n");
- info("PATH: $PATH\n");
- for my $cmd (sort(qw/kpsewhich updmap fmtutil tlmgr tex pdftex luatex xetex
- mktexpk dvips dvipdfmx/)) {
- info(sprintf("%-10s %s\n", "$cmd:", TeXLive::TLUtils::which($cmd)));
- }
- info("=========================== active config files ==========================\n");
- for my $m (sort(qw/fmtutil.cnf config.ps mktex.cnf pdftexconfig.tex/)) {
- info(sprintf("%-17s %s", "$m:", `kpsewhich $m`));
- }
- for my $m (qw/texmf.cnf updmap.cfg/) {
- for my $f (`kpsewhich -all $m`) {
- info(sprintf("%-17s %s", "$m:", $f));
- }
- }
-
- #tlwarn("$prg: missing finding of XDvi, config!\n");
-
- info("============================= font map files =============================\n");
- for my $m (sort(qw/psfonts.map pdftex.map ps2pk.map kanjix.map/)) {
- info(sprintf("%-12s %s", "$m:", `kpsewhich $m`));
- }
-
- info("=========================== kpathsea variables ===========================\n");
- for my $v (sort(qw/TEXMFMAIN TEXMFDIST TEXMFLOCAL TEXMFSYSVAR TEXMFSYSCONFIG TEXMFVAR TEXMFCONFIG TEXMFHOME VARTEXFONTS TEXMF SYSTEXMF TEXMFDBS WEB2C TEXPSHEADERS TEXCONFIG ENCFONTS TEXFONTMAPS/)) {
- info("$v=" . `kpsewhich -var-value=$v`);
- }
-
- info("==== kpathsea variables from environment only (ok if no output here) ====\n");
- my @envVars = qw/
- AFMFONTS BIBINPUTS BSTINPUTS CMAPFONTS CWEBINPUTS ENCFONTS GFFONTS
- GLYPHFONTS INDEXSTYLE LIGFONTS MFBASES MFINPUTS MFPOOL MFTINPUTS
- MISCFONTS MPINPUTS MPMEMS MPPOOL MPSUPPORT OCPINPUTS OFMFONTS
- OPENTYPEFONTS OPLFONTS OTPINPUTS OVFFONTS OVPFONTS PDFTEXCONFIG PKFONTS
- PSHEADERS SFDFONTS T1FONTS T1INPUTS T42FONTS TEXBIB TEXCONFIG TEXDOCS
- TEXFONTMAPS TEXFONTS TEXFORMATS TEXINDEXSTYLE TEXINPUTS TEXMFCNF
- TEXMFDBS TEXMFINI TEXMFSCRIPTS TEXPICTS TEXPKS TEXPOOL TEXPSHEADERS
- TEXSOURCES TFMFONTS TRFONTS TTFONTS VFFONTS WEB2C WEBINPUTS
- /;
- for my $v (@envVars) {
- if (defined($ENV{$v})) {
- info("$v=$ENV{$v}\n");
- }
- }
-}
-
-
-# KEY
-#
-# gpg key management:
-# tlmgr key list
-# tlmgr key add <filename>
-# tlmgr key remove <keyid>
-#
-sub action_key {
- my $arg = shift @ARGV;
-
- if (!defined($arg)) {
- tlwarn("missing arguments to action `key'\n");
- return $F_ERROR;
- }
-
- $arg = lc($arg);
- if ($arg =~ /^(add|remove|list)$/) {
- handle_gpg_config_settings();
- if (!$::gpg) {
- tlwarn("gnupg is not found or not set up, cannot continue with action `key'\n");
- return $F_ERROR;
- }
- chomp (my $TEXMFSYSCONFIG = `kpsewhich -var-value=TEXMFSYSCONFIG`);
- my $local_keyring = "$Master/tlpkg/gpg/repository-keys.gpg";
- if ($arg eq 'list') {
- debug("running $::gpg --list-keys\n");
- system("$::gpg --list-keys");
- return $F_OK;
- } elsif ($arg eq 'remove') {
- my $what = shift @ARGV;
- if (!$what) {
- tlwarn("missing argument to `key remove'\n");
- return $F_ERROR;
- }
- # we need to make sure that $local_keyring exists.
- if (! -r $local_keyring) {
- tlwarn("no local keyring available, cannot remove!\n");
- return $F_ERROR;
- }
- debug("running: $::gpg --primary-keyring repository-keys.gpg --delete-key $what\n");
- my ($out, $ret) =
- TeXLive::TLUtils::run_cmd("$::gpg --primary-keyring repository-keys.gpg --delete-key \"$what\" 2>&1");
- if ($ret == 0) {
- info("$prg: key successfully removed\n");
- return $F_OK;
- } else {
- tlwarn("$prg: key removal failed, output:\n$out\n");
- return $F_ERROR;
- }
-
- } elsif ($arg eq 'add') {
- my $what = shift @ARGV;
- if (!$what) {
- tlwarn("$prg: missing argument to `key add'\n");
- return $F_ERROR;
- }
- # we need to make sure that $local_keyring is existent!
- if (! -r $local_keyring) {
- open(FOO, ">$local_keyring") || die("Cannot create $local_keyring: $!");
- close(FOO);
- }
- debug("running: $::gpg --primary-keyring repository-keys.gpg --import $what\n");
- my ($out, $ret) =
- TeXLive::TLUtils::run_cmd("$::gpg --primary-keyring repository-keys.gpg --import \"$what\" 2>&1");
- if ($ret == 0) {
- info("$prg: key successfully imported\n");
- return $F_OK;
- } else {
- tlwarn("$prg: key import failed, output:\n$out\n");
- return $F_ERROR;
- }
- } else {
- tldie("$prg: should not be reached: tlmgr key $arg\n");
- }
-
- } else {
- tlwarn("$prg: unknown directive `$arg' to action `key'\n");
- return $F_ERROR;
- }
- return $F_OK;
-}
-
-
-# SHELL
-# interactive shell.
-#
-sub action_shell {
- my $protocol = 1;
- my $default_prompt = "tlmgr>";
- # keys which can be set/get and are also settable via global cmdline opts
- my @valid_bool_keys
- = qw/debug-translation machine-readable no-execute-actions
- verify-repo json/;
- my @valid_string_keys = qw/repository prompt/;
- my @valid_keys = (@valid_bool_keys, @valid_string_keys);
- # set auto flush unconditionally in action shell
- $| = 1;
- # we need to do an anonymous sub here otherwise the $default_prompt will get
- # only fixed once and remain forever. With anonymous subs it is rebound
- # on every call!
- my $do_prompt;
- $do_prompt = sub {
- my $prompt = "";
- my @options;
- my @guarantee;
- my @savedargs = @_;
- my $did_prompt = 0;
- while (defined(my $arg = shift @_)) {
- if ($arg =~ m/^-prompt$/) {
- # only do allow for prompt rewriting in case of ! machine readable!
- if (!$::machinereadable) {
- print shift @_, " ";
- $did_prompt = 1;
- }
- } elsif ($arg =~ m/^-menu$/) {
- my $options = shift @_;
- @options = @$options;
- print "\n";
- my $c = 1;
- for my $o (@options) {
- print " $c) $o\n";
- $c++;
- }
- } elsif ($arg =~ m/^-guarantee$/) {
- my $guarantee = shift @_;
- @guarantee = @$guarantee;
- } elsif ($arg =~ m/^-/) {
- print "ERROR unsupported prompt command, please report: $arg!\n";
- } else {
- # only do allow for prompt rewriting in case of ! machine readable!
- if (!$::machinereadable) {
- print $arg, " ";
- $did_prompt = 1;
- }
- }
- }
- print "$default_prompt " if (!$did_prompt);
- # to make sure that flushing is done properly, we ship out
- # a new line in buffer mode:
- print "\n" if $::machinereadable;
- my $ans = <STDIN>;
- if (!defined($ans)) {
- # we got Ctrl-D, just break out
- return;
- }
- chomp($ans);
- if (@options) {
- if ($ans =~ /^[0-9]+$/ && 0 <= $ans - 1 && $ans - 1 <= $#options) {
- $ans = $options[$ans - 1];
- } else {
- print "ERROR invalid answer $ans\n";
- # return empty string so caller knows not to continue
- $ans = "";
- }
- }
- if (@guarantee) {
- my $isok = 0;
- for my $g (@guarantee) {
- if ($ans eq $g) {
- $isok = 1;
- last;
- }
- }
- if (!$isok) {
- print("Please answer one of: @guarantee\n");
- print "\n" if $::machinereadable;
- return(&$do_prompt(@savedargs));
- }
- }
- return($ans);
- };
-
- print "protocol $protocol\n";
- while (1) {
- my $ans = &$do_prompt($default_prompt);
- return $F_OK if !defined($ans); # done if eof
-
- my ($cmd, @args) = TeXLive::TLUtils::quotewords('\s+', 0, $ans);
- next if (!defined($cmd));
- if ($cmd eq "protocol") {
- print "protocol $protocol\n";
- } elsif ($cmd eq "help") {
- print "Please see tlmgr help or https://tug.org/texlive/tlmgr.html.\n";
- } elsif ($cmd eq "version") {
- print give_version();
- } elsif ($cmd =~ m/^(quit|end|bye(bye)?)$/i) {
- return $F_OK;
- } elsif ($cmd eq "setup-location") {
- my $dest = shift @args;
- print "ERROR not implemented: $cmd\n";
- } elsif ($cmd eq "restart") {
- exec("tlmgr", @::SAVEDARGV);
-
- } elsif ($cmd =~ m/^(set|get)$/) {
- #
- my $key = shift @args;
- my $val = shift @args;
- if (!$key) {
- $key = &$do_prompt('Choose one of...', -menu => \@valid_keys, '>');
- }
- if (!$key) {
- print("ERROR missing key argument for get/set\n");
- next;
- }
- if ($cmd eq "get" && defined($val)) {
- print("ERROR argument not allowed for get: $val\n");
- next;
- }
- if ($cmd eq "set" && !defined($val)) {
- if ($key eq "repository") {
- $val = &$do_prompt('Enter repository:');
- } elsif ($key eq "prompt") {
- $val = &$do_prompt('Enter new prompt:');
- } else {
- $val = &$do_prompt('Enter 1 for on, 0 for off:', -guarantee => [0,1]);
- }
- # deal with Ctrl-D
- if (!defined($val)) {
- print("ERROR missing value for set\n");
- next;
- }
- }
-
- if ($key eq "repository") {
- if ($cmd eq "set") {
- $location = scalar($val);
- } else {
- if (defined($location) && $location) {
- print "repository = $location\n";
- } else {
- print "repository = <UNDEFINED>\n";
- }
- }
- print "OK\n";
- } elsif ($key eq "prompt") {
- if ($cmd eq "set") {
- $default_prompt = scalar($val);
- } else {
- print "Current prompt: $default_prompt (but you know that, or?)\n";
- }
- print "OK\n";
- } elsif (TeXLive::TLUtils::member($key, @valid_bool_keys)) {
- if ($cmd eq "set") {
- if ($val eq "0") {
- $opts{$key} = 0;
- } elsif ($val eq "1") {
- $opts{$key} = 1;
- } else {
- print "ERROR invalid value $val for key $key\n";
- next;
- }
- # special cases
- $::debug_translation = $opts{"debug-translation"};
- $::machinereadable = $opts{"machine-readable"};
- $::no_execute_actions = $opts{'no-execute-actions'};
- } else {
- print "$key = ", ($opts{$key} ? 1 : 0), "\n";
- }
- print "OK\n";
- } else {
- print "ERROR unknown get/set key $key\n";
- }
- } elsif ($cmd eq "load") {
- my $what = shift @args;
- if (!defined($what)) {
- $what = &$do_prompt("Choose...", -menu => ['local', 'remote'], '>');
- }
- if ($what eq "local") {
- init_local_db();
- print "OK\n";
- } elsif ($what eq "remote") {
- my ($ret, $err) = init_tlmedia();
- if ($ret) {
- print("OK\n");
- } else {
- if ($::machinereadable) {
- # replace \n with \\n to get single line
- $err =~ s/\n/\\n/g;
- }
- print("ERROR $err\n");
- }
- } else {
- print "ERROR can only load 'local' or 'remote', not $what\n";
- }
- } elsif ($cmd eq "save") {
- $localtlpdb->save;
- print "OK\n";
- } elsif (defined($action_specification{$cmd})) {
- # an action
- if (!defined($action_specification{$cmd}{"function"})) {
- print "ERROR undefined action function $cmd\n";
- next;
- }
- # redo the option parsing
- my %optarg;
- for my $k (@valid_bool_keys) {
- if ($globaloptions{$k} eq "1") {
- $optarg{$k} = 1;
- } else {
- $optarg{"$k" . $globaloptions{$k}} = 1;
- }
- }
- if (defined($action_specification{$cmd}{'options'})) {
- my %actopts = %{$action_specification{$cmd}{'options'}};
- for my $k (keys %actopts) {
- if ($actopts{$k} eq "1") {
- $optarg{$k} = 1;
- } else {
- $optarg{"$k" . $actopts{$k}} = 1;
- }
- }
- }
- # save command line options for later restart, if necessary
- @ARGV = @args;
- my %savedopts = %opts;
- %opts = ();
- # stuff global options back into the %opts array
- for my $k (@valid_keys) {
- $opts{$k} = $savedopts{$k} if (exists($savedopts{$k}));
- }
- if (!GetOptions(\%opts, keys(%optarg))) {
- print "ERROR unsupported arguments\n";
- next;
- }
- my $ret = execute_action($cmd, @ARGV);
- if ($ret & $F_ERROR) {
- print "ERROR\n";
- } elsif ($ret & $F_WARNING) {
- print "OK\n";
- } else {
- print "OK\n";
- }
- # make sure that we restart after having called update --self!
- if (($cmd eq 'update') && $opts{'self'} && !$opts{'no-restart'}) {
- print "tlmgr has been updated, restarting!\n";
- exec("tlmgr", @::SAVEDARGV);
- }
- %opts = %savedopts;
- } else {
- print "ERROR unknown command $cmd\n";
- }
- }
-}
-
-
-
-# Subroutines galore.
-#
-# set global $location variable.
-#
-# argument $should_i_die specifies what is required
-# to succeed during initialization.
-#
-# undef or false: TLPDB needs to be found and initialized, but
-# support programs need not be found
-# 1 : TLPDB initialized and support programs must work
-# 2 : not even TLPDB needs to be found
-# if we cannot read tlpdb, die if arg SHOULD_I_DIE is true.
-#
-sub init_local_db {
- my ($should_i_die) = @_;
- defined($should_i_die) or ($should_i_die = 0);
- # if the localtlpdb is already defined do simply return here already
- # to make sure that the settings in the local tlpdb do not overwrite
- # stuff changed via the GUI
- return if defined $localtlpdb;
- $localtlpdb = TeXLive::TLPDB->new ( root => $::maintree );
- if (!defined($localtlpdb)) {
- if ($should_i_die == 2) {
- return undef;
- } else {
- die("cannot setup TLPDB in $::maintree");
- }
- }
- # setup the programs, for w32 we need the shipped wget/xz etc, so we
- # pass the location of these files to setup_programs.
- if (!setup_programs("$Master/tlpkg/installer", $localtlpdb->platform)) {
- tlwarn("$prg: 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) {
- die("$prg: no way to continue!\n");
- } else {
- tlwarn("$prg: Continuing anyway ...\n");
- return ($F_WARNING);
- }
- }
- # let cmd line options override the settings in localtlpdb
- my $loc = norm_tlpdb_path($localtlpdb->option("location"));
- if (defined($loc)) {
- $location = $loc;
- }
- if (defined($opts{"location"})) {
- $location = $opts{"location"};
- }
- if (!defined($location)) {
- die("$prg: No installation source found: neither in texlive.tlpdb nor on command line.\n$prg: Please specify one!");
- }
- if ($location =~ m/^ctan$/i) {
- $location = "$TeXLive::TLConfig::TeXLiveURL";
- }
- # we normalize the path only if it is
- # - a url starting with neither http or ftp
- # - if we are on Windows, it does not start with Drive:[\/]
- if (! ( $location =~ m!^(https?|ftp)://!i ||
- $location =~ m!$TeXLive::TLUtils::SshURIRegex!i ||
- (win32() && (!(-e $location) || ($location =~ m!^.:[\\/]!) ) ) ) ) {
- # seems to be a local path, try to normalize it
- my $testloc = abs_path($location);
- # however, if we were given a url, that will get "normalized" to the
- # empty string, it not being a path. Restore the original value if so.
- $location = $testloc if $testloc;
- }
-}
-
-sub handle_gpg_config_settings {
- # setup gpg if available
- # by default we setup gpg
- # default value
- my $do_setup_gpg = "main";
- # the value is set in the config file
- if (defined($config{'verify-repo'})) {
- $do_setup_gpg = $config{'verify-repo'};
- }
- # command line
- if (defined($opts{'verify-repo'})) {
- $do_setup_gpg = $opts{'verify-repo'};
- }
- # now we know whether we setup gpg or not
- if ($do_setup_gpg ne "none") {
- if (TeXLive::TLCrypto::setup_gpg($Master)) {
- debug("will verify cryptographic signatures\n")
- } else {
- my $prefix = "$prg: No gpg found"; # just to shorten the strings
- if (defined($opts{'verify-repo'}) && $opts{'verify-repo'} eq "all") {
- # verification requested on the command line, but did not succeed: die
- tldie("$prefix, verification explicitly requested on command line, quitting.\n");
- }
- if (defined($config{'verify-repo'}) && $config{'verify-repo'} eq "all") {
- # verification explicitly requested in config file, but not gpg, die
- tldie("$prefix, verification explicitly requested in config file, quitting.\n");
- }
- # it was requested via the default setting, so just debug it
- # the list of repositories will contain verified/not verified statements
- debug ("$prefix, verification implicitly requested, "
- . "continuing without verification\n");
- }
- } else {
- # we do not setup gpg: when explicitly requested, be silent, otherwise info
- my $prefix = "$prg: not setting up gpg";
- if (defined($opts{'verify-repo'})) {
- # log normally is *NOT* logged to a file
- # tlmgr does by default *NOT* set up a log file (cmd line option)
- # user requested it, so don't bother with output
- debug("$prefix, requested on command line\n");
- } elsif (defined($config{'verify-repo'})) {
- debug("$prefix, requested in config file\n");
- } else {
- tldie("$prg: how could this happen? gpg setup.\n");
- }
- }
-}
-
-# initialize the global $remotetlpdb object, or die.
-# uses the global $location.
-#
-sub init_tlmedia_or_die {
- my $silent = shift;
- $silent = ($silent ? 1 : 0);
- my ($ret, $err) = init_tlmedia($silent);
- if (!$ret) {
- tldie("$prg: $err\n");
- }
-}
-
-sub init_tlmedia {
- my $silent = shift;
- # first check if $location contains multiple locations
- # in this case we go to virtual mode
- #my %repos = repository_to_array($localtlpdb->option("location"));
- my %repos = repository_to_array($location);
- my @tags = keys %repos;
- # if we have only one repo, but this one contains a name tag #....
- # then we remove it and save the local tlpdb
- if ($#tags == 0 && ($location =~ m/#/)) {
- $location = $repos{$tags[0]};
- $localtlpdb->option("location", $location);
- $localtlpdb->save;
- %repos = repository_to_array($location);
- }
-
- # checksums and gpg stuff
- if (TeXLive::TLCrypto::setup_checksum_method()) {
- # it is only possible to do gpg verification if we can
- # find a checksum method
- # do the gpg stuff only when loading the remote tlpdb
- handle_gpg_config_settings();
- } else {
- if (!$config{'no-checksums'}) {
- tlwarn(<<END_NO_CHECKSUMS);
-$prg: warning: Cannot find a checksum implementation.
-Please install Digest::SHA (from CPAN), openssl, or sha512sum.
-To silence this warning, set no-checksums in the tlmgr configuration
-file, e.g., by running:
- tlmgr conf tlmgr no-checksums 1
-Continuing without checksum verifications ...
-
-END_NO_CHECKSUMS
- }
- }
-
- # check if we are only one tag/repo
- if ($#tags == 0) {
- # go to normal mode
- return _init_tlmedia($silent);
- }
- # we are still here, so we have more tags
-
- # check that there is a main repository
- if (!TeXLive::TLUtils::member('main', @tags)) {
- return(0, "Cannot find main repository, you have to tag one as main!");
- }
-
- # TODO TODO
- # - abstract the set up of a single media tlpdb
- # - make clear how to check for a already loaded remotetlpdb
- $remotetlpdb = TeXLive::TLPDB->new();
- $remotetlpdb->make_virtual;
-
- my $locstr = $repos{'main'};
- my ($tlmdb, $errormsg) = setup_one_remotetlpdb($locstr, 'main');
- if (!defined($tlmdb)) {
- return (0, $errormsg);
- }
- $remotetlpdb->virtual_add_tlpdb($tlmdb, "main");
- for my $t (@tags) {
- if ($t ne 'main') {
- my ($tlmdb, $errormsg) = setup_one_remotetlpdb($repos{$t});
- if (!defined($tlmdb)) {
- return(0, $errormsg);
- }
- $remotetlpdb->virtual_add_tlpdb($tlmdb, $t);
- $locstr .= " $repos{$t}";
- }
- }
-
- # now check/setup pinning
- if (!$opts{"pin-file"}) {
- # check for pinning file in TEXMFLOCAL/tlpkg/pinning.txt
- chomp (my $TEXMFLOCAL = `kpsewhich -var-value=TEXMFLOCAL`);
- debug("trying to load pinning file $TEXMFLOCAL/tlpkg/pinning.txt\n");
- # since we use TLConfFile it does not matter if the file
- # is not existing, it will be treated properly in TLConfFile
- $opts{"pin-file"} = "$TEXMFLOCAL/tlpkg/pinning.txt";
- }
- $pinfile = TeXLive::TLConfFile->new($opts{"pin-file"}, "#", ":", 'multiple');
- $remotetlpdb->virtual_pinning($pinfile);
- # this "location-url" line should not be changed since GUI programs
- # depend on it:
- if ($::machinereadable && !$silent) {
- print "location-url\t$locstr\n";
- return 1;
- }
- if ($silent) {
- return 1;
- }
- # from here on only in non-machine-readable mode and not silent
- info("$prg: package repositories\n");
- my $show_verification_page_link = 0;
- my $verstat = "";
- if (!$remotetlpdb->virtual_get_tlpdb('main')->is_verified) {
- $show_verification_page_link = 1;
- $verstat = ": ";
- $verstat .= $VerificationStatusDescription{$remotetlpdb->virtual_get_tlpdb('main')->verification_status};
- }
- info("\tmain = " . $repos{'main'} . " (" .
- ($remotetlpdb->virtual_get_tlpdb('main')->is_verified ? "" : "not ") .
- "verified$verstat)\n");
- for my $t (@tags) {
- if ($t ne 'main') {
- $verstat = "";
- if (!$remotetlpdb->virtual_get_tlpdb($t)->is_verified) {
- my $tlpdb_ver_stat = $remotetlpdb->virtual_get_tlpdb($t)->verification_status;
- $verstat = ": ";
- $verstat .= $VerificationStatusDescription{$tlpdb_ver_stat};
- # if the db is not verified *but* was signed, give the page link info
- if ($tlpdb_ver_stat != $VS_UNSIGNED) {
- $show_verification_page_link = 1;
- }
- }
- info("\t$t = " . $repos{$t} . " (" .
- ($remotetlpdb->virtual_get_tlpdb($t)->is_verified ? "" : "not ") .
- "verified$verstat)\n");
- }
- }
- if ($show_verification_page_link) {
- info("For more about verification, see https://texlive.info/verification.html.\n");
- }
- return 1;
-}
-
-sub _init_tlmedia {
- my $silent = shift;
- # if we are already initialized to the same location, nothing
- # needs to be done.
- # if we are initialized to a virtual tlpdb, then we have to
- # do in any case an initialization
- if (defined($remotetlpdb) && !$remotetlpdb->is_virtual &&
- ($remotetlpdb->root eq $location)) {
- # nothing to be done
- return 1;
- }
-
- # choose a mirror if we are asked.
- if ($location =~ m/^ctan$/i) {
- $location = give_ctan_mirror();
- } elsif ($location =~ m,^$TeXLiveServerURL,) {
- my $mirrorbase = TeXLive::TLUtils::give_ctan_mirror_base();
- $location =~ s,^$TeXLiveServerURL,$mirrorbase,;
- }
-
- my $errormsg;
- ($remotetlpdb, $errormsg) = setup_one_remotetlpdb($location, 'main');
- if (!defined($remotetlpdb)) {
- return(0, $errormsg);
- }
-
- return 1 if ($silent);
-
-
- # this "location-url" line should not be changed since GUI programs
- # depend on it:
- if ($::machinereadable) {
- print "location-url\t$location\n";
- } else {
- my $verstat = "";
- if (!$remotetlpdb->is_verified) {
- $verstat = ": ";
- $verstat .= $VerificationStatusDescription{$remotetlpdb->verification_status};
- }
- info("$prg: package repository $location (" .
- ($remotetlpdb->is_verified ? "" : "not ") . "verified$verstat)\n");
- }
- return 1;
-}
-
-sub setup_one_remotetlpdb {
- my $location = shift;
- my $addarg = shift;
- my $is_main = ((defined($addarg) && ($addarg eq 'main')) ? 1 : 0);
- my $remotetlpdb;
-
- # TODO
- # check if that is already loaded!!!
-
- # choose a mirror if we are asked.
- if ($location =~ m/^ctan$/i) {
- $location = give_ctan_mirror();
- } elsif ($location =~ m,^$TeXLiveServerURL,) {
- my $mirrorbase = TeXLive::TLUtils::give_ctan_mirror_base();
- $location =~ s,^$TeXLiveServerURL,$mirrorbase,;
- }
-
- # if we talk about a net location try to download the hash of the tlpdb
- # - if that is possible, check for the locally saved file and if the hash
- # agrees load the local copy if present instead of the remote one,
- # if the hashes disagree, load the remote tlpdb
- # - if that does not work assume we are offline or target not reachable,
- # so warn the user and use saved, but note that installation will
- # not work
- info("start load $location\n") if ($::machinereadable);
-
- my $local_copy_tlpdb_used = 0;
- if ($location =~ m;^(https?|ftp)://;) {
- # first check that the saved tlpdb is present at all
- my $loc_digest = TeXLive::TLCrypto::tl_short_digest($location);
- my $loc_copy_of_remote_tlpdb =
- ($is_main ?
- "$Master/$InfraLocation/texlive.tlpdb.main.$loc_digest" :
- "$Master/$InfraLocation/texlive.tlpdb.$loc_digest");
- ddebug("loc_digest = $loc_digest\n");
- ddebug("loc_copy = $loc_copy_of_remote_tlpdb\n");
- if (-r $loc_copy_of_remote_tlpdb) {
- ddebug("loc copy found!\n");
- # we found the tlpdb matching the current location
- # check for the remote hash
- my $path = "$location/$InfraLocation/$DatabaseName";
- ddebug("remote path of digest = $path\n");
- my ($verified, $status)
- = TeXLive::TLCrypto::verify_checksum_and_check_return($loc_copy_of_remote_tlpdb, $path,
- $is_main, 1); # the 1 means local copy mode!
- # deal with those cases that need special treatment
- if ($status == $VS_CONNECTION_ERROR) {
- info(<<END_NO_INTERNET);
-Unable to download the checksum of the remote TeX Live database,
-but found a local copy, so using that.
-
-You may want to try specifying an explicit or different CTAN mirror,
-or maybe you need to specify proxy information if you're behind a firewall;
-see the information and examples for the -repository option at
-https://tug.org/texlive/doc/install-tl.html
-(and in the output of install-tl --help).
-
-END_NO_INTERNET
- # above text duplicated in install-tl
- $remotetlpdb = TeXLive::TLPDB->new(root => $location,
- tlpdbfile => $loc_copy_of_remote_tlpdb);
- $local_copy_tlpdb_used = 1;
- } elsif ($status == $VS_VERIFIED || $status == $VS_EXPKEYSIG || $status == $VS_REVKEYSIG) {
- $remotetlpdb = TeXLive::TLPDB->new(root => $location,
- tlpdbfile => $loc_copy_of_remote_tlpdb);
- $local_copy_tlpdb_used = 1;
- # if verification was successful, make sure that is recorded
- $remotetlpdb->verification_status($status);
- $remotetlpdb->is_verified($verified);
- }
- # nothing to do in the else case
- # we tldie already in the verify_checksum_and_check_return
- # for all other cases
- }
- }
- if (!$local_copy_tlpdb_used) {
- $remotetlpdb = TeXLive::TLPDB->new(root => $location, verify => 1);
- if ($is_main && $remotetlpdb) {
- if ($remotetlpdb->verification_status == $VS_UNSIGNED) {
- # we don't allow unsigned main data bases!
- tldie("$prg: main database at $location is not signed\n");
- }
- }
- }
- if (!defined($remotetlpdb)) {
- info("fail load $location\n") if ($::machinereadable);
- return(undef, $loadmediasrcerror . $location);
- }
- if ($opts{"require-verification"} && !$remotetlpdb->is_verified) {
- info("fail load $location\n") if ($::machinereadable);
- tldie("Remote TeX Live database ($location) is not verified, exiting.\n");
- }
-
- # we allow a range of years to be specified by the remote tlpdb
- # for which it might work.
- # the lower limit is TLPDB->config_minrelease
- # the upper limit is TLPDB->config_release
- # if the later is not present only the year in config_release is accepted
- # checks are done on the first 4 digits only
- # Why only the first four places: some optional network distributions
- # might use
- # release/2009-foobar
- # If it should work for 2009 and 2010, please use
- # minrelease/2009-foobar
- # release/2010-foobar
- # One exception: if there *is* an extension like -foobar (-gpg, ..)
- # we allow the local release to be smaller than the max,
- # so that additional repos can do
- # release/3000-foobar
- # and be usable with all future releases, too.
- my $texlive_release = $remotetlpdb->config_release;
- my $texlive_minrelease = $remotetlpdb->config_minrelease;
- my $rroot = $remotetlpdb->root;
- if (!defined($texlive_release)) {
- info("fail load $location\n") if ($::machinereadable);
- return(undef, "The installation repository ($rroot) does not specify a "
- . "release year for which it was prepared, goodbye.");
- }
- # still here, so we have $texlive_release defined
- my $texlive_release_year = $texlive_release;
- $texlive_release_year =~ s/^(....).*$/$1/;
- if ($texlive_release_year !~ m/^[1-9][0-9][0-9][0-9]$/) {
- info("fail load $location\n") if ($::machinereadable);
- return(undef, "The installation repository ($rroot) does not specify a "
- . "valid release year, goodbye: $texlive_release");
- }
- # so $texlive_release_year is numeric, good
- if (defined($texlive_minrelease)) {
- # we specify a range of years!
- my $texlive_minrelease_year = $texlive_minrelease;
- $texlive_minrelease_year =~ s/^(....).*$/$1/;
- if ($texlive_minrelease_year !~ m/^[1-9][0-9][0-9][0-9]$/) {
- info("fail load $location\n") if ($::machinereadable);
- return(undef, "The installation repository ($rroot) does not specify a "
- . "valid minimal release year, goodbye: $texlive_minrelease");
- }
- # ok, all numeric and fine, check for range
- if ($TeXLive::TLConfig::ReleaseYear < $texlive_minrelease_year
- || $TeXLive::TLConfig::ReleaseYear > $texlive_release_year) {
- info("fail load $location\n") if ($::machinereadable);
- return (undef, "The TeX Live versions supported by the repository
-$rroot
- ($texlive_minrelease_year--$texlive_release_year)
-do not include the version of the local installation
- ($TeXLive::TLConfig::ReleaseYear).");
- }
- #
- # if the release of the installed TL is less than the release
- # of the main remote repository, then
- # warn that one needs to call update-tlmgr-latest.sh --update
- # We do this only if there is no extension like 2100-gpg etc
- if ($is_main && $TeXLive::TLConfig::ReleaseYear < $texlive_release_year) {
- if (length($texlive_release) > 4) {
- debug("Accepting a newer release as remote due to presence of release extension!\n");
- } else {
- info("fail load $location\n") if ($::machinereadable);
- return (undef, "Local TeX Live ($TeXLive::TLConfig::ReleaseYear)"
- . " is older than remote repository ($texlive_release_year).\n"
- . "Cross release updates are only supported with\n"
- . " update-tlmgr-latest(.sh/.exe) --update\n"
- . "See https://tug.org/texlive/upgrade.html for details.")
- }
- }
- } else {
- # $texlive_minrelease not defined, so only one year is valid
- if ($texlive_release_year != $TeXLive::TLConfig::ReleaseYear) {
- info("fail load $location\n") if ($::machinereadable);
- return(undef, "The TeX Live versions of the local installation
-and the repository are not compatible:
- local: $TeXLive::TLConfig::ReleaseYear
- repository: $texlive_release_year ($rroot)
-(Perhaps you need to use a different CTAN mirror? Just a guess.)");
- }
- }
-
- # check for being frozen
- if ($remotetlpdb->config_frozen) {
- my $frozen_msg = <<FROZEN;
-TeX Live $TeXLive::TLConfig::ReleaseYear is frozen and will no longer
-be routinely updated. This happens in preparation for a new release.
-
-If you're willing to help with pretesting the new release, and we hope
-you are, (when pretests are available), please read
-https://tug.org/texlive/pretest.html.
-
-Otherwise, just wait, and the new release will be ready in due time.
-FROZEN
- # don't die here, we want to allow updates even if tlnet is frozen!
- tlwarn($frozen_msg);
- }
-
- # save remote database if it is a net location
- # make sure that the writeout of the tlpdb is done in UNIX mode
- # since otherwise the checksum will change.
- if (!$local_copy_tlpdb_used && $location =~ m;^(https?|ftp)://;) {
- my $loc_digest = TeXLive::TLCrypto::tl_short_digest($location);
- my $loc_copy_of_remote_tlpdb =
- ($is_main ?
- "$Master/$InfraLocation/texlive.tlpdb.main.$loc_digest" :
- "$Master/$InfraLocation/texlive.tlpdb.$loc_digest");
- my $tlfh;
- if (!open($tlfh, ">:unix", $loc_copy_of_remote_tlpdb)) {
- # that should be only a debug statement, since a user without
- # write permission might have done a tlmgr search --global or
- # similar
- &debug("Cannot save remote TeX Live database to $loc_copy_of_remote_tlpdb: $!\n");
- } else {
- &debug("writing out tlpdb to $loc_copy_of_remote_tlpdb\n");
- $remotetlpdb->writeout($tlfh);
- close($tlfh);
- # Remove all other copies of main databases in case different mirrors
- # are used $Master/$InfraLocation/texlive.tlpdb.main.$loc_digest
- if ($is_main) {
- for my $fn (<"$Master/$InfraLocation/texlive.tlpdb.main.*">) {
- next if ($fn eq $loc_copy_of_remote_tlpdb);
- unlink($fn);
- }
- }
- }
- }
-
- info("finish load $location\n") if ($::machinereadable);
- return($remotetlpdb);
-}
-
-
-
-# finish handles the -pause option (wait for input from stdin),
-# and then exits unless the global $::gui_mode is set, in which case we
-# merely return.
-#
-sub finish {
- my ($ret) = @_;
-
- if ($ret > 0) {
- print "$prg: exiting unsuccessfully (status $ret).\n";
- }
-
- if ($::gui_mode) {
- return $ret;
- } else {
- exit($ret);
- }
-}
-
-
-# tlmgr config file handling. These config files are located in
-# TEXMFCONFIG/tlmgr/config, thus specific for each user.
-#
-# format:
-# key=value
-#
-sub load_config_file {
- #
- # first set default values
- $config{"gui-expertmode"} = 1;
- $config{"auto-remove"} = 1;
- $config{"persistent-downloads"} = 1;
- $config{"verify-repo"} = "main";
- # do NOT set this here, we distinguish between explicitly set in the
- # config file or implicitly true:
- # $config{"verify-downloads"} = 1;
-
- # loads system config file, this cannot be changes with tlmgr
- chomp (my $TEXMFSYSCONFIG = `kpsewhich -var-value=TEXMFSYSCONFIG`);
- my $fnsys = "$TEXMFSYSCONFIG/tlmgr/config";
- my $tlmgr_sys_config_file = TeXLive::TLConfFile->new($fnsys, "#", "=");
- load_options_from_config($tlmgr_sys_config_file, 'sys')
- if $tlmgr_sys_config_file;
-
- chomp (my $TEXMFCONFIG = `kpsewhich -var-value=TEXMFCONFIG`);
- my $fn = "$TEXMFCONFIG/tlmgr/config";
- $tlmgr_config_file = TeXLive::TLConfFile->new($fn, "#", "=");
- load_options_from_config($tlmgr_config_file) if $tlmgr_config_file;
-
- $config{"verify-repo"}
- = convert_crypto_options($config{"verify-downloads"},
- $config{"require-verification"},
- $config{"verify-repo"});
- delete $config{"require-verification"};
- delete $config{"verify-downloads"};
-
- # TODO TODO
- # rename old crypto options
- #
- # TODO TODO
- # what should we do with this?
- # set $opts{"key"} from $config{"key"} if not passed in on cmd line
- if (!defined($opts{"require-verification"})) {
- $opts{"require-verification"} = $config{"require-verification"};
- }
- if (!defined($opts{"persistent-downloads"})) {
- $opts{"persistent-downloads"} = $config{"persistent-downloads"};
- }
-
- # switched names for this one after initial release.
- if ($tlmgr_config_file->key_present("gui_expertmode")) {
- $tlmgr_config_file->rename_key("gui_expertmode", "gui-expertmode");
- }
-}
-
-sub load_options_from_config {
- my ($tlmgr_config_file, $sysmode) = @_;
- my $fn = $tlmgr_config_file->file;
- for my $key ($tlmgr_config_file->keys) {
- my $val = $tlmgr_config_file->value($key);
- if ($key eq "gui-expertmode") {
- if ($val eq "0") {
- $config{"gui-expertmode"} = 0;
- } elsif ($val eq "1") {
- $config{"gui-expertmode"} = 1;
- } else {
- tlwarn("$prg: $fn: unknown value for gui-expertmode: $val\n");
- }
-
- } elsif ($key eq "persistent-downloads") {
- if (($val eq "0") || ($val eq "1")) {
- $config{'persistent-downloads'} = $val;
- } else {
- tlwarn("$prg: $fn: unknown value for persistent-downloads: $val\n");
- }
-
- } elsif ($key eq "update-exclude") {
- my @exs = split(/,/, $val);
- $config{'update-exclude'} = \@exs;
-
- } elsif ($key eq "gui-lang") {
- $config{'gui-lang'} = $val;
-
- } elsif ($key eq "auto-remove") {
- if ($val eq "0") {
- $config{"auto-remove"} = 0;
- } elsif ($val eq "1") {
- $config{"auto-remove"} = 1;
- } else {
- tlwarn("$prg: $fn: unknown value for auto-remove: $val\n");
- }
-
- } elsif ($key eq "require-verification") {
- if ($val eq "0") {
- $config{"require-verification"} = 0;
- } elsif ($val eq "1") {
- $config{"require-verification"} = 1;
- } else {
- tlwarn("$prg: $fn: unknown value for require-verification: $val\n");
- }
-
- } elsif ($key eq "verify-downloads") {
- if ($val eq "0") {
- $config{"verify-downloads"} = 0;
- } elsif ($val eq "1") {
- $config{"verify-downloads"} = 1;
- } else {
- tlwarn("$prg: $fn: unknown value for verify-downloads: $val\n");
- }
-
- } elsif ($key eq "verify-repo") {
- if ($val =~ m/$allowed_verify_args_regex/) {
- $config{"verify-repo"} = $val;
- } else {
- tlwarn("$prg: $fn: unknown value for verify-repo: $val\n");
- }
-
- } elsif ($key eq "no-checksums") {
- if ($val eq "1") {
- $config{"no-checksums"} = 1;
- } elsif ($val eq "0") {
- $config{"no-checksums"} = 0;
- } else {
- tlwarn("$prg: $fn: unknown value for no-checksums: $val\n");
- }
-
- } elsif ($key eq "tkfontscale") {
- $config{'tkfontscale'} = $val;
-
- } elsif ($sysmode) {
- # keys here are only allowed in sys mode
- if ($key eq "allowed-actions") {
- my @acts = split(/,/, $val);
- $config{'allowed-actions'} = \@acts;
- } else {
- tlwarn("$prg: $fn: unknown tlmgr configuration variable: $key\n");
- }
- } else {
- tlwarn("$prg: $fn: unknown tlmgr configuration variable: $key\n");
- }
- }
-}
-
-sub write_config_file {
- if (!defined($tlmgr_config_file)) {
- chomp (my $TEXMFCONFIG = `kpsewhich -var-value=TEXMFCONFIG`);
- my $dn = "$TEXMFCONFIG/tlmgr";
- my $fn = "$dn/config";
- # create a new one
- $tlmgr_config_file = TeXLive::TLConfFile->new($fn, "#", "=");
- }
- for my $k (keys %config) {
- # it doesn't hurt to save all config settings as we check in TLConfFile
- # if the value has actually changed
- $tlmgr_config_file->value($k, $config{$k});
- }
- # make sure that deleted config entries are carried over
- for my $k ($tlmgr_config_file->keys) {
- if (not(defined($config{$k}))) {
- $tlmgr_config_file->delete_key($k);
- }
- }
- if ($tlmgr_config_file->is_changed) {
- $tlmgr_config_file->save;
- }
-}
-
-sub convert_crypto_options {
- my ($verify_downloads, $require_verification, $verify_repo) = @_;
-
- # leave undefined to deal with case that nothing has been passed in
- my $ret;
-
- # crypto options rewriting
- if ((defined($verify_downloads) || defined($require_verification)) &&
- defined($verify_repo)) {
- # we cannot have all three, warn and bail out
- tldie("$prg: options verify-downloads and require-verification have\n"
- . "$prg: been superseded by verify-repo; please use only the latter!\n");
- }
- # return immediately if verify_repo is already set
- return($verify_repo) if (defined($verify_repo));
-
- if (defined($verify_downloads)) {
- if ($verify_downloads) {
- # explicit --verify-downloads was given
- if ($require_verification) {
- # --require-verification was given
- $ret = "all";
- } else {
- # either nothing passed or --no-require-verification (default)
- # use explicit setting to main
- $ret = "main";
- }
- } else {
- # explicit --no-verify-downloads was given
- if ($require_verification) {
- # --require-verification was given
- tldie("You cannot ask for --no-verify-downloads and"
- . " --require-verification at the same time!\n");
- } else {
- $ret = "none";
- }
- }
- } else {
- # nothing related to verify-download was given, so it is by default
- # set to true
- if ($require_verification) {
- $ret = "all";
- } else {
- # don't set anything, as nothing has been passed in
- }
- }
- return($ret);
-}
-
-# if the packagelog variable is set then write to PACKAGELOG filehandle
-#
-sub logpackage {
- if ($packagelogfile) {
- $packagelogged++;
- my $tim = localtime();
- print PACKAGELOG "[$tim] @_\n";
- }
-}
-sub logcommand {
- if ($commandlogfile) {
- my $tim = localtime();
- print COMMANDLOG "[$tim] @_\n";
- }
-}
-
-
-# resolve relative paths from tlpdb wrt tlroot
-sub norm_tlpdb_path {
- my ($path) = @_;
- return if (!defined($path));
- $path =~ s!\\!/!;
- # just return if absolute path
- return $path if ($path =~ m!^/|:!);
- init_local_db() unless defined($localtlpdb);
- return $localtlpdb->root . "/$path";
-}
-
-# clear the backup dir for $pkg and keep only $autobackup packages
-# mind that with $autobackup == 0 all packages are cleared
-#
-sub clear_old_backups {
- my ($pkg, $backupdir, $autobackup, $dry, $v) = @_;
-
- my $verb = ($v ? 1 : 0);
- my $dryrun = 0;
- $dryrun = 1 if ($dry);
- # keep arbitrary many backups
- return if ($autobackup == -1);
-
- opendir (DIR, $backupdir) || die "opendir($backupdir) failed: $!";
- my @dirents = readdir (DIR);
- closedir (DIR) || warn "closedir($backupdir) failed: $!";
- my @backups;
- for my $dirent (@dirents) {
- next if (-d $dirent);
- next if ($dirent !~ m/^$pkg\.r([0-9]+)\.tar\.$CompressorExtRegexp$/);
- push @backups, [ $1, $dirent ] ;
- }
- my $i = 1;
- for my $e (reverse sort {$a->[0] <=> $b->[0]} @backups) {
- if ($i > $autobackup) {
- # only echo out if explicitly asked for verbose which is done
- # in the backup --clean action
- if ($verb) {
- info("$prg: Removing backup $backupdir/$e->[1]\n");
- } else {
- debug("Removing backup $backupdir/$e->[1]\n");
- }
- unlink("$backupdir/$e->[1]") unless $dryrun;
- }
- $i++;
- }
-}
-
-# check for updates to tlcritical packages
-#
-sub check_for_critical_updates {
- my ($localtlpdb, $mediatlpdb) = @_;
-
- my $criticalupdate = 0;
- my @critical = $localtlpdb->expand_dependencies("-no-collections",
- $localtlpdb, @CriticalPackagesList);
- my @critical_upd;
- for my $pkg (sort @critical) {
- my $tlp = $localtlpdb->get_package($pkg);
- if (!defined($tlp)) {
- # that should not happen, we expanded in the localtlpdb so why
- # should it not be present, any anyway, those are so fundamental
- # so they have to be there
- tlwarn("\n$prg: Fundamental package $pkg not present, uh oh, goodbye");
- die "Should not happen, $pkg not found";
- }
- my $localrev = $tlp->revision;
- my $mtlp = $mediatlpdb->get_package($pkg);
- if (!defined($mtlp)) {
- debug("Very surprising, $pkg is not present in the remote tlpdb.\n");
- next;
- }
- my $remoterev = $mtlp->revision;
- push (@critical_upd, $pkg) if ($remoterev > $localrev);
- }
- return(@critical_upd);
-}
-
-sub critical_updates_warning {
- tlwarn("=" x 79, "\n");
- tlwarn("tlmgr itself needs to be updated.\n");
- tlwarn("Please do this via either\n");
- tlwarn(" tlmgr update --self\n");
- tlwarn("or by getting the latest updater for Unix-ish systems:\n");
- tlwarn(" $TeXLiveURL/update-tlmgr-latest.sh\n");
- tlwarn("and/or Windows systems:\n");
- tlwarn(" $TeXLiveURL/update-tlmgr-latest.exe\n");
- tlwarn("Then continue with other updates as usual.\n");
- tlwarn("=" x 79, "\n");
-}
-
-#
-# our compare function for package sorting, which makes sure that
-# packages with .ARCH names are sorted *before* the main packages
-sub packagecmp {
- my $aa = $a;
- my $bb = $b;
- # remove the part after the . if at all present
- $aa =~ s/\..*$//;
- $bb =~ s/\..*$//;
- if ($aa lt $bb) {
- return -1;
- } elsif ($aa gt $bb) {
- return 1;
- } else {
- # the parts before the . are the same
- # sort the .something *before* the ones without
- if ($a eq $aa && $b eq $bb) {
- return 0;
- } elsif ($a eq $aa) {
- # so $a = foobar
- # and $b = foobar.something
- # this is the special case where we want to invert the order
- return 1;
- } elsif ($b eq $bb) {
- # so $a = foobar.something
- # and $b = foobar
- return -1;
- } else {
- return ($a cmp $b);
- }
- }
-}
-
-sub check_on_writable {
- return 1 if $opts{"usermode"};
- if (!TeXLive::TLUtils::dir_writable("$Master/tlpkg")) {
- tlwarn("You don't have permission to change the installation in any way,\n");
- tlwarn("specifically, the directory $Master/tlpkg/ is not writable.\n");
- tlwarn("Please run this program as administrator, or contact your local admin.\n");
- if ($opts{"dry-run"}) {
- tlwarn("$prg: Continuing due to --dry-run\n");
- return 1;
- } else {
- return 0;
- }
- }
- return 1;
-}
-
-
-1;
-__END__
-
-=head1 NAME
-
-tlmgr - the native TeX Live Manager
-
-=head1 SYNOPSIS
-
-tlmgr [I<option>...] I<action> [I<option>...] [I<operand>...]
-
-=head1 DESCRIPTION
-
-B<tlmgr> manages an existing TeX Live installation, both packages and
-configuration options. For information on initially downloading and
-installing TeX Live, see L<https://tug.org/texlive/acquire.html>.
-
-The most up-to-date version of this documentation (updated nightly from
-the development sources) is available at
-L<https://tug.org/texlive/tlmgr.html>, along with procedures for updating
-C<tlmgr> itself and information about test versions.
-
-TeX Live is organized into a few top-level I<schemes>, each of which is
-specified as a different set of I<collections> and I<packages>, where a
-collection is a set of packages, and a package is what contains actual
-files. Schemes typically contain a mix of collections and packages, but
-each package is included in exactly one collection, no more and no less.
-A TeX Live installation can be customized and managed at any level.
-
-See L<https://tug.org/texlive/doc> for all the TeX Live documentation
-available.
-
-=head1 EXAMPLES
-
-After successfully installing TeX Live, here are a few common operations
-with C<tlmgr>:
-
-=over 4
-
-=item C<tlmgr option repository ctan>
-
-=item C<tlmgr option repository http://mirror.ctan.org/systems/texlive/tlnet>
-
-Tell C<tlmgr> to use a nearby CTAN mirror for future updates; useful if
-you installed TeX Live from the DVD image and want to have continuing
-updates. The two commands are equivalent; C<ctan> is just an alias for
-the given url.
-
-Caveat: C<mirror.ctan.org> resolves to many different hosts, and they
-are not perfectly synchronized; we recommend updating only daily (at
-most), and not more often. You can choose a particular mirror if
-problems; the list of all CTAN mirrors with the status of each is at
-L<https://ctan.org/mirrors/mirmon>.
-
-=item C<tlmgr update --list>
-
-Report what would be updated without actually updating anything.
-
-=item C<tlmgr update --all>
-
-Make your local TeX installation correspond to what is in the package
-repository (typically useful when updating from CTAN).
-
-=item C<tlmgr info> I<what>
-
-Display detailed information about a package I<what>, such as the installation
-status and description, of searches for I<what> in all packages.
-
-=back
-
-For all the capabilities and details of C<tlmgr>, please read the
-following voluminous information.
-
-=head1 OPTIONS
-
-The following options to C<tlmgr> are global options, not specific to
-any action. All options, whether global or action-specific, can be
-given anywhere on the command line, and in any order. The first
-non-option argument will be the main action. In all cases,
-C<-->I<option> and C<->I<option> are equivalent, and an C<=> is optional
-between an option name and its value.
-
-=over 4
-
-=item B<--repository> I<url|path>
-
-Specify the package repository from which packages should be installed
-or updated, either a local directory or network location, as below. This
-overridesthe default package repository found in the installation's TeX
-Live Package Database (a.k.a. the TLPDB, which is given entirely in the
-file C<tlpkg/texlive.tlpdb>).
-
-This C<--repository> option changes the location only for the current
-run; to make a permanent change, use C<option repository> (see the
-L</option> action).
-
-As an example, you can choose a particular CTAN mirror with something
-like this:
-
- -repository http://ctan.example.org/its/ctan/dir/systems/texlive/tlnet
-
-Of course a real hostname and its particular top-level CTAN directory
-have to be specified. The list of CTAN mirrors is available at
-L<https://ctan.org/mirrors/mirmon>.
-
-Here's an example of using a local directory:
-
- -repository /local/TL/repository
-
-For backward compatibility and convenience, C<--location> and C<--repo>
-are accepted as aliases for this option.
-
-Locations can be specified as any of the following:
-
-=over 4
-
-=item C</some/local/dir>
-
-=item C<file:/some/local/dir>
-
-Equivalent ways of specifying a local directory.
-
-=item C<ctan>
-
-=item C<http://mirror.ctan.org/systems/texlive/tlnet>
-
-Pick a CTAN mirror automatically, trying for one that is both nearby and
-up-to-date. The chosen mirror is used for the entire download. The bare
-C<ctan> is merely an alias for the full url. (See L<https://ctan.org> for
-more about CTAN and its mirrors.)
-
-=item C<http://server/path/to/tlnet>
-
-Standard HTTP. If the (default) LWP method is used, persistent
-connections are supported. TL can also use C<curl> or C<wget> to do the
-downloads, or an arbitrary user-specified program, as described in the
-C<tlmgr> documentation
-(L<https://tug.org/texlive/doc/tlmgr.html#ENVIRONMENT-VARIABLES>).
-
-=item C<https://server/path/to/tlnet>
-
-Again, if the (default) LWP method is used, this supports persistent
-connections. Unfortunately, some versions of C<wget> and C<curl> do not
-support https, and even when C<wget> supports https, certificates may be
-rejected even when the certificate is fine, due to a lack of local
-certificate roots. The simplest workaround for this problem is to use
-http or ftp.
-
-=item C<ftp://server/path/to/tlnet>
-
-If the (default) LWP method is used, persistent connections are
-supported.
-
-=item C<user@machine:/path/to/tlnet>
-
-=item C<scp://user@machine/path/to/tlnet>
-
-=item C<ssh://user@machine/path/to/tlnet>
-
-These forms are equivalent; they all use C<scp> to transfer files. Using
-C<ssh-agent> is recommended. (Info:
-L<https://en.wikipedia.org/wiki/OpenSSH>,
-L<https://en.wikipedia.org/wiki/Ssh-agent>.)
-
-=back
-
-If the repository is on the network, trailing C</> characters and/or
-trailing C</tlpkg> and/or C</archive> components are ignored.
-
-=item B<--gui> [I<action>]
-
-Two notable GUI front-ends for C<tlmgr>, C<tlshell> and C<tlcockpit>,
-are started up as separate programs; see their own documentation.
-
-C<tlmgr> itself has a graphical interface as well as the command line
-interface. You can give the option to invoke it, C<--gui>, 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. If no action is given, the
-GUI will be started at the main screen. See L<GUI FOR TLMGR>.
-
-However, the native GUI requires Perl/TK, which is no longer included in
-TeX Live's Perl distribution for Windows. You may find C<tlshell> or
-C<tlcockpit> easier to work with.
-
-
-=for comment Keep language list in sync with install-tl.
-
-=item B<--gui-lang> I<llcode>
-
-By default, 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 with a
-language code (based on ISO 639-1). Currently supported (but not
-necessarily completely translated) are: S<English (en, default)>,
-S<Czech (cs)>, S<German (de)>, S<French (fr)>, S<Italian (it)>,
-S<Japanese (ja)>, S<Dutch (nl)>, S<Polish (pl)>, S<Brazilian Portuguese
-(pt_BR)>, S<Russian (ru)>, S<Slovak (sk)>, S<Slovenian (sl)>, S<Serbian
-(sr)>, S<Ukrainian (uk)>, S<Vietnamese (vi)>, S<simplified Chinese
-(zh_CN)>, and S<traditional Chinese (zh_TW)>.
-
-tlshell shares its message catalog with tlmgr.
-
-=item B<--command-logfile> I<file>
-
-C<tlmgr> logs the output of all programs invoked (mktexlr, mtxrun, fmtutil,
-updmap) to a separate log file, by default
-C<TEXMFSYSVAR/web2c/tlmgr-commands.log>. This option allows you to specify a
-different file for the log.
-
-=item B<--debug-translation>
-
-In GUI mode, this switch tells C<tlmgr> to report any untranslated (or
-missing) messages to standard error. This can help translators to see
-what remains to be done.
-
-=item B<--machine-readable>
-
-Instead of the normal output intended for human consumption, write (to
-standard output) a fixed format more suitable for machine parsing. See
-the L<MACHINE-READABLE OUTPUT> section below.
-
-=item B<--no-execute-actions>
-
-Suppress the execution of the execute actions as defined in the tlpsrc
-files. Documented only for completeness, as this is only useful in
-debugging.
-
-=item B<--package-logfile> I<file>
-
-C<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 specify a
-different file for the log.
-
-=item B<--pause>
-
-This option makes C<tlmgr> wait for user input before exiting. Useful on
-Windows to avoid disappearing command windows.
-
-=item B<--persistent-downloads>
-
-=item B<--no-persistent-downloads>
-
-For network-based installations, this option (on by default) makes
-C<tlmgr> try to set up a persistent connection (using the C<LWP> Perl
-module). The idea is to open and reuse only one connection per session
-between your computer and the server, instead of initiating a new
-download for each package.
-
-If this is not possible, C<tlmgr> will fall back to using C<wget>. To
-disable these persistent connections, use C<--no-persistent-downloads>.
-
-=item B<--pin-file>
-
-Change the pinning file location from C<TEXMFLOCAL/tlpkg/pinning.txt>
-(see L</Pinning> below). Documented only for completeness, as this is
-only useful in debugging.
-
-=item B<--usermode>
-
-Activates user mode for this run of C<tlmgr>; see L<USER MODE> below.
-
-=item B<--usertree> I<dir>
-
-Uses I<dir> for the tree in user mode; see L<USER MODE> below.
-
-=item B<--verify-repo=[none|main|all]>
-
-Defines the level of verification done: If C<none> is specified, no
-verification whatsoever is done. If C<main> is given and a working GnuPG
-(C<gpg>) binary is available, all repositories are checked, but only the
-main repository is required to be signed. If C<all> is given, then all
-repositories need to be signed. See L<CRYPTOGRAPHIC VERIFICATION> below
-for details.
-
-=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 C<TeXLive::TLUtils> documentation.
-
-The C<--version> option shows version information about the TeX Live
-release and about the C<tlmgr> script itself. If C<-v> is also given,
-revision number for the loaded TeX Live Perl modules are shown, too.
-
-=head1 ACTIONS
-
-=head2 help
-
-Display this help information and exit (same as C<--help>, and on the
-web at L<https://tug.org/texlive/doc/tlmgr.html>). Sometimes the
-C<perldoc> and/or C<PAGER> programs on the system have problems,
-resulting in control characters being literally output. This can't
-always be detected, but you can set the C<NOPERLDOC> environment
-variable and C<perldoc> will not be used.
-
-=head2 version
-
-Gives version information (same as C<--version>).
-
-If C<-v> has been given the revisions of the used modules are reported, too.
-
-=head2 backup
-
-=over 4
-
-=item B<backup [I<option>...] --all>
-
-=item B<backup [I<option>...] I<pkg>...>
-
-If the C<--clean> option is not specified, this action makes a backup of
-the given packages, or all packages given C<--all>. These backups are
-saved to the value of the C<--backupdir> option, if that is an existing
-and writable directory. If C<--backupdir> is not given, the C<backupdir>
-option setting in the TLPDB is used, if present. If both are missing, no
-backups are made. (The installer sets C<backupdir> to
-C<.../tlpkg/backups>, under the TL root installation directory, so it is
-usually defined; see the L</option> description for more information.)
-
-If the C<--clean> option is specified, backups are pruned (removed)
-instead of saved. The optional integer value I<N> may be specified to
-set the number of backups that will be retained when cleaning. If C<N>
-is not given, the value of the C<autobackup> option is used. If both are
-missing, an error is issued. For more details of backup pruning, see
-the C<option> action.
-
-Options:
-
-=over 4
-
-=item B<--backupdir> I<directory>
-
-Overrides the C<backupdir> option setting in the TLPDB.
-The I<directory> argument is required and must specify an existing,
-writable directory where backups are to be placed.
-
-=item B<--all>
-
-If C<--clean> is not specified, make a backup of all packages in the TeX
-Live installation; this will take quite a lot of space and time. If
-C<--clean> is specified, all packages are pruned.
-
-=item B<--clean>[=I<N>]
-
-Instead of making backups, prune the backup directory of old backups, as
-explained above. The optional integer argument I<N> overrides the
-C<autobackup> option set in the TLPDB. You must use C<--all> or a list
-of packages together with this option, as desired.
-
-=item B<--dry-run>
-
-Nothing is actually backed up or removed; instead, the actions to be
-performed are written to the terminal.
-
-=back
-
-=back
-
-=head2 candidates I<pkg>
-
-Shows the available candidate repositories for package I<pkg>.
-See L<MULTIPLE REPOSITORIES> below.
-
-=head2 check [I<option>...] [depends|executes|files|runfiles|texmfdbs|all]
-
-Execute one (or all) check(s) of the consistency of the installation.
-If no problems are found, there will be no output. (To get a view of
-what is being done, run C<tlmgr -v check>.)
-
-=over 4
-
-=item B<depends>
-
-Lists those packages which occur as dependencies in an installed collection,
-but are themselves not installed, and those packages which are not
-contained in any collection.
-
-If you call C<tlmgr check collections> this test will be carried out
-instead since former versions for C<tlmgr> called it that way.
-
-=item B<executes>
-
-Check that the files referred to by C<execute> directives in the TeX
-Live Database are present.
-
-=item B<files>
-
-Checks that all files listed in the local TLPDB (C<texlive.tlpdb>) are
-actually present, and lists those missing.
-
-=item B<runfiles>
-
-List those filenames that are occurring more than one time in the
-runfiles sections, except for known duplicates.
-
-=item B<texmfdbs>
-
-Checks related to the C<ls-R> files. If you have defined new trees, or
-changed the C<TEXMF> or C<TEXMFDBS> variables, it can't hurt to run
-this. It checks that:
-
-=over 8
-
-=item - all items in C<TEXMFDBS> have the C<!!> prefix.
-
-=item - all items in C<TEXMFBDS> have an C<ls-R> file (if they exist at all).
-
-=item - all items in C<TEXMF> with C<!!> are listed in C<TEXMFDBS>.
-
-=item - all items in C<TEXMF> with an C<ls-R> file are listed in C<TEXMFDBS>.
-
-=back
-
-=back
-
-Options:
-
-=over 4
-
-=item B<--use-svn>
-
-Use the output of C<svn status> instead of listing the files; for
-checking the TL development repository. (This is run nightly.)
-
-=back
-
-=head2 conf
-
-=over 4
-
-=item B<conf [texmf|tlmgr|updmap [--conffile I<file>] [--delete] [I<key> [I<value>]]]>
-
-=item B<conf auxtrees [--conffile I<file>] [show|add|remove] [I<value>]>
-
-With only C<conf>, show general configuration information for TeX Live,
-including active configuration files, path settings, and more. This is
-like running C<texconfig conf>, but works on all supported platforms.
-
-With one of C<conf texmf>, C<conf tlmgr>, or C<conf updmap>, shows all
-key/value pairs (i.e., all settings) as saved in C<ROOT/texmf.cnf>, the
-user-specific C<tlmgr> configuration file (see below), or the first
-found (via C<kpsewhich>) C<updmap.cfg> file, respectively.
-
-If I<key> is given in addition, shows the value of only that I<key> in
-the respective file. If option I<--delete> is also given, the value in
-the given configuration file is entirely removed (not just commented
-out).
-
-If I<value> is given in addition, I<key> is set to I<value> in the
-respective file. I<No error checking is done!>
-
-The C<PATH> value shown by C<conf> is as used by C<tlmgr>. The
-directory in which the C<tlmgr> executable is found is automatically
-prepended to the PATH value inherited from the environment.
-
-Here is a practical example of changing configuration values. If the
-execution of (some or all) system commands via C<\write18> was left
-enabled during installation, you can disable it afterwards:
-
- tlmgr conf texmf shell_escape 0
-
-The subcommand C<auxtrees> allows adding and removing arbitrary
-additional texmf trees, completely under user control. C<auxtrees show>
-shows the list of additional trees, C<auxtrees add> I<tree> adds a tree
-to the list, and C<auxtrees remove> I<tree> removes a tree from the list
-(if present). The trees should not contain an C<ls-R> file (or files
-will not be found if the C<ls-R> becomes stale). This works by
-manipulating the Kpathsea variable C<TEXMFAUXTREES>, in (by default)
-C<ROOT/texmf.cnf>. Example:
-
- tlmgr conf auxtrees add /quick/test/tree
- tlmgr conf auxtrees remove /quick/test/tree
-
-In all cases the configuration file can be explicitly specified via the
-option C<--conffile> I<file>, e.g., if you don't want to change the
-system-wide configuration.
-
-Warning: The general facility for changing configuration values is here,
-but tinkering with settings in this way is strongly discouraged. Again,
-no error checking on either keys or values is done, so any sort of
-breakage is possible.
-
-=back
-
-=head2 dump-tlpdb [I<option>...] [--json]
-
-Dump complete local or remote TLPDB to standard output, as-is. The
-output is analogous to the C<--machine-readable> output; see
-L<MACHINE-READABLE OUTPUT> section.
-
-Options:
-
-=over 4
-
-=item B<--local>
-
-Dump the local TLPDB.
-
-=item B<--remote>
-
-Dump the remote TLPDB.
-
-=item B<--json>
-
-Instead of dumping the actual content, the database is dumped as
-JSON. For the format of JSON output see C<tlpkg/doc/JSON-formats.txt>,
-format definition C<TLPDB>.
-
-=back
-
-Exactly one of C<--local> and C<--remote> must be given.
-
-In either case, the first line of the output specifies the repository
-location, in this format:
-
- "location-url" "\t" location
-
-where C<location-url> is the literal field name, followed by a tab, and
-I<location> is the file or url to the repository.
-
-Line endings may be either LF or CRLF depending on the current platform.
-
-=head2 generate
-
-=over 4
-
-=item B<generate [I<option>...] language>
-
-=item B<generate [I<option>...] language.dat>
-
-=item B<generate [I<option>...] language.def>
-
-=item B<generate [I<option>...] language.dat.lua>
-
-=back
-
-The C<generate> action overwrites any manual changes made in the
-respective files: it recreates them from scratch based on the
-information of the installed packages, plus local adaptions.
-The TeX Live installer and C<tlmgr> routinely call C<generate> for
-all of these files.
-
-For managing your own fonts, please read the C<updmap --help>
-information and/or L<https://tug.org/fonts/fontinstall.html>.
-
-For managing your own formats, please read the C<fmtutil --help>
-information.
-
-In more detail: C<generate> remakes any of the configuration files
-C<language.dat>, C<language.def>, and C<language.dat.lua>
-from the information present in the local TLPDB, plus
-locally-maintained files.
-
-The locally-maintained files are C<language-local.dat>,
-C<language-local.def>, or C<language-local.dat.lua>,
-searched for in C<TEXMFLOCAL> in the respective
-directories. If local additions are present, the final file is made by
-starting with the main file, omitting any entries that the local file
-specifies to be disabled, and finally appending the local file.
-
-(Historical note: The formerly supported C<updmap-local.cfg> and
-C<fmtutil-local.cnf> are no longer read, since C<updmap> and C<fmtutil>
-now reads and supports multiple configuration files. Thus,
-local additions can and should be put into an C<updmap.cfg> of C<fmtutil.cnf>
-file in C<TEXMFLOCAL>. The C<generate updmap> and C<generate fmtutil> actions
-no longer exist.)
-
-Local files specify entries to be disabled with a comment line, namely
-one of these:
-
- %!NAME
- --!NAME
-
-where C<language.dat> and C<language.def> use C<%>,
-and C<language.dat.lua> use C<-->. In all cases, the I<name> is
-the respective format name or hyphenation pattern identifier.
-Examples:
-
- %!german
- --!usenglishmax
-
-(Of course, you're not likely to actually want to disable those
-particular items. They're just examples.)
-
-After such a disabling line, the local file can include another entry
-for the same item, if a different definition is desired. In general,
-except for the special disabling lines, the local files follow the same
-syntax as the master files.
-
-The form C<generate language> recreates all three files C<language.dat>,
-C<language.def>, and C<language.dat.lua>, while the forms with an
-extension recreates only that given language file.
-
-Options:
-
-=over 4
-
-=item B<--dest> I<output_file>
-
-specifies the output file (defaults to the respective location in
-C<TEXMFSYSVAR>). If C<--dest> is given to C<generate language>, it
-serves as a basename onto which C<.dat> will be appended for the name of
-the C<language.dat> output file, C<.def> will be appended to the value
-for the name of the C<language.def> output file, and C<.dat.lua> to the
-name of the C<language.dat.lua> file. (This is just to avoid
-overwriting; if you want a specific name for each output file, we
-recommend invoking C<tlmgr> twice.)
-
-=item B<--localcfg> I<local_conf_file>
-
-specifies the (optional) local additions (defaults to the respective
-location in C<TEXMFLOCAL>).
-
-=item B<--rebuild-sys>
-
-tells C<tlmgr> to run necessary programs after config files have been
-regenerated. These are:
-C<fmtutil-sys --all> after C<generate fmtutil>,
-C<fmtutil-sys --byhyphen .../language.dat> after C<generate language.dat>,
-and
-C<fmtutil-sys --byhyphen .../language.def> after C<generate language.def>.
-
-These subsequent calls cause the newly-generated files to actually take
-effect. This is not done by default since those calls are lengthy
-processes and one might want to made several related changes in
-succession before invoking these programs.
-
-=back
-
-The respective locations are as follows:
-
- tex/generic/config/language.dat (and language-local.dat)
- tex/generic/config/language.def (and language-local.def)
- tex/generic/config/language.dat.lua (and language-local.dat.lua)
-
-=head2 gui
-
-Start the graphical user interface. See B<GUI> below.
-
-=head2 info
-
-=over 4
-
-=item B<info [I<option>...] I<pkg>...>
-
-=item B<info [I<option>...] collections>
-
-=item B<info [I<option>...] schemes>
-
-With no argument, lists all packages available at the package
-repository, prefixing those already installed with C<i>.
-
-With the single word C<collections> or C<schemes> as the argument, lists
-the request type instead of all packages.
-
-With any other arguments, display information about I<pkg>: the name,
-category, short and long description, sizes, installation status, and TeX Live
-revision number. If I<pkg> is not locally installed, searches in the
-remote installation source.
-
-For normal packages (not collections or schemes), the sizes of the four
-groups of files (run/src/doc/bin files) are shown separately. For
-collections, the cumulative size is shown, including all
-directly-dependent packages (but not dependent collections). For
-schemes, the cumulative size is also shown, including all
-directly-dependent collections and packages.
-
-If I<pkg> is not found locally or remotely, the search action is used
-and lists matching packages and files.
-
-It also displays information taken from the TeX Catalogue, namely the
-package version, date, and license. Consider these, especially the
-package version, as approximations only, due to timing skew of the
-updates of the different pieces. By contrast, the C<revision> value
-comes directly from TL and is reliable.
-
-The former actions C<show> and C<list> are merged into this action,
-but are still supported for backward compatibility.
-
-Options:
-
-=over 4
-
-=item B<--list>
-
-If the option C<--list> is given with a package, the list of contained
-files is also shown, including those for platform-specific dependencies.
-When given with schemes and collections, C<--list> outputs their
-dependencies in a similar way.
-
-=item B<--only-installed>
-
-If this option is given, the installation source will not be used; only
-locally installed packages, collections, or schemes are listed.
-
-=item B<--only-remote>
-
-Only list packages from the remote repository. Useful when checking what
-is available in a remote repository using
-C<tlmgr --repo ... --only-remote info>. Note that
-C<--only-installed> and C<--only-remote> cannot both be specified.
-
-=item B<--data C<item1,item2,...>>
-
-If the option C<--data> is given, its argument must be a comma separated
-list of field names from: C<name>, C<category>, C<localrev>,
-C<remoterev>, C<shortdesc>, C<longdesc>, C<installed>, C<size>,
-C<relocatable>, C<depends>, C<cat-version>, C<cat-date>, C<cat-license>,
-plus various C<cat-contact-*> fields (see below).
-
-The C<cat-*> fields all come from the TeX Catalogue
-(L<https://ctan.org/pkg/catalogue>). For each, there are two more
-variants with prefix C<l> and C<r>, e.g., C<lcat-version> and
-C<rcat-version>, which indicate the local and remote information,
-respectively. The variants without C<l> and C<r> show the most current
-one, which is normally the remote value.
-
-The requested packages' information is listed in CSV format, one package
-per line, and the column information is given by the C<itemN>. The
-C<depends> column contains the names of all the dependencies separated
-by C<:> characters.
-
-At this writing, the C<cat-contact-*> fields include: C<home>,
-C<repository>, C<support>, C<bugs>, C<announce>, C<development>. Each
-may be empty or a url value. A brief description is on the CTAN upload
-page for new packages: L<https://ctan.org/upload>.
-
-=item B<--json>
-
-In case C<--json> is specified, the output is a JSON encoded array where
-each array element is the JSON representation of a single C<TLPOBJ> but
-with additional information. For details see
-C<tlpkg/doc/JSON-formats.txt>, format definition: C<TLPOBJINFO>. If both
-C<--json> and C<--data> are given, C<--json> takes precedence.
-
-=back
-
-=back
-
-=head2 init-usertree
-
-Sets up a texmf tree for so-called user mode management, either the
-default user tree (C<TEXMFHOME>), or one specified on the command line
-with C<--usertree>. See L<USER MODE> below.
-
-=head2 install [I<option>...] I<pkg>...
-
-Install each I<pkg> given on the command line, if it is not already
-installed. It does not touch existing packages; see the C<update>
-action for how to get the latest version of a package.
-
-By default this also installs all packages on which the given I<pkg>s are
-dependent. Options:
-
-=over 4
-
-=item B<--dry-run>
-
-Nothing is actually installed; instead, the actions to be performed are
-written to the terminal.
-
-=item B<--file>
-
-Instead of fetching a package from the installation repository, use
-the package files given on the command line. These files must
-be standard TeX Live package files (with contained tlpobj file).
-
-=item B<--force>
-
-If updates to C<tlmgr> itself (or other parts of the basic
-infrastructure) are present, C<tlmgr> will bail out and not perform the
-installation unless this option is given. Not recommended.
-
-=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>
-
-Normally, when you install a package which ships binary files the
-respective binary package will also be installed. That is, for a
-package C<foo>, the package C<foo.i386-linux> will also be installed on
-an C<i386-linux> system. This option suppresses this behavior, and also
-implies C<--no-depends>. Don't use it unless you are sure of what you
-are doing.
-
-=item B<--reinstall>
-
-Reinstall a package (including dependencies for collections) even if it
-already seems to be 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
-(i.e., not those of category Scheme or Collection).
-
-=item B<--with-doc>
-
-=item B<--with-src>
-
-While not recommended, the C<install-tl> program provides an option to
-omit installation of all documentation and/or source files. (By
-default, everything is installed.) After such an installation, you may
-find that you want the documentation or source files for a given package
-after all. You can get them by using these options in conjunction with
-C<--reinstall>, as in (using the C<fontspec> package as the example):
-
- tlmgr install --reinstall --with-doc --with-src fontspec
-
-=back
-
-This action does not automatically add new symlinks in system
-directories; you need to run C<tlmgr path add> (L</path>) yourself if
-you are using this feature and want new symlinks added.
-
-=head2 key
-
-=over 4
-
-=item B<key list>
-
-=item B<key add I<file>>
-
-=item B<key remove I<keyid>>
-
-The action C<key> allows listing, adding and removing additional GPG
-keys to the set of trusted keys, that is, those that are used to verify
-the TeX Live databases.
-
-With the C<list> argument, C<key> lists all keys.
-
-The C<add> argument requires another argument, either a filename or
-C<-> for stdin, from which the key is added. The key is added to the
-local keyring C<GNUPGHOME/repository-keys.gpg>, which is normally
-C<tlpkg/gpg/repository-keys.gpg>.
-
-The C<remove> argument requires a key id and removes the requested id
-from the local keyring.
-
-=back
-
-=head2 list
-
-Synonym for L</info>.
-
-=head2 option
-
-=over 4
-
-=item B<option [--json] [show]>
-
-=item B<option [--json] showall|help>
-
-=item B<option I<key> [I<value>]>
-
-=back
-
-The first form, C<show>, shows the global TeX Live settings currently
-saved in the TLPDB with a short description and the C<key> used for
-changing it in parentheses.
-
-The second form, C<showall>, is similar, but also shows options which
-can be defined but are not currently set to any value (C<help> is a
-synonym).
-
-Both C<show...> forms take an option C<--json>, which dumps the option
-information in JSON format. In this case, both forms dump the same
-data. For the format of the JSON output see
-C<tlpkg/doc/JSON-formats.txt>, format definition C<TLOPTION>.
-
-In the third form, with I<key>, 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 (run C<tlmgr option showall> for
-the definitive list):
-
- repository (default package repository),
- formats (generate formats at installation or update time),
- postcode (run postinst code blobs)
- docfiles (install documentation files),
- srcfiles (install source files),
- backupdir (default directory for backups),
- autobackup (number of backups to keep).
- sys_bin (directory to which executables are linked by the path action)
- sys_man (directory to which man pages are linked by the path action)
- sys_info (directory to which Info files are linked by the path action)
- desktop_integration (Windows-only: create Start menu shortcuts)
- fileassocs (Windows-only: change file associations)
- multiuser (Windows-only: install for all users)
-
-One common use of C<option> is to permanently change the installation to
-get further updates from the Internet, after originally installing from
-DVD. To do this, you can run
-
- tlmgr option repository http://mirror.ctan.org/systems/texlive/tlnet
-
-The C<install-tl> documentation has more information about the possible
-values for C<repository>. (For backward compatibility, C<location> can
-be used as a synonym for C<repository>.)
-
-If C<formats> is set (this is the default), then formats are regenerated
-when either the engine or the format files have changed. Disable this
-only when you know how and want to regenerate formats yourself whenever
-needed (which is often, in practice).
-
-The C<postcode> option controls execution of per-package
-postinstallation action code. It is set by default, and again disabling
-is not likely to be of interest except to developers doing debugging.
-
-The C<docfiles> and C<srcfiles> options control the installation of
-their respective file groups (documentation, sources; grouping is
-approximate) per package. By default both are enabled (1). Either or
-both can be disabled (set to 0) if disk space is limited or for minimal
-testing installations, etc. When disabled, the respective files are not
-downloaded at all.
-
-The options C<autobackup> and C<backupdir> determine the defaults for
-the actions C<update>, C<backup> and C<restore>. These three actions
-need a directory in which to read or write the backups. If
-C<--backupdir> is not specified on the command line, the C<backupdir>
-option value is used (if set). The TL installer sets C<backupdir> to
-C<.../tlpkg/backups>, under the TL root installation directory.
-
-The C<autobackup> option (de)activates automatic generation of backups.
-Its value is an integer. If the C<autobackup> value is C<-1>, no
-backups are removed. If C<autobackup> is 0 or more, it specifies the
-number of backups to keep. Thus, backups are disabled if the value is
-0. In the C<--clean> mode of the C<backup> action this option also
-specifies the number to be kept. The default value is 1, so that
-backups are made, but only one backup is kept.
-
-To setup C<autobackup> to C<-1> on the command line, use:
-
- tlmgr option -- autobackup -1
-
-The C<--> avoids having the C<-1> treated as an option. (The C<-->
-stops parsing for options at the point where it appears; this is a
-general feature across most Unix programs.)
-
-The C<sys_bin>, C<sys_man>, and C<sys_info> options are used on Unix
-systems to control the generation of links for executables, Info files
-and man pages. See the C<path> action for details.
-
-The last three options affect behavior on Windows installations. If
-C<desktop_integration> is set, then some packages will install items in
-a sub-folder of the Start menu for C<tlmgr gui>, documentation, etc. If
-C<fileassocs> is set, Windows file associations are made (see also the
-C<postaction> action). Finally, if C<multiuser> is set, then adaptions
-to the registry and the menus are done for all users on the system
-instead of only the current user. All three options are on by default.
-
-=head2 paper
-
-=over 4
-
-=item B<paper [a4|letter]>
-
-=item B<<[xdvi|pdftex|dvips|dvipdfmx|context|psutils] paper [I<papersize>|--list]>>
-
-=item B<paper --json>
-
-=back
-
-With no arguments (C<tlmgr paper>), shows the default paper size setting
-for all known programs.
-
-With one argument (e.g., C<tlmgr paper a4>), sets the default for all
-known programs to that paper size.
-
-With a program given as the first argument and no paper size specified
-(e.g., C<tlmgr dvips paper>), shows the default paper size for that
-program.
-
-With a program given as the first argument and a paper size as the last
-argument (e.g., C<tlmgr dvips paper a4>), set the default for that
-program to that paper size.
-
-With a program given as the first argument and C<--list> given as the
-last argument (e.g., C<tlmgr dvips paper --list>), shows all valid paper
-sizes for that program. The first size shown is the default.
-
-If C<--json> is specified without other options, the paper setup is
-dumped in JSON format. For the format of JSON output see
-C<tlpkg/doc/JSON-formats.txt>, format definition C<TLPAPER>.
-
-Incidentally, this syntax of having a specific program name before the
-C<paper> keyword is unusual. It is inherited from the longstanding
-C<texconfig> script, which supports other configuration settings for
-some programs, notably C<dvips>. C<tlmgr> does not support those extra
-settings.
-
-=head2 path
-
-=over 4
-
-=item B<path [--w32mode=user|admin] add>
-
-=item B<path [--w32mode=user|admin] remove>
-
-On Unix, adds or removes symlinks for executables, man pages, and info
-pages in the system directories specified by the respective options (see
-the L</option> description above). Does not change any initialization
-files, either system or personal. Furthermore, any executables added or
-removed by future updates are not taken care of automatically; this
-command must be rerun as needed.
-
-On Windows, the registry part where the binary directory is added or
-removed is determined in the following way:
-
-If the user has admin rights, and the option C<--w32mode> is not given,
-the setting I<w32_multi_user> determines the location (i.e., if it is
-on then the system path, otherwise the user path is changed).
-
-If the user has admin rights, and the option C<--w32mode> is given, this
-option determines the path to be adjusted.
-
-If the user does not have admin rights, and the option C<--w32mode>
-is not given, and the setting I<w32_multi_user> is off, the user path
-is changed, while if the setting I<w32_multi_user> is on, a warning is
-issued that the caller does not have enough privileges.
-
-If the user does not have admin rights, and the option C<--w32mode>
-is given, it must be C<user> and the user path will be adjusted. If a
-user without admin rights uses the option C<--w32mode admin> a warning
-is issued that the caller does not have enough privileges.
-
-=back
-
-=head2 pinning
-
-The C<pinning> action manages the pinning file, see L</Pinning> below.
-
-=over 4
-
-=item C<pinning show>
-
-Shows the current pinning data.
-
-=item C<pinning add> I<repo> I<pkgglob>...
-
-Pins the packages matching the I<pkgglob>(s) to the repository
-I<repo>.
-
-=item C<pinning remove> I<repo> I<pkgglob>...
-
-Any packages recorded in the pinning file matching the <pkgglob>s for
-the given repository I<repo> are removed.
-
-=item C<pinning remove I<repo> --all>
-
-Remove all pinning data for repository I<repo>.
-
-=back
-
-=head2 platform
-
-=over 4
-
-=item B<platform list|add|remove I<platform>...>
-
-=item B<platform set I<platform>>
-
-=item B<platform set auto>
-
-C<platform list> lists the TeX Live names of all the platforms
-(a.k.a. architectures), (C<i386-linux>, ...) available at the package
-repository.
-
-C<platform add> I<platform>... adds the executables for each given platform
-I<platform> to the installation from the repository.
-
-C<platform remove> I<platform>... removes the executables for each given
-platform I<platform> from the installation, but keeps the currently
-running platform in any case.
-
-C<platform set> I<platform> switches TeX Live to always use the given
-platform instead of auto detection.
-
-C<platform set auto> switches TeX Live to auto detection mode for platform.
-
-Platform detection is needed to select the proper C<xz> and
-C<wget> binaries that are shipped with TeX Live.
-
-C<arch> is a synonym for C<platform>.
-
-Options:
-
-=over 4
-
-=item B<--dry-run>
-
-Nothing is actually installed; instead, the actions to be performed are
-written to the terminal.
-
-=back
-
-=back
-
-=cut
-
-# keep the following on *ONE* line otherwise Losedows perldoc does
-# not show it!!!!
-
-=pod
-
-=head2 postaction
-
-=over 4
-
-=item B<postaction [I<option>...] install [shortcut|fileassoc|script] [I<pkg>...]>
-
-=item B<postaction [I<option>...] remove [shortcut|fileassoc|script] [I<pkg>...]>
-
-Carry out the postaction C<shortcut>, C<fileassoc>, or C<script> given
-as the second required argument in install or remove mode (which is the
-first required argument), for either the packages given on the command
-line, or for all if C<--all> is given.
-
-Options:
-
-=over 4
-
-=item B<--w32mode=[user|admin]>
-
-If the option C<--w32mode> is given the value C<user>, all actions will
-only be carried out in the user-accessible parts of the
-registry/filesystem, while the value C<admin> selects the system-wide
-parts of the registry for the file associations. If you do not have
-enough permissions, using C<--w32mode=admin> will not succeed.
-
-=item B<--fileassocmode=[1|2]>
-
-C<--fileassocmode> specifies the action for file associations. If it is
-set to 1 (the default), only new associations are added; if it is set to
-2, all associations are set to the TeX Live programs. (See also
-C<option fileassocs>.)
-
-=item B<--all>
-
-Carry out the postactions for all packages
-
-=back
-
-=back
-
-=head2 print-platform
-
-Print the TeX Live identifier for the detected platform
-(hardware/operating system) combination to standard output, and exit.
-C<--print-arch> is a synonym.
-
-=head2 print-platform-info
-
-Print the TeX Live platform identifier, TL platform long name, and
-original output from guess.
-
-=head2 remove [I<option>...] I<pkg>...
-
-Remove each I<pkg> specified. Removing a collection removes all package
-dependencies (unless C<--no-depends> is specified), but not any
-collection dependencies of that collection. However, when removing a
-package, dependencies are never removed. Options:
-
-=over 4
-
-=item B<--all>
-
-Uninstalls all of TeX Live, asking for confirmation unless C<--force> is
-also specified.
-
-=item B<--backup>
-
-=item B<--backupdir> I<directory>
-
-These options behave just as with the L<update|/update
-[I<option>...] [I<pkg>...]> action (q.v.), except they apply to making
-backups of packages before they are removed. The default is to make
-such a backup, that is, to save a copy of packages before removal.
-
-The L</restore> action explains how to restore from a backup.
-
-=item B<--no-depends>
-
-Do not remove dependent packages.
-
-=item B<--no-depends-at-all>
-
-See above under L<install|/install [I<option>...] I<pkg>...> (and beware).
-
-=item B<--force>
-
-By default, removal of a package or collection that is a dependency of
-another collection or scheme is not allowed. With this option, the
-package will be removed unconditionally. Use with care.
-
-A package that has been removed using the C<--force> option because it
-is still listed in an installed collection or scheme will not be
-updated, and will be mentioned as C<forcibly removed> in the output of
-C<tlmgr update --list>.
-
-=item B<--dry-run>
-
-Nothing is actually removed; instead, the actions to be performed are
-written to the terminal.
-
-=back
-
-This action does not automatically remove symlinks to executables from
-system directories; you need to run C<tlmgr path remove> (L</path>)
-yourself if you are using this feature and want stale symlinks removed.
-
-=head2 repository
-
-=over 4
-
-=item B<repository list>
-
-=item B<repository list I<path|url|tag>>
-
-=item B<repository add I<path> [I<tag>]>
-
-=item B<repository remove I<path|tag>>
-
-=item B<repository set I<path>[#I<tag>] [I<path>[#I<tag>] ...]>
-
-=item B<repository status>
-
-This action manages the list of repositories. See L<MULTIPLE
-REPOSITORIES> below for detailed explanations.
-
-The first form, C<repository list>, lists all configured repositories
-and the respective tags if set. If a path, url, or tag is given after
-the C<list> keyword, it is interpreted as the source from which to
-initialize a TL database and lists the contained packages. This can also
-be an otherwise-unused repository, either local or remote. If the option
-C<--with-platforms> is specified in addition, for each package the
-available platforms (if any) are also listed.
-
-The form C<repository add> adds a repository (optionally attaching a
-tag) to the list of repositories, while C<repository remove> removes a
-repository, either by full path/url, or by tag.
-
-The form C<repository set> sets the list of available repositories to
-the items given on the command line, overwriting previous settings.
-
-The form C<repository status> reports the verification status of the
-loaded repositories with the format of one repository per line
-with fields separated by a single space:
-
-=over 4
-
-=item The tag (which can be the same as the url);
-
-= the url;
-
-= iff machine-readable output is specified, the verification code (a
-number);
-
-= a textual description of the verification status, as the last field
-extending to the end of line.
-
-=back
-
-That is, in normal (not machine-readable) output, the third field
-(numeric verification status) is not present.
-
-In all cases, one of the repositories must be tagged as C<main>;
-otherwise, all operations will fail!
-
-=back
-
-=head2 restore
-
-=over 4
-
-=item B<restore [I<option>...] I<pkg> [I<rev>]>
-
-=item B<restore [I<option>...] --all>
-
-Restore a package from a previously-made backup.
-
-If C<--all> is given, try to restore the latest revision of all
-package backups found in the backup directory.
-
-Otherwise, if neither I<pkg> nor I<rev> are given, list the available
-backup revisions for all packages. With I<pkg> given but no I<rev>,
-list all available backup revisions of I<pkg>.
-
-When listing available packages, C<tlmgr> shows the revision, and in
-parenthesis the creation time if available (in format yyyy-mm-dd hh:mm).
-
-If (and only if) both I<pkg> and a valid revision number I<rev> are
-specified, try to restore the package from the specified backup.
-
-Options:
-
-=over 4
-
-=item B<--all>
-
-Try to restore the latest revision of all package backups found in the
-backup directory. Additional non-option arguments (like I<pkg>) are not
-allowed.
-
-=item B<--backupdir> I<directory>
-
-Specify the directory where the backups are to be found. If not given it
-will be taken from the configuration setting in the TLPDB.
-
-=item B<--dry-run>
-
-Nothing is actually restored; instead, the actions to be performed are
-written to the terminal.
-
-=item B<--force>
-
-Don't ask questions.
-
-=item B<--json>
-
-When listing backups, the option C<--json> turn on JSON output.
-The format is an array of JSON objects (C<name>, C<rev>, C<date>).
-For details see C<tlpkg/doc/JSON-formats.txt>, format definition: C<TLBACKUPS>.
-If both C<--json> and C<--data> are given, C<--json> takes precedence.
-
-=back
-
-=back
-
-=head2 search
-
-=over 4
-
-=item B<search [I<option>...] I<what>>
-
-=item B<search [I<option>...] --file I<what>>
-
-=item B<search [I<option>...] --all I<what>>
-
-By default, search the names, short descriptions, and long descriptions
-of all locally installed packages for the argument I<what>, interpreted
-as a (Perl) regular expression.
-
-Options:
-
-=over 4
-
-=item B<--file>
-
-List all filenames containing I<what>.
-
-=item B<--all>
-
-Search everything: package names, descriptions and filenames.
-
-=item B<--global>
-
-Search the TeX Live Database of the installation medium, instead of the
-local installation.
-
-=item B<--word>
-
-Restrict the search of package names and descriptions (but not
-filenames) to match only full words. For example, searching for
-C<table> with this option will not output packages containing the word
-C<tables> (unless they also contain the word C<table> on its own).
-
-=back
-
-=back
-
-=head2 shell
-
-Starts an interactive mode, where tlmgr prompts for commands. This can
-be used directly, or for scripting. The first line of output is
-C<protocol> I<n>, where I<n> is an unsigned number identifying the
-protocol version (currently 1).
-
-In general, tlmgr actions that can be given on the command line
-translate to commands in this shell mode. For example, you can say
-C<update --list> to see what would be updated. The TLPDB is loaded the
-first time it is needed (not at the beginning), and used for the rest of
-the session.
-
-Besides these actions, a few commands are specific to shell mode:
-
-=over 4
-
-=item protocol
-
-Print C<protocol I<n>>, the current protocol version.
-
-=item help
-
-Print pointers to this documentation.
-
-=item version
-
-Print tlmgr version information.
-
-=item quit, end, bye, byebye, EOF
-
-Exit.
-
-=item restart
-
-Restart C<tlmgr shell> with the original command line; most useful when
-developing C<tlmgr>.
-
-=item load [local|remote]
-
-Explicitly load the local or remote, respectively, TLPDB.
-
-=item save
-
-Save the local TLPDB, presumably after other operations have changed it.
-
-=item get [I<var>]
-=item set [I<var> [I<val>]]
-
-Get the value of I<var>, or set it to I<val>. Possible I<var> names:
-C<debug-translation>, C<machine-readable>, C<no-execute-actions>,
-C<require-verification>, C<verify-downloads>, C<repository>, and
-C<prompt>. All except C<repository> and C<prompt> are booleans, taking
-values 0 and 1, and behave like the corresponding command line option.
-The C<repository> variable takes a string, and sets the remote
-repository location. The C<prompt> variable takes a string, and sets the
-current default prompt.
-
-If I<var> or then I<val> is not specified, it is prompted for.
-
-=back
-
-=head2 show
-
-Synonym for L</info>.
-
-=head2 uninstall
-
-Synonym for L<remove|/remove [I<option>...] I<pkg>...>.
-
-=head2 update [I<option>...] [I<pkg>...]
-
-Updates the packages given as arguments to the latest version available
-at the installation source. Either C<--all> or at least one I<pkg> name
-must be specified. Options:
-
-=over 4
-
-=item B<--all>
-
-Update all installed packages except for C<tlmgr> itself. If updates to
-C<tlmgr> itself are present, this gives an error, unless also the option
-C<--force> or C<--self> is given. (See below.)
-
-In addition to updating the installed packages, during the update of a
-collection the local installation is (by default) synchronized to the
-status of the collection on the server, for both additions and removals.
-
-This means that if a package has been removed on the server (and thus
-has also been removed from the respective collection), C<tlmgr> will
-remove the package in the local installation. This is called
-``auto-remove'' and is announced as such when using the option
-C<--list>. This auto-removal can be suppressed using the option
-C<--no-auto-remove> (not recommended, see option description).
-
-Analogously, if a package has been added to a collection on the server
-that is also installed locally, it will be added to the local
-installation. This is called ``auto-install'' and is announced as such
-when using the option C<--list>. This auto-installation can be
-suppressed using the option C<--no-auto-install> (also not recommended).
-
-An exception to the collection dependency checks (including the
-auto-installation of packages just mentioned) are those that have been
-``forcibly removed'' by you, that is, you called C<tlmgr remove --force>
-on them. (See the C<remove> action documentation.) To reinstall any
-such forcibly removed packages use C<--reinstall-forcibly-removed>.
-
-To reiterate: automatic removals and additions are entirely determined
-by comparison of collections. Thus, if you manually install an
-individual package C<foo> which is later removed from the server,
-C<tlmgr> will not notice and will not remove it locally. (It has to be
-this way, without major rearchitecture work, because the tlpdb does not
-record the repository from which packages come from.)
-
-If you want to exclude some packages from the current update run (e.g.,
-due to a slow link), see the C<--exclude> option below.
-
-=item B<--self>
-
-Update C<tlmgr> itself (that is, the infrastructure packages) if updates
-to it are present. On Windows this includes updates to the private Perl
-interpreter shipped inside TeX Live.
-
-If this option is given together with either C<--all> or a list of
-packages, then C<tlmgr> will be updated first and, if this update
-succeeds, the new version will be restarted to complete the rest of the
-updates.
-
-In short:
-
- tlmgr update --self # update infrastructure only
- tlmgr update --self --all # update infrastructure and all packages
- tlmgr update --force --all # update all packages but *not* infrastructure
- # ... this last at your own risk, not recommended!
-
-=item B<--dry-run>
-
-Nothing is actually installed; instead, the actions to be performed are
-written to the terminal. This is a more detailed report than C<--list>.
-
-=item B<--list> [I<pkg>]
-
-Concisely list the packages which would be updated, newly installed, or
-removed, without actually changing anything.
-If C<--all> is also given, all available updates are listed.
-If C<--self> is given, but not C<--all>, only updates to the
-critical packages (tlmgr, texlive infrastructure, perl on Windows, etc.)
-are listed.
-If neither C<--all> nor C<--self> is given, and in addition no I<pkg> is
-given, then C<--all> is assumed (thus, C<tlmgr update --list> is the
-same as C<tlmgr update --list --all>).
-If neither C<--all> nor C<--self> is given, but specific package names are
-given, those packages are checked for updates.
-
-=item B<--exclude> I<pkg>
-
-Exclude I<pkg> from the update process. If this option is given more
-than once, its arguments accumulate.
-
-An argument I<pkg> excludes both the package I<pkg> itself and all
-its related platform-specific packages I<pkg.ARCH>. For example,
-
- tlmgr update --all --exclude a2ping
-
-will not update C<a2ping>, C<a2ping.i386-linux>, or
-any other C<a2ping.>I<ARCH> package.
-
-If this option specifies a package that would otherwise be a candidate
-for auto-installation, auto-removal, or reinstallation of a forcibly
-removed package, C<tlmgr> quits with an error message. Excludes are not
-supported in these circumstances.
-
-This option can also be set permanently in the tlmgr config file with
-the key C<update-exclude>.
-
-=item B<--no-auto-remove> [I<pkg>...]
-
-By default, C<tlmgr> tries to remove packages in an existing collection
-which have disappeared on the server, as described above under C<--all>.
-This option prevents such removals, either for all packages (with
-C<--all>), or for just the given I<pkg> names. This can lead to an
-inconsistent TeX installation, since packages are not infrequently
-renamed or replaced by their authors. Therefore this is not recommended.
-
-=item B<--no-auto-install> [I<pkg>...]
-
-Under normal circumstances C<tlmgr> will install packages which are new
-on the server, as described above under C<--all>. This option prevents
-any such automatic installation, either for all packages (with
-C<--all>), or the given I<pkg> names.
-
-Furthermore, after the C<tlmgr> run using this has finished, the
-packages that would have been auto-installed I<will be considered as
-forcibly removed>. So, if C<foobar> is the only new package on the
-server, then
-
- tlmgr update --all --no-auto-install
-
-is equivalent to
-
- tlmgr update --all
- tlmgr remove --force foobar
-
-Again, since packages are sometimes renamed or replaced, using this
-option is not recommended.
-
-=item B<--reinstall-forcibly-removed>
-
-Under normal circumstances C<tlmgr> will not install packages that have
-been forcibly removed by the user; that is, removed with C<remove
---force>, or whose installation was prohibited by C<--no-auto-install>
-during an earlier update.
-
-This option makes C<tlmgr> ignore the forcible removals and re-install
-all such packages. This can be used to completely synchronize an
-installation with the server's idea of what is available:
-
- tlmgr update --reinstall-forcibly-removed --all
-
-=item B<--backup>
-
-=item B<--backupdir> I<directory>
-
-These two options control the creation of backups of packages I<before>
-updating; that is, backing up packages as currently installed. If
-neither option is given, no backup will made. If C<--backupdir> is
-given and specifies a writable directory then a backup will be made in
-that location. If only C<--backup> is given, then a backup will be made
-to the directory previously set via the L</option> action (see
-below). If both are given then a backup will be made to the specified
-I<directory>.
-
-You can also set options via the L</option> action to automatically make
-backups for all packages, and/or keep only a certain number of backups.
-
-C<tlmgr> always makes a temporary backup when updating packages, in case
-of download or other failure during an update. In contrast, the purpose
-of this C<--backup> option is to save a persistent backup in case the
-actual I<content> of the update causes problems, e.g., introduces an TeX
-incompatibility.
-
-The L</restore> action explains how to restore from a backup.
-
-=item B<--no-depends>
-
-If you call for updating a package normally all depending packages
-will also be checked for updates and updated if necessary. This switch
-suppresses this behavior.
-
-=item B<--no-depends-at-all>
-
-See above under L<install|/install [I<option>...] I<pkg>...> (and beware).
-
-=item B<--force>
-
-Force update of normal packages, without updating C<tlmgr> itself
-(unless the C<--self> option is also given). Not recommended.
-
-Also, C<update --list> is still performed regardless of this option.
-
-=back
-
-If the package on the server is older than the package already installed
-(e.g., if the selected mirror is out of date), C<tlmgr> does not
-downgrade. Also, packages for uninstalled platforms are not installed.
-
-C<tlmgr> saves one copy of the main C<texlive.tlpdb> file used for an
-update with a suffix representing the repository url, as in
-C<tlpkg/texlive.tlpdb.main.>I<long-hash-string>. Thus, even when many
-mirrors are used, only one main C<tlpdb> backup is kept. For non-main
-repositories, which do not generally have (m)any mirrors, no pruning of
-backups is done.
-
-This action does not automatically add or remove new symlinks in system
-directories; you need to run C<tlmgr> L</path> yourself if you are using
-this feature and want new symlinks added.
-
-=head1 CONFIGURATION FILE FOR TLMGR
-
-C<tlmgr> reads two configuration files: one is system-wide, in
-C<TEXMFSYSCONFIG/tlmgr/config>, and the other is user-specific, in
-C<TEXMFCONFIG/tlmgr/config>. The user-specific one is the default for
-the C<conf tlmgr> action. (Run C<kpsewhich
--var-value=TEXMFSYSCONFIG> or C<... TEXMFCONFIG ...> to see the actual
-directory names.)
-
-A few defaults corresponding to command-line options can be set in these
-configuration files. In addition, the system-wide file can contain a
-directive to restrict the allowed actions.
-
-In these config files, empty lines and lines starting with # are
-ignored. All other lines must look like:
-
- key = value
-
-where the spaces are optional but the C<=> is required.
-
-The allowed keys are:
-
-=over 4
-
-=item C<auto-remove>, value 0 or 1 (default 1), same as command-line
-option.
-
-=item C<gui-expertmode>, value 0 or 1 (default 1).
-This switches between the full GUI and a simplified GUI with only the
-most common settings.
-
-=item C<gui-lang> I<llcode>, with a language code value as with the
-command-line option.
-
-=item C<no-checksums>, value 0 or 1 (default 0, see below).
-
-=item C<persistent-downloads>, value 0 or 1 (default 1), same as
-command-line option.
-
-=item C<require-verification>, value 0 or 1 (default 0), same as
-command-line option.
-
-=item C<tkfontscale>, value any float.
-Controls the scaling of fonts in the Tk based frontends.
-
-=item C<update-exclude>, value: comma-separated list of packages
-(no space allowed). Same as the command line option C<--exclude>
-for the action C<update>.
-
-=item C<verify-downloads>, value 0 or 1 (default 1), same as
-command-line option.
-
-=back
-
-The system-wide config file can contain one additional key:
-
-=over 4
-
-=item C<allowed-actions> I<action1> [,I<action>,...]
-The value is a comma-separated list of C<tlmgr> actions which are
-allowed to be executed when C<tlmgr> is invoked in system mode (that is,
-without C<--usermode>).
-
-This allows distributors to include the C<tlmgr> in their packaging, but
-allow only a restricted set of actions that do not interfere with their
-distro package manager. For native TeX Live installations, it doesn't
-make sense to set this.
-
-=back
-
-The C<no-checksums> key needs more explanation. By default, package
-checksums computed and stored on the server (in the TLPDB) are compared
-to checksums computed locally after downloading. C<no-checksums>
-disables this process.
-
-The checksum algorithm is SHA-512. Your system must have one of (looked
-for in this order) the Perl C<Digest::SHA> module, the C<openssl>
-program (L<https://openssl.org>), the C<sha512sum> program (from GNU
-Coreutils, L<https://www.gnu.org/software/coreutils>), or finally the
-C<shasum> program (just to support old Macs). If none of these are
-available, a warning is issued and C<tlmgr> proceeds without checking
-checksums. (Incidentally, other SHA implementations, such as the pure
-Perl and pure Lua modules, are much too slow to be usable in our
-context.) C<no-checksums> avoids the warning.
-
-=head1 CRYPTOGRAPHIC VERIFICATION
-
-C<tlmgr> and C<install-tl> perform cryptographic verification if
-possible. If verification is performed and successful, the programs
-report C<(verified)> after loading the TLPDB; otherwise, they report
-C<(not verified)>. But either way, by default the installation and/or
-updates proceed normally.
-
-If a program named C<gpg> is available (that is, found in C<PATH>), by
-default cryptographic signatures will be checked: we require the main
-repository be signed, but not any additional repositories. If C<gpg> is
-not available, by default signatures are not checked and no verification
-is carried out, but C<tlmgr> still proceeds normally.
-
-The behavior of the verification can be controlled by the command line
-and config file option C<verify-repo> which takes one of the following
-values: C<none>, C<main>, or C<all>. With C<none>, no verification
-whatsoever is attempted. With C<main> (the default) verification is
-required only for the main repository, and only if C<gpg> is available;
-though attempted for all, missing signatures of subsidiary repositories
-will not result in an error. Finally, in the case of C<all>, C<gpg>
-must be available and all repositories need to be signed.
-
-In all cases, if a signature is checked and fails to verify, an error
-is raised.
-
-Cryptographic verification requires checksum checking (described just
-above) to succeed, and a working GnuPG (C<gpg>) program (see below for
-search method). Then, unless cryptographic verification has been
-disabled, a signature file (C<texlive.tlpdb.*.asc>) of the checksum file
-is downloaded and the signature verified. The signature is created by
-the TeX Live Distribution GPG key 0x0D5E5D9106BAB6BC, which in turn is
-signed by Karl Berry's key 0x0716748A30D155AD and
-Norbert Preining's key 0x6CACA448860CDC13. All
-of these keys are obtainable from the standard key servers.
-
-Additional trusted keys can be added using the C<key> action.
-
-=head2 Configuration of GnuPG invocation
-
-The executable used for GnuPG is searched as follows: If the environment
-variable C<TL_GNUPG> is set, it is tested and used; otherwise C<gpg> is
-checked; finally C<gpg2> is checked.
-
-Further adaptation of the C<gpg> invocation can be made using the two
-environment variables C<TL_GNUPGHOME>, which is passed to C<gpg> as the
-value for C<--homedir>, and C<TL_GNUPGARGS>, which replaces the default
-options C<--no-secmem-warning --no-permission-warning>.
-
-=head1 USER MODE
-
-C<tlmgr> provides a restricted way, called ``user mode'', to manage
-arbitrary texmf trees in the same way as the main installation. For
-example, this allows people without write permissions on the
-installation location to update/install packages into a tree of their
-own.
-
-C<tlmgr> is switched into user mode with the command line option
-C<--usermode>. It does not switch automatically, nor is there any
-configuration file setting for it. Thus, this option has to be
-explicitly given every time user mode is to be activated.
-
-This mode of C<tlmgr> works on a user tree, by default the value of the
-C<TEXMFHOME> variable. This can be overridden with the command line
-option C<--usertree>. In the following when we speak of the user tree
-we mean either C<TEXMFHOME> or the one given on the command line.
-
-Not all actions are allowed in user mode; C<tlmgr> will warn you and not
-carry out any problematic actions. Currently not supported (and
-probably will never be) is the C<platform> action. The C<gui> action is
-currently not supported, but may be in a future release.
-
-Some C<tlmgr> actions don't need any write permissions and thus work the
-same in user mode and normal mode. Currently these are: C<check>,
-C<help>, C<list>, C<print-platform>, C<print-platform-info>, C<search>,
-C<show>, C<version>.
-
-On the other hand, most of the actions dealing with package management
-do need write permissions, and thus behave differently in user mode, as
-described below: C<install>, C<update>, C<remove>, C<option>, C<paper>,
-C<generate>, C<backup>, C<restore>, C<uninstall>, C<symlinks>.
-
-Before using C<tlmgr> in user mode, you have to set up the user tree
-with the C<init-usertree> action. This creates I<usertree>C</web2c> and
-I<usertree>C</tlpkg/tlpobj>, and a minimal
-I<usertree>C</tlpkg/texlive.tlpdb>. At that point, you can tell
-C<tlmgr> to do the (supported) actions by adding the C<--usermode>
-command line option.
-
-In user mode the file I<usertree>C</tlpkg/texlive.tlpdb> contains only
-the packages that have been installed into the user tree using C<tlmgr>,
-plus additional options from the ``virtual'' package
-C<00texlive.installation> (similar to the main installation's
-C<texlive.tlpdb>).
-
-All actions on packages in user mode can only be carried out on packages
-that are known as C<relocatable>. This excludes all packages containing
-executables and a few other core packages. Of the 2500 or so packages
-currently in TeX Live the vast majority are relocatable and can be
-installed into a user tree.
-
-Description of changes of actions in user mode:
-
-=head2 User mode install
-
-In user mode, the C<install> action checks that the package and all
-dependencies are all either relocated or already installed in the system
-installation. If this is the case, it unpacks all containers to be
-installed into the user tree (to repeat, that's either C<TEXMFHOME> or
-the value of C<--usertree>) and add the respective packages to the user
-tree's C<texlive.tlpdb> (creating it if need be).
-
-Currently installing a collection in user mode installs all dependent
-packages, but in contrast to normal mode, does I<not> install dependent
-collections. For example, in normal mode C<tlmgr install
-collection-context> would install C<collection-basic> and other
-collections, while in user mode, I<only> the packages mentioned in
-C<collection-context> are installed.
-
-If a package shipping map files is installed in user mode, a backup of
-the user's C<updmap.cfg> in C<USERTREE/web2c/> is made, and then this file
-regenerated from the list of installed packages.
-
-=head2 User mode backup, restore, remove, update
-
-In user mode, these actions check that all packages to be acted on are
-installed in the user tree before proceeding; otherwise, they behave
-just as in normal mode.
-
-=head2 User mode generate, option, paper
-
-In user mode, these actions operate only on the user tree's
-configuration files and/or C<texlive.tlpdb>.
-creates configuration files in user tree
-
-=head1 MULTIPLE REPOSITORIES
-
-The main TeX Live repository contains a vast array of packages.
-Nevertheless, additional local repositories can be useful to provide
-locally-installed resources, such as proprietary fonts and house styles.
-Also, alternative package repositories distribute packages that cannot
-or should not be included in TeX Live, for whatever reason.
-
-The simplest and most reliable method is to temporarily set the
-installation source to any repository (with the C<-repository> or
-C<option repository> command line options), and perform your operations.
-
-When you are using multiple repositories over a sustained length of
-time, however, explicitly switching between them becomes inconvenient.
-Thus, it's possible to tell C<tlmgr> about additional repositories you
-want to use. The basic command is C<tlmgr repository add>. The rest of
-this section explains further.
-
-When using multiple repositories, one of them has to be set as the main
-repository, which distributes most of the installed packages. When you
-switch from a single repository installation to a multiple repository
-installation, the previous sole repository will be set as the main
-repository.
-
-By default, even if multiple repositories are configured, packages are
-I<still> I<only> installed from the main repository. Thus, simply
-adding a second repository does not actually enable installation of
-anything from there. You also have to specify which packages should be
-taken from the new repository, by specifying so-called ``pinning''
-rules, described next.
-
-=head2 Pinning
-
-When a package C<foo> is pinned to a repository, a package C<foo> in any
-other repository, even if it has a higher revision number, will not be
-considered an installable candidate.
-
-As mentioned above, by default everything is pinned to the main
-repository. Let's now go through an example of setting up a second
-repository and enabling updates of a package from it.
-
-First, check that we have support for multiple repositories, and have
-only one enabled (as is the case by default):
-
- $ tlmgr repository list
- List of repositories (with tags if set):
- /var/www/norbert/tlnet
-
-Ok. Let's add the C<tlcontrib> repository (this is a real
-repository hosted at L<http://contrib.texlive.info>) with the tag C<tlcontrib>:
-
- $ tlmgr repository add http://contrib.texlive.info/current tlcontrib
-
-Check the repository list again:
-
- $ tlmgr repository list
- List of repositories (with tags if set):
- http://contrib.texlive.info/current (tlcontrib)
- /var/www/norbert/tlnet (main)
-
-Now we specify a pinning entry to get the package C<classico> from
-C<tlcontrib>:
-
- $ tlmgr pinning add tlcontrib classico
-
-Check that we can find C<classico>:
-
- $ tlmgr show classico
- package: classico
- ...
- shortdesc: URW Classico fonts
- ...
-
-- install C<classico>:
-
- $ tlmgr install classico
- tlmgr: package repositories:
- ...
- [1/1, ??:??/??:??] install: classico @tlcontrib [737k]
-
-In the output here you can see that the C<classico> package has been
-installed from the C<tlcontrib> repository (C<@tlcontrib>).
-
-Finally, C<tlmgr pinning> also supports removing certain or all packages
-from a given repository:
-
- $ tlmgr pinning remove tlcontrib classico # remove just classico
- $ tlmgr pinning remove tlcontrib --all # take nothing from tlcontrib
-
-A summary of C<tlmgr pinning> actions is given above.
-
-=head1 GUI FOR TLMGR
-
-The graphical user interface for C<tlmgr> requires Perl/Tk
-L<https://search.cpan.org/search?query=perl%2Ftk>. For Unix-based
-systems Perl/Tk (as well as Perl of course) has to be installed
-outside of TL. L<https://tug.org/texlive/distro.html#perltk> has a
-list of invocations for some distros. For Windows the necessary
-modules are no longer shipped within TeX Live, so you'll have to have an
-external Perl available that includes them.
-
-We are talking here about the GUI built into tlmgr itself, not about the
-other tlmgr GUIs, which are: tlshell (Tcl/Tk-based), tlcockpit
-(Java-based) and, only on Macs, TeX Live Utility. These are invoked as
-separate programs.
-
-The GUI mode of tlmgr is started with the invocation C<tlmgr gui>;
-assuming Tk is loadable, the graphical user interface will be shown.
-The main window contains a menu bar, the main display, and a status
-area where messages normally shown on the console are displayed.
-
-Within the main display there are three main parts: the C<Display
-configuration> area, the list of packages, and the action buttons.
-
-Also, at the top right the currently loaded repository is shown; this
-also acts as a button and when clicked will try to load the default
-repository. To load a different repository, see the C<tlmgr> menu item.
-
-Finally, the status area at the bottom of the window gives additional
-information about what is going on.
-
-=head2 Main display
-
-=head3 Display configuration area
-
-The first part of the main display allows you to specify (filter) which
-packages are shown. By default, all are shown. Changes here are
-reflected right away.
-
-=over 4
-
-=item Status
-
-Select whether to show all packages (the default), only those installed,
-only those I<not> installed, or only those with update available.
-
-=item Category
-
-Select which categories are shown: packages, collections, and/or
-schemes. These are briefly explained in the L</DESCRIPTION> section
-above.
-
-=item Match
-
-Select packages matching for a specific pattern. By default, this
-searches both descriptions and filenames. You can also select a subset
-for searching.
-
-=item Selection
-
-Select packages to those selected, those not selected, or all. Here,
-``selected'' means that the checkbox in the beginning of the line of a
-package is ticked.
-
-=item Display configuration buttons
-
-To the right there are three buttons: select all packages, select none
-(a.k.a. deselect all), and reset all these filters to the defaults,
-i.e., show all available.
-
-=back
-
-=head3 Package list area
-
-The second are of the main display lists all installed packages. If a
-repository is loaded, those that are available but not installed are
-also listed.
-
-Double clicking on a package line pops up an informational window with
-further details: the long description, included files, etc.
-
-Each line of the package list consists of the following items:
-
-=over 4
-
-=item a checkbox
-
-Used to select particular packages; some of the action buttons (see
-below) work only on the selected packages.
-
-=item package name
-
-The name (identifier) of the package as given in the database.
-
-=item local revision (and version)
-
-If the package is installed the TeX Live revision number for the
-installed package will be shown. If there is a catalogue version given
-in the database for this package, it will be shown in parentheses.
-However, the catalogue version, unlike the TL revision, is not
-guaranteed to reflect what is actually installed.
-
-=item remote revision (and version)
-
-If a repository has been loaded the revision of the package in the
-repository (if present) is shown. As with the local column, if a
-catalogue version is provided it will be displayed. And also as with
-the local column, the catalogue version may be stale.
-
-=item short description
-
-The short description of the package.
-
-=back
-
-=head3 Main display action buttons
-
-Below the list of packages are several buttons:
-
-=over 4
-
-=item Update all installed
-
-This calls C<tlmgr update --all>, i.e., tries to update all available
-packages. Below this button is a toggle to allow reinstallation of
-previously removed packages as part of this action.
-
-The other four buttons only work on the selected packages, i.e., those
-where the checkbox at the beginning of the package line is ticked.
-
-=item Update
-
-Update only the selected packages.
-
-=item Install
-
-Install the selected packages; acts like C<tlmgr install>, i.e., also
-installs dependencies. Thus, installing a collection installs all its
-constituent packages.
-
-=item Remove
-
-Removes the selected packages; acts like C<tlmgr remove>, i.e., it will
-also remove dependencies of collections (but not dependencies of normal
-packages).
-
-=item Backup
-
-Makes a backup of the selected packages; acts like C<tlmgr backup>. This
-action needs the option C<backupdir> set (see C<Options -> General>).
-
-=back
-
-=head2 Menu bar
-
-The following entries can be found in the menu bar:
-
-=over 4
-
-=item C<tlmgr> menu
-
-The items here load various repositories: the default as specified in
-the TeX Live database, the default network repository, the repository
-specified on the command line (if any), and an arbitrarily
-manually-entered one. Also has the so-necessary C<quit> operation.
-
-=item C<Options menu>
-
-Provides access to several groups of options: C<Paper> (configuration of
-default paper sizes), C<Platforms> (only on Unix, configuration of the
-supported/installed platforms), C<GUI Language> (select language used in
-the GUI interface), and C<General> (everything else).
-
-Several toggles are also here. The first is C<Expert options>, which is
-set by default. If you turn this off, the next time you start the GUI a
-simplified screen will be shown that display only the most important
-functionality. This setting is saved in the configuration file of
-C<tlmgr>; see L<CONFIGURATION FILE FOR TLMGR> for details.
-
-The other toggles are all off by default: for debugging output, to
-disable the automatic installation of new packages, and to disable the
-automatic removal of packages deleted from the server. Playing with the
-choices of what is or isn't installed may lead to an inconsistent TeX Live
-installation; e.g., when a package is renamed.
-
-=item C<Actions menu>
-
-Provides access to several actions: update the filename database (aka
-C<ls-R>, C<mktexlsr>, C<texhash>), rebuild all formats (C<fmtutil-sys
---all>), update the font map database (C<updmap-sys>), restore from a backup
-of a package, and use of symbolic links in system directories (not on
-Windows).
-
-The final action is to remove the entire TeX Live installation (also not
-on Windows).
-
-=item C<Help menu>
-
-Provides access to the TeX Live manual (also on the web at
-L<https://tug.org/texlive/doc.html>) and the usual ``About'' box.
-
-=back
-
-=head2 GUI options
-
-Some generic Perl/Tk options can be specified with C<tlmgr gui> to
-control the display:
-
-=over 4
-
-=item C<-background> I<color>
-
-Set background color.
-
-=item C<-font "> I<fontname> I<fontsize> C<">
-
-Set font, e.g., C<tlmgr gui -font "helvetica 18">. The argument to
-C<-font> must be quoted, i.e., passed as a single string.
-
-=item C<-foreground> I<color>
-
-Set foreground color.
-
-=item C<-geometry> I<geomspec>
-
-Set the X geometry, e.g., C<tlmgr gui -geometry 1024x512-0+0> creates
-the window of (approximately) the given size in the upper-right corner
-of the display.
-
-=item C<-xrm> I<xresource>
-
-Pass the arbitrary X resource string I<xresource>.
-
-=back
-
-A few other obscure options are recognized but not mentioned here. See
-the Perl/Tk documentation (L<https://search.cpan.org/perldoc?Tk>) for the
-complete list, and any X documentation for general information.
-
-=head1 MACHINE-READABLE OUTPUT
-
-With the C<--machine-readable> option, C<tlmgr> writes to stdout in the
-fixed line-oriented format described here, and the usual informational
-messages for human consumption are written to stderr (normally they are
-written to stdout). The idea is that a program can get all the
-information it needs by reading stdout.
-
-Currently this option only applies to the
-L<update|/update [I<option>...] [I<pkg>...]>,
-L<install|/install [I<option>...] I<pkg>...>, and
-L</option> actions.
-
-=head2 Machine-readable C<update> and C<install> output
-
-The output format is as follows:
-
- fieldname "\t" value
- ...
- "end-of-header"
- pkgname status localrev serverrev size runtime esttot
- ...
- "end-of-updates"
- other output from post actions, not in machine readable form
-
-The header section currently has two fields: C<location-url> (the
-repository source from which updates are being drawn), and
-C<total-bytes> (the total number of bytes to be downloaded).
-
-The I<localrev> and I<serverrev> fields for each package are the
-revision numbers in the local installation and server repository,
-respectively. The I<size> field is the number of bytes to be
-downloaded, i.e., the size of the compressed tar file for a network
-installation, not the unpacked size. The runtime and esttot fields
-are only present for updated and auto-install packages, and contain
-the currently passed time since start of installation/updates
-and the estimated total time.
-
-Line endings may be either LF or CRLF depending on the current platform.
-
-=over 4
-
-=item C<location-url> I<location>
-
-The I<location> may be a url (including C<file:///foo/bar/...>), or a
-directory name (C</foo/bar>). It is the package repository from which
-the new package information was drawn.
-
-=item C<total-bytes> I<count>
-
-The I<count> is simply a decimal number, the sum of the sizes of all the
-packages that need updating or installing (which are listed subsequently).
-
-=back
-
-Then comes a line with only the literal string C<end-of-header>.
-
-Each following line until a line with literal string C<end-of-updates>
-reports on one package. The fields on
-each line are separated by a tab. Here are the fields.
-
-=over 4
-
-=item I<pkgname>
-
-The TeX Live package identifier, with a possible platform suffix for
-executables. For instance, C<pdftex> and C<pdftex.i386-linux> are given
-as two separate packages, one on each line.
-
-=item I<status>
-
-The status of the package update. One character, as follows:
-
-=over 8
-
-=item C<d>
-
-The package was removed on the server.
-
-=item C<f>
-
-The package was removed in the local installation, even though a
-collection depended on it. (E.g., the user ran C<tlmgr remove
---force>.)
-
-=item C<u>
-
-Normal update is needed.
-
-=item C<r>
-
-Reversed non-update: the locally-installed version is newer than the
-version on the server.
-
-=item C<a>
-
-Automatically-determined need for installation, the package is new on
-the server and is (most probably) part of an installed collection.
-
-=item C<i>
-
-Package will be installed and isn't present in the local installation
-(action install).
-
-=item C<I>
-
-Package is already present but will be reinstalled (action install).
-
-=back
-
-=item I<localrev>
-
-The revision number of the installed package, or C<-> if it is not
-present locally.
-
-=item I<serverrev>
-
-The revision number of the package on the server, or C<-> if it is not
-present on the server.
-
-=item I<size>
-
-The size in bytes of the package on the server. The sum of all the
-package sizes is given in the C<total-bytes> header field mentioned above.
-
-=item I<runtime>
-
-The run time since start of installations or updates.
-
-=item I<esttot>
-
-The estimated total time.
-
-=back
-
-=head2 Machine-readable C<option> output
-
-The output format is as follows:
-
- key "\t" value
-
-If a value is not saved in the database the string C<(not set)> is shown.
-
-If you are developing a program that uses this output, and find that
-changes would be helpful, do not hesitate to write the mailing list.
-
-=head1 ENVIRONMENT VARIABLES
-
-C<tlmgr> uses many of the standard TeX environment variables, as
-reported by, e.g., C<tlmgr conf> (L</conf>).
-
-In addition, for ease in scripting and debugging, C<tlmgr> looks for the
-following environment variables. These are not of interest for normal
-user installations.
-
-=over 4
-
-=item C<TEXLIVE_COMPRESSOR>
-
-This variable allows selecting a different compressor program for
-backups and intermediate rollback containers. The order of selection is:
-
-=over 8
-
-=item 1.
-
-If the environment variable C<TEXLIVE_COMPRESSOR> is
-defined, use it; abort if it doesn't work. Possible values:
-C<lz4>, C<gzip>, C<xz>. The necessary options are added internally.
-
-=item 2.
-
-If lz4 is available (either from the system or TL) and working, use that.
-
-=item 3.
-
-If gzip is available (from the system) and working, use that.
-
-=item 4.
-
-If xz is available (either from the system or TL) and working, use that.
-
-=back
-
-lz4 and gzip are faster in creating tlmgr's local backups, hence they
-are preferred. The unconditional use of xz for the tlnet containers is
-unaffected, to minimize download sizes.
-
-=item C<TEXLIVE_DOWNLOADER>
-
-=item C<TL_DOWNLOAD_PROGRAM>
-
-=item C<TL_DOWNLOAD_ARGS>
-
-These options allow selecting different download programs then the ones
-automatically selected by the installer. The order of selection is:
-
-=over 8
-
-=item 1.
-
-If the environment variable C<TEXLIVE_DOWNLOADER> is defined, use it;
-abort if the specified program doesn't work. Possible values: C<lwp>,
-C<curl>, C<wget>. The necessary options are added internally.
-
-=item 2.
-
-If the environment variable C<TL_DOWNLOAD_PROGRAM> is
-defined (can be any value), use it together with
-C<TL_DOWNLOAD_ARGS>; abort if it doesn't work.
-
-=item 3.
-
-If LWP is available and working, use that (by far the most
-efficient method, as it supports persistent downloads).
-
-=item 4.
-
-If curl is available (from the system) and working, use that.
-
-=item 5.
-
-If wget is available (either from the system or TL) and working, use that.
-
-=back
-
-TL provides C<wget> binaries for platforms where necessary, so some
-download method should always be available.
-
-=item C<TEXLIVE_PREFER_OWN>
-
-By default, compression and download programs provided by the system,
-i.e., found along C<PATH> are preferred over those shipped with TeX
-Live.
-
-This can create problems with systems that are too old, and so can be
-overridden by setting the environment variable C<TEXLIVE_PREFER_OWN> to
-1. In this case, executables shipped with TL will be preferred.
-
-Extra compression/download programs not provided by TL, such as gzip,
-lwp, and curl, are still checked for on the system and used if
-available, per the above. C<TEXLIVE_PREFER_OWN> only applies when the
-program being checked for is shipped with TL, namely the lz4 and
-xz compressors and wget downloader.
-
-Exception: on Windows, the C<tar.exe> shipped with TL is always used,
-regardless of any setting.
-
-=back
-
-=head1 AUTHORS AND COPYRIGHT
-
-This script and its documentation were written for the TeX Live
-distribution (L<https://tug.org/texlive>) and both are licensed under the
-GNU General Public License Version 2 or later.
-
-$Id: tlmgr.pl 57772 2021-02-17 19:01:36Z karl $
-=cut
-
-# test HTML version: pod2html --cachedir=/tmp tlmgr.pl >/tmp/tlmgr.html
-
-### Local Variables:
-### perl-indent-level: 2
-### tab-width: 2
-### indent-tabs-mode: nil
-### End:
-# vim:set tabstop=2 expandtab: #