The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/nw/dist/prod/bin/perl -w
use strict;
use vars qw($VERSION $running_under_some_shell);
$VERSION = '#VERSION#';
use Envy::DB;
use Envy::UI qw(cmd_2re alength);

warn "[WARNING: Envy version mismatch -- Envy::DB $Envy::DB::VERSION ne $VERSION]\n\n"
    if $Envy::DB::VERSION ne $VERSION;

my $is_unload=0;
my $is_csh=0;
my $is_showing=0;

sub sync_env {
    my ($db) = @_;
    my $failed = $db->commit;
    for ($db->warnings) { print }
    exit 1
	if $failed;
    $db->write_log if !$is_showing;
    my $old = select STDOUT if !$is_showing;
    for my $z ($db->to_sync()) {
	my ($k,$v) = @$z;
	# hope that single-quoting is enough XXX
	if (defined $v) {
	    if ($is_csh) {
		print "setenv $k '$v';\n";
	    } else {
		print "$k='$v'; export $k;\n";
	    }
	} else {
	    if ($is_csh) {
		print "unsetenv $k;\n";
	    } else {
		print "unset $k;\n";
	    }
	}
    }
    select $old if $old;
}

sub Huh {
    my ($why) = @_;
    if ($why) {
	print "** $why\n";
    } else {
	print "\n";
    }
    print "** Type 'envy help' for usage information. **\n";
}

