#!/usr/bin/env perl
# $Id: install-menu-text.pl 65951 2023-02-20 10:13:03Z siepo $
#
# Copyright 2007-2022 Norbert Preining, Karl Berry
# Copyright 2007-2008 Reinhard Kotucha
# This file is licensed under the GNU General Public License version 2
# or any later version.
#
# This file implements the text based menu system for the TeX Live installer.

use vars qw(@::end_install_hook
           $::opt_no_cls $::opt_select_repository $::run_menu);

our %vars;
our $opt_in_place;
our $tlpdb;
our @media_available;
our $media;
our $previoustlpdb;
our @collections_std;
our $texlive_release;

my $MENU_CONTINUE = -1;
our $MENU_INSTALL = 0;
our $MENU_ABORT   = 1;
our $MENU_QUIT    = 2;


my $RETURN = $MENU_CONTINUE;

my $portable_toggled = 0;
my $ptoggle_alert = "\n".
   "   !! Portable option changed;\n" .
   "   !! Directories have been reinitialized!\n";

# some strings to describe the different meanings of tlpdbopt_file_assoc
$::fileassocdesc[0] = __("None");
$::fileassocdesc[1] = __("Only new");
$::fileassocdesc[2] = __("All");

$::deskintdesc[0] = __("None");
$::deskintdesc[1] = __("Menu shortcuts");
if (wndws()) { $::deskintdesc[2] = __("Launcher"); }

sub clear_screen {
 return 0 if ($::opt_no_cls);
 system (unix() ? 'clear' : 'cls');
}

