Index: tlpkg/TeXLive/TLConfig.pm =================================================================== --- tlpkg/TeXLive/TLConfig.pm (revision 20263) +++ tlpkg/TeXLive/TLConfig.pm (working copy) @@ -173,7 +173,8 @@ our %TLPDBSettings = ( "platform" => [ "s", "Main platform for this computer" ], - "available_architectures" => [ "l", "All available/installed architectures" ] + "available_architectures" => [ "l", "All available/installed architectures" ], + "usertree" => [ "b", "This tree acts as user tree" ] ); our $WindowsMainMenuName = "TeX Live $ReleaseYear"; Index: tlpkg/TeXLive/TLMedia.pm =================================================================== --- tlpkg/TeXLive/TLMedia.pm (revision 20263) +++ tlpkg/TeXLive/TLMedia.pm (working copy) @@ -59,6 +59,8 @@ } if (-d "$location/texmf/web2c") { $media = 'DVD'; + } elsif (-d "$location/web2c") { + $media = 'DVD'; } elsif (-d "$location/$Archive") { $media = 'CD'; } else { @@ -247,8 +249,12 @@ # if a package is relocatable we have to cancel the reloc prefix # and unset the relocated setting # before we save it to the local tlpdb - if ($tlpobj->relocated) { - $tlpobj->cancel_reloc_prefix; + if ($reloc) { + if ($totlpdb->setting("usertree")) { + $tlpobj->cancel_reloc_prefix; + } else { + $tlpobj->replace_reloc_prefix; + } $tlpobj->relocated(0); } # we have to write out the tlpobj file since it is contained in the @@ -321,7 +327,11 @@ } elsif ($what =~ m,\.tar(\.xz)?$,) { my $type = defined($1) ? "xz" : "tar"; - $target .= "/$TeXLive::TLConfig::RelocTree" if $reloc; + if ($reloc) { + if (!$totlpdb->setting("usertree")) { + $target .= "/$TeXLive::TLConfig::RelocTree" if $reloc; + } + } # this is the case when we install from CD or the NET, or a backup # @@ -404,6 +414,7 @@ my ($self, $pkg, %opts) = @_; my $localtlpdb = $self->tlpdb; my $tlp = $localtlpdb->get_package($pkg); + my $usertree = $localtlpdb->setting("usertree"); if (!defined($tlp)) { tlwarn ("$pkg: package not present, cannot remove\n"); } else { @@ -430,9 +441,12 @@ # in user mode we just remove the prefix, in normal mode we # replace it with texmf-dist # since we don't have user mode + if ($tlp->relocated) { for (@files) { - s:^$RelocPrefix/:$RelocTree/:; + if (!$usertree) { + s:^$RelocPrefix/:$RelocTree/:; + } } } # Index: tlpkg/TeXLive/TLPOBJ.pm =================================================================== --- tlpkg/TeXLive/TLPOBJ.pm (revision 20263) +++ tlpkg/TeXLive/TLPOBJ.pm (working copy) @@ -473,6 +473,21 @@ sub cancel_reloc_prefix { my $self = shift; my @docfiles = $self->docfiles; + for (@docfiles) { s:^$RelocPrefix/::; } + $self->docfiles(@docfiles); + my @runfiles = $self->runfiles; + for (@runfiles) { s:^$RelocPrefix/::; } + $self->runfiles(@runfiles); + my @srcfiles = $self->srcfiles; + for (@srcfiles) { s:^$RelocPrefix/::; } + $self->srcfiles(@srcfiles); + # if there are bin files they have definitely NOT the + # texmf-dist prefix, so we cannot cancel it anyway +} + +sub replace_reloc_prefix { + my $self = shift; + my @docfiles = $self->docfiles; for (@docfiles) { s:^$RelocPrefix/:$RelocTree/:; } $self->docfiles(@docfiles); my @runfiles = $self->runfiles; Index: tlpkg/TeXLive/TLUtils.pm =================================================================== --- tlpkg/TeXLive/TLUtils.pm (revision 20263) +++ tlpkg/TeXLive/TLUtils.pm (working copy) @@ -1279,7 +1279,7 @@ # if a package is relocatable we have to cancel the reloc prefix # before we save it to the local tlpdb if ($tlpobj->relocated) { - $tlpobj->cancel_reloc_prefix; + $tlpobj->replace_reloc_prefix; } $totlpdb->add_tlpobj($tlpobj); # we have to write out the tlpobj file since it is contained in the Index: texmf/scripts/texlive/tlmgr.pl =================================================================== --- texmf/scripts/texlive/tlmgr.pl (revision 20263) +++ texmf/scripts/texlive/tlmgr.pl (working copy) @@ -128,6 +128,8 @@ "persistent-downloads" => "!", "no-execute-actions" => 1, "pause" => 1, + "usermode" => 1, + "usertree" => "=s", "print-platform|print-arch" => 1, "version" => 1, "help|h|?" => 1); @@ -333,13 +335,30 @@ pod2usage(-msg => $msg, -exitstatus => 1, -verbose => 1, @noperldoc); } + # + # the main tree we will be working on + $::maintree = $Master; + if ($opts{"usermode"}) { + # we could also try to detect that we don't have write permissions + # and switch to user mode automatically + if (defined($opts{"usertree"})) { + $::maintree = $opts{"usertree"}; + } else { + chomp($::maintree = `kpsewhich -var-value TEXMFHOME`); + } + } + # besides doing normal logging if -logfile is specified, we try to log # package related actions (install, remove, update) to # the package-log file TEXMFSYSVAR/web2c/tlmgr.log $packagelogged = 0; # how many msgs we logged chomp (my $texmfsysvar = `kpsewhich -var-value=TEXMFSYSVAR`); $packagelogfile = $opts{"package-logfile"}; - $packagelogfile ||= "$texmfsysvar/web2c/tlmgr.log"; + if ($opts{"usermode"}) { + $packagelogfile ||= "$::maintree/web2c/tlmgr.log"; + } else { + $packagelogfile ||= "$texmfsysvar/web2c/tlmgr.log"; + } # # Try to open the packagelog file, but do NOT die when that does not work if (!open(PACKAGELOG, ">>$packagelogfile")) { @@ -510,6 +529,9 @@ } elsif ($action =~ m/^show$/i) { action_show(); finish(0); + } elsif ($action =~ m/^init-usertree$/i) { + action_init_usertree(); + finish(0); } elsif ($action =~ m/^remove$/i) { action_remove(); } elsif ($action =~ /^paper$/) { @@ -573,6 +595,8 @@ { my $errors = 0; + my $sysmode = ($opts{"usermode"} ? "" : "-sys"); + if ($::files_changed) { $errors += do_cmd_and_check("mktexlsr"); $::files_changed = 0; @@ -603,14 +627,14 @@ } } for my $m (keys %do_disable) { - $errors += do_cmd_and_check("updmap-sys --nomkmap --nohash --disable $m"); + $errors += do_cmd_and_check("updmap$sysmode --nomkmap --nohash --disable $m"); } for my $m (keys %do_enable) { - my $str = "updmap-sys --nomkmap --nohash --enable " . + my $str = "updmap$sysmode --nomkmap --nohash --enable " . $::execute_actions{'enable'}{'maps'}{$m} . "=$m"; $errors += do_cmd_and_check($str); } - $errors += do_cmd_and_check("updmap-sys") if $run_full; + $errors += do_cmd_and_check("updmap$sysmode") if $run_full; } @@ -655,7 +679,7 @@ # first regenerate all formats --byengine for my $e (keys %updated_engines) { log ("updating formats based on $e\n"); - $errors += do_cmd_and_check("fmtutil-sys --no-error-if-no-format --byengine $e"); + $errors += do_cmd_and_check("fmtutil$sysmode --no-error-if-no-format --byengine $e"); } # now rebuild all other formats for my $f (keys %do_enable) { @@ -663,7 +687,7 @@ # ignore disabled formats next if !$::execute_actions{'enable'}{'formats'}{$f}{'mode'}; log ("(re)creating format dump $f\n"); - $errors += do_cmd_and_check("fmtutil-sys --byfmt $f"); + $errors += do_cmd_and_check("fmtutil$sysmode --byfmt $f"); $done_formats{$f} = 1; } } @@ -704,7 +728,7 @@ } if ($localtlpdb->option("create_formats") && !$::regenerate_all_formats) { - $errors += do_cmd_and_check("fmtutil-sys --byhyphen $lang"); + $errors += do_cmd_and_check("fmtutil$sysmode --byhyphen $lang"); } } } @@ -713,7 +737,7 @@ # if ($::regenerate_all_formats) { info("Regenerating all formats, this may take some time ..."); - $errors += do_cmd_and_check("fmtutil-sys --all"); + $errors += do_cmd_and_check("fmtutil$sysmode --all"); info("done\n"); $::regenerate_all_formats = 0; } @@ -881,8 +905,13 @@ # sub action_paper { init_local_db(); - chomp(my $texmfsysconfig = `kpsewhich -var-value=TEXMFSYSCONFIG`); - $ENV{"TEXMFCONFIG"} = $texmfsysconfig; + my $texmfconfig; + if ($opts{"usermode"}) { + chomp($texmfconfig = `kpsewhich -var-value=TEXMFCONFIG`); + } else { + chomp($texmfconfig = `kpsewhich -var-value=TEXMFSYSCONFIG`); + } + $ENV{"TEXMFCONFIG"} = $texmfconfig; my $action = shift @ARGV; if ($action =~ m/^paper$/i) { # generic paper @@ -894,7 +923,7 @@ "as in: tlmgr pdftex paper --list\n"); } elsif (!defined($newpaper)) { # tlmgr paper => show all current sizes. - TeXLive::TLPaper::paper_all($texmfsysconfig,undef); + TeXLive::TLPaper::paper_all($texmfconfig,undef); } elsif ($newpaper !~ /^(a4|letter)$/) { # tlmgr paper junk => complain. $newpaper = "the empty string" if !defined($newpaper); @@ -902,7 +931,7 @@ } else { # tlmgr paper {a4|letter} => do it. return if !check_on_writable(); - TeXLive::TLPaper::paper_all($texmfsysconfig,$newpaper); + TeXLive::TLPaper::paper_all($texmfconfig,$newpaper); } } else { # program-specific paper @@ -919,7 +948,7 @@ return if !check_on_writable(); } unshift(@ARGV, "--list") if $opts{"list"}; - TeXLive::TLPaper::do_paper($prog,$texmfsysconfig,@ARGV); + TeXLive::TLPaper::do_paper($prog,$texmfconfig,@ARGV); } } @@ -971,6 +1000,7 @@ print "longdesc: ", $tlp->longdesc, "\n" if ($tlp->longdesc); print "installed: ", ($installed ? "Yes" : "No"), "\n"; print "revision: ", $tlp->revision, "\n" if ($installed); + print "relocatable: ", ($tlp->relocated ? "Yes" : "No"), "\n"; print "cat-version: ", $tlp->cataloguedata->{'version'}, "\n" if $tlp->cataloguedata->{'version'}; print "cat-date: ", $tlp->cataloguedata->{'date'}, "\n" @@ -1071,6 +1101,10 @@ # PATH # sub action_path { + if ($opts{"usermode"}) { + tlwarn("action `path' not supported in usermode!\n"); + exit 1; + } my $what = shift @ARGV; if (!defined($what) || ($what !~ m/^(add|remove)$/i)) { tlwarn("tlmgr: action path needs one argument, either add or remove\n"); @@ -2078,7 +2112,10 @@ # check for updates to tlmgr and die unless either --force or --list or --self # is given - my @critical = check_for_critical_updates($localtlpdb, $tlmediatlpdb); + my @critical; + if (!$opts{"usermode"}) { + @critical = check_for_critical_updates($localtlpdb, $tlmediatlpdb); + } my $dry_run_cont = $opts{"dry-run"} && ($opts{"dry-run"} < 0); if ( !$dry_run_cont && !$opts{"self"} && @critical) { critical_updates_warning(); @@ -2939,16 +2976,18 @@ init_tlmedia(); # check for updates to tlmgr itself, and die unless --force is given - if (check_for_critical_updates( $localtlpdb, $tlmediatlpdb)) { - critical_updates_warning(); - if ($opts{"force"}) { - tlwarn("Continuing due to --force\n"); - } else { - if ($::gui_mode) { - # return here and don't do any updates - return; + if (!$opts{"usermode"}) { + if (check_for_critical_updates( $localtlpdb, $tlmediatlpdb)) { + critical_updates_warning(); + if ($opts{"force"}) { + tlwarn("Continuing due to --force\n"); } else { - die "tlmgr: Not continuing, please see warning above!\n"; + if ($::gui_mode) { + # return here and don't do any updates + return; + } else { + die "tlmgr: Not continuing, please see warning above!\n"; + } } } } @@ -2961,7 +3000,7 @@ @packs = $tlmediatlpdb->expand_dependencies("-only-arch", $localtlpdb, @ARGV) unless $opts{"no-depends-at-all"}; # now expand all others unless $opts{"no-depends"} # if $opts{"reinstall"} do not collection->collection dependencies - if ($opts{"reinstall"}) { + if ($opts{"reinstall"} || $opts{"usermode"}) { @packs = $tlmediatlpdb->expand_dependencies("-no-collections", $localtlpdb, @packs) unless $opts{"no-depends"}; } else { @packs = $tlmediatlpdb->expand_dependencies($localtlpdb, @packs) unless $opts{"no-depends"}; @@ -3029,6 +3068,15 @@ foreach my $pkg (@todo) { my $flag = $FLAG_INSTALL; my $re = ""; + my $tlp = $tlmediasrc->tlpdb->get_package($pkg); + if (!defined($tlp)) { + info("Unknown package $pkg\n"); + next; + } + if (!$tlp->relocated && $opts{"usermode"}) { + info("Package $pkg is not relocatable, cannot install it in user mode!\n"); + next; + } if (defined($localtlpdb->get_package($pkg))) { if ($opts{"reinstall"}) { $re = "re"; @@ -3248,11 +3296,11 @@ # now also save the TLPOBJ of 00texlive.installation my $tlpo = $localtlpdb->get_package("00texlive.installation"); if ($tlpo) { - if (open(TOFD, ">$Master/tlpkg/tlpobj/00texlive.installation.tlpobj")) { + if (open(TOFD, ">$::maintree/tlpkg/tlpobj/00texlive.installation.tlpobj")) { $tlpo->writeout(\*TOFD); close(TOFD); } else { - tlwarn("Cannot save 00texlive.installation to $Master/tlpkg/tlpobj/00texlive.installation.tlpobj\n"); + tlwarn("Cannot save 00texlive.installation to $::maintree/tlpkg/tlpobj/00texlive.installation.tlpobj\n"); } } } else { @@ -3285,6 +3333,10 @@ warn("action `platform' not supported on Windows\n"); return(); } + if ($opts{"usermode"}) { + tlwarn("action `platform' not supported in usermode\n"); + exit 1; + } my $what = shift @ARGV; init_local_db(1); info("platform: dry run, no changes will be made\n") if $opts{"dry-run"}; @@ -3421,6 +3473,10 @@ # GENERATE # sub action_generate { + if ($opts{"usermode"}) { + tlwarn("action `generate' not supported in usermode!\n"); + exit 1; + } my $what = shift @ARGV; init_local_db(); @@ -4284,6 +4340,35 @@ } } +# INIT USER TREE +# sets up the user tree for tlmgr in user mode +sub action_init_usertree { + init_local_db(); + my $tlpdb = TeXLive::TLPDB->new; + my $usertree; + if ($opts{"usertree"}) { + $usertree = $opts{"usertree"}; + } else { + chomp($usertree = `kpsewhich -var-value TEXMFHOME`); + } + $tlpdb->root($usertree); + my $inst = TeXLive::TLPOBJ->new; + $inst->name("00texlive.installation"); + $inst->category("TLCore"); + my @deps; + push @deps, "opt_location:$location"; + push @deps, "setting_available_architectures:"; + push @deps, "setting_usertree:1"; + $inst->depends(@deps); + $tlpdb->add_tlpobj($inst); + $tlpdb->save; + # + # we need to create web2c dir for TLMedia to succeed setting up + # and for tlmgr.log file + mkdir ("$usertree/web2c"); + mkdir ("$usertree/tlpkg/tlpobj"); +} + # CONF # tries to mimic texconfig conf but can also set values for both tlmgr # and texmf conf files. @@ -4408,10 +4493,10 @@ # stuff changed via the GUI return if defined $localtlmedia; return if defined $localtlpdb; - $localtlmedia = TeXLive::TLMedia->new ( $Master ); - die("cannot setup TLMedia in $Master") unless (defined($localtlmedia)); + $localtlmedia = TeXLive::TLMedia->new ( $::maintree ); + die("cannot setup TLMedia in $::maintree") unless (defined($localtlmedia)); $localtlpdb = $localtlmedia->tlpdb; - die("cannot find tlpdb in $Master") unless (defined($localtlpdb)); + die("cannot find tlpdb in $::maintree") unless (defined($localtlpdb)); # setup the programs, for w32 we need the shipped wget/xz etc, so we # pass the location of these files to setup_programs. if (!setup_programs("$Master/tlpkg/installer", $localtlmedia->platform)) { @@ -4845,6 +4930,7 @@ } sub check_on_writable { + return 1 if $opts{"usermode"}; if (!TeXLive::TLUtils::dir_writable("$Master/tlpkg")) { tlwarn("You don't have permission to change the installation in any way,\n"); tlwarn("specifically, the directory $Master/tlpkg/ is not writable.\n"); @@ -5017,6 +5103,15 @@ likely untranslated, messages to standard error. Helpful for translators to see what remains to be done. +=item B<--usermode> + +Activates the user mode, see L below. + +=item B<--usertree> I + +Uses I as user tree in user mode, seel L below. + + =back The standard options for TeX Live programs are also accepted: @@ -5923,7 +6018,110 @@ C, you need to run C yourself after making any local changes. + +=head2 init-usertree +sets up the default user tree (C) or the one specified +on the command line using C<--usertree> for user mode management. +See L below. + + +=head1 USER MODE + +B B B +This is very much work in progress +and experimental. Expect breakage, and the need to reinstall your +user tree a new. + +C provides a restricted way to manage arbitrary texmf trees +in the same way as the installation. This allows a user without write +permissions to the installation location to update/install packages +into a user tree. + +C is set into user mode by giving the command line option +C<--usermode>. It will not switch automatically, nor does it support +a config file by now. Thus, this option has to be given every time. + +This mode of C works on a user tree, which by default will be +the value of C, but can be overridden with the command line +option C<--usertree>. In the following when we speak of the user tree +we mean either C or the one given on the command line. + +Not all actions are allowed in user mode, C will warn you and +not carry out this action. The actions of C can be classified into +those that don't need write permissions and thus are working the same +in user mode or normal mode. Currently these are +B, B, B, B, B, B, B. + +Most of the actions dealing with package management on the other hand +need write permissions, and behave differently: +B, B, B, B