sub Help {
    my $page = shift;
    if (!$page) {
	print "
  Envy $Envy::DB::VERSION -- Environment Dimension Manager

  Try:

     envy help usage    for command line arguments
     envy help author   for help writing .env files
     envy help path     for an explaination of search paths
     envy help env      for a list of envy specific environment variables
     envy help license  for licensing information

     envy help custom   for a description of \$HOME/.custom/ files [OPTIONAL]

  Send email to envy\@listbox.com for support.  Thanks!

";
    } elsif ($page eq 'usage') {
	print "
   list [<envy>]                - See descriptions
   paths [<envy>]               - Location of source files
   load <envy> [<envy> ...]     - Loads <envy>
   reload <envy> [<envy> ...]   - Unloads & reloads <envy>
   show <envy> [<envy> ...]     - Shows changes (shell env is unchanged)
   un <envy> [<envy> ...]       - Unloads <envy>
   unload all                   - Unloads all envies
   <str>                        - Lists/loads envies that match <str>
                                  AVOID IN SCRIPTS; CMD-LINE ONLY

   These options must be first on the command line:
   -csh                         - csh mode
   -quiet                       - Only report errors
   -v\\d                         - Set verbosity level
   -strict 1                    - Upgrade warnings to errors.
   --trace                      - Trace activity
   --debug                      - Maximum verbosity

";
    } elsif ($page eq 'custom') {
	require Envy::Conf;
	# avoid silly warning
	my $startup = $Envy::Conf::startup = $Envy::Conf::startup;
	print '
   [THIS IS AN OPTIONAL FEATURE.  YOUR SYSTEM MIGHT BE DIFFERENT.]

   The following files can be found in $HOME/.custom/...

     startup     - Your startup envy (default: '.$startup.')
     win.name    - Your window manager setup

   Bourne Shell:
     profile     - Sourced once upon login
     shrc        - Sourced for each new shell instance

   C-Shell:
     login       - Sourced once upon login
     cshrc       - Sourced for each new shell instance

';

    } elsif ($page eq 'author') {
	print "
   dimension java                    # Declares dimension membership
   desc Java 1.2 Test                # Description for 'envy list'
   echo Java admin - call Joe x1212  # Outputs when loading
   alpha                             # Notify is alpha software
   beta                              # Notify is beta software
   depreciated                       # Notify is depreciated
   error Java is no longer available.  Sorry.
   require Envy 2.16                 # Uses required features from 2.16

   require objstore                  # Insures objstore is loaded
   JAVA_HOME=/nw/prod/usr            # Sets environment variable
   JAVA_HOME:=\$HOME/java             # Overrides environment variable
   PATH+=\$JAVA_HOME/bin              # Prepend to colin separated list
   PATH=+\$JAVA_HOME/bin              # Append to colin separated list
   MYTOP=\$ENVY_BASE                  # Real path to .env file's tree top
   MYTOP=\$ENVY_LINKBASE              # Path to .env file's tree top

";
    } elsif ($page eq 'path') {
	print "
   Current search path (order is observed):
     ".join("\n     ", split /:+/, $ENV{ENVY_PATH})."

   All search paths also include the .priv directory (if found).

   Envy also looks in \$HOME/.envy/ for .env files.  You can
   use this to test new stuff.

";
    } elsif ($page eq 'env') {
	print "
   \$ENVY_PATH      - Colin separated search path.  Envies are typically
                     installed in \$ETOP/env/envy.  See 'envy help path' too.
   \$ENVY_STATE     - Keeps track of which modules are loaded & dependencies.
   \$ENVY_DIMENSION - Keeps track of dimensions.
   \$ENVY_VERSION   - Envy protocol version.
   \$ENVY_VERBOSE   - Default verbosity level.

   \$ETOP           - Prefix for login scripts. [OPTIONAL]

";
    } elsif ($page eq 'copy' or $page eq 'license') {
	print q[
   Copyright © 1997-1999 Joshua Nathaniel Pritikin.  All rights reserved.

   This package is free software and is provided "as is" without express
   or implied warranty.  It may be used, redistributed and/or modified
   under the terms of the Perl Artistic License (see
   http://www.perl.com/perl/misc/Artistic.html)

   ('Envy' is not an acronym.  Pronounced: 'n-v')

];
    } else {
	print "No help for $page.  Sorry.\n";
    }
}

my $db = Envy::DB->new(\%ENV);

while (@ARGV and $ARGV[0] =~ m/^\-/) {
    my $arg = shift @ARGV;
    if    ($arg eq '-csh')           { $is_csh=1 }   #rename: --csh
    elsif ($arg eq '-un')            { $is_unload=1 } #deprecate? XXX
    elsif ($arg =~ m/^-q(uiet)?$/)   { $db->warnlevel(0) } #rename
    elsif ($arg eq '--trace')        { $db->warnlevel(3) }
    elsif ($arg eq '--debug')        { $db->warnlevel(4) }
    elsif ($arg eq '-v')             { $db->warnlevel($db->warnlevel() + 1) }
    elsif ($arg =~ m/^-v(\d*)/)      { $db->warnlevel(length $1? $1 : 2); }
    elsif ($arg =~ m/^(-h|--help)$/) { select STDERR; &Help; exit }
    elsif ($arg eq '-strict')        { $db->{strictness} = shift @ARGV } #XXX
    else {
	warn "option '$arg' ignored\n"
    }
}

my $cmd = cmd_2re(shift @ARGV);

select STDERR;    # STDOUT goes the shell eval

if ($cmd eq 'show' and @ARGV >= 1 and $ARGV[0] eq 'paths') {
    print "[Just use 'paths' instead of 'show paths'.]\n\n";
    shift @ARGV;
    $cmd = 'paths';
}

if ($cmd eq 'paths') {
    $cmd = cmd_2re(shift @ARGV);
    my ($mo, $loaded) = $db->all_matching($cmd);
    for ($db->warnings) { print }
    my $l = alength(keys %$mo);
    print "Location of source files".
	($cmd eq '.*'?'':" matching '$cmd'").":\n\n";
    for my $m (sort keys %$mo) {
	my $NumSpaces = 1 + $l - length $m;
	my $desc = $mo->{$m};
	print(($$loaded{$m}? " x ":"   ").
	      $m . ' 'x($NumSpaces-1) . " $desc\n");
    }
    Huh();
    exit;
}
elsif ($cmd eq "load" or $cmd eq "reload") {
    if ($is_unload) {
	Huh("Unenvy load means what?");
	exit 1;
    }
    if (@ARGV < 1) {
	&Help;
	exit 1;
    }
    my ($loaded, $dim) = $db->quick_status;
    while (@ARGV) {
	my $e = shift @ARGV;
	$db->envy(1, $e)
	    if ($cmd eq 'reload' and $loaded->{$e});
	$db->envy(0, $e);
    }
    sync_env($db);
    
} elsif ($cmd eq "show") {
    if (@ARGV < 1) {
	&Help;
	exit 1;
    }
    $is_showing = 1;
    for my $mo (@ARGV) { $db->envy(!$is_unload, $mo); }
    $db->commit;
    $db->warnings; #flush
    $db->begin;
    for my $mo (@ARGV) { $db->envy($is_unload, $mo); }
    sync_env($db);
    
} elsif ($cmd eq "un" or $cmd eq "unload") {
    if ($is_unload) {
	Huh("Unenvy unload doesn't mean what?");
	exit 1;
    }
	    
    if (@ARGV == 0 or $ARGV[0] eq 'all') {
	$db->unload_all
    } else {
	while (@ARGV) { $db->envy(1, shift @ARGV); }
    }
    sync_env($db);
    
} elsif ($cmd eq "list") {
    $cmd = cmd_2re(shift @ARGV);
    my ($mo, $loaded) = $db->all_matching($cmd);
    for ($db->warnings) { print }
    my $l = alength(keys %$mo);
    
    if ($cmd eq '.*') {
	print "All available envies:\n\n";
    } else {
	print "Envies matching '$cmd':\n\n";
    }
    for my $m (sort keys %$mo) {
	my $NumSpaces = 1 + $l - length $m;
	my $padding = "\n" . ' 'x($l+4);
	my $desc = $db->description($m) || '-';
	$desc =~ s/\n/$padding/g;
	print(($$loaded{$m}? " x ":"   ").
	      $m . ' 'x($NumSpaces-1) . " $desc\n");
    }
    Huh();
    exit;
    
} elsif ($cmd eq 'diff') {
    Huh("The 'diff' command is not implemented yet.");
    exit 1;
    
} elsif ($cmd eq "help") {
    &Help(shift @ARGV);
    exit 0;
    
} else {
    my ($mo, $loaded) = $db->try_match($cmd);
    for ($db->warnings) { print }
    my @mo = @$mo;
    if (@mo == 0) {
	print "Envy '$cmd' not found.\n";
    } elsif (@mo == 1) {
	$db->envy($is_unload, $mo[0]);
	sync_env($db);
    } else {
	if ($cmd eq '.*') {
	    print "All available envies:\n\n";
	} else {
	    print "Envies matching '$cmd':\n\n";
	}
	use integer;
	my $mid = (1+@mo)/2;
	for (my $i=0; $i < $mid; $i++) {
	    my $m = $mo[$i];
	    print $$loaded{$m}? " x " : "   ";
	    print $m.' 'x(34-length $m);
	    if ($mo[$i+$mid]) {
		my $m = $mo[$i+$mid];
		print $$loaded{$m}? " x " : "   ";
		print $m;
	    }
	    print "\n";
	}
	exit 1;
    }
}
exit;