sub string_to_list {
 my $string=shift;
 return split(//, $string);
}

sub button { # for main menu: 1 char
 my $val=shift;
 my $vals=shift; # array ref to descriptions of possible values
 if (defined $vals) {
   return "[$val]";
 } else { # just yes/no
   return ($val)? '[X]':'[ ]';
 }
}

sub obutton { # for options menu: more chars allowed
 my $val=shift;
 my $vals=shift; # array ref to descriptions of possible values
 $vals = [' ', 'X'] unless defined $vals;
 return "[$$vals[$val]]";
}

sub hbar {
 return '=' x79, "\n";
}

sub toggle {
 my $var=shift;
 my $vals=shift; # array ref to descriptions of possible values
 $vals = [' ', 'X'] unless defined $vals;
 my $n_vals = @$vals;
 $vars{$var} += 1;
 $vars{$var} = 0 unless $vars{$var} < $n_vals;
}

sub menu_head {
 my $text = shift;
 clear_screen;
 print hbar(), "$text\n\n";
}

sub other_options {
 my @options=@_;
 my %opts=(
   '-' => 'deselect all',
   '+' => 'select all',
   'R' => 'return to main menu',
   'P' => 'save installation profile to \'texlive.profile\' and exit',
   'Q' => 'quit'
     );

 $opts{'I'}=$vars{'instopt_portable'} ? 'start portable installation' :
   'start installation to hard disk';

 print "\nActions:";
 if ($options[$#options] eq 'diskspace') {
   pop @options;
   calc_depends ();
   print " (disk space required: $vars{'total_size'} MB)";
 }
 print "\n";

 for my $option (@options) {
   if (defined $opts{"$option"}) {
     printf " <%s> %s\n", $option, $opts{$option};
   } else {
     die "other_options: $opts{$option} undefined.\n";
   }
 }
}

sub prompt {
 my $prompt = shift;
 print "\n$prompt: ";
 my $answer = <STDIN>;
 $answer = "q" if !defined($answer);
 chomp($answer);
 return "$answer";
}

# The menu loop. A menu is a function.  Its return value is a
# reference to another menu or to itself.
sub run_menu_text {
 my (@args) = @_;
 my $default = 0;
 # select mirror if -select-mirror is given
 if ($::opt_select_repository) {
   # network is always available
   my @mirror_list = TeXLive::TLUtils::create_mirror_list();
   print "Please select a repository:\n";
   print "Network repositories:" if ($#media_available >= 0);
   my @sel_to_index;
   my $selind = 0;
   # this is for 0 mirror.ctan.org, but it is printed last!
   push @sel_to_index, 0;
   $selind++;
   for my $i (0..$#mirror_list) {
     if ($mirror_list[$i] !~ m/^  /) {
       print "\n$mirror_list[$i]\n";
     } else {
       print " " if 1 <= $selind && $selind <= 9; # align
       print "[$selind] $mirror_list[$i]\n";
       push @sel_to_index, $i;
       $selind++;
     }
   }
   print "----\n";
   print "[0] default mirror   https://mirror.ctan.org\n";
   my $local_ind = "a";
   if ($#media_available >= 0) {
     print "Local repositories:\n";
     # we have some local media present, propose to use it
     for my $l (@media_available) {
       my ($a, $b) = split ('#', $l);
       if ($a eq 'local_compressed') {
         print "[$local_ind] compressed archive at $b\n";
         $default = $local_ind;
         #$local_ind = chr(ord($local_ind)+1);
         $local_ind++;
       } elsif ($a eq 'local_uncompressed') {
         print "[$local_ind] uncompressed archive at $b\n";
         $default = $local_ind;
         $local_ind++;
       } elsif ($a eq 'NET') {
         print "[$local_ind] cmd line repository: $b\n";
         $default = $local_ind;
         $local_ind++;
       } else {
         warn "$0: Unknown media $l";
       }
     }
   }
   print "[q] quit\n";
   $selind--;
   my $selstr = "Your selection ";
   if ($local_ind ne "a") {
     # we got at least some local repository
     if ($local_ind eq "b") {
       $selstr .= "(a,0-$selind,q)";
       $local_ind = chr(ord($local_ind)-1);
     } else {
       # that does not work!!!
       #$local_ind--;
       $local_ind = chr(ord($local_ind)-1);
       $selstr .= "(a-$local_ind,0-$selind,q)";
     }
   }
   $selstr .= " [$default]: ";
   my $got_answer = 0;
   my $ans = undef;
   while (!defined($ans)) {
     print $selstr;
     $ans = readline(*STDIN);
     if (!defined($ans)) {
       print "Please select `q' to quit the program!\n";
     } else {
       chomp($ans);
       $ans = $default if ($ans eq "");
       if ($ans =~ m/^[0-9]+$/) {
         if (0 <= $ans && $ans <= $selind) {
           my $mfull;
           if ($ans == 0) {
             $::init_remote_needed = 'ctan';
           } else {
             # only if something else but the predefined mirror is selected
             # we something here
             $mfull = TeXLive::TLUtils::extract_mirror_entry($mirror_list[$sel_to_index[$ans]]);
             print "selected mirror: ", $mfull, "\n";
             $::init_remote_needed = $mfull;
           }
         }
       } elsif ($ans =~ m/^[a-$local_ind]$/) {
         my $i = ord($ans) - ord('a');
         my $t = $media_available[$i];
         $t =~ s/^[^#]*#//;
         $::init_remote_needed = $t;
       } elsif ($ans eq 'q' || $ans eq 'Q') {
         print "Goodbye.\n";
         exit 0;
       } else {
         print "Not a valid answer: $ans.\n";
         $ans = undef;
       }
     }
   }
 }

 # run remote init
 if (!do_remote_init($::init_remote_needed)) {
   warn "\n";
   warn "Please select a different mirror!  See info above.\n";
   print STDERR "Press Enter to exit... ";
   my $ans = readline (*STDIN);
   exit (1);
 }

 # the text mode installer does not take look at any argument
 # except -old-installation-found.
 while (@args) {
   my $f = shift @args;
   if ($f =~ m/^-old-installation-found=(.*)$/) {
     my $dn = $1;
     print "\nAn existing installation of TeX Live has been found in $dn\n";
     print "
If you want its selection of schemes/collections and various options
to be used, press `y', otherwise anything else.

Import settings from previous TeX Live installation: (y/n): ";
     chomp(my $yn = <STDIN>);
     if (defined $yn && $yn =~ m/^y$/i) {
       import_settings_from_old_tlpdb($dn);
     }
   }
 }
 my $menu=\&main_menu;
 while ($RETURN == $MENU_CONTINUE) {
   $menu=$menu->();
 }
 return($RETURN);
}
$::run_menu = \&run_menu_text;

sub binary_menu {
 my %command=(
   'self' => \&binary_menu,
   'R' => \&main_menu,
   'Q' => \&quit
     );

 my @binaries;
 my @keys=string_to_list "abcdefghijklmopstuvwxyz";
 my $index=0;
 my %keyval;
 my $selected_platform;

 menu_head "Available platforms:";

 foreach my $key (keys %vars) {
   if ($key =~ /binary_(.*)/) {
     push @binaries, $1;
   }
 }

 @binaries=sort TeXLive::TLUtils::sort_archs @binaries;

 foreach my $binary (@binaries) {
   printf "   %s %s %s (%s)\n", $keys[$index],
          button($vars{"binary_$binary"}),
          platform_desc($binary),
          "$binary";
   $keyval{"$keys[$index]"} = "binary_$binary";
   ++$index;
 }
 other_options qw(- + R Q diskspace);

 my $answer = prompt 'Enter letter(s) to (de)select platforms';

 my @keystrokes=string_to_list $answer;

 foreach my $keystroke (@keystrokes) {
   if ($keystroke eq '-') {
     for my $binary (@binaries) {
       $vars{"binary_$binary"}=0 if defined $vars{"binary_$binary"};
     }
   }
   if ($keystroke eq '+') {
     for my $binary (@binaries) {
       $vars{"binary_$binary"}=1 if defined $vars{"binary_$binary"};
     }
   }
   if (defined $keyval{$keystroke}) {
     toggle "$keyval{$keystroke}";
   } elsif (!defined $command{"\u$answer"}) {
     print "Unknown command: $answer\n\n";
   }
 }
 if ($vars{"binary_windows"}) {
   $vars{"collection-wintools"} = 1;
 } else {
   $vars{"collection-wintools"} = 0;
 }
 if (defined $command{"\u$answer"}) {
   return $command{"\u$answer"}->();
 } else {
   return $command{'self'}->();
 }
}


sub scheme_menu {
 my %command=(
   'self' => \&scheme_menu,
   'R' => \&main_menu,
   'Q' => \&quit
     );

 my @schemes;
 my @keys=string_to_list "abcdefghijklmnopstuvwxyz";
 my %keyval;
 my $index=0;

 menu_head 'Select scheme:';

 @schemes = schemes_ordered_for_presentation();
 foreach my $pkg (@schemes) {
   $vars{"$pkg"}=($vars{'selected_scheme'} eq $pkg)? 1:0;
 }
 push @schemes, "scheme-custom";

 foreach my $scheme (@schemes) {
   $keyval{$keys[$index]}="$scheme";
   if ($scheme ne "scheme-custom") {
     my $tlpobj = $tlpdb->get_package("$scheme");
     printf " %s %s %s\n", $keys[$index], button($vars{"$scheme"}),
            $tlpobj->shortdesc;
   } else {
     printf " %s %s custom selection of collections\n",
            $keys[$index], button($vars{'selected_scheme'} eq "scheme-custom");
   }
   ++$index;
 }

 select_scheme($vars{'selected_scheme'});

 if ($vars{"binary_windows"}) {
   $vars{"collection-wintools"} = 1;
 } else {
   $vars{"collection-wintools"} = 0;
 }

 other_options qw(R Q diskspace);
 my $answer = prompt 'Enter letter to select scheme';

 if (defined $keyval{"$answer"}) {
   $vars{'selected_scheme'}=$keyval{"$answer"};
   select_scheme($vars{'selected_scheme'});
   return $command{'self'}->();
 }
 if (defined $command{"\u$answer"}) {
   return $command{"\u$answer"}->();
 } else {
   print "Unknown command: $answer\n\n";
   return $command{'self'}->();
 }
}


sub collection_menu {
 my %command=(
   'self' => \&collection_menu,
   'R' => \&main_menu,
   'Q' => \&quit
     );

 my @collections;
 my @keys=string_to_list "abcdefghijklmnopstuvwxyzABCDEFGHIJKLMNOPSTUVWXYZ";
 my %keyval;
 my $index=0;
 my @coll_short_desc;
 my @coll_long_desc;

 menu_head 'Select collections:';

 @collections=sort @collections_std;

 foreach my $collection (@collections) {
   next if ($collection eq 'collection-perl');
   my $tlpobj = $tlpdb->get_package("$collection");
   if (length $tlpobj->shortdesc>30) {
     push @coll_long_desc, $collection;
   } else {
     push @coll_short_desc, $collection;
   }
 }
 my $singlecolumn_index=@coll_short_desc-1;

##<cols=2>
 my $lines=@coll_short_desc/2;
 ++$lines if (@coll_short_desc%2);
 for (0..$lines-1) {
   $index=$_;
   my $collection=$coll_short_desc[$index];
   my $tlpobj = $tlpdb->get_package("$collection");
   $keyval{$keys[$index]}="$collection";
   printf " %s %s %-33s", $keys[$index], button($vars{"$collection"}),
   substr($tlpobj->shortdesc,0,33);
   if (defined $coll_short_desc[$index+$lines]) {
     my $collection=$coll_short_desc[$index+$lines];
     my $tlpobj=$tlpdb->get_package("$collection");
     $keyval{$keys[$index+$lines]}="$collection";
     printf " %s %s %-32s\n", $keys[$index+$lines],
     button($vars{"$collection"}), substr($tlpobj->shortdesc,0,32);
   } else {
     print "\n";
   }
 }
##</cols=2>
 $index=$singlecolumn_index;
#  print "\n$index\n\n";
 foreach my $collection (@coll_long_desc) {
   my $tlpobj=$tlpdb->get_package("$collection");
   $keyval{$keys[$index+1]}="$collection";
   printf " %s %s %s\n", $keys[$index+1], button($vars{"$collection"}),
   $tlpobj->shortdesc;
   ++$index;
 }
##</cols=1>

 other_options qw(- + R Q diskspace);
 my $answer = prompt 'Enter letter(s) to (de)select collection(s)';

 my @keystrokes=string_to_list $answer;

 foreach my $keystroke (@keystrokes) {
   if ($keystroke eq '-') {
     for my $collection (@collections) {
       $vars{"$collection"}=0 if defined $vars{"$collection"};
     }
   }
   if ($keystroke eq '+') {
     for my $collection (@collections) {
       $vars{"$collection"}=1 if defined $vars{"$collection"};
     }
   }
   if (defined $keyval{$keystroke}) {
     toggle "$keyval{$keystroke}";
   } elsif (!defined $command{"\u$answer"}) {
     print "Unknown command: $answer\n\n";
   }
 }

 if (defined $command{"\u$answer"}) {
   # if we play around with collections we also select custom-scheme
   # but we do not switch back to originally afterwards, too complicated
   # to be done
   select_scheme("scheme-custom");
   return $command{"\u$answer"}->();
 } else {
   return $command{'self'}->();
 }
}


sub directories_menu
{
 my %command=(
   'self' => \&directories_menu,
   'R' => \&main_menu,
   'Q' => \&quit
     );

 menu_head "Directories customization:";
 if (!TeXLive::TLUtils::texdir_check($vars{'TEXDIR'}, 1)) {
   print "!! The default location as given below is forbidden or
!! can't be written to.
!! Either change the destination directory using <1> or create it
!! outside this script.
";
 }
 my $texmfdir = $vars{'TEXDIR'} .
   ($vars{'TEXDIR'} =~ /\/$/ ? 'texmf-dist' : '/texmf-dist');
 if (!$opt_in_place) {
   print <<"EOF";
<1> TEXDIR:         $vars{'TEXDIR'}
    main tree:      $texmfdir
EOF
 } else {
   print <<"EOF";
    TEXDIR:         $vars{'TEXDIR'}
    main tree:      $texmfdir
EOF
 }
 if (!$vars{'instopt_portable'}) {
   print <<"EOF";

<2> TEXMFLOCAL:     $vars{'TEXMFLOCAL'}
<3> TEXMFSYSVAR:    $vars{'TEXMFSYSVAR'}
<4> TEXMFSYSCONFIG: $vars{'TEXMFSYSCONFIG'}

<5> TEXMFVAR:       $vars{'TEXMFVAR'}
<6> TEXMFCONFIG:    $vars{'TEXMFCONFIG'}
<7> TEXMFHOME:      $vars{'TEXMFHOME'}

EOF

   if (wndws()) {
     print " Note: ~ will expand to %USERPROFILE%\n";
   } else {
     print " Note: ~ will expand to \$HOME (or to %USERPROFILE% on Windows)\n";
   }
 }

 other_options qw(R Q);
 my $answer = prompt 'Enter command';

 if ("\u$answer" eq '1' and !$opt_in_place) {
   print "New value for TEXDIR [$vars{'TEXDIR'}]: ";
   $answer = &input_dirname ();
   # update free space information
   if ($answer ne $vars{'TEXDIR'}) {
     $vars{'free_size'} = TeXLive::TLUtils::diskfree($answer);
   }
   $vars{'TEXDIR'} = $answer if $answer ne "";
   my $texdirnoslash;
   if ($vars{'TEXDIR'}=~/^(.*)\/$texlive_release$/) {
     $texdirnoslash = $1;
     $vars{'TEXMFLOCAL'}="$texdirnoslash/texmf-local";
     $vars{'TEXMFSYSVAR'}="$texdirnoslash/$texlive_release/texmf-var";
     $vars{'TEXMFSYSCONFIG'}="$texdirnoslash/$texlive_release/texmf-config";
   } elsif ($vars{'TEXDIR'}=~/^(.*)$/) {
     $texdirnoslash = $1;
     $texdirnoslash =~ s!/$!!;
     $vars{'TEXMFLOCAL'}="$texdirnoslash/texmf-local";
     $vars{'TEXMFSYSVAR'}="$texdirnoslash/texmf-var";
     $vars{'TEXMFSYSCONFIG'}="$texdirnoslash/texmf-config";
   }
   return $command{'self'};

 } elsif ("\u$answer" eq '2' and !$vars{'instopt_portable'}) {
   print "New value for TEXMFLOCAL [$vars{'TEXMFLOCAL'}]: ";
   $answer = &input_dirname ();
   $vars{'TEXMFLOCAL'} = $answer if $answer ne "";
   return $command{'self'};

 } elsif ("\u$answer" eq '3' and !$vars{'instopt_portable'}) {
   print "New value for TEXMFSYSVAR [$vars{'TEXMFSYSVAR'}]: ";
   $answer = &input_dirname ();
   $vars{'TEXMFSYSVAR'} = $answer if $answer ne "";
   return $command{'self'};

 } elsif ("\u$answer" eq '4' and !$vars{'instopt_portable'}) {
   print "New value for TEXMFSYSCONFIG [$vars{'TEXMFSYSCONFIG'}]: ";
   $answer = &input_dirname ();
   $vars{'TEXMFSYSCONFIG'} = $answer if $answer ne "";
   return $command{'self'};

 } elsif ("\u$answer" eq '5' and !$vars{'instopt_portable'}) {
   print "New value for TEXMFVAR [$vars{'TEXMFVAR'}]: ";
   $answer = &input_dirname ("noexpansion");
   $vars{'TEXMFVAR'} = $answer if $answer ne "";
   return $command{'self'};

 } elsif ("\u$answer" eq '6' and !$vars{'instopt_portable'}) {
   print "New value for TEXMFCONFIG [$vars{'TEXMFCONFIG'}]: ";
   $answer = &input_dirname ("noexpansion");
   $vars{'TEXMFCONFIG'} = $answer if $answer ne "";
   return $command{'self'};

 } elsif ("\u$answer" eq '7' and !$vars{'instopt_portable'}) {
   print "New value for TEXMFHOME [$vars{'TEXMFHOME'}]: ";
   $answer = &input_dirname ("noexpansion");
   $vars{'TEXMFHOME'} = $answer if $answer ne "";
   return $command{'self'};
 }

 if (defined $command{"\u$answer"}) {
   return $command{"\u$answer"}->();
 } else {
   print "Unknown command: $answer\n\n";
   return $command{'self'}->();
 }
}


# Helper function to read a directory name and clean it up.
# Unless NO_EXPANSION is true, convert to absolute path.
#
sub input_dirname
{
 my $noexpansion = shift;
 chomp (my $answer = <STDIN>);
 return "" if $answer eq "";

 $answer =~ s!\\!/!g if wndws();  # switch to forward slashes

 if (!$noexpansion) {
   $answer = TeXLive::TLUtils::expand_tilde($answer);
 }

 if ($answer !~ m/^~/) {
   # relative paths are unlikely to work in texmf.cnf, et al.,
   # and don't have any apparent practical use.  Convert to absolute.
   if (! File::Spec->file_name_is_absolute($answer)) {
     $answer = TeXLive::TLUtils::tl_abs_path($answer);
     $answer = "" unless defined $answer;
   }
 }
 return $answer;
}


$vars{'page'}=0;

sub html2text {
 my $filename=shift;
 my @text;
 open IN, "$filename";
 @all_lines=<IN>;
 close IN;
 chomp @all_lines;

 my $itemcnt;
 my $ordered_list=0;
 my $h1_indent=25;
 my $h2_indent=3;
 my $h3_indent=6;

 for (@all_lines) {
   next if /DOCTYPE/;
   next if /<!--/;
   next if /<title/i;
   next if /<\/?body/i;
   next if /<\/?html/i;
   next if /<\/?head/i;
   next if /<\/?meta/i;
   next if /^\s*$/; # ignore empty lines

   s/<i>/"/gi;  s/<\/i>/"/gi;  # italics
   s/<tt>/'/gi; s/<\/tt>/'/gi; # typewriter
   s/<p>.*//gi;                # paragraphs
   s/<\/ul>.*//gi;             # unsorted lists
   s/<\/ol>.*//gi;             # ordered lists
   s/&mdash;/--/gi;            # mdash
   s/&lt;/</gi; s/&gt;/>/gi;   # < and >
   if (/<h1>(.*?)<\/h1>/i) {
     push @text, " " x $h1_indent. "$1\n";
     push @text, " " x $h1_indent. "=" x (length $1). "\n";
     push @text, "\n";
   } elsif (/<h2>(.*?)<\/h2>/i) {
     push @text, "\n";
     push @text, " " x $h2_indent. "$1\n";
     push @text, " " x $h2_indent. "~" x (length $1). "\n";
     push @text, "\n";
   } elsif (/<h3>(.*?)<\/h3>/i) {
     push @text, "\n";
     push @text, " " x $h3_indent. "$1\n";
     push @text, " " x $h3_indent. "-" x (length $1). "\n";
     push @text, "\n";
   } elsif (/<ol>/i) {
     $ordered_list=1;
     $itemcnt=1;
   } elsif (/<ul>/i) {
     $ordered_list=0;
   } elsif (/^\s*<li>\s*(.*)/) {
     if ($ordered_list) {
       push @text, "\n";
       push @text, " $itemcnt. $1\n";
       ++$itemcnt;
     } else {
       push @text, "\n";
       push @text, " * $1\n";
     }
   } else {
     push @text, "$_\n";
   }
 }
 return @text;
}


# dumping the html doc to stdout does not seem very helpful,
# and did not work for years with no one noticing, so this
# function is no longer used.
sub help_menu {
 my %command=(
   'self' => \&help_menu,
   'R' => \&main_menu,
   'Q' => \&quit
     );
 my $installer_help = "notused/tlpkg/installer/install-tl.html";

 clear_screen;

 my @text=html2text ($installer_help);
 my $lines=(@text);
 my $overlap=3;
 my $lps=32; # lines per screen - overlap
 my $firstline=$vars{'page'}*$lps;
 my $lastline=$firstline+$lps+$overlap;
 my $line=0;
#  print "<<<$firstline>>> <<<$lastline>>>\n";
 for (@text) {
   print "$_" if ($line>=$firstline and $line<=$lastline);
   ++$line;
 }
 print "\n", hbar,
 "  <T> top  <N> next page  <P> previous page  <R> return"
     . "  <Q> quit         --", $vars{'page'}+1, "--\n";

 my $answer = prompt 'Enter command';

 if ("\u$answer" eq 'T') {
   $vars{'page'}=0;
   return $command{'self'};

 } elsif ("\u$answer" eq 'N') {
   $vars{'page'}+=1 unless $lastline>$lines;
   return $command{'self'};

 } elsif ("\u$answer" eq 'P') {
   $vars{'page'}-=1 if $vars{'page'}>=1;
   return $command{'self'};

 } elsif (defined $command{"\u$answer"}) {
   return $command{"\u$answer"};

 } else {
   print "Unknown command: $answer\n\n";
   return $command{'self'};
 }
}


sub options_menu {
 my $b_path=obutton($vars{'instopt_adjustpath'});
 my $b_doc=obutton($vars{'tlpdbopt_install_docfiles'});
 my $b_src=obutton($vars{'tlpdbopt_install_srcfiles'});
 my $b_fmt=obutton($vars{'tlpdbopt_create_formats'});
 my $b_letter=obutton($vars{'instopt_letter'});
 my $b_adjustrepo=obutton($vars{'instopt_adjustrepo'});
 my $b_deskint=obutton(
   $vars{'tlpdbopt_desktop_integration'}, \@::deskintdesc);
 my $b_admin=obutton($vars{'tlpdbopt_w32_multi_user'});
 my $b_addoneditor=obutton($vars{'collection-texworks'});
 my $b_restricted=obutton($vars{'instopt_write18_restricted'});

 my $sys_bin=$vars{'tlpdbopt_sys_bin'};
 my $sys_man=$vars{'tlpdbopt_sys_man'};
 my $sys_info=$vars{'tlpdbopt_sys_info'};

 my $t_sys_bin=($vars{'instopt_adjustpath'})? $vars{'tlpdbopt_sys_bin'}:'';
 my $t_sys_man=($vars{'instopt_adjustpath'})? $vars{'tlpdbopt_sys_man'}:'';
 my $t_sys_info=($vars{'instopt_adjustpath'})? $vars{'tlpdbopt_sys_info'}:'';

 my %command=(
   'self' => \&options_menu,
   'R' => \&main_menu,
   'Q' => \&quit
     );

 clear_screen;
 menu_head "Options customization:";

 print <<"EOF";
<P> use letter size instead of A4 by default: $b_letter
<E> execution of restricted list of programs: $b_restricted
<F> create all format files:                  $b_fmt
EOF
;
 if ($vars{'doc_splitting_supported'} and !$opt_in_place) {
   print " <D> install font/macro doc tree:              $b_doc\n";
 }
 if ($vars{'src_splitting_supported'} and !$opt_in_place) {
   print " <S> install font/macro source tree:           $b_src\n";
 }
 if (!$vars{'instopt_portable'}) {
   if (unix() || $::opt_all_options) {
     print <<"EOF";
<L> create symlinks in standard directories:  $b_path
           binaries to: $t_sys_bin
           manpages to: $t_sys_man
               info to: $t_sys_info
EOF
;
   } else {
     print <<"EOF";
<L> adjust registry entry for path:           $b_path
EOF
;
   }
   if ((wndws() && !$vars{'instopt_portable'}) || $::opt_all_options) {
     print " <M> Start menu shortcuts / launcher:          ".obutton(
         $vars{'tlpdbopt_desktop_integration'}, \@::deskintdesc)."\n";
     print " <N> update file associations:                 ".obutton(
       $vars{'tlpdbopt_file_assocs'}, \@::fileassocdesc)."\n";
     if ($::opt_all_options || TeXLive::TLWinGoo::admin()) {
       # if we are admin we allow normal user installation, too
       print " <U> make installation available to all users: $b_admin\n";
     }
   }
 }
 if (wndws() || $::opt_all_options) {
   print " <W> install TeXworks front end:               $b_addoneditor\n";
 }
 if ($media ne "NET") {
   print " <Y> after install, set CTAN as source for package updates: $b_adjustrepo\n";
 }
 other_options qw(R Q diskspace);
 my $answer = prompt 'Enter command';

 # instopt_adjustpath

 if (unix()) {
   if (("\u$answer" eq 'L') and !$vars{'instopt_portable'}) {
     my $home = TeXLive::TLUtils::get_user_home();
     toggle 'instopt_adjustpath';
     if ($vars{'instopt_adjustpath'}) {
       print "New value for binary directory [$sys_bin]: ";
       chomp($answer=<STDIN>);
       $vars{'tlpdbopt_sys_bin'} =  "$answer" if (length $answer);
       $vars{'tlpdbopt_sys_bin'} =~ s@\\@/@g if (wndws());
       $vars{'tlpdbopt_sys_bin'} =~ s/^~/$home/;
       if ($vars{'tlpdbopt_sys_bin'}=~/^(.*)\/bin$/) {
         $vars{'tlpdbopt_sys_man'}="$1/man";
         $vars{'tlpdbopt_sys_info'}="$1/info";
       }
       print "New value for man directory    [$vars{'tlpdbopt_sys_man'}]: ";
       chomp($answer=<STDIN>);
       $vars{'tlpdbopt_sys_man'}="$answer" if (length $answer);
       $vars{'tlpdbopt_sys_man'} =~ s@\\@/@g if (wndws());
       $vars{'tlpdbopt_sys_man'} =~ s/^~/$home/;

       print "New value for info directory   [$vars{'tlpdbopt_sys_info'}]: ";
       chomp($answer=<STDIN>);
       $vars{'tlpdbopt_sys_info'}="$answer" if (length $answer);
       $vars{'tlpdbopt_sys_info'} =~ s@\\@/@g if (wndws());
       $vars{'tlpdbopt_sys_info'} =~ s/^~/$home/;
     }
     return $command{'self'};
   }
 } else {
   if (("\u$answer" eq 'L') and !$vars{'instopt_portable'}) {
     my $home = TeXLive::TLUtils::get_user_home;
     toggle 'instopt_adjustpath';
     return $command{'self'};
   }
 }

#  # tlpdbopt_desktop_integration, tlpdbopt_file_assocs
#
#  if (wndws() || $::opt_all_options) {
#    if ("\u$answer" eq 'M' and !$vars{'instopt_portable'}) {
#      toggle 'tlpdbopt_desktop_integration';
#      return $command{'self'};
#
#    } elsif ("\u$answer" eq 'N' and !$vars{'instopt_portable'}) {
#      print "New value for file_assocs:\n";
#      print "  0 -- don't tweak the file associations\n";
#      print "  1 -- only add new file associations, don't overwrite old ones\n";
#      print "  2 -- always create file associations to TeX Live programs\n";
#      print "New value for file_assocs [$vars{'tlpdbopt_file_assocs'}]: ";
#      chomp (my $a = <STDIN>);
#      if ($a eq "0" || $a eq "1" || $a eq "2") {
#        $vars{'tlpdbopt_file_assocs'} = $a;
#      }
#      return $command{'self'};
#
#    } elsif ("\u$answer" eq 'U' and !$vars{'instopt_portable'}) {
#      toggle 'tlpdbopt_w32_multi_user';
#      return $command{'self'};
#    }
#  }

 # other options

 if (("\u$answer" eq 'M') && !$vars{'instopt_portable'}) {
   toggle ('tlpdbopt_desktop_integration', \@::deskintdesc);
   return $command{'self'};

 } elsif (("\u$answer" eq 'N') && !$vars{'instopt_portable'}) {
   toggle ('tlpdbopt_file_assocs', \@::fileassocdesc);
   return $command{'self'};

 } elsif ("\u$answer" eq 'P') {
   toggle 'instopt_letter';
   return $command{'self'};

 } elsif ("\u$answer" eq 'F') {
   toggle 'tlpdbopt_create_formats';
   return $command{'self'};

 } elsif ("\u$answer" eq 'E') {
   toggle 'instopt_write18_restricted';
   return $command{'self'};

 } elsif ("\u$answer" eq 'S' and !$opt_in_place) {
   toggle 'tlpdbopt_install_srcfiles';
   return $command{'self'};

 } elsif ("\u$answer" eq 'D' and !$opt_in_place) {
   toggle 'tlpdbopt_install_docfiles';
   return $command{'self'};

 } elsif (defined $command{"\u$answer"}) {
   return $command{"\u$answer"};

 } elsif (("\u$answer" eq 'W') && ($::opt_all_options || wndws()) &&
      !$opt_in_place ) {
   toggle 'collection-texworks';
   return $command{'self'};

 } elsif ("\u$answer" eq 'Y' and $media ne "NET") {
   toggle 'instopt_adjustrepo';
   return $command{'self'};

 } else {
   print "Unknown or unsupported command: $answer\n\n";
   return $command{'self'};
 }
}


sub quit {
 exit 0;
 $RETURN = $MENU_QUIT;
}

sub do_install {
 my $reserve = 100;
 my $doit = 1;
 if ($vars{'free_size'} > 0
     && $vars{'free_size'} + $reserve < $vars{'total_size'}) {
   print STDERR <<"EOF";
*** WARNING ****************************************************
The installation requires $vars{'total_size'}M of disk space
but only $vars{'free_size'}M is available.

You probably want to either clean up the destination filesystem,
or choose a different installation location,
or reduce what gets installed.

Press Enter to return to the menu, or i to install anyway.
****************************************************************
EOF
   my $ans = readline (*STDIN);
   $doit = 0;
   chomp($ans);
   if ($ans eq "i" or $ans eq "I") {
     $doit = 1;
   }
 }
 if ($doit) {
   # set env variable to make install-tl not trip over
   $ENV{'TEXLIVE_INSTALL_NO_DISKCHECK'} = 1;
   $RETURN = $MENU_INSTALL;
 } else {
   main_menu();
 }
}

sub toggle_portable {
 if ($vars{'instopt_portable'}) {
   $vars{'instopt_portable'} = 0;
   $vars{'tlpdbopt_desktop_integration'} = 0;
   $vars{'instopt_adjustpath'} = 0;
   $vars{'tlpdbopt_file_assocs'} = 0;
   $vars{'tlpdbopt_w32_multi_user'} = 0;
 } else {
   $vars{'instopt_portable'} = 1;
   $vars{'tlpdbopt_desktop_integration'} = 1;
   $vars{'instopt_adjustpath'} = 1;
   $vars{'tlpdbopt_file_assocs'} = 1;
   $vars{'tlpdbopt_w32_multi_user'} = 1;
 }
 $portable_toggled = 1;
 set_texlive_default_dirs(); # this sub tests for portable and in_place
 main_menu;
}

sub callback_save_profile {
 create_profile("texlive.profile");
 print "Installation profile saved to 'texlive.profile', exiting.\n";
 exit(0);
}

sub main_menu {
 my $this_platform=platform_desc($vars{'this_platform'});

 my $b_path=button($vars{'instopt_adjustpath'});
 my $b_doc=button($vars{'tlpdbopt_install_docfiles'});
 my $b_src=button($vars{'tlpdbopt_install_srcfiles'});
 my $b_fmt=button($vars{'tlpdbopt_create_formats'});
 my $b_letter=button($vars{'instopt_letter'});
 my $b_deskint=button($vars{'tlpdbopt_desktop_integration'}, \@::deskintdesc);
 if (wndws()) {
   my $b_fileassoc=button($vars{'tlpdbopt_file_assocs'}, \@::fileassocdesc);
 }
 my $b_admin=button($vars{'tlpdbopt_w32_multi_user'});
 my $b_addoneditor=button($vars{'collection-texworks'});
 my $b_restricted=button($vars{'instopt_write18_restricted'});
 my $b_adjustrepo=button($vars{'instopt_adjustrepo'});

 my $warn_nobin;

 $warn_nobin=set_install_platform;

 $vars{'n_systems_selected'}=0;
 $vars{'n_collections_selected'}=0;
 foreach my $key (keys %vars) {
   if ($key=~/^binary.*/) {
     ++$vars{'n_systems_selected'} if $vars{$key}==1;
   }
   if ($key=~/^collection/) {
     ++$vars{'n_collections_selected'} if $vars{$key}==1;
   }
 }
 calc_depends();

 my %command = (
   'self' => \&main_menu,
   'D' => \&directories_menu,
   'I' => \&do_install,
   'O' => \&options_menu,
   'Q' => \&quit,
   'V' => \&toggle_portable,
   'P' => \&callback_save_profile,
 );
 if (!$opt_in_place) {
   $command{'B'} = \&binary_menu if unix();
   $command{'C'} = \&collection_menu;
   $command{'S'} = \&scheme_menu;
 }

 clear_screen;
 my $freestring = ($vars{'free_size'} >= 0 ? " (free: $vars{'free_size'} MB)" : "");
 print <<"EOF";
======================> TeX Live installation procedure <=====================

======>   Letters/digits in <angle brackets> indicate   <=======
======>   menu items for actions or customizations      <=======
= help>   https://tug.org/texlive/doc/install-tl.html   <=======

Detected platform: $this_platform
$warn_nobin
EOF

 if (!$opt_in_place) {
   print <<"EOF";
<B> set binary platforms: $vars{'n_systems_selected'} out of $vars{'n_systems_available'}

<S> set installation scheme: $vars{'selected_scheme'}

<C> set installation collections:
    $vars{'n_collections_selected'} collections out of $vars{'n_collections_available'}, disk space required: $vars{'total_size'} MB$freestring
EOF

 }
   if ($portable_toggled) {
     print $ptoggle_alert;
     $portable_toggled = 0;
   }
   print <<"EOF";

<D> set directories:
  TEXDIR (the main TeX directory):
EOF

 if (TeXLive::TLUtils::texdir_check($vars{'TEXDIR'}, 1)) {
   print "     $vars{'TEXDIR'}\n";
 } else {
   print "     !! default location: $vars{'TEXDIR'}\n";
   print "     !! is not writable or not allowed, please select a different one!\n";
 }
 print <<"EOF";
  TEXMFLOCAL (directory for site-wide local files):
    $vars{'TEXMFLOCAL'}
  TEXMFSYSVAR (directory for variable and automatically generated data):
    $vars{'TEXMFSYSVAR'}
  TEXMFSYSCONFIG (directory for local config):
    $vars{'TEXMFSYSCONFIG'}
  TEXMFVAR (personal directory for variable and automatically generated data):
    $vars{'TEXMFVAR'}
  TEXMFCONFIG (personal directory for local config):
    $vars{'TEXMFCONFIG'}
  TEXMFHOME (directory for user-specific files):
    $vars{'TEXMFHOME'}

EOF

print <<"EOF";
<O> options:
  $b_letter use letter size instead of A4 by default
  $b_restricted allow execution of restricted list of programs via \\write18
  $b_fmt create all format files
EOF

 if (!$opt_in_place) {
   if ($vars{'doc_splitting_supported'}) {
     print "   $b_doc install macro/font doc tree\n";
   }
   if ($vars{'src_splitting_supported'}) {
     print "   $b_src install macro/font source tree\n";
   }
 }
 if (wndws()) {
   if (!$vars{'instopt_portable'}) {
     print "   $b_path adjust search path\n";
     print "   $b_deskint add menu items, shortcuts, etc.\n";
     print "   [$vars{'tlpdbopt_file_assocs'}] update file associations\n";
     if (admin()) {
       print "   $b_admin make installation available to all users\n";
     }
   }
   print "   $b_addoneditor install TeXworks front end\n";
 }

 print "   $b_path create symlinks to standard directories\n"
   unless ($vars{'instopt_portable'} || wndws());

 print "   $b_adjustrepo after install, set CTAN as source "
         . "for package updates\n"
   unless ($media eq 'NET');

 if ($vars{'instopt_portable'}) {
   print "\n <V> set up for regular installation to hard disk\n";
 } else {
   print "\n <V> set up for portable installation\n";
 }

 other_options qw(I P Q);
 my $answer = prompt 'Enter command';

 if (defined $command{"\u$answer"}) {
   return $command{"\u$answer"};
 } else {
   print "Unknown command: $answer\n\n";
   return $command{'self'};
 }
}
# needs a terminal 1 for require to succeed!
1;

__END__

### Local Variables:
### perl-indent-level: 2
### tab-width: 2
### indent-tabs-mode: nil
### End:
# vim:set tabstop=2 expandtab: #