"Fossies" - the Fresh Open Source Software Archive

Member "install-tl-20231127/tlpkg/TeXLive/TLPDB.pm" (16 Oct 2023, 87924 Bytes) of package /linux/misc/install-tl-unx.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Perl source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 # $Id: TLPDB.pm 68562 2023-10-16 17:17:01Z karl $
    2 # TeXLive::TLPDB.pm - tlpdb plain text database files.
    3 # Copyright 2007-2023 Norbert Preining
    4 # This file is licensed under the GNU General Public License version 2
    5 # or any later version.
    6 
    7 use strict; use warnings;
    8 package TeXLive::TLPDB;
    9 
   10 my $svnrev = '$Revision: 68562 $';
   11 my $_modulerevision = ($svnrev =~ m/: ([0-9]+) /) ? $1 : "unknown";
   12 sub module_revision { return $_modulerevision; }
   13 
   14 =pod
   15 
   16 =head1 NAME
   17 
   18 C<TeXLive::TLPDB> -- TeX Live Package Database (C<texlive.tlpdb>) module
   19 
   20 =head1 SYNOPSIS
   21 
   22   use TeXLive::TLPDB;
   23 
   24   TeXLive::TLPDB->new ();
   25   TeXLive::TLPDB->new (root => "/path/to/texlive/installation/root");
   26 
   27   $tlpdb->root("/path/to/root/of/texlive/installation");
   28   $tlpdb->copy;
   29   $tlpdb->from_file($filename);
   30   $tlpdb->writeout;
   31   $tlpdb->writeout(FILEHANDLE);
   32   $tlpdb->as_json;
   33   $tlpdb->save;
   34   $tlpdb->media;
   35   $tlpdb->available_architectures();
   36   $tlpdb->add_tlpobj($tlpobj);
   37   $tlpdb->needed_by($pkg);
   38   $tlpdb->remove_tlpobj($pkg);
   39   $tlpdb->get_package("packagename");
   40   $tlpdb->list_packages ( [$tag] );
   41   $tlpdb->expand_dependencies(["-only-arch",] $totlpdb, @list);
   42   $tlpdb->expand_dependencies(["-no-collections",] $totlpdb, @list);
   43   $tlpdb->find_file("filename");
   44   $tlpdb->collections;
   45   $tlpdb->schemes;
   46   $tlpdb->updmap_cfg_lines;
   47   $tlpdb->fmtutil_cnf_lines;
   48   $tlpdb->language_dat_lines;
   49   $tlpdb->language_def_lines;
   50   $tlpdb->language_lua_lines;
   51   $tlpdb->package_revision("packagename");
   52   $tlpdb->location;
   53   $tlpdb->platform;
   54   $tlpdb->is_verified;
   55   $tlpdb->verification_status;
   56   $tlpdb->config_src_container;
   57   $tlpdb->config_doc_container;
   58   $tlpdb->config_container_format;
   59   $tlpdb->config_release;
   60   $tlpdb->config_minrelease;
   61   $tlpdb->config_revision;
   62   $tlpdb->config_frozen;
   63   $tlpdb->options;
   64   $tlpdb->option($key, [$value]);
   65   $tlpdb->reset_options();
   66   $tlpdb->add_default_options();
   67   $tlpdb->settings;
   68   $tlpdb->setting($key, [$value]);
   69   $tlpdb->setting([-clear], $key, [$value]);
   70   $tlpdb->sizes_of_packages($opt_src, $opt_doc, $ref_arch_list [, @packs ]);
   71   $tlpdb->sizes_of_packages_with_deps($opt_src, $opt_doc, $ref_arch_list [, @packs ]);
   72   $tlpdb->install_package($pkg, $dest_tlpdb);
   73   $tlpdb->remove_package($pkg, %options);
   74   $tlpdb->install_package_files($file [, $file ]);
   75 
   76   TeXLive::TLPDB->listdir([$dir]);
   77   $tlpdb->generate_listfiles([$destdir]);
   78 
   79   $tlpdb->make_virtual;
   80   $tlpdb->is_virtual;
   81   $tlpdb->virtual_add_tlpdb($tlpdb, $tag);
   82   $tlpdb->virtual_remove_tlpdb($tag);
   83   $tlpdb->virtual_get_tags();
   84   $tlpdb->virtual_get_tlpdb($tag);
   85   $tlpdb->virtual_get_package($pkg, $tag);
   86   $tlpdb->candidates($pkg);
   87   $tlpdb->virtual_candidate($pkg);
   88   $tlpdb->virtual_pinning( [ $pin_file_TLConfFile ] );
   89 
   90 =head1 DESCRIPTION
   91 
   92 =cut
   93 
   94 use TeXLive::TLConfig qw($CategoriesRegexp $DefaultCategory $InfraLocation
   95       $DatabaseName $DatabaseLocation $MetaCategoriesRegexp $Archive
   96       $DefaultCompressorFormat %Compressors $CompressorExtRegexp
   97       %TLPDBOptions %TLPDBSettings $ChecksumExtension
   98       $RelocPrefix $RelocTree);
   99 use TeXLive::TLCrypto;
  100 use TeXLive::TLPOBJ;
  101 use TeXLive::TLUtils qw(dirname mkdirhier member wndws info log debug ddebug
  102                         tlwarn basename download_file merge_into tldie
  103                         system_pipe);
  104 use TeXLive::TLWinGoo;
  105 
  106 use Cwd 'abs_path';
  107 
  108 my $_listdir;
  109 
  110 =pod
  111 
  112 =over 4
  113 
  114 =item C<< TeXLive::TLPDB->new >>
  115 
  116 =item C<< TeXLive::TLPDB->new( [root => "$path"] ) >>
  117 
  118 C<< TeXLive::TLPDB->new >> creates a new C<TLPDB> object. If the
  119 argument C<root> is given it will be initialized from the respective
  120 location starting at $path. If C<$path> begins with C<http://>, C<https://>,
  121 C<ftp://>, C<scp://>, C<ssh://> or C<I<user>@I<host>:>, the respective file
  122 is downloaded.  The C<$path> can also start with C<file:/> in which case it
  123 is treated as a file on the filesystem in the usual way.
  124 
  125 Returns an object of type C<TeXLive::TLPDB>, or undef if the root was
  126 given but no package could be read from that location.
  127 
  128 =cut
  129 
  130 sub new { 
  131   my $class = shift;
  132   my %params = @_;
  133   my $self = {
  134     root => $params{'root'},
  135     tlps => $params{'tlps'},
  136     verified => 0
  137   };
  138   my $verify = defined($params{'verify'}) ? $params{'verify'} : 0;
  139   ddebug("TLPDB new: verify=$verify\n");
  140   $_listdir = $params{'listdir'} if defined($params{'listdir'});
  141   bless $self, $class;
  142   if (defined($params{'tlpdbfile'})) {
  143     my $nr_packages_read = $self->from_file($params{'tlpdbfile'}, 
  144       'from-file' => 1, 'verify' => $verify);
  145     if ($nr_packages_read == 0) {
  146       # that is bad, we didn't read anything, so return undef.
  147       return undef;
  148     }
  149     return $self;
  150   } 
  151   if (defined($self->{'root'})) {
  152     my $nr_packages_read
  153       = $self->from_file("$self->{'root'}/$DatabaseLocation",
  154         'verify' => $verify);
  155     if ($nr_packages_read == 0) {
  156       # that is bad, we didn't read anything, so return undef.
  157       return undef;
  158     }
  159   }
  160   return $self;
  161 }
  162 
  163 
  164 sub copy {
  165   my $self = shift;
  166   my $bla = {};
  167   %$bla = %$self;
  168   bless $bla, "TeXLive::TLPDB";
  169   return $bla;
  170 }
  171 
  172 =pod
  173 
  174 =item C<< $tlpdb->add_tlpobj($tlpobj) >>
  175 
  176 The C<add_tlpobj> adds an object of the type TLPOBJ to the TLPDB.
  177 
  178 =cut
  179 
  180 sub add_tlpobj {
  181   my ($self,$tlp) = @_;
  182   if ($self->is_virtual) {
  183     tlwarn("TLPDB: cannot add tlpobj to a virtual tlpdb\n");
  184     return 0;
  185   }
  186   $self->{'tlps'}{$tlp->name} = $tlp;
  187 }
  188 
  189 =pod
  190 
  191 =item C<< $tlpdb->needed_by($pkg) >>
  192 
  193 Returns an array of package names depending on $pkg.
  194 
  195 =cut
  196 
  197 sub needed_by {
  198   my ($self,$pkg) = @_;
  199   my @ret;
  200   for my $p ($self->list_packages) {
  201     my $tlp = $self->get_package($p);
  202     DEPENDS: for my $d ($tlp->depends) {
  203       # exact match
  204       if ($d eq $pkg) {
  205         push @ret, $p;
  206         last DEPENDS;  # of the for loop on all depends
  207       }
  208       # 
  209       if ($d =~ m/^(.*)\.ARCH$/) {
  210         my $parent = $1;
  211         for my $a ($self->available_architectures) {
  212           if ($pkg eq "$parent.$a") {
  213             push @ret, $p;
  214             last DEPENDS;
  215           }
  216         }
  217       }
  218     }
  219   }
  220   return @ret;
  221 }
  222 
  223 =pod
  224 
  225 =item C<< $tlpdb->remove_tlpobj($pkg) >>
  226 
  227 Remove the package named C<$pkg> from the tlpdb. Gives a warning if the
  228 package is not present
  229 
  230 =cut
  231 
  232 sub remove_tlpobj {
  233   my ($self,$pkg) = @_;
  234   if ($self->is_virtual) {
  235     tlwarn("TLPDB: cannot remove tlpobj from a virtual tlpdb\n");
  236     return 0;
  237   }
  238   if (defined($self->{'tlps'}{$pkg})) {
  239     delete $self->{'tlps'}{$pkg};
  240   } else {
  241     tlwarn("TLPDB: package to be removed not found: $pkg\n");
  242   }
  243 }
  244 
  245 =pod
  246 
  247 =item C<< $tlpdb->from_file($filename, @args) >>
  248 
  249 The C<from_file> function initializes the C<TLPDB> if the root was not
  250 given at generation time.  See L<TLPDB::new> for more information.
  251 
  252 It returns the actual number of packages (TLPOBJs) read from
  253 C<$filename>, and zero if there are problems (and gives warnings).
  254 
  255 =cut
  256 
  257 sub from_file {
  258   my ($self, $path, @args) = @_;
  259   my %params = @args;
  260   if ($self->is_virtual) {
  261     tlwarn("TLPDB: cannot initialize a virtual tlpdb from_file\n");
  262     return 0;
  263   }
  264   if (@_ < 2) {
  265     die "$0: from_file needs filename for initialization";
  266   }
  267   my $root_from_path = dirname(dirname($path));
  268   if (defined($self->{'root'})) {
  269     if ($self->{'root'} ne $root_from_path) {
  270      if (!$params{'from-file'}) {
  271       tlwarn("TLPDB: initialization from different location than original;\n");
  272       tlwarn("TLPDB: hope you are sure!\n");
  273       tlwarn("TLPDB: root=$self->{'root'}, root_from_path=$root_from_path\n");
  274      }
  275     }
  276   } else {
  277     $self->root($root_from_path);
  278   }
  279   $self->verification_status($VS_UNKNOWN);
  280   my $retfh;
  281   my $tlpdbfile;
  282   my $is_verified = 0;
  283   # do media detection
  284   my $rootpath = $self->root;
  285   my $media;
  286   if ($rootpath =~ m,https?://|ftp://,) {
  287     $media = 'NET';
  288   } elsif ($rootpath =~ m,$TeXLive::TLUtils::SshURIRegex,) {
  289     $media = 'NET';
  290   } else {
  291     if ($rootpath =~ m,file://*(.*)$,) {
  292       $rootpath = "/$1";
  293     }
  294     if ($params{'media'}) {
  295       $media = $params{'media'};
  296     } elsif (! -d $rootpath) {
  297       # no point in going on if we don't even have a directory.
  298       tlwarn("TLPDB: not a directory, not loading: $rootpath\n");
  299       return 0;
  300     } elsif (-d "$rootpath/texmf-dist/web2c") {
  301       $media = 'local_uncompressed';
  302     } elsif (-d "$rootpath/texmf/web2c") { # older
  303       $media = 'local_uncompressed';
  304     } elsif (-d "$rootpath/web2c") {
  305       $media = 'local_uncompressed';
  306     } elsif (-d "$rootpath/$Archive") {
  307       $media = 'local_compressed';
  308     } else {
  309       # we cannot find the right type, return zero, hope people notice
  310       tlwarn("TLPDB: Cannot determine type of tlpdb from $rootpath!\n");
  311       return 0;
  312     }
  313   }
  314   $self->{'media'} = $media;
  315   #
  316   # actually load the TLPDB
  317   if ($path =~ m;^((https?|ftp)://|file:\/\/*); || $path =~ m;$TeXLive::TLUtils::SshURIRegex;) {
  318     debug("TLPDB.pm: trying to initialize from $path\n");
  319     # now $xzfh filehandle is open, the file created
  320     # TLUtils::download_file will just overwrite what is there
  321     # on windows that doesn't work, so we close the fh immediately
  322     # this creates a short loophole, but much better than before anyway
  323     my $tlpdbfh;
  324     ($tlpdbfh, $tlpdbfile) = TeXLive::TLUtils::tl_tmpfile();
  325     # same as above
  326     close($tlpdbfh);
  327     # if we have xz available we try the xz file
  328     my $xz_succeeded = 0 ;
  329     my $compressorextension = "<UNSET>";
  330     if (defined($::progs{$DefaultCompressorFormat})) {
  331       # we first try the xz compressed file
  332       my ($xzfh, $xzfile) = TeXLive::TLUtils::tl_tmpfile();
  333       close($xzfh);
  334       my $decompressor = $::progs{$DefaultCompressorFormat};
  335       $compressorextension = $Compressors{$DefaultCompressorFormat}{'extension'};
  336       my @decompressorArgs = @{$Compressors{$DefaultCompressorFormat}{'decompress_args'}};
  337       debug("trying to download $path.$compressorextension to $xzfile\n");
  338       my $ret = TeXLive::TLUtils::download_file("$path.$compressorextension", "$xzfile");
  339       # better to check both, the return value AND the existence of the file
  340       if ($ret && (-r "$xzfile")) {
  341         # ok, let the fun begin
  342         debug("decompressing $xzfile to $tlpdbfile\n");
  343         # xz *hopefully* returns 0 on success and anything else on failure
  344         # we don't have to negate since not zero means error in the shell
  345         # and thus in perl true
  346         if (!system_pipe($decompressor, $xzfile, $tlpdbfile, 1, @decompressorArgs)) {
  347           debug("$decompressor $xzfile failed, trying plain file\n");
  348           unlink($xzfile); # the above command only removes in case of success
  349         } else {
  350           $xz_succeeded = 1;
  351           debug("found the uncompressed $DefaultCompressorFormat file\n");
  352         }
  353       } 
  354     } else {
  355       debug("no $DefaultCompressorFormat defined ...\n");
  356     }
  357     if (!$xz_succeeded) {
  358       debug("TLPDB: downloading $path.$compressorextension didn't succeed, try $path\n");
  359       my $ret = TeXLive::TLUtils::download_file($path, $tlpdbfile);
  360       # better to check both, the return value AND the existence of the file
  361       if ($ret && (-r $tlpdbfile)) {
  362         # do nothing
  363       } else {
  364         unlink($tlpdbfile);
  365         tldie(  "$0: TLPDB::from_file could not initialize from: $path\n"
  366               . "$0: Maybe the repository setting should be changed.\n"
  367               . "$0: More info: https://tug.org/texlive/acquire.html\n");
  368       }
  369     }
  370     # if we are still here, then either the xz version was downloaded
  371     # and unpacked, or the non-xz version was downloaded, and in both
  372     # cases the result, i.e., the unpackaged tlpdb, is in $tlpdbfile
  373     #
  374     # before we open and proceed, verify the downloaded file
  375     if ($params{'verify'} && $media ne 'local_uncompressed') {
  376       my ($verified, $status) = TeXLive::TLCrypto::verify_checksum_and_check_return($tlpdbfile, $path);
  377       $is_verified = $verified;
  378       $self->verification_status($status);
  379     }
  380     open($retfh, "<$tlpdbfile") || die "$0: open($tlpdbfile) failed: $!";
  381   } else {
  382     if ($params{'verify'} && $media ne 'local_uncompressed') {
  383       my ($verified, $status) = TeXLive::TLCrypto::verify_checksum_and_check_return($path, $path);
  384       $is_verified = $verified;
  385       $self->verification_status($status);
  386     }
  387     open(TMP, "<$path") || die "$0: open($path) failed: $!";
  388     $retfh = \*TMP;
  389   }
  390   my $found = 0;
  391   my $ret = 0;
  392   do {
  393     my $tlp = TeXLive::TLPOBJ->new;
  394     $ret = $tlp->from_fh($retfh,1);
  395     if ($ret) {
  396       $self->add_tlpobj($tlp);
  397       $found++;
  398     }
  399   } until (!$ret);
  400   if (! $found) {
  401     debug("$0: Could not load packages from\n");
  402     debug("  $path\n");
  403   }
  404 
  405   $self->{'verified'} = $is_verified;
  406 
  407   close($retfh);
  408   return($found);
  409 }
  410 
  411 =pod
  412 
  413 =item C<< $tlpdb->writeout >>
  414 
  415 =item C<< $tlpdb->writeout(FILEHANDLE) >>
  416 
  417 The C<writeout> function writes the database to C<STDOUT>, or 
  418 the file handle given as argument.
  419 
  420 =cut
  421 
  422 sub writeout {
  423   my $self = shift;
  424   if ($self->is_virtual) {
  425     tlwarn("TLPDB: cannot writeout a virtual tlpdb\n");
  426     return 0;
  427   }
  428   my $fd = (@_ ? $_[0] : *STDOUT);
  429   foreach (sort keys %{$self->{'tlps'}}) {
  430     TeXLive::TLUtils::dddebug("writeout: tlpname=$_  ",
  431                               $self->{'tlps'}{$_}->name, "\n");
  432     $self->{'tlps'}{$_}->writeout($fd);
  433     print $fd "\n";
  434   }
  435 }
  436 
  437 =pod
  438 
  439 =item C<< $tlpdb->as_json >>
  440 
  441 The C<as_json> function returns a JSON UTF8 encoded representation of the
  442 database, that is a JSON array of packages. If the database is virtual,
  443 a JSON array where each element is a hash with two keys, C<tag> giving
  444 the tag of the sub-database, and C<tlpdb> giving the JSON of the database.
  445 
  446 =cut
  447 
  448 sub as_json {
  449   my $self = shift;
  450   my $ret = "{";
  451   if ($self->is_virtual) {
  452     my $firsttlpdb = 1;
  453     for my $k (keys %{$self->{'tlpdbs'}}) {
  454       $ret .= ",\n" if (!$firsttlpdb);
  455       $ret .= "\"$k\":";
  456       $firsttlpdb = 0;
  457       $ret .= $self->{'tlpdbs'}{$k}->_as_json;
  458     }
  459   } else {
  460     $ret .= "\"main\":";
  461     $ret .= $self->_as_json;
  462   }
  463   $ret .= "}\n";
  464   return($ret);
  465 }
  466 
  467 sub options_as_json {
  468   my $self = shift;
  469   die("calling _as_json on virtual is not supported!") if ($self->is_virtual);
  470   my $opts = $self->options;
  471   my @opts;
  472   for my $k (keys %TLPDBOptions) {
  473     my %foo;
  474     $foo{'name'} = $k;
  475     $foo{'tlmgrname'} = $TLPDBOptions{$k}[2];
  476     $foo{'description'} = $TLPDBOptions{$k}[3];
  477     $foo{'format'} = $TLPDBOptions{$k}[0];
  478     $foo{'default'} = "$TLPDBOptions{$k}[1]";
  479     # if ($TLPDBOptions{$k}[0] =~ m/^n/) {
  480     #   if (exists($opts->{$k})) {
  481     #     $foo{'value'} = $opts->{$k};
  482     #     $foo{'value'} += 0;
  483     #   }
  484     #   $foo{'default'} += 0;
  485     # } elsif ($TLPDBOptions{$k}[0] eq "b") {
  486     #   if (exists($opts->{$k})) {
  487     #     $foo{'value'} = ($opts->{$k} ? TeXLive::TLUtils::True() : TeXLive::TLUtils::False());
  488     #   }
  489     #   $foo{'default'} = ($foo{'default'} ? TeXLive::TLUtils::True() : TeXLive::TLUtils::False());
  490     # } elsif ($k eq "location") {
  491     #   my %def;
  492     #   $def{'main'} = $TLPDBOptions{$k}[1];
  493     #   $foo{'default'} = \%def;
  494     #   if (exists($opts->{$k})) {
  495     #     my %repos = TeXLive::TLUtils::repository_to_array($opts->{$k});
  496     #     $foo{'value'} = \%repos;
  497     #   }
  498     # } elsif ($TLPDBOptions{$k}[0] eq "p") {
  499     #   # strings/path
  500     #   if (exists($opts->{$k})) {
  501     #     $foo{'value'} = $opts->{$k};
  502     #   }
  503     # } else {
  504     
  505     # TREAT ALL VALUES AS STRINGS, otherwise not parsable JSON
  506       # treat as strings
  507       if (exists($opts->{$k})) {
  508         $foo{'value'} = $opts->{$k};
  509       }
  510     #  }
  511     push @opts, \%foo;
  512   }
  513   return(TeXLive::TLUtils::encode_json(\@opts));
  514 }
  515 
  516 sub settings_as_json {
  517   my $self = shift;
  518   die("calling _as_json on virtual is not supported!") if ($self->is_virtual);
  519   my $sets = $self->settings;
  520   my @json;
  521   for my $k (keys %TLPDBSettings) {
  522     my %foo;
  523     $foo{'name'} = $k;
  524     $foo{'type'} = $TLPDBSettings{$k}[0];
  525     $foo{'description'} = $TLPDBSettings{$k}[1];
  526     # if ($TLPDBSettings{$k}[0] eq "b") {
  527     #   if (exists($sets->{$k})) {
  528     #     $foo{'value'} = ($sets->{$k} ? TeXLive::TLUtils::True() : TeXLive::TLUtils::False());
  529     #   }
  530     # } elsif ($TLPDBSettings{$k} eq "available_architectures") {
  531     #   if (exists($sets->{$k})) {
  532     #     my @lof = $self->available_architectures;
  533     #     $foo{'value'} = \@lof;
  534     #   }
  535     # } else {
  536       if (exists($sets->{$k})) {
  537         $foo{'value'} = "$sets->{$k}";
  538       }
  539     # }
  540     push @json, \%foo;
  541   }
  542   return(TeXLive::TLUtils::encode_json(\@json));
  543 }
  544 
  545 sub configs_as_json {
  546   my $self = shift;
  547   die("calling _as_json on virtual is not supported!") if ($self->is_virtual);
  548   my %cfgs;
  549   $cfgs{'container_split_src_files'} = ($self->config_src_container ? TeXLive::TLUtils::True() : TeXLive::TLUtils::False());
  550   $cfgs{'container_split_doc_files'} = ($self->config_doc_container ? TeXLive::TLUtils::True() : TeXLive::TLUtils::False());
  551   $cfgs{'container_format'} = $self->config_container_format;
  552   $cfgs{'release'} = $self->config_release;
  553   $cfgs{'minrelease'} = $self->config_minrelease;
  554   return(TeXLive::TLUtils::encode_json(\%cfgs));
  555 }
  556 
  557 sub _as_json {
  558   my $self = shift;
  559   die("calling _as_json on virtual is not supported!") if ($self->is_virtual);
  560   my $ret = "{";
  561   $ret .= '"options":';
  562   $ret .= $self->options_as_json();
  563   $ret .= ',"settings":';
  564   $ret .= $self->settings_as_json();
  565   $ret .= ',"configs":';
  566   $ret .= $self->configs_as_json();
  567   $ret .= ',"tlpkgs": [';
  568   my $first = 1;
  569   foreach (keys %{$self->{'tlps'}}) {
  570     $ret .= ",\n" if (!$first);
  571     $first = 0;
  572     $ret .= $self->{'tlps'}{$_}->as_json;
  573   }
  574   $ret .= "]}";
  575   return($ret);
  576 }
  577 
  578 =pod
  579 
  580 =item C<< $tlpdb->save >>
  581 
  582 The C<save> functions saves the C<TLPDB> to the file which has been set
  583 as location. If the location is undefined, die.
  584 
  585 =cut
  586 
  587 sub save {
  588   my $self = shift;
  589   if ($self->is_virtual) {
  590     tlwarn("TLPDB: cannot save a virtual tlpdb\n");
  591     return 0;
  592   }
  593   my $path = $self->location;
  594   mkdirhier(dirname($path));
  595   my $tmppath = "$path.tmp";
  596   open(FOO, ">$tmppath") || die "$0: open(>$tmppath) failed: $!";
  597   $self->writeout(\*FOO);
  598   close(FOO);
  599   # on Windows the renaming sometimes fails, try to copy and unlink the
  600   # .tmp file. This we do for all archs, cannot hurt.
  601   # if we managed that one, we move it over
  602   TeXLive::TLUtils::copy ("-f", $tmppath, $path);
  603   unlink ($tmppath) or tlwarn ("TLPDB: cannot unlink $tmppath: $!\n");
  604 }
  605 
  606 =pod
  607 
  608 =item C<< $tlpdb->media >>
  609 
  610 Returns the media code the respective installation resides on.
  611 
  612 =cut
  613 
  614 sub media { 
  615   my $self = shift ; 
  616   if ($self->is_virtual) {
  617     return "virtual";
  618   }
  619   return $self->{'media'};
  620 }
  621 
  622 =pod
  623 
  624 =item C<< $tlpdb->available_architectures >>
  625 
  626 The C<available_architectures> functions returns the list of available 
  627 architectures as set in the options section 
  628 (i.e., using setting("available_architectures"))
  629 
  630 =cut
  631 
  632 sub available_architectures {
  633   my $self = shift;
  634   my @archs;
  635   if ($self->is_virtual) {
  636     for my $k (keys %{$self->{'tlpdbs'}}) {
  637       TeXLive::TLUtils::push_uniq \@archs, $self->{'tlpdbs'}{$k}->available_architectures;
  638     }
  639     return sort @archs;
  640   } else {
  641     return $self->_available_architectures;
  642   }
  643 }
  644 
  645 sub _available_architectures {
  646   my $self = shift;
  647   my @archs = $self->setting("available_architectures");
  648   if (! @archs) {
  649     # fall back to the old method checking tex\.*
  650     my @packs = $self->list_packages;
  651     map { s/^tex\.// ; push @archs, $_ ; } grep(/^tex\.(.*)$/, @packs);
  652   }
  653   return @archs;
  654 }
  655 
  656 =pod
  657 
  658 =item C<< $tlpdb->get_package("pkgname") >> 
  659 
  660 The C<get_package> function returns a reference to the C<TLPOBJ> object
  661 corresponding to the I<pkgname>, or undef.
  662 
  663 =cut
  664 
  665 sub get_package {
  666   my ($self,$pkg,$tag) = @_;
  667   if ($self->is_virtual) {
  668     if (defined($tag)) {
  669       if (defined($self->{'packages'}{$pkg}{'tags'}{$tag})) {
  670         return $self->{'packages'}{$pkg}{'tags'}{$tag}{'tlp'};
  671       } else {
  672         debug("TLPDB::get_package: package $pkg not found in repository $tag\n");
  673         return;
  674       }
  675     } else {
  676       $tag = $self->{'packages'}{$pkg}{'target'};
  677       if (defined($tag)) {
  678         return $self->{'packages'}{$pkg}{'tags'}{$tag}{'tlp'};
  679       } else {
  680         return;
  681       }
  682     }
  683   } else {
  684     return $self->_get_package($pkg);
  685   }
  686 }
  687 
  688 sub _get_package {
  689   my ($self,$pkg) = @_;
  690   return undef if (!$pkg);
  691   if (defined($self->{'tlps'}{$pkg})) {
  692   my $ret = $self->{'tlps'}{$pkg};
  693     return $self->{'tlps'}{$pkg};
  694   } else {
  695     return undef;
  696   }
  697 }
  698 
  699 =pod
  700 
  701 =item C<< $tlpdb->media_of_package($pkg [, $tag]) >>
  702 
  703 returns the media type of the package. In the virtual case a tag can
  704 be given and the media of that repository is used, otherwise the
  705 media of the virtual candidate is given.
  706 
  707 =cut
  708 
  709 sub media_of_package {
  710   my ($self, $pkg, $tag) = @_;
  711   if ($self->is_virtual) {
  712     if (defined($tag)) {
  713       if (defined($self->{'tlpdbs'}{$tag})) {
  714         return $self->{'tlpdbs'}{$tag}->media;
  715       } else {
  716         tlwarn("TLPDB::media_of_package: tag not known: $tag\n");
  717         return;
  718       }
  719     } else {
  720       my (undef,undef,undef,$maxtlpdb) = $self->virtual_candidate($pkg);
  721       return $maxtlpdb->media;
  722     }
  723   } else {
  724     return $self->media;
  725   }
  726 }
  727 
  728 =pod
  729 
  730 =item C<< $tlpdb->list_packages >>
  731 
  732 The C<list_packages> function returns the list of all included packages.
  733 
  734 By default, for virtual tlpdbs only packages that are installable
  735 are listed. That means, packages that are only in subsidiary repositories
  736 but are not specifically pinned to it cannot be installed and are thus
  737 not listed. Adding "-all" argument lists also these packages.
  738 
  739 Finally, if there is another argument, the tlpdb must be virtual,
  740 and the argument must specify a tag/name of a sub-tlpdb. In this
  741 case all packages (without exceptions) from this repository are returned.
  742 
  743 =cut
  744 
  745 sub list_packages {
  746   my $self = shift;
  747   my $arg = shift;
  748   my $tag;
  749   my $showall = 0;
  750   if (defined($arg)) {
  751     if ($arg eq "-all") {
  752       $showall = 1;
  753     } else {
  754       $tag = $arg;
  755     }
  756   }
  757   if ($self->is_virtual) {
  758     if ($showall) {
  759       return (sort keys %{$self->{'packages'}});
  760     }
  761     if ($tag) {
  762       if (defined($self->{'tlpdbs'}{$tag})) {
  763         return $self->{'tlpdbs'}{$tag}->list_packages;
  764       } else {
  765         tlwarn("TLPDB::list_packages: tag not defined: $tag\n");
  766         return 0;
  767       }
  768     }
  769     # we have to be careful here: If a package
  770     # is only present in a subsidiary repository
  771     # and the package is *not* explicitly
  772     # pinned to it, it will not be installable.
  773     # This is what we want. But in this case
  774     # we don't want it to be listed by default.
  775     #
  776     my @pps;
  777     for my $p (keys %{$self->{'packages'}}) {
  778       push @pps, $p if (defined($self->{'packages'}{$p}{'target'}));
  779     }
  780     return (sort @pps);
  781   } else {
  782     return $self->_list_packages;
  783   }
  784 }
  785 
  786 sub _list_packages {
  787   my $self = shift;
  788   return (sort keys %{$self->{'tlps'}});
  789 }
  790 
  791 =pod
  792 
  793 =item C<< $tlpdb->expand_dependencies(["control",] $tlpdb, ($pkgs)) >>
  794 
  795 If the first argument is the string C<"-only-arch">, expands only
  796 dependencies of the form C<.>I<ARCH>.
  797 
  798 If the first argument is C<"-no-collections">, then dependencies between
  799 "same-level" packages (scheme onto scheme, collection onto collection,
  800 package onto package) are ignored.
  801 
  802 C<-only-arch> and C<-no-collections> cannot be specified together; has
  803 to be one or the other.
  804 
  805 The next (or first) argument is the target TLPDB, then a list of
  806 packages.
  807 
  808 In the virtual case, if a package name is tagged with C<@repository-tag>
  809 then all the dependencies will still be expanded between all included
  810 databases.  Only in case of C<.>I<ARCH> dependencies the repository-tag
  811 is sticky.
  812 
  813 We return a list of package names, the closure of the package list with
  814 respect to the depends operator. (Sorry, that was for mathematicians.)
  815 
  816 =cut
  817 
  818 sub expand_dependencies {
  819   my $self = shift;
  820   my $only_arch = 0;
  821   my $no_collections = 0;
  822   my $first = shift;
  823   my $totlpdb;
  824   if ($first eq "-only-arch") {
  825     $only_arch = 1;
  826     $totlpdb = shift;
  827   } elsif ($first eq "-no-collections") {
  828     $no_collections = 1;
  829     $totlpdb = shift;
  830   } else {
  831     $totlpdb = $first;
  832   }
  833   my %install = ();
  834   my @archs = $totlpdb->available_architectures;
  835   for my $p (@_) {
  836     next if ($p =~ m/^\s*$/);
  837     my ($pp, $aa) = split('@', $p);
  838     $install{$pp} = (defined($aa) ? $aa : 0);;
  839   }
  840   my $changed = 1;
  841   while ($changed) {
  842     $changed = 0;
  843     my @pre_select = keys %install;
  844     ddebug("pre_select = @pre_select\n");
  845     for my $p (@pre_select) {
  846       next if ($p =~ m/^00texlive/);
  847       my $pkg = $self->get_package($p, ($install{$p}?$install{$p}:undef));
  848       if (!defined($pkg)) {
  849         ddebug("W: $p is mentioned somewhere but not available, disabling\n");
  850         $install{$p} = 0;
  851         next;
  852       }
  853       for my $p_dep ($pkg->depends) {
  854         ddebug("checking $p_dep in $p\n");
  855         my $tlpdd = $self->get_package($p_dep);
  856         if (defined($tlpdd)) {
  857           # before we ignored all deps of schemes and colls if -no-collections
  858           # was given, but this prohibited auto-install of new collections
  859           # even if the scheme is updated.
  860           # Now we suppress only "same-level dependencies", so scheme -> scheme
  861           # and collections -> collections and package -> package
  862           # hoping that this works out better
  863           # if ($tlpdd->category =~ m/$MetaCategoriesRegexp/) {
  864           if ($tlpdd->category eq $pkg->category) {
  865             # we ignore same-level dependencies if "-no-collections" is given
  866             ddebug("expand_deps: skipping $p_dep in $p due to -no-collections\n");
  867             next if $no_collections;
  868           }
  869         }
  870         if ($p_dep =~ m/^(.*)\.ARCH$/) {
  871           my $foo = "$1";
  872           foreach $a (@archs) {
  873             # install .ARCH packages from the same sub repository as the
  874             # main packages
  875             $install{"$foo.$a"} = $install{$foo}
  876               if defined($self->get_package("$foo.$a"));
  877           }
  878         } elsif ($p_dep =~ m/^(.*)\.windows$/) {
  879           # a windows package should *only* be installed if we are installing
  880           # the windows arch
  881           if (grep(/^windows$/,@archs)) {
  882             $install{$p_dep} = 0;
  883           }
  884         } else {
  885           $install{$p_dep} = 0 unless $only_arch;
  886         }
  887       }
  888     }
  889 
  890     # check for newly selected packages
  891     my @post_select = keys %install;
  892     ddebug("post_select = @post_select\n");
  893     if ($#pre_select != $#post_select) {
  894       $changed = 1;
  895     }
  896   }
  897   # create return list
  898   return map { $install{$_} eq "0"?$_:"$_\@" . $install{$_} } keys %install;
  899   #return(keys %install);
  900 }
  901 
  902 =pod
  903 
  904 =item C<< $tlpdb->find_file("filename") >>
  905 
  906 The C<find_file> returns a list of packages:filename
  907 containing a file named C<filename>.
  908 
  909 =cut
  910 
  911 # TODO adapt for searching in *all* tags ???
  912 sub find_file {
  913   my ($self,$fn) = @_;
  914   my @ret = ();
  915   for my $pkg ($self->list_packages) {
  916     for my $f ($self->get_package($pkg)->contains_file($fn)) {
  917       push (@ret, "$pkg:$f");
  918     }
  919   }
  920   return @ret;
  921 }
  922 
  923 =pod
  924 
  925 =item C<< $tlpdb->collections >>
  926 
  927 The C<collections> function returns a list of all collection names.
  928 
  929 =cut
  930 
  931 sub collections {
  932   my $self = shift;
  933   my @ret;
  934   foreach my $p ($self->list_packages) {
  935     if ($self->get_package($p)->category eq "Collection") {
  936       push @ret, $p;
  937     }
  938   }
  939   return @ret;
  940 }
  941 
  942 =pod
  943 
  944 =item C<< $tlpdb->schemes >>
  945 
  946 The C<schemes> function returns a list of all scheme names.
  947 
  948 =cut
  949 
  950 sub schemes {
  951   my $self = shift;
  952   my @ret;
  953   foreach my $p ($self->list_packages) {
  954     if ($self->get_package($p)->category eq "Scheme") {
  955       push @ret, $p;
  956     }
  957   }
  958   return @ret;
  959 }
  960 
  961 
  962 
  963 =pod
  964 
  965 =item C<< $tlpdb->package_revision("packagename") >>
  966 
  967 The C<package_revision> function returns the revision number of the
  968 package named in the first argument.
  969 
  970 =cut
  971 
  972 sub package_revision {
  973   my ($self,$pkg) = @_;
  974   my $tlp = $self->get_package($pkg);
  975   if (defined($tlp)) {
  976     return $tlp->revision;
  977   } else {
  978     return;
  979   }
  980 }
  981 
  982 =pod
  983 
  984 =item C<< $tlpdb->generate_packagelist >>
  985 
  986 The C<generate_packagelist> prints TeX Live package names in the object
  987 database, together with their revisions, to the file handle given in the
  988 first (optional) argument, or C<STDOUT> by default.  It also outputs all
  989 available architectures as packages with revision number -1.
  990 
  991 =cut
  992 
  993 sub generate_packagelist {
  994   my $self = shift;
  995   my $fd = (@_ ? $_[0] : *STDOUT);
  996   foreach (sort $self->list_packages) {
  997     print $fd $self->get_package($_)->name, " ",
  998               $self->get_package($_)->revision, "\n";
  999   }
 1000   foreach ($self->available_architectures) {
 1001     print $fd "$_ -1\n";
 1002   }
 1003 }
 1004 
 1005 =pod
 1006 
 1007 =item C<< $tlpdb->generate_listfiles >>
 1008 
 1009 =item C<< $tlpdb->generate_listfiles($destdir) >>
 1010 
 1011 The C<generate_listfiles> generates the list files for the old 
 1012 installers. This function will probably go away.
 1013 
 1014 =cut
 1015 
 1016 sub generate_listfiles {
 1017   my ($self,$destdir) = @_;
 1018   if (not(defined($destdir))) {
 1019     $destdir = TeXLive::TLPDB->listdir;
 1020   }
 1021   foreach (sort $self->list_package) {
 1022     my $tlp = $self->get_package($_);
 1023     $self->_generate_listfile($tlp, $destdir);
 1024   }
 1025 }
 1026 
 1027 sub _generate_listfile {
 1028   my ($self,$tlp,$destdir) = @_;
 1029   my $listname = $tlp->name;
 1030   my @files = $tlp->all_files;
 1031   @files = TeXLive::TLUtils::sort_uniq(@files);
 1032   &mkpath("$destdir") if (! -d "$destdir");
 1033   my (@lop, @lot);
 1034   foreach my $d ($tlp->depends) {
 1035     my $subtlp = $self->get_package($d);
 1036     if (defined($subtlp)) {
 1037       if ($subtlp->is_meta_package) {
 1038         push @lot, $d;
 1039       } else {
 1040         push @lop, $d;
 1041       }
 1042     } else {
 1043       # pseudo-dependencies on $Package.ARCH can be ignored
 1044       if ($d !~ m/\.ARCH$/) {
 1045         tlwarn("TLPDB: package $tlp->name depends on $d, but this does not exist\n");
 1046       }
 1047     }
 1048   }
 1049   open(TMP, ">$destdir/$listname")
 1050   || die "$0: open(>$destdir/$listname) failed: $!";
 1051 
 1052   # title and size information for collections and schemes in the
 1053   # first two lines, marked with *
 1054     if ($tlp->category eq "Collection") {
 1055     print TMP "*Title: ", $tlp->shortdesc, "\n";
 1056     # collections references Packages, we have to collect the sizes of
 1057     # all the Package-tlps included
 1058     # What is unclear for me is HOW the size is computed for bin-*
 1059     # packages. The collection-basic contains quite a lot of
 1060     # bin-files, but the sizes for the different archs differ.
 1061     # I guess we have to take the maximum?
 1062     my $s = 0;
 1063     foreach my $p (@lop) {
 1064       my $subtlp = $self->get_package($p);
 1065       if (!defined($subtlp)) {
 1066         tlwarn("TLPDB: $listname references $p, but this is not in tlpdb\n");
 1067       }
 1068       $s += $subtlp->total_size;
 1069     }
 1070     # in case the collection itself ships files ...
 1071     $s += $tlp->runsize + $tlp->srcsize + $tlp->docsize;
 1072     print TMP "*Size: $s\n";
 1073   } elsif ($tlp->category eq "Scheme") {
 1074     print TMP "*Title: ", $tlp->shortdesc, "\n";
 1075     my $s = 0;
 1076     # schemes size includes ONLY those packages which are directly
 1077     # included and directly included files, not the size of the
 1078     # included collections. But if a package is included in one of
 1079     # the called for collections AND listed directly, we don't want
 1080     # to count its size two times
 1081     my (@inccol,@incpkg,@collpkg);
 1082     # first we add all the packages tlps that are directly included
 1083     @incpkg = @lop;
 1084     # now we select all collections, and for all collections we
 1085     # again select all non-meta-packages
 1086     foreach my $c (@lot) {
 1087       my $coll = $self->get_package($c);
 1088       foreach my $d ($coll->depends) {
 1089         my $subtlp = $self->get_package($d);
 1090         if (defined($subtlp)) {
 1091           if (!($subtlp->is_meta_package)) {
 1092             TeXLive::TLUtils::push_uniq(\@collpkg,$d);
 1093           }
 1094         } else {
 1095           tlwarn("TLPDB: collection $coll->name depends on $d, but this does not exist\n");
 1096         }
 1097       }
 1098     }
 1099     # finally go through all packages and add the ->total_size
 1100     foreach my $p (@incpkg) {
 1101       if (!TeXLive::TLUtils::member($p,@collpkg)) {
 1102         $s += $self->get_package($p)->total_size;
 1103       }
 1104     } 
 1105     $s += $tlp->runsize + $tlp->srcsize + $tlp->docsize;
 1106     print TMP "*Size: $s\n";
 1107   }
 1108   # dependencies and inclusion of packages
 1109   foreach my $t (@lot) {
 1110     # strange, schemes mark included collections via -, while collections
 1111     # themselves mark deps on other collections with +. collections are
 1112     # never referenced in Packages.
 1113     if ($listname =~ m/^scheme/) {
 1114       print TMP "-";
 1115     } else {
 1116       print TMP "+";
 1117     }
 1118     print TMP "$t\n";
 1119   }
 1120   foreach my $t (@lop) { print TMP "+$t\n"; }
 1121   # included files
 1122   foreach my $f (@files) { print TMP "$f\n"; }
 1123   # also print the listfile itself
 1124   print TMP "$destdir/$listname\n";
 1125   # execute statements
 1126   foreach my $e ($tlp->executes) {
 1127     print TMP "!$e\n";
 1128   }
 1129   # finish
 1130   close(TMP);
 1131 }
 1132 
 1133 =pod
 1134 
 1135 =item C<< $tlpdb->root([ "/path/to/installation" ]) >>
 1136 
 1137 The function C<root> allows to read and set the root of the
 1138 installation. 
 1139 
 1140 =cut
 1141 
 1142 sub root {
 1143   my $self = shift;
 1144   if ($self->is_virtual) {
 1145     tlwarn("TLPDB: cannot set/edit root of a virtual tlpdb\n");
 1146     return 0;
 1147   }
 1148   if (@_) { $self->{'root'} = shift }
 1149   return $self->{'root'};
 1150 }
 1151 
 1152 =pod
 1153 
 1154 =item C<< $tlpdb->location >>
 1155 
 1156 Return the location of the actual C<texlive.tlpdb> file used. This is a
 1157 read-only function; you cannot change the root of the TLPDB using this
 1158 function.
 1159 
 1160 See C<00texlive.installation.tlpsrc> for a description of the
 1161 special value C<__MASTER>.
 1162 
 1163 =cut
 1164 
 1165 sub location {
 1166   my $self = shift;
 1167   if ($self->is_virtual) {
 1168     tlwarn("TLPDB: cannot get location of a virtual tlpdb\n");
 1169     return 0;
 1170   }
 1171   return "$self->{'root'}/$DatabaseLocation";
 1172 }
 1173 
 1174 =pod
 1175 
 1176 =item C<< $tlpdb->platform >>
 1177 
 1178 returns the platform of this installation.
 1179 
 1180 =cut
 1181 
 1182 # deduce the platform of the referenced media as follows:
 1183 # - if the $tlpdb->setting("platform") is there it overrides the detected
 1184 #   setting
 1185 # - if it is not there call TLUtils::platform()
 1186 sub platform {
 1187   # try to deduce the platform
 1188   my $self = shift;
 1189   my $ret = $self->setting("platform");
 1190   #print STDERR "platform $ret set in tlpdb\n" if defined $ret;
 1191   return $ret if defined $ret;
 1192   # the platform setting wasn't found in the tlpdb, try TLUtils::platform
 1193   #print STDERR "Setting platform to ",TeXLive::TLUtils::platform(), "\n";
 1194   return TeXLive::TLUtils::platform();
 1195 }
 1196 
 1197 =pod
 1198 
 1199 =item C<< $tlpdb->is_verified >>
 1200 
 1201 Returns 0/1 depending on whether the tlpdb was verified by checking
 1202 the cryptographic signature.
 1203 
 1204 =cut
 1205 
 1206 sub is_verified {
 1207   my $self = shift;
 1208   if ($self->is_virtual) {
 1209     tlwarn("TLPDB: cannot set/edit verified property of a virtual tlpdb\n");
 1210     return 0;
 1211   }
 1212   if (@_) { $self->{'verified'} = shift }
 1213   return $self->{'verified'};
 1214 }
 1215 =pod
 1216 
 1217 =item C<< $tlpdb->verification_status >>
 1218 
 1219 Returns the id of the verification status. To obtain a textual representation
 1220 us %TLCrypto::VerificationStatusDescription.
 1221 
 1222 =cut
 1223 
 1224 sub verification_status {
 1225   my $self = shift;
 1226   if ($self->is_virtual) {
 1227     tlwarn("TLPDB: cannot set/edit verification status of a virtual tlpdb\n");
 1228     return 0;
 1229   }
 1230   if (@_) { $self->{'verification_status'} = shift }
 1231   return $self->{'verification_status'};
 1232 }
 1233 
 1234 =pod
 1235 
 1236 =item C<< $tlpdb->listdir >>
 1237 
 1238 The function C<listdir> allows to read and set the packages variable
 1239 specifying where generated list files are created.
 1240 
 1241 =cut
 1242 
 1243 sub listdir {
 1244   my $self = shift;
 1245   if (@_) { $_listdir = $_[0] }
 1246   return $_listdir;
 1247 }
 1248 
 1249 =pod
 1250 
 1251 =item C<< $tlpdb->config_src_container >>
 1252 
 1253 Returns 1 if the texlive config option for src files splitting on 
 1254 container level is set. See Options below.
 1255 
 1256 =cut
 1257 
 1258 sub config_src_container {
 1259   my $self = shift;
 1260   my $tlp;
 1261   if ($self->is_virtual) {
 1262     $tlp = $self->{'tlpdbs'}{'main'}->get_package('00texlive.config');
 1263   } else {
 1264     $tlp = $self->{'tlps'}{'00texlive.config'};
 1265   }
 1266   if (defined($tlp)) {
 1267     foreach my $d ($tlp->depends) {
 1268       if ($d =~ m!^container_split_src_files/(.*)$!) {
 1269         return "$1";
 1270       }
 1271     }
 1272   }
 1273   return 0;
 1274 }
 1275 
 1276 =pod
 1277 
 1278 =item C<< $tlpdb->config_doc_container >>
 1279 
 1280 Returns 1 if the texlive config option for doc files splitting on 
 1281 container level is set. See Options below.
 1282 
 1283 =cut
 1284 
 1285 sub config_doc_container {
 1286   my $self = shift;
 1287   my $tlp;
 1288   if ($self->is_virtual) {
 1289     $tlp = $self->{'tlpdbs'}{'main'}->get_package('00texlive.config');
 1290   } else {
 1291     $tlp = $self->{'tlps'}{'00texlive.config'};
 1292   }
 1293   if (defined($tlp)) {
 1294     foreach my $d ($tlp->depends) {
 1295       if ($d =~ m!^container_split_doc_files/(.*)$!) {
 1296         return "$1";
 1297       }
 1298     }
 1299   }
 1300   return 0;
 1301 }
 1302 
 1303 =pod
 1304 
 1305 =item C<< $tlpdb->config_container_format >>
 1306 
 1307 Returns the currently set default container format. See Options below.
 1308 
 1309 =cut
 1310 
 1311 sub config_container_format {
 1312   my $self = shift;
 1313   my $tlp;
 1314   if ($self->is_virtual) {
 1315     $tlp = $self->{'tlpdbs'}{'main'}->get_package('00texlive.config');
 1316   } else {
 1317     $tlp = $self->{'tlps'}{'00texlive.config'};
 1318   }
 1319   if (defined($tlp)) {
 1320     foreach my $d ($tlp->depends) {
 1321       if ($d =~ m!^container_format/(.*)$!) {
 1322         return "$1";
 1323       }
 1324     }
 1325   }
 1326   return "";
 1327 }
 1328 
 1329 =pod
 1330 
 1331 =item C<< $tlpdb->config_release >>
 1332 
 1333 Returns the currently set release. See Options below.
 1334 
 1335 =cut
 1336 
 1337 sub config_release {
 1338   my $self = shift;
 1339   my $tlp;
 1340   if ($self->is_virtual) {
 1341     $tlp = $self->{'tlpdbs'}{'main'}->get_package('00texlive.config');
 1342   } else {
 1343     $tlp = $self->{'tlps'}{'00texlive.config'};
 1344   }
 1345   if (defined($tlp)) {
 1346     foreach my $d ($tlp->depends) {
 1347       if ($d =~ m!^release/(.*)$!) {
 1348         return "$1";
 1349       }
 1350     }
 1351   }
 1352   return "";
 1353 }
 1354 
 1355 =pod
 1356 
 1357 =item C<< $tlpdb->config_minrelease >>
 1358 
 1359 Returns the currently allowed minimal release. See Options below.
 1360 
 1361 =cut
 1362 
 1363 sub config_minrelease {
 1364   my $self = shift;
 1365   my $tlp;
 1366   if ($self->is_virtual) {
 1367     $tlp = $self->{'tlpdbs'}{'main'}->get_package('00texlive.config');
 1368   } else {
 1369     $tlp = $self->{'tlps'}{'00texlive.config'};
 1370   }
 1371   if (defined($tlp)) {
 1372     foreach my $d ($tlp->depends) {
 1373       if ($d =~ m!^minrelease/(.*)$!) {
 1374         return "$1";
 1375       }
 1376     }
 1377   }
 1378   return;
 1379 }
 1380 
 1381 =pod
 1382 
 1383 =item C<< $tlpdb->config_frozen >>
 1384 
 1385 Returns true if the location is frozen.
 1386 
 1387 =cut
 1388 
 1389 sub config_frozen {
 1390   my $self = shift;
 1391   my $tlp;
 1392   if ($self->is_virtual) {
 1393     $tlp = $self->{'tlpdbs'}{'main'}->get_package('00texlive.config');
 1394   } else {
 1395     $tlp = $self->{'tlps'}{'00texlive.config'};
 1396   }
 1397   if (defined($tlp)) {
 1398     foreach my $d ($tlp->depends) {
 1399       if ($d =~ m!^frozen/(.*)$!) {
 1400         return "$1";
 1401       }
 1402     }
 1403   }
 1404   return;
 1405 }
 1406 
 1407 
 1408 =pod
 1409 
 1410 =item C<< $tlpdb->config_revision >>
 1411 
 1412 Returns the currently set revision. See Options below.
 1413 
 1414 =cut
 1415 
 1416 sub config_revision {
 1417   my $self = shift;
 1418   my $tlp;
 1419   if ($self->is_virtual) {
 1420     $tlp = $self->{'tlpdbs'}{'main'}->get_package('00texlive.config');
 1421   } else {
 1422     $tlp = $self->{'tlps'}{'00texlive.config'};
 1423   }
 1424   if (defined($tlp)) {
 1425     foreach my $d ($tlp->depends) {
 1426       if ($d =~ m!^revision/(.*)$!) {
 1427         return "$1";
 1428       }
 1429     }
 1430   }
 1431   return "";
 1432 }
 1433 
 1434 =pod
 1435 
 1436 =item C<< $tlpdb->sizes_of_packages_with_deps ( $opt_src, $opt_doc, $ref_arch_list, [ @packs ] ) >>
 1437 
 1438 =item C<< $tlpdb->sizes_of_packages ( $opt_src, $opt_doc, $ref_arch_list, [ @packs ] ) >>
 1439 
 1440 These functions return a reference to a hash with package names as keys
 1441 and the sizes in bytes as values. The sizes are computed for the list of
 1442 package names given as the fourth argument, or all packages if not
 1443 specified. The difference between the two functions is that the C<_with_deps>
 1444 gives the size of packages including the size of all depending sizes.
 1445 
 1446 If anything has been computed one additional key is synthesized,
 1447 C<__TOTAL__>, which contains the total size of all packages under
 1448 consideration. In the case of C<_with_deps> this total computation
 1449 does B<not> count packages multiple times, even if they appear
 1450 multiple times as dependencies.
 1451 
 1452 If the third argument is a reference to a list of architectures, then
 1453 only the sizes for the binary packages for these architectures are used,
 1454 otherwise all sizes for all architectures are summed.
 1455 
 1456 =cut
 1457 
 1458 sub sizes_of_packages {
 1459   my ($self, $opt_src, $opt_doc, $arch_list_ref, @packs) = @_;
 1460   return $self->_sizes_of_packages(0, $opt_src, $opt_doc, $arch_list_ref, @packs);
 1461 }
 1462 
 1463 sub sizes_of_packages_with_deps {
 1464   my ($self, $opt_src, $opt_doc, $arch_list_ref, @packs) = @_;
 1465   return $self->_sizes_of_packages(1, $opt_src, $opt_doc, $arch_list_ref, @packs);
 1466 }
 1467 
 1468 
 1469 sub _sizes_of_packages {
 1470   my ($self, $with_deps, $opt_src, $opt_doc, $arch_list_ref, @packs) = @_;
 1471   @packs || ( @packs = $self->list_packages() );
 1472   my @exppacks;
 1473   if ($with_deps) {
 1474     # don't expand collection->collection dependencies
 1475     #@exppacks = $self->expand_dependencies('-no-collections', $self, @packs);
 1476     @exppacks = $self->expand_dependencies($self, @packs);
 1477   } else {
 1478     @exppacks = @packs;
 1479   }
 1480   my @archs;
 1481   if ($arch_list_ref) {
 1482     @archs = @$arch_list_ref;
 1483   } else {
 1484     # if nothing is passed on, we use all available archs
 1485     @archs = $self->available_architectures;
 1486   }
 1487   my %tlpsizes;
 1488   my %tlpobjs;
 1489   my $totalsize = 0;
 1490   foreach my $p (@exppacks) {
 1491     $tlpobjs{$p} = $self->get_package($p);
 1492     my $media = $self->media_of_package($p);
 1493     if (!defined($tlpobjs{$p})) {
 1494       warn "STRANGE: $p not to be found in ", $self->root;
 1495       next;
 1496     }
 1497     #
 1498     # in case we are calling the _with_deps variant, we always
 1499     # compute *UNCOMPRESSED* sizes (not the container sizes!!!)
 1500     if ($with_deps) {
 1501       $tlpsizes{$p} = $self->size_of_one_package('local_uncompressed' , $tlpobjs{$p},
 1502                                                  $opt_src, $opt_doc, @archs);
 1503     } else {
 1504       $tlpsizes{$p} = $self->size_of_one_package($media, $tlpobjs{$p},
 1505                                                  $opt_src, $opt_doc, @archs);
 1506     }
 1507     $totalsize += $tlpsizes{$p};
 1508   }
 1509   my %realtlpsizes;
 1510   if ($totalsize) {
 1511     $realtlpsizes{'__TOTAL__'} = $totalsize;
 1512   }
 1513   if (!$with_deps) {
 1514     for my $p (@packs) {
 1515       $realtlpsizes{$p} = $tlpsizes{$p};
 1516     }
 1517   } else { # the case with dependencies
 1518     # make three rounds: for packages, collections, schemes
 1519     # size computations include only those from lower-levels
 1520     # that is, scheme-scheme, collection-collection
 1521     # does not contribute to the size
 1522     for my $p (@exppacks) {
 1523       next if ($p =~ m/scheme-/);
 1524       next if ($p =~ m/collection-/);
 1525       $realtlpsizes{$p} = $tlpsizes{$p};
 1526     }
 1527     for my $p (@exppacks) {
 1528       # only collections
 1529       next if ($p !~ m/collection-/);
 1530       $realtlpsizes{$p} = $tlpsizes{$p};
 1531       ddebug("=== $p adding deps\n");
 1532       for my $d ($tlpobjs{$p}->depends) {
 1533         next if ($d =~ m/^collection-/);
 1534         next if ($d =~ m/^scheme-/);
 1535         ddebug("=== going for $d\n");
 1536         if (defined($tlpsizes{$d})) {
 1537           $realtlpsizes{$p} += $tlpsizes{$d};
 1538           ddebug("=== found $tlpsizes{$d} for $d\n");
 1539         } else {
 1540           # silently ignore missing defined packages - they should have
 1541           # been computed by expand-dependencies
 1542           debug("TLPDB.pm: size with deps: sub package not found main=$d, dep=$p\n");
 1543         }
 1544       }
 1545     }
 1546     for my $p (@exppacks) {
 1547       # only schemes
 1548       next if ($p !~ m/scheme-/);
 1549       $realtlpsizes{$p} = $tlpsizes{$p};
 1550       ddebug("=== $p adding deps\n");
 1551       for my $d ($tlpobjs{$p}->depends) {
 1552         # should not be necessary, we don't have collection -> scheme deps
 1553         next if ($d =~ m/^scheme-/);
 1554         ddebug("=== going for $d\n");
 1555         if (defined($realtlpsizes{$d})) {
 1556           $realtlpsizes{$p} += $realtlpsizes{$d};
 1557           ddebug("=== found $realtlpsizes{$d} for $d\n");
 1558         } else {
 1559           # silently ignore missing defined packages - they should have
 1560           # been computed by expand-dependencies
 1561           debug("TLPDB.pm: size with deps: sub package not found main=$d, dep=$p\n");
 1562         }
 1563       }
 1564     }
 1565   }
 1566   return \%realtlpsizes;
 1567 }
 1568 
 1569 sub size_of_one_package {
 1570   my ($self, $media, $tlpobj, $opt_src, $opt_doc, @used_archs) = @_;
 1571   my $size = 0;
 1572   if ($media ne 'local_uncompressed') {
 1573     # we use the container size as the measuring unit since probably
 1574     # downloading will be the limiting factor
 1575     $size =  $tlpobj->containersize;
 1576     $size += $tlpobj->srccontainersize if $opt_src;
 1577     $size += $tlpobj->doccontainersize if $opt_doc;
 1578   } else {
 1579     # we have to add the respective sizes, that is checking for
 1580     # installation of src and doc file
 1581     $size  = $tlpobj->runsize;
 1582     $size += $tlpobj->srcsize if $opt_src;
 1583     $size += $tlpobj->docsize if $opt_doc;
 1584     my %foo = %{$tlpobj->binsize};
 1585     for my $k (keys %foo) { 
 1586       if (@used_archs && member($k, @used_archs)) {
 1587         $size += $foo{$k};
 1588       }
 1589     }
 1590     # packages sizes are stored in blocks; transform that to bytes.
 1591     $size *= $TeXLive::TLConfig::BlockSize;
 1592   }
 1593   return $size;
 1594 }
 1595 
 1596 =pod
 1597 
 1598 =item C<< $tlpdb->install_package_files($f [, $f]) >>
 1599 
 1600 Install a package from a package file, i.e. a .tar.xz.
 1601 Returns the number of packages actually installed successfully.
 1602 
 1603 =cut
 1604 
 1605 sub install_package_files {
 1606   my ($self, @files) = @_;
 1607 
 1608   my $ret = 0;
 1609 
 1610   my $opt_src = $self->option("install_srcfiles");
 1611   my $opt_doc = $self->option("install_docfiles");
 1612 
 1613   for my $f (@files) {
 1614 
 1615     # - create a tmp directory
 1616     my $tmpdir = TeXLive::TLUtils::tl_tmpdir();
 1617     # - unpack everything there
 1618     {
 1619       my ($ret, $msg) = TeXLive::TLUtils::unpack($f, $tmpdir);
 1620       if (!$ret) {
 1621         tlwarn("TLPDB::install_package_files: $msg\n");
 1622         next;
 1623       }
 1624     }
 1625     # we are  still here, so the files have been unpacked properly
 1626     # we need now to find the tlpobj in $tmpdir/tlpkg/tlpobj/
 1627     my ($tlpobjfile, $anotherfile) = <$tmpdir/tlpkg/tlpobj/*.tlpobj>;
 1628     if (defined($anotherfile)) {
 1629       # we found several tlpobj files, that is not allowed, stop
 1630       tlwarn("TLPDB::install_package_files: several tlpobj files "
 1631              . "($tlpobjfile, $anotherfile) in tlpkg/tlpobj/, stopping!\n");
 1632       next;
 1633     }
 1634     # - read the tlpobj from there
 1635     my $tlpobj = TeXLive::TLPOBJ->new;
 1636     $tlpobj->from_file($tlpobjfile);
 1637     # we didn't die in this process, so that seems to be a proper tlpobj
 1638     # (btw, why didn't I work on proper return values!?!)
 1639 
 1640     #
 1641     # we are now ready for installation
 1642     # if this package existed before, remove it from the tlpdb
 1643     if ($self->get_package($tlpobj->name)) {
 1644       $self->remove_package($tlpobj->name);
 1645     }
 1646 
 1647     # code partially from TLPDB->not_virtual_install_package!!!
 1648     my @installfiles = ();
 1649     my $reloc = 1 if $tlpobj->relocated;
 1650     foreach ($tlpobj->runfiles) { push @installfiles, $_; };
 1651     foreach ($tlpobj->allbinfiles) { push @installfiles, $_; };
 1652     if ($opt_src) { foreach ($tlpobj->srcfiles) { push @installfiles, $_; } }
 1653     if ($opt_doc) { foreach ($tlpobj->docfiles) { push @installfiles, $_; } }
 1654     # 
 1655     # remove the RELOC prefix, but do NOT replace it with RelocTree
 1656     @installfiles = map { s!^$RelocPrefix/!!; $_; } @installfiles;
 1657     # if the first argument of _install_data is scalar, it is the
 1658     # place from where files should be installed
 1659     if (!_install_data ($tmpdir, \@installfiles, $reloc, \@installfiles,
 1660                         $self)) {
 1661       tlwarn("TLPDB::install_package_files: couldn't install_data files: "
 1662              . "@installfiles\n"); 
 1663       next;
 1664     }
 1665     if ($reloc) {
 1666       if ($self->setting("usertree")) {
 1667         $tlpobj->cancel_reloc_prefix;
 1668       } else {
 1669         $tlpobj->replace_reloc_prefix;
 1670       }
 1671       $tlpobj->relocated(0);
 1672     }
 1673     my $tlpod = $self->root . "/tlpkg/tlpobj";
 1674     mkdirhier( $tlpod );
 1675     open(TMP,">$tlpod/".$tlpobj->name.".tlpobj") or
 1676       die("Cannot open tlpobj file for ".$tlpobj->name);
 1677     $tlpobj->writeout(\*TMP);
 1678     close(TMP);
 1679     $self->add_tlpobj($tlpobj);
 1680     $self->save;
 1681     TeXLive::TLUtils::announce_execute_actions("enable", $tlpobj);
 1682     # do the postinstallation actions
 1683     #
 1684     # Run the post installation code in the postaction tlpsrc entries
 1685     # in case we are on w32 and the admin did install for himself only
 1686     # we switch off admin mode
 1687     if (wndws() && admin() && !$self->option("w32_multi_user")) {
 1688       non_admin();
 1689     }
 1690     # for now desktop_integration maps to both installation
 1691     # of desktop shortcuts and menu items, but we can split them later
 1692     &TeXLive::TLUtils::do_postaction("install", $tlpobj,
 1693       $self->option("file_assocs"),
 1694       $self->option("desktop_integration"),
 1695       $self->option("desktop_integration"),
 1696       $self->option("post_code"));
 1697 
 1698     # remember that we installed this package correctly
 1699     $ret++;
 1700   }
 1701   return $ret;
 1702 }
 1703 
 1704 
 1705 =pod
 1706 
 1707 =item C<< $tlpdb->install_package($pkg, $dest_tlpdb [, $tag]) >>
 1708 
 1709 Installs the package $pkg into $dest_tlpdb.
 1710 If C<$tag> is present and the tlpdb is virtual, tries to install $pkg
 1711 from the repository tagged with $tag.
 1712 
 1713 =cut
 1714 
 1715 sub install_package {
 1716   my ($self, $pkg, $totlpdb, $tag) = @_;
 1717   if ($self->is_virtual) {
 1718     if (defined($tag)) {
 1719       if (defined($self->{'packages'}{$pkg}{'tags'}{$tag})) {
 1720         return $self->{'tlpdbs'}{$tag}->install_package($pkg, $totlpdb);
 1721       } else {
 1722         tlwarn("TLPDB::install_package: package $pkg not found"
 1723                . " in repository $tag\n");
 1724         return;
 1725       }
 1726     } else {
 1727       my ($maxtag, $maxrev, $maxtlp, $maxtlpdb)
 1728         = $self->virtual_candidate($pkg);
 1729       return $maxtlpdb->install_package($pkg, $totlpdb);
 1730     }
 1731   } else {
 1732     if (defined($tag)) {
 1733       tlwarn("TLPDB: not a virtual tlpdb, ignoring tag $tag"
 1734               . " on installation of $pkg\n");
 1735     }
 1736     return $self->not_virtual_install_package($pkg, $totlpdb);
 1737   }
 1738   return;
 1739 }
 1740 
 1741 sub not_virtual_install_package {
 1742   my ($self, $pkg, $totlpdb) = @_;
 1743   my $fromtlpdb = $self;
 1744   my $ret;
 1745   die("TLPDB not initialized, cannot find tlpdb!")
 1746     unless (defined($fromtlpdb));
 1747 
 1748   my $tlpobj = $fromtlpdb->get_package($pkg);
 1749   if (!defined($tlpobj)) {
 1750     tlwarn("TLPDB::not_virtual_install_package: cannot find package: $pkg\n");
 1751     return 0;
 1752   } else {
 1753     my $container_src_split = $fromtlpdb->config_src_container;
 1754     my $container_doc_split = $fromtlpdb->config_doc_container;
 1755     # get options about src/doc splitting from $totlpdb
 1756     my $opt_src = $totlpdb->option("install_srcfiles");
 1757     my $opt_doc = $totlpdb->option("install_docfiles");
 1758     my $real_opt_doc = $opt_doc;
 1759     my $reloc = 1 if $tlpobj->relocated;
 1760     my $container;
 1761     my @installfiles;
 1762     my $root = $self->root;
 1763     # make sure that there is no terminal / in $root, otherwise we
 1764     # will get double // somewhere
 1765     $root =~ s!/$!!;
 1766     foreach ($tlpobj->runfiles) {
 1767       # s!^!$root/!;
 1768       push @installfiles, $_;
 1769     }
 1770     foreach ($tlpobj->allbinfiles) {
 1771       # s!^!$root/!;
 1772       push @installfiles, $_;
 1773     }
 1774     if ($opt_src) {
 1775       foreach ($tlpobj->srcfiles) {
 1776         # s!^!$root/!;
 1777         push @installfiles, $_;
 1778       }
 1779     }
 1780     if ($real_opt_doc) {
 1781       foreach ($tlpobj->docfiles) {
 1782         # s!^!$root/!;
 1783         push @installfiles, $_;
 1784       }
 1785     }
 1786     my $media = $self->media;
 1787     my $container_is_versioned = 0;
 1788     if ($media eq 'local_uncompressed') {
 1789       $container = \@installfiles;
 1790     } elsif ($media eq 'local_compressed') {
 1791       for my $ext (map { $Compressors{$_}{'extension'} } keys %Compressors) {
 1792         # request versioned containers when local (i.e., ISO image),
 1793         # since the unversioned symlinks cannot be dereferenced
 1794         # on Windows.
 1795         my $rev = $tlpobj->revision;
 1796         if (-r "$root/$Archive/$pkg.r$rev.tar.$ext") {
 1797           $container_is_versioned = 1;
 1798           $container = "$root/$Archive/$pkg.r$rev.tar.$ext";
 1799         } elsif (-r "$root/$Archive/$pkg.tar.$ext") {
 1800           $container_is_versioned = 0;
 1801           $container = "$root/$Archive/$pkg.tar.$ext";
 1802         }
 1803       }
 1804       if (!$container) {
 1805         tlwarn("TLPDB: cannot find package $pkg.tar.$CompressorExtRegexp"
 1806                . " in $root/$Archive\n");
 1807         return(0);
 1808       }
 1809     } elsif (&media eq 'NET') {
 1810       # Since the NET server cannot be a Windows machine,
 1811       # ok to request the unversioned file.
 1812       $container = "$root/$Archive/$pkg.tar."
 1813                    . $Compressors{$DefaultCompressorFormat}{'extension'};
 1814       $container_is_versioned = 0;
 1815     }
 1816     my $container_str = ref $container eq "ARRAY"
 1817                         ? "[" . join (" ", @$container) . "]" : $container;
 1818     ddebug("TLPDB::not_virtual_install_package: installing container: ",
 1819           $container_str, "\n");
 1820     $self->_install_data($container, $reloc, \@installfiles, $totlpdb,
 1821                          $tlpobj->containersize, $tlpobj->containerchecksum)
 1822       || return(0);
 1823     # if we are installing from local_compressed or NET we have to fetch
 1824     # respective source and doc packages $pkg.source and $pkg.doc and
 1825     # install them, too
 1826     if (($media eq 'NET') || ($media eq 'local_compressed')) {
 1827       # we install split containers under the following conditions:
 1828       # - the container were split generated
 1829       # - src/doc files should be installed
 1830       # (- the package is not already a split one (like .i386-linux))
 1831       # the above test has been removed because it would mean that
 1832       #   texlive.infra.doc.tar.xz
 1833       # will never be installed, and we do already check that there
 1834       # are at all src/doc files, which in split packages of the form 
 1835       # foo.ARCH are not present. And if they are present, than that is fine,
 1836       # too (bin-foobar.windows.doc.tar.xz)
 1837       # - there are actually src/doc files present
 1838       if ($container_src_split && $opt_src && $tlpobj->srcfiles) {
 1839         my $srccontainer = $container;
 1840         if ($container_is_versioned) {
 1841           $srccontainer =~ s/\.(r[0-9]*)\.tar\.$CompressorExtRegexp$/.source.$1.tar.$2/;
 1842         } else {
 1843           $srccontainer =~ s/\.tar\.$CompressorExtRegexp$/.source.tar.$1/;
 1844         }
 1845         $self->_install_data($srccontainer, $reloc, \@installfiles, $totlpdb,
 1846                       $tlpobj->srccontainersize, $tlpobj->srccontainerchecksum)
 1847           || return(0);
 1848       }
 1849       if ($container_doc_split && $real_opt_doc && $tlpobj->docfiles) {
 1850         my $doccontainer = $container;
 1851         if ($container_is_versioned) {
 1852           $doccontainer =~ s/\.(r[0-9]*)\.tar\.$CompressorExtRegexp$/.doc.$1.tar.$2/;
 1853         } else {
 1854           $doccontainer =~ s/\.tar\.$CompressorExtRegexp$/.doc.tar.$1/;
 1855         }
 1856         $self->_install_data($doccontainer, $reloc, \@installfiles,
 1857             $totlpdb, $tlpobj->doccontainersize, $tlpobj->doccontainerchecksum)
 1858           || return(0);
 1859       }
 1860       #
 1861       # if we installed from NET/local_compressed and we got a relocatable container
 1862       # make sure that the stray texmf-dist/tlpkg directory is removed
 1863       # in USER MODE that should NOT be done because we keep the information
 1864       # there, but for now do it unconditionally
 1865       if ($tlpobj->relocated) {
 1866         my $reloctree = $totlpdb->root . "/" . $RelocTree;
 1867         my $tlpkgdir = $reloctree . "/" . $InfraLocation;
 1868         my $tlpod = $tlpkgdir .  "/tlpobj";
 1869         TeXLive::TLUtils::rmtree($tlpod) if (-d $tlpod);
 1870         # we try to remove the tlpkg directory, that will succeed only
 1871         # if it is empty. So in normal installations it won't be, but
 1872         # if we are installing a relocated package it is texmf-dist/tlpkg
 1873         # which will be (hopefully) empty
 1874         rmdir($tlpkgdir) if (-d "$tlpkgdir");
 1875       }
 1876     }
 1877     # we don't want to have wrong information in the tlpdb, so remove the
 1878     # src/doc files if they are not installed ...
 1879     if (!$opt_src) {
 1880       $tlpobj->clear_srcfiles;
 1881     }
 1882     if (!$real_opt_doc) {
 1883       $tlpobj->clear_docfiles;
 1884     }
 1885     # if a package is relocatable we have to cancel the reloc prefix
 1886     # and unset the relocated setting
 1887     # before we save it to the local tlpdb
 1888     if ($tlpobj->relocated) {
 1889       if ($totlpdb->setting("usertree")) {
 1890         $tlpobj->cancel_reloc_prefix;
 1891       } else {
 1892         $tlpobj->replace_reloc_prefix;
 1893       }
 1894       $tlpobj->relocated(0);
 1895     }
 1896     # we have to write out the tlpobj file since it is contained in the
 1897     # archives (.tar.xz) but at DVD install time we don't have them
 1898     my $tlpod = $totlpdb->root . "/tlpkg/tlpobj";
 1899     mkdirhier($tlpod);
 1900     my $count = 0;
 1901     my $tlpobj_file = ">$tlpod/" . $tlpobj->name . ".tlpobj";
 1902     until (open(TMP, $tlpobj_file)) {
 1903       # The open might fail for no good reason on Windows.
 1904       # Try again for a while, but not forever.
 1905       if ($count++ == 100) { die "$0: open($tlpobj_file) failed: $!"; }
 1906       select (undef, undef, undef, .1);  # sleep briefly
 1907     }
 1908     $tlpobj->writeout(\*TMP);
 1909     close(TMP);
 1910     $totlpdb->add_tlpobj($tlpobj);
 1911     $totlpdb->save;
 1912     # compute the return value
 1913     TeXLive::TLUtils::announce_execute_actions("enable", $tlpobj);
 1914     # do the postinstallation actions
 1915     #
 1916     # Run the post installation code in the postaction tlpsrc entries
 1917     # in case we are on w32 and the admin did install for himself only
 1918     # we switch off admin mode
 1919     if (wndws() && admin() && !$totlpdb->option("w32_multi_user")) {
 1920       non_admin();
 1921     }
 1922     # for now desktop_integration maps to both installation
 1923     # of desktop shortcuts and menu items, but we can split them later
 1924     &TeXLive::TLUtils::do_postaction("install", $tlpobj,
 1925       $totlpdb->option("file_assocs"),
 1926       $totlpdb->option("desktop_integration"),
 1927       $totlpdb->option("desktop_integration"),
 1928       $totlpdb->option("post_code"));
 1929   }
 1930   return 1;
 1931 }
 1932 
 1933 #
 1934 # _install_data
 1935 # actually does the installation work
 1936 # returns 1 on success and 0 on error
 1937 #
 1938 # if the first argument is a string, then files are taken from this directory
 1939 # otherwise it is a tlpdb from where to install
 1940 #
 1941 sub _install_data {
 1942   my ($self, $what, $reloc, $filelistref, $totlpdb, $whatsize, $whatcheck) = @_;
 1943 
 1944   my $target = $totlpdb->root;
 1945   my $tempdir = TeXLive::TLUtils::tl_tmpdir();
 1946 
 1947   my @filelist = @$filelistref;
 1948 
 1949   if (ref $what) {
 1950     # determine the root from where we install
 1951     # if the first argument $self is a string, then it should be the
 1952     # root from where to install the files, otherwise it should be 
 1953     # a TLPDB object (installation from DVD)
 1954     my $root;
 1955     if (!ref($self)) {
 1956       $root = $self;
 1957     } else {
 1958       $root = $self->root;
 1959     }
 1960     # if we are installing a reloc, add the RelocTree to the target
 1961     if ($reloc) {
 1962       if (!$totlpdb->setting("usertree")) {
 1963         $target .= "/$RelocTree";
 1964       }
 1965     }
 1966 
 1967     foreach my $file (@$what) {
 1968       # @what is taken, not @filelist!
 1969       # is this still needed?
 1970       my $dn=dirname($file);
 1971       mkdirhier("$target/$dn");
 1972       TeXLive::TLUtils::copy "$root/$file", "$target/$dn";
 1973     }
 1974     # we always assume that copy will work
 1975     return(1);
 1976   } elsif ($what =~ m,\.tar\.$CompressorExtRegexp$,) {
 1977     if ($reloc) {
 1978       if (!$totlpdb->setting("usertree")) {
 1979         $target .= "/$RelocTree";
 1980       }
 1981     }
 1982     my $ww = ($whatsize || "<unset>");
 1983     my $ss = ($whatcheck || "<unset>");
 1984     debug("TLPDB::_install_data: what=$what, target=$target, size=$ww, checksum=$ss, tmpdir=$tempdir\n");
 1985     my ($ret, $pkg) = TeXLive::TLUtils::unpack($what, $target, 'size' => $whatsize, 'checksum' => $whatcheck, 'tmpdir' => $tempdir);
 1986     if (!$ret) {
 1987       tlwarn("TLPDB::_install_data: $pkg for $what\n"); # $pkg is error msg
 1988       return(0);
 1989     }
 1990     # remove the $pkg.tlpobj, we recreate it anyway again
 1991     unlink ("$target/tlpkg/tlpobj/$pkg.tlpobj") 
 1992       if (-r "$target/tlpkg/tlpobj/$pkg.tlpobj");
 1993     return(1);
 1994   } else {
 1995     tlwarn("TLPDB::_install_data: don't know how to install $what\n");
 1996     return(0);
 1997   }
 1998 }
 1999 
 2000 =pod
 2001 
 2002 =item << $tlpdb->remove_package($pkg, %options) >>
 2003 
 2004 Removes a single package with all the files and the entry in the db;
 2005 warns if the package does not exist.
 2006 
 2007 =cut
 2008 
 2009 # remove_package removes a single package with all files (including the
 2010 # tlpobj files) and the entry from the tlpdb.
 2011 sub remove_package {
 2012   my ($self, $pkg, %opts) = @_;
 2013   my $localtlpdb = $self;
 2014   my $tlp = $localtlpdb->get_package($pkg);
 2015   my $usertree = $localtlpdb->setting("usertree");
 2016   if (!defined($tlp)) {
 2017     # we should not be called.
 2018     tlwarn ("TLPDB::remove_package: package not present, ",
 2019             "so nothing to remove: $pkg\n");
 2020   } else {
 2021     my $currentarch = $self->platform();
 2022     if ($pkg eq "texlive.infra" || $pkg eq "texlive.infra.$currentarch") {
 2023       log ("Not removing $pkg, it is essential!\n");
 2024       return 0;
 2025     }
 2026     # we have to chdir to $localtlpdb->root
 2027     my $Master = $localtlpdb->root;
 2028     chdir ($Master) || die "chdir($Master) failed: $!";
 2029     my @files = $tlp->all_files;
 2030     #
 2031     # also remove the .tlpobj file
 2032     push @files, "tlpkg/tlpobj/$pkg.tlpobj";
 2033     #
 2034     # and the ones from src/doc splitting
 2035     if (-r "tlpkg/tlpobj/$pkg.source.tlpobj") {
 2036       push @files, "tlpkg/tlpobj/$pkg.source.tlpobj";
 2037     }
 2038     if (-r "tlpkg/tlpobj/$pkg.doc.tlpobj") {
 2039       push @files, "tlpkg/tlpobj/$pkg.doc.tlpobj";
 2040     }
 2041     #
 2042     # some packages might be relocated, thus having the RELOC prefix
 2043     # in user mode we just remove the prefix, in normal mode we
 2044     # replace it with texmf-dist
 2045     # since we don't have user mode 
 2046     if ($tlp->relocated) {
 2047       for (@files) {
 2048         if (!$usertree) {
 2049           s:^$RelocPrefix/:$RelocTree/:;
 2050         }
 2051       }
 2052     }
 2053     #
 2054     # we want to check that a file is only listed in one package, so
 2055     # in case that a file to be removed is listed in another package
 2056     # we will warn and *not* remove it
 2057     my %allfiles;
 2058     for my $p ($localtlpdb->list_packages) {
 2059       next if ($p eq $pkg); # we have to skip the to be removed package
 2060       for my $f ($localtlpdb->get_package($p)->all_files) {
 2061         $allfiles{$f} = $p;
 2062       }
 2063     }
 2064     my @goodfiles = ();
 2065     my @badfiles = ();
 2066     my @debugfiles = ();
 2067     for my $f (@files) {
 2068       # in usermode we have to add texmf-dist again for comparison
 2069       if (defined($allfiles{$f})) {
 2070         # this file should be removed but is mentioned somewhere, too
 2071         # take into account if we got a warn list
 2072         if (defined($opts{'remove-warn-files'})) {
 2073           my %a = %{$opts{'remove-warn-files'}};
 2074           if (defined($a{$f})) {
 2075             push @badfiles, $f;
 2076           } else {
 2077             # NO NOTHING HERE!!!
 2078             # DON'T PUSH IT ON @goodfiles, it will be removed, which we do
 2079             # NOT want. We only want to suppress the warning!
 2080             push @debugfiles, $f;
 2081           }
 2082         } else {
 2083           push @badfiles, $f;
 2084         }
 2085       } else {
 2086         push @goodfiles, $f;
 2087       }
 2088     }
 2089     if ($#debugfiles >= 0) {
 2090       debug("The following files will not be removed due to the removal of $pkg.\n");
 2091       debug("But we do not warn on it because they are moved to other packages.\n");
 2092       for my $f (@debugfiles) {
 2093         debug(" $f - $allfiles{$f}\n");
 2094       }
 2095     }
 2096     if ($#badfiles >= 0) {
 2097       # warn the user
 2098       tlwarn("TLPDB: These files would have been removed due to removal of\n");
 2099       tlwarn("TLPDB: $pkg, but are part of another package:\n");
 2100       for my $f (@badfiles) {
 2101         tlwarn(" $f - $allfiles{$f}\n");
 2102       }
 2103     }
 2104     #
 2105     # Run only the postaction code thing now since afterwards the
 2106     # files will be gone ...
 2107     if (defined($opts{'nopostinstall'}) && $opts{'nopostinstall'}) {
 2108       &TeXLive::TLUtils::do_postaction("remove", $tlp,
 2109         0, # tlpdbopt_file_assocs,
 2110         0, # tlpdbopt_desktop_integration, menu part
 2111         0, # tlpdbopt_desktop_integration, desktop part
 2112         $localtlpdb->option("post_code"));
 2113     }
 2114     # we want to check whether we can actually remove files
 2115     # there might be various reasons that this fails, like texmf-dist
 2116     # directory suddently becoming ro (for whatever definition of
 2117     # suddenly).
 2118     my (%by_dirs, %removed_dirs) = &TeXLive::TLUtils::all_dirs_and_removed_dirs (@goodfiles);
 2119     my @removals = keys %removed_dirs;
 2120 
 2121     # we have already check for the existence of the dirs returned
 2122     for my $d (keys %by_dirs) {
 2123       if (! &TeXLive::TLUtils::dir_writable($d)) {
 2124         tlwarn("TLPDB::remove_package: directories are not writable, cannot remove files: $d\n");
 2125         return 0;
 2126       }
 2127     }
 2128 
 2129     # now do the removal
 2130     for my $entry (@goodfiles) {
 2131       # sometimes the files might not be there: 1) we remove .tlpobj
 2132       # explicitly above; 2) we're called from tl-update-containers
 2133       # to update the network tlpdb, and that doesn't have an expanded
 2134       # texmf-dist.
 2135       next unless -e $entry;
 2136       #
 2137       unlink($entry)
 2138       || tlwarn("TLPDB::remove_package: Could not unlink $entry: $!\n");
 2139     }
 2140     for my $d (@removals) {
 2141       rmdir($d)
 2142       || tlwarn("TLPDB::remove_package: Could not rmdir $d: $!\n")
 2143     }
 2144     $localtlpdb->remove_tlpobj($pkg);
 2145     TeXLive::TLUtils::announce_execute_actions("disable", $tlp);
 2146     
 2147     # should we save at each removal???
 2148     # advantage: the tlpdb actually reflects what is installed
 2149     # disadvantage: removing a collection calls the save routine several times
 2150     # still I consider it better that the tlpdb is in a consistent state
 2151     $localtlpdb->save;
 2152     #
 2153     # Run the post installation code in the postaction tlpsrc entries
 2154     # in case we are on w32 and the admin did install for himself only
 2155     # we switch off admin mode
 2156     if (wndws() && admin() && !$localtlpdb->option("w32_multi_user")) {
 2157       non_admin();
 2158     }
 2159     #
 2160     # Run the post installation code in the postaction tlpsrc entries
 2161     # the postaction code part cannot be evaluated now since the
 2162     # files are already removed.
 2163     # Again, desktop integration maps to desktop and menu links
 2164     if (!$opts{'nopostinstall'}) {
 2165       debug(" TLPDB::remove_package: running remove postinstall");
 2166       &TeXLive::TLUtils::do_postaction("remove", $tlp,
 2167         $localtlpdb->option("file_assocs"),
 2168         $localtlpdb->option("desktop_integration"),
 2169         $localtlpdb->option("desktop_integration"),
 2170         0);
 2171     }
 2172   }
 2173   return 1;
 2174 }
 2175 
 2176 
 2177 =pod
 2178 
 2179 =item C<< $tlpdb->option($key [, $val]) >>
 2180 =item C<< $tlpdb->setting($key [, $val]) >>
 2181 
 2182 Need to be documented
 2183 
 2184 =cut
 2185 
 2186 sub _set_option_value {
 2187   my $self = shift;
 2188   $self->_set_value_pkg('00texlive.installation', 'opt_', @_);
 2189 }
 2190 sub _set_setting_value {
 2191   my $self = shift;
 2192   $self->_set_value_pkg('00texlive.installation', 'setting_', @_);
 2193 }
 2194 sub _set_value_pkg {
 2195   my ($self,$pkgname,$pre,$key,$value) = @_;
 2196   my $k = "$pre$key";
 2197   my $pkg;
 2198   if ($self->is_virtual) {
 2199     $pkg = $self->{'tlpdbs'}{'main'}->get_package($pkgname);
 2200   } else {
 2201     $pkg = $self->{'tlps'}{$pkgname};
 2202   }
 2203   my @newdeps;
 2204   if (!defined($pkg)) {
 2205     $pkg = new TeXLive::TLPOBJ;
 2206     $pkg->name($pkgname);
 2207     $pkg->category("TLCore");
 2208     push @newdeps, "$k:$value";
 2209   } else {
 2210     my $found = 0;
 2211     foreach my $d ($pkg->depends) {
 2212       if ($d =~ m!^$k:!) {
 2213         $found = 1;
 2214         push @newdeps, "$k:$value";
 2215       } else {
 2216         push @newdeps, $d;
 2217       }
 2218     }
 2219     if (!$found) {
 2220       push @newdeps, "$k:$value";
 2221     }
 2222   }
 2223   $pkg->depends(@newdeps);
 2224   $self->add_tlpobj($pkg);
 2225 }
 2226 
 2227 sub _clear_option {
 2228   my $self = shift;
 2229   $self->_clear_pkg('00texlive.installation', 'opt_', @_);
 2230 }
 2231 
 2232 sub _clear_setting {
 2233   my $self = shift;
 2234   $self->_clear_pkg('00texlive.installation', 'setting_', @_);
 2235 }
 2236 
 2237 sub _clear_pkg {
 2238   my ($self,$pkgname,$pre,$key) = @_;
 2239   my $k = "$pre$key";
 2240   my $pkg;
 2241   if ($self->is_virtual) {
 2242     $pkg = $self->{'tlpdbs'}{'main'}->get_package($pkgname);
 2243   } else {
 2244     $pkg = $self->{'tlps'}{$pkgname};
 2245   }
 2246   my @newdeps;
 2247   if (!defined($pkg)) {
 2248     return;
 2249   } else {
 2250     foreach my $d ($pkg->depends) {
 2251       if ($d =~ m!^$k:!) {
 2252         # do nothing, we drop the value
 2253       } else {
 2254         push @newdeps, $d;
 2255       }
 2256     }
 2257   }
 2258   $pkg->depends(@newdeps);
 2259   $self->add_tlpobj($pkg);
 2260 }
 2261 
 2262 
 2263 sub _get_option_value {
 2264   my $self = shift;
 2265   $self->_get_value_pkg('00texlive.installation', 'opt_', @_);
 2266 }
 2267 
 2268 sub _get_setting_value {
 2269   my $self = shift;
 2270   $self->_get_value_pkg('00texlive.installation', 'setting_', @_);
 2271 }
 2272 
 2273 sub _get_value_pkg {
 2274   my ($self,$pkg,$pre,$key) = @_;
 2275   my $k = "$pre$key";
 2276   my $tlp;
 2277   if ($self->is_virtual) {
 2278     $tlp = $self->{'tlpdbs'}{'main'}->get_package($pkg);
 2279   } else {
 2280     $tlp = $self->{'tlps'}{$pkg};
 2281   }
 2282   if (defined($tlp)) {
 2283     foreach my $d ($tlp->depends) {
 2284       if ($d =~ m!^$k:(.*)$!) {
 2285         return "$1";
 2286       }
 2287     }
 2288     return;
 2289   }
 2290   tlwarn("TLPDB: $pkg not found, cannot read option $key.\n");
 2291   return;
 2292 }
 2293 
 2294 sub option_pkg {
 2295   my $self = shift;
 2296   my $pkg = shift;
 2297   my $key = shift;
 2298   if (@_) { $self->_set_value_pkg($pkg, "opt_", $key, shift); }
 2299   my $ret = $self->_get_value_pkg($pkg, "opt_", $key);
 2300   # special case for location == __MASTER__
 2301   if (defined($ret) && $ret eq "__MASTER__" && $key eq "location") {
 2302     return $self->root;
 2303   }
 2304   return $ret;
 2305 }
 2306 sub option {
 2307   my $self = shift;
 2308   my $key = shift;
 2309   if (@_) { $self->_set_option_value($key, shift); }
 2310   my $ret = $self->_get_option_value($key);
 2311   # special case for location == __MASTER__
 2312   if (defined($ret) && $ret eq "__MASTER__" && $key eq "location") {
 2313     return $self->root;
 2314   }
 2315   return $ret;
 2316 }
 2317 sub setting_pkg {
 2318   my $self = shift;
 2319   my $pkg = shift;
 2320   my $key = shift;
 2321   if (@_) { 
 2322     if ($TLPDBSettings{$key}->[0] eq "l") {
 2323       $self->_set_value_pkg($pkg, "setting_", $key, "@_"); 
 2324     } else {
 2325       $self->_set_value_pkg($pkg, "setting_", $key, shift); 
 2326     }
 2327   }
 2328   my $ret = $self->_get_value_pkg($pkg, "setting_", $key);
 2329   # check the types of the settings, and if it is a "l" return a list
 2330   if ($TLPDBSettings{$key}->[0] eq "l") {
 2331     my @ret;
 2332     if (defined $ret) {
 2333       @ret = split(" ", $ret);
 2334     } else {
 2335       tlwarn "TLPDB::setting_pkg: no $key, returning empty list\n";
 2336       @ret = ();
 2337     }
 2338     return @ret;
 2339   }
 2340   return $ret;
 2341 }
 2342 sub setting {
 2343   my $self = shift;
 2344   my $key = shift;
 2345   if ($key eq "-clear") {
 2346     my $realkey = shift;
 2347     $self->_clear_setting($realkey);
 2348     return;
 2349   }
 2350   if (@_) { 
 2351     if ($TLPDBSettings{$key}->[0] eq "l") {
 2352       $self->_set_setting_value($key, "@_"); 
 2353     } else {
 2354       $self->_set_setting_value($key, shift); 
 2355     }
 2356   }
 2357   my $ret = $self->_get_setting_value($key);
 2358   # check the types of the settings, and if it is a "l" return a list
 2359   if ($TLPDBSettings{$key}->[0] eq "l") {
 2360     my @ret;
 2361     if (defined $ret) {
 2362       @ret = split(" ", $ret);
 2363     } else {
 2364       tlwarn("TLPDB::setting: no $key, returning empty list\n");
 2365       @ret = ();
 2366     }
 2367     return @ret;
 2368   }
 2369   return $ret;
 2370 }
 2371 
 2372 sub reset_options {
 2373   my $self = shift;
 2374   for my $k (keys %TLPDBOptions) {
 2375     $self->option($k, $TLPDBOptions{$k}->[1]);
 2376   }
 2377 }
 2378 
 2379 sub add_default_options {
 2380   my $self = shift;
 2381   for my $k (sort keys %TLPDBOptions) {
 2382     # if the option is not set already, do set it to defaults
 2383     if (! $self->option($k) ) {
 2384       $self->option($k, $TLPDBOptions{$k}->[1]);
 2385     }
 2386   }
 2387 }
 2388 
 2389 =pod
 2390 
 2391 =item C<< $tlpdb->options >>
 2392 
 2393 Returns a reference to a hash with option names.
 2394 
 2395 =cut
 2396 
 2397 sub _keyshash {
 2398   my ($self, $pre, $hr) = @_;
 2399   my @allowed = keys %$hr;
 2400   my %ret;
 2401   my $pkg;
 2402   if ($self->is_virtual) {
 2403     $pkg = $self->{'tlpdbs'}{'main'}->get_package('00texlive.installation');
 2404   } else {
 2405     $pkg = $self->{'tlps'}{'00texlive.installation'};
 2406   }
 2407   if (defined($pkg)) {
 2408     foreach my $d ($pkg->depends) {
 2409       if ($d =~ m!^$pre([^:]*):(.*)!) {
 2410         if (member($1, @allowed)) {
 2411           $ret{$1} = $2;
 2412         } else {
 2413           tlwarn("TLPDB::_keyshash: Unsupported option/setting $d\n");
 2414         }
 2415       }
 2416     }
 2417   }
 2418   return \%ret;
 2419 }
 2420 
 2421 sub options {
 2422   my $self = shift;
 2423   return ($self->_keyshash('opt_', \%TLPDBOptions));
 2424 }
 2425 sub settings {
 2426   my $self = shift;
 2427   return ($self->_keyshash('setting_', \%TLPDBSettings));
 2428 }
 2429 
 2430 =pod
 2431 
 2432 =item C<< $tlpdb->format_definitions >>
 2433 
 2434 This function returns a list of references to hashes where each hash
 2435 represents a parsed AddFormat line.
 2436 
 2437 =cut
 2438 
 2439 sub format_definitions {
 2440   my $self = shift;
 2441   my @ret;
 2442   foreach my $p ($self->list_packages) {
 2443     my $obj = $self->get_package ($p);
 2444     die "$0: No TeX Live package named $p, strange" if ! $obj;
 2445     push @ret, $obj->format_definitions;
 2446   }
 2447   return(@ret);
 2448 }
 2449 
 2450 =item C<< $tlpdb->fmtutil_cnf_lines >>
 2451 
 2452 The function C<fmtutil_cnf_lines> returns the list of a fmtutil.cnf file
 2453 containing only those formats present in the installation.
 2454 
 2455 Every format listed in the tlpdb but listed in the arguments
 2456 will not be included in the list of lines returned.
 2457 
 2458 =cut
 2459 sub fmtutil_cnf_lines {
 2460   my $self = shift;
 2461   my @lines;
 2462   foreach my $p ($self->list_packages) {
 2463     my $obj = $self->get_package ($p);
 2464     die "$0: No TeX Live package named $p, strange" if ! $obj;
 2465     push @lines, $obj->fmtutil_cnf_lines(@_);
 2466   }
 2467   return(@lines);
 2468 }
 2469 
 2470 =item C<< $tlpdb->updmap_cfg_lines ( [@disabled_maps] ) >>
 2471 
 2472 The function C<updmap_cfg_lines> returns the list of a updmap.cfg file
 2473 containing only those maps present in the installation.
 2474 
 2475 A map file mentioned in the tlpdb but listed in the arguments will not 
 2476 be included in the list of lines returned.
 2477 
 2478 =cut
 2479 sub updmap_cfg_lines {
 2480   my $self = shift;
 2481   my @lines;
 2482   foreach my $p ($self->list_packages) {
 2483     my $obj = $self->get_package ($p);
 2484     die "$0: No TeX Live package named $p, strange" if ! $obj;
 2485     push @lines, $obj->updmap_cfg_lines(@_);
 2486   }
 2487   return(@lines);
 2488 }
 2489 
 2490 =item C<< $tlpdb->language_dat_lines ( [@disabled_hyphen_names] ) >>
 2491 
 2492 The function C<language_dat_lines> returns the list of all
 2493 lines for language.dat that can be generated from the tlpdb.
 2494 
 2495 Every hyphenation pattern listed in the tlpdb but listed in the arguments
 2496 will not be included in the list of lines returned.
 2497 
 2498 =cut
 2499 
 2500 sub language_dat_lines {
 2501   my $self = shift;
 2502   my @lines;
 2503   foreach my $p ($self->list_packages) {
 2504     my $obj = $self->get_package ($p);
 2505     die "$0: No TeX Live package named $p, strange" if ! $obj;
 2506     push @lines, $obj->language_dat_lines(@_);
 2507   }
 2508   return(@lines);
 2509 }
 2510 
 2511 =item C<< $tlpdb->language_def_lines ( [@disabled_hyphen_names] ) >>
 2512 
 2513 The function C<language_def_lines> returns the list of all
 2514 lines for language.def that can be generated from the tlpdb.
 2515 
 2516 Every hyphenation pattern listed in the tlpdb but listed in the arguments
 2517 will not be included in the list of lines returned.
 2518 
 2519 =cut
 2520 
 2521 sub language_def_lines {
 2522   my $self = shift;
 2523   my @lines;
 2524   foreach my $p ($self->list_packages) {
 2525     my $obj = $self->get_package ($p);
 2526     die "$0: No TeX Live package named $p, strange" if ! $obj;
 2527     push @lines, $obj->language_def_lines(@_);
 2528   }
 2529   return(@lines);
 2530 }
 2531 
 2532 =item C<< $tlpdb->language_lua_lines ( [@disabled_hyphen_names] ) >>
 2533 
 2534 The function C<language_lua_lines> returns the list of all
 2535 lines for language.dat.lua that can be generated from the tlpdb.
 2536 
 2537 Every hyphenation pattern listed in the tlpdb but listed in the arguments
 2538 will not be included in the list of lines returned.
 2539 
 2540 =cut
 2541 
 2542 sub language_lua_lines {
 2543   my $self = shift;
 2544   my @lines;
 2545   foreach my $p ($self->list_packages) {
 2546     my $obj = $self->get_package ($p);
 2547     die "$0: No TeX Live package named $p, strange" if ! $obj;
 2548     push @lines, $obj->language_lua_lines(@_);
 2549   }
 2550   return(@lines);
 2551 }
 2552 
 2553 =back
 2554 
 2555 =head1 VIRTUAL DATABASES
 2556 
 2557 The purpose of virtual databases is to collect several data sources
 2558 and present them in one way. The normal functions will always return
 2559 the best candidate for the set of functions.
 2560 
 2561 More docs to be written someday, maybe.
 2562 
 2563 =over 4
 2564 
 2565 =cut
 2566 
 2567 #
 2568 # packages are saved:
 2569 # $self->{'packages'}{$pkgname}{'tags'}{$tag}{'revision'} = $rev
 2570 # $self->{'packages'}{$pkgname}{'tags'}{$tag}{'tlp'} = $tlp
 2571 # $self->{'packages'}{$pkgname}{'target'} = $target_tag
 2572 #
 2573 
 2574 sub is_virtual {
 2575   my $self = shift;
 2576   if (defined($self->{'virtual'}) && $self->{'virtual'}) {
 2577     return 1;
 2578   }
 2579   return 0;
 2580 }
 2581 
 2582 sub make_virtual {
 2583   my $self = shift;
 2584   if (!$self->is_virtual) {
 2585     if ($self->list_packages) {
 2586       tlwarn("TLPDB: cannot convert initialized tlpdb to virtual\n");
 2587       return 0;
 2588     }
 2589     $self->{'virtual'} = 1;
 2590   }
 2591   return 1;
 2592 }
 2593 
 2594 sub virtual_get_tags {
 2595   my $self = shift;
 2596   return keys %{$self->{'tlpdbs'}};
 2597 }
 2598 
 2599 sub virtual_get_tlpdb {
 2600   my ($self, $tag) = @_;
 2601   if (!$self->is_virtual) {
 2602     tlwarn("TLPDB: cannot remove tlpdb from a non-virtual tlpdb!\n");
 2603     return 0;
 2604   }
 2605   if (!defined($self->{'tlpdbs'}{$tag})) {
 2606     tlwarn("TLPDB::virtual_get_tlpdb: unknown tag: $tag\n");
 2607     return 0;
 2608   }
 2609   return $self->{'tlpdbs'}{$tag};
 2610 }
 2611 
 2612 sub virtual_add_tlpdb {
 2613   my ($self, $tlpdb, $tag) = @_;
 2614   if (!$self->is_virtual) {
 2615     tlwarn("TLPDB: cannot virtual_add_tlpdb to a non-virtual tlpdb!\n");
 2616     return 0;
 2617   }
 2618   $self->{'tlpdbs'}{$tag} = $tlpdb;
 2619   for my $p ($tlpdb->list_packages) {
 2620     my $tlp = $tlpdb->get_package($p);
 2621     $self->{'packages'}{$p}{'tags'}{$tag}{'revision'} = $tlp->revision;
 2622     $self->{'packages'}{$p}{'tags'}{$tag}{'tlp'} = $tlp;
 2623   }
 2624   $self->check_evaluate_pinning();
 2625   return 1;
 2626 }
 2627 
 2628 sub virtual_remove_tlpdb {
 2629   my ($self, $tag) = @_;
 2630   if (!$self->is_virtual) {
 2631     tlwarn("TLPDB: Cannot remove tlpdb from a non-virtual tlpdb!\n");
 2632     return 0;
 2633   }
 2634   if (!defined($self->{'tlpdbs'}{$tag})) {
 2635     tlwarn("TLPDB: virtual_remove_tlpdb: unknown tag $tag\n");
 2636     return 0;
 2637   }
 2638   for my $p ($self->{'tlpdbs'}{$tag}->list_packages) {
 2639     delete $self->{'packages'}{$p}{'tags'}{$tag};
 2640   }
 2641   delete $self->{'tlpdbs'}{$tag};
 2642   $self->check_evaluate_pinning();
 2643   return 1;
 2644 }
 2645 
 2646 sub virtual_get_package {
 2647   my ($self, $pkg, $tag) = @_;
 2648   if (defined($self->{'packages'}{$pkg}{'tags'}{$tag})) {
 2649     return $self->{'packages'}{$pkg}{'tags'}{$tag}{'tlp'};
 2650   } else {
 2651     tlwarn("TLPDB: virtual pkg $pkg not found in tag $tag\n");
 2652     return;
 2653   }
 2654 }
 2655 
 2656 =item C<< $tlpdb->candidates ( $pkg ) >>
 2657 
 2658 Returns the list of candidates for the given package in the
 2659 format
 2660 
 2661   tag/revision
 2662 
 2663 If the returned list is empty, then the database was not virtual and
 2664 no install candidate was found.
 2665 
 2666 If the returned list contains undef as first element, the database
 2667 is virtual, and no install candidate was found.
 2668 
 2669 The remaining elements in the list are all repositories that provide
 2670 that package.
 2671 
 2672 Note that there might not be an install candidate, but still the
 2673 package is provided by a sub-repository. This can happen if a package
 2674 is present only in the sub-repository and there is no explicit pin
 2675 for that package in the pinning file.
 2676 
 2677 =cut
 2678 
 2679 sub is_repository {
 2680   my $self = shift;
 2681   my $tag = shift;
 2682   if (!$self->is_virtual) {
 2683     return ( ($tag eq $self->{'root'}) ? 1 : 0 );
 2684   }
 2685   return ( defined($self->{'tlpdbs'}{$tag}) ? 1 : 0 );
 2686 }
 2687 
 2688 
 2689 # returns a list of tag/rev
 2690 sub candidates {
 2691   my $self = shift;
 2692   my $pkg = shift;
 2693   my @ret = ();
 2694   if ($self->is_virtual) {
 2695     if (defined($self->{'packages'}{$pkg})) {
 2696       my $t = $self->{'packages'}{$pkg}{'target'};
 2697       if (defined($t)) {
 2698         push @ret, "$t/" . $self->{'packages'}{$pkg}{'tags'}{$t}{'revision'};
 2699       } else {
 2700         $t = "";
 2701         # no target found, but maybe available somewhere else,
 2702         # we return undef as first one
 2703         push @ret, undef;
 2704       }
 2705       # make sure that we always check for main as repo
 2706       my @repos = keys %{$self->{'packages'}{$pkg}};
 2707       for my $r (sort keys %{$self->{'packages'}{$pkg}{'tags'}}) {
 2708         push @ret, "$r/" . $self->{'packages'}{$pkg}{'tags'}{$r}{'revision'}
 2709           if ($t ne $r);
 2710       }
 2711     }
 2712   } else {
 2713     my $tlp = $self->get_package($pkg);
 2714     if (defined($tlp)) {
 2715       push @ret, "main/" . $tlp->revision;
 2716     }
 2717   }
 2718   return @ret;
 2719 }
 2720 
 2721 =item C<< $tlpdb->candidate ( ) >>
 2722 
 2723 Returns either a list of four undef, if no install candidate is found,
 2724 or the following information on the install candidate as list: the tag
 2725 name of the repository, the revision number of the package in the
 2726 candidate repository, the tlpobj of the package in the candidate
 2727 repository, and the candidate repository's TLPDB itself.
 2728 
 2729 =cut
 2730 
 2731 #
 2732 sub virtual_candidate {
 2733   my ($self, $pkg) = @_;
 2734   my $t = $self->{'packages'}{$pkg}{'target'};
 2735   if (defined($t)) {
 2736     return ($t, $self->{'packages'}{$pkg}{'tags'}{$t}{'revision'},
 2737       $self->{'packages'}{$pkg}{'tags'}{$t}{'tlp'}, $self->{'tlpdbs'}{$t});
 2738   }
 2739   return(undef,undef,undef,undef);
 2740 }
 2741 
 2742 =item C<< $tlpdb->virtual_pinning ( [ $pinfile_TLConfFile] ) >>
 2743 
 2744 Sets or returns the C<TLConfFile> object for the pinning data.
 2745 
 2746 =cut
 2747 
 2748 sub virtual_pindata {
 2749   my $self = shift;
 2750   return ($self->{'pindata'});
 2751 }
 2752 
 2753 sub virtual_update_pins {
 2754   my $self = shift;
 2755   if (!$self->is_virtual) {
 2756     tlwarn("TLPDB::virtual_update_pins: Non-virtual tlpdb can't have pins.\n");
 2757     return 0;
 2758   }
 2759   my $pincf = $self->{'pinfile'};
 2760   my @pins;
 2761   for my $k ($pincf->keys) {
 2762     for my $v ($pincf->value($k)) {
 2763       # we recompose the values into lines again, as we *might* have
 2764       # options later, i.e., lines of the format
 2765       #   repo:pkg:opt
 2766       push (@pins, $self->make_pin_data_from_line("$k:$v"));
 2767     }
 2768   }
 2769   $self->{'pindata'} = \@pins;
 2770   $self->check_evaluate_pinning();
 2771   return ($self->{'pindata'});
 2772 }
 2773 sub virtual_pinning {
 2774   my ($self, $pincf) = @_;
 2775   if (!$self->is_virtual) {
 2776     tlwarn("TLPDB::virtual_pinning: Non-virtual tlpdb can't have pins.\n");
 2777     return 0;
 2778   }
 2779   if (!defined($pincf)) {
 2780     return ($self->{'pinfile'});
 2781   }
 2782   $self->{'pinfile'} = $pincf;
 2783   $self->virtual_update_pins();
 2784   return ($self->{'pinfile'});
 2785 }
 2786 
 2787 #
 2788 # current format:
 2789 # <repo>:<pkg_glob>[,<pkg_glob>,...][:<options>]
 2790 # only supported options for now is
 2791 #   revision
 2792 # meaning that, if for the selected package there is no other
 2793 # "non-revision" pinning, then all repo/package versions are compared
 2794 # using normal revision comparison, and the biggest revision number wins.
 2795 # That allows you to have the same package in several repos:
 2796 #   repo1:foo:revision
 2797 #   repo2:foo:revision
 2798 #   repo1:*
 2799 #   repo2:*
 2800 # means that:
 2801 # for package "foo" the revision numbers of "foo" in the repos "repo1",
 2802 # "repo2", and "main" are numerically compared and biggest number wins.
 2803 # for all other packages of "repo1" and "repo2", other repositories
 2804 # are not considered.
 2805 #
 2806 # NOT IMPLEMENTED YET!!!
 2807 #
 2808 # $pin{'repo'} = $repo;
 2809 # $pin{'glob'} = $glob;
 2810 # $pin{'re'} = $re;
 2811 # $pin{'line'} = $line; # for debug/warning purpose
 2812 sub make_pin_data_from_line {
 2813   my $self = shift;
 2814   my $l = shift;
 2815   my ($a, $b, $c) = split(/:/, $l);
 2816   my @ret;
 2817   my %m;
 2818   $m{'repo'} = $a;
 2819   $m{'line'} = $l;
 2820   if (defined($c)) {
 2821     $m{'options'} = $c;
 2822   }
 2823   # split the package globs
 2824   for (split(/,/, $b)) {
 2825     # remove leading and terminal white space
 2826     s/^\s*//;
 2827     s/\s*$//;
 2828     my %mm = %m;
 2829     $mm{'glob'} = $_;
 2830     $mm{'re'} = glob_to_regex($_);
 2831     push @ret, \%mm;
 2832   }
 2833   return @ret;
 2834 }
 2835 
 2836 sub check_evaluate_pinning {
 2837   my $self = shift;
 2838   my @pins = (defined($self->{'pindata'}) ? @{$self->{'pindata'}} : ());
 2839   #
 2840   # run through the pin lines and make sure that all the conditions
 2841   # and requirements are obeyed
 2842   my %pkgs = %{$self->{'packages'}};
 2843   # main:*
 2844   my ($mainpin) = $self->make_pin_data_from_line("main:*");
 2845   # the default main:* is always considered to be matched
 2846   $mainpin->{'hit'} = 1;
 2847   push @pins, $mainpin;
 2848   # # sort pins so that we first check specific lines without occurrences of
 2849   # # special characters, and then those with special characters.
 2850   # # The definitions are based on glob style rules, saved in $pp->{'glob'}
 2851   # # so we simply check whether there is * or ? in the string
 2852   # @pins = sort {
 2853   #   my $ag = $a->{'glob'};
 2854   #   my $bg = $b->{'glob'};
 2855   #   my $cAs = () = $ag =~ /\*/g; # number of * in glob of $a
 2856   #   my $cBs = () = $bg =~ /\*/g; # number of * in glob of $b
 2857   #   my $cAq = () = $ag =~ /\?/g; # number of ? in glob of $a
 2858   #   my $cBq = () = $bg =~ /\?/g; # number of ? in glob of $b
 2859   #   my $aVal = 2 * $cAs + $cAq;
 2860   #   my $bVal = 2 * $cBs + $cBq;
 2861   #   $aVal <=> $bVal
 2862   # } @pins;
 2863   for my $pkg (keys %pkgs) {
 2864     PINS: for my $pp (@pins) {
 2865       my $pre = $pp->{'re'};
 2866       if (($pkg =~ m/$pre/) &&
 2867           (defined($self->{'packages'}{$pkg}{'tags'}{$pp->{'repo'}}))) {
 2868         $self->{'packages'}{$pkg}{'target'} = $pp->{'repo'};
 2869         # register that this pin was hit
 2870         $pp->{'hit'} = 1;
 2871         last PINS;
 2872       }
 2873     }
 2874   }
 2875   # check that all pinning lines where hit
 2876   # If a repository has a catch-all pin
 2877   #   foo:*
 2878   # then we do not warn about any other pin (foo:abcde) not being hit.
 2879   my %catchall;
 2880   for my $p (@pins) {
 2881     $catchall{$p->{'repo'}} = 1 if ($p->{'glob'} eq "*");
 2882   }
 2883   for my $p (@pins) {
 2884     next if defined($p->{'hit'});
 2885     next if defined($catchall{$p->{'repo'}});
 2886     tlwarn("tlmgr (TLPDB): pinning warning: the package pattern ",
 2887            $p->{'glob'}, " on the line:\n  ", $p->{'line'},
 2888            "\n  does not match any package\n");
 2889   }
 2890 }
 2891 
 2892 
 2893 # implementation copied from Text/Glob.pm (copyright Richard Clamp).
 2894 # changes made:
 2895 # remove $strict_leading_dot and $strict_wildcard_slash if calls
 2896 # and execute the code unconditionally, as we do not change the
 2897 # default settings of 1 of these two variables.
 2898 sub glob_to_regex {
 2899     my $glob = shift;
 2900     my $regex = glob_to_regex_string($glob);
 2901     return qr/^$regex$/;
 2902 }
 2903 
 2904 sub glob_to_regex_string
 2905 {
 2906     my $glob = shift;
 2907     my ($regex, $in_curlies, $escaping);
 2908     local $_;
 2909     my $first_byte = 1;
 2910     for ($glob =~ m/(.)/gs) {
 2911         if ($first_byte) {
 2912             $regex .= '(?=[^\.])' unless $_ eq '.';
 2913             $first_byte = 0;
 2914         }
 2915         if ($_ eq '/') {
 2916             $first_byte = 1;
 2917         }
 2918         if ($_ eq '.' || $_ eq '(' || $_ eq ')' || $_ eq '|' ||
 2919             $_ eq '+' || $_ eq '^' || $_ eq '$' || $_ eq '@' || $_ eq '%' ) {
 2920             $regex .= "\\$_";
 2921         }
 2922         elsif ($_ eq '*') {
 2923             $regex .= $escaping ? "\\*" : "[^/]*";
 2924         }
 2925         elsif ($_ eq '?') {
 2926             $regex .= $escaping ? "\\?" : "[^/]";
 2927         }
 2928         elsif ($_ eq '{') {
 2929             $regex .= $escaping ? "\\{" : "(";
 2930             ++$in_curlies unless $escaping;
 2931         }
 2932         elsif ($_ eq '}' && $in_curlies) {
 2933             $regex .= $escaping ? "}" : ")";
 2934             --$in_curlies unless $escaping;
 2935         }
 2936         elsif ($_ eq ',' && $in_curlies) {
 2937             $regex .= $escaping ? "," : "|";
 2938         }
 2939         elsif ($_ eq "\\") {
 2940             if ($escaping) {
 2941                 $regex .= "\\\\";
 2942                 $escaping = 0;
 2943             }
 2944             else {
 2945                 $escaping = 1;
 2946             }
 2947             next;
 2948         }
 2949         else {
 2950             $regex .= $_;
 2951             $escaping = 0;
 2952         }
 2953         $escaping = 0;
 2954     }
 2955     print "# $glob $regex\n" if debug;
 2956 
 2957     return $regex;
 2958 }
 2959 
 2960 sub match_glob {
 2961     print "# ", join(', ', map { "'$_'" } @_), "\n" if debug;
 2962     my $glob = shift;
 2963     my $regex = glob_to_regex $glob;
 2964     local $_;
 2965     grep { $_ =~ $regex } @_;
 2966 }
 2967 
 2968 =pod
 2969 
 2970 =back
 2971 
 2972 =head1 OPTIONS
 2973 
 2974 Options regarding the full TeX Live installation to be described are saved
 2975 in a package C<00texlive.config> as values of C<depend> lines. This special
 2976 package C<00texlive.config> does not contain any files, only depend lines
 2977 which set one or more of the following options:
 2978 
 2979 =over 4
 2980 
 2981 =item C<container_split_src_files/[01]>
 2982 
 2983 =item C<container_split_doc_files/[01]>
 2984 
 2985 These options specify that at container generation time the source and
 2986 documentation files for a package have been put into a separate container
 2987 named C<package.source.extension> and C<package.doc.extension>.
 2988 
 2989 =item C<container_format/I<format>>
 2990 
 2991 This option specifies a format for containers. The currently supported 
 2992 formats are C<xz> and C<zip>. But note that C<zip> is untested.
 2993 
 2994 =item C<release/I<relspec>>
 2995 
 2996 This option specifies the current release. The first four characters must
 2997 be a year.
 2998 
 2999 =item C<minrelease/I<relspec>>
 3000 
 3001 This option specifies the minimum release for which this repository is
 3002 valid.
 3003 
 3004 =back
 3005 
 3006 To set these options the respective lines should be added to
 3007 C<00texlive.config.tlpsrc>.
 3008 
 3009 =head1 SEE ALSO
 3010 
 3011 The modules L<TeXLive::TLPSRC>, L<TeXLive::TLPOBJ>, L<TeXLive::TLTREE>,
 3012 L<TeXLive::TLUtils>, etc., and the documentation in the repository:
 3013 C<Master/tlpkg/doc/>.
 3014 
 3015 =head1 AUTHORS AND COPYRIGHT
 3016 
 3017 This script and its documentation were written for the TeX Live
 3018 distribution (L<https://tug.org/texlive>) and both are licensed under the
 3019 GNU General Public License Version 2 or later.
 3020 
 3021 =cut
 3022 
 3023 1;
 3024 
 3025 ### Local Variables:
 3026 ### perl-indent-level: 2
 3027 ### tab-width: 2
 3028 ### indent-tabs-mode: nil
 3029 ### End:
 3030 # vim:set tabstop=2 shiftwidth=2 expandtab autoindent: #