/usr/local/CPAN/ShellScript-Env/ShellScript/Env.pm
package ShellScript::Env;
use strict;
use File::Find;
sub new {
my $this = shift;
my $class = ref($this) || $this;
my $self = {};
bless $self, $class;
$self->{'prefix'} = shift;
if (!defined $self->{'prefix'}) {
warn ref($self) . ' constructed with no argument, using `.\' as prefix';
$self->{'prefix'} = '.';
}
%{$self->{'dir_search'}} =
(
'LD_LIBRARY_PATH' => ['lib'],
'PATH' => ['bin'],
'MANPATH' => ['man'],
'INFOPATH' => ['info'],
);
push @{$self->{'skip_dirs'}}, 'src';
return $self;
}
#######################
# Functions to be considered public
# Functions that searches the prefix directory for common path names.
sub automatic {
my $self = shift;
foreach my $env (keys %{$self->{'dir_search'}}) {
my @found = $self->dir_find(@{$self->{'dir_search'}->{$env}});
if ($#{@found} >= 0) {
$self->set_path($env, @found, "\$$env");
}
}
return $self;
}
# Just sets a list, no processing or notten.
sub set {
my $self = shift;
my $env = shift;
@{$self->{'env'}->{$env}} = @_;
$self->{'utok'}->{$env} = 0;
push @{$self->{'order'}}, $env;
return $self;
}
# Make a list and check it twice. Well, appends $self->{'prefix'} if
# needed and check if you want utok.
sub set_path {
my $self = shift;
my $env = shift;
my @dirs = @_;
for (@dirs) {
my $prefix = quotemeta($self->{'prefix'});
if (($_ !~ m/^$prefix/) && ($_ =~ m:^[^\$\/]:)) {
s:^:$self->{'prefix'}/:;
}
}
$self->set($env, @dirs);
$self->{'utok'}->{$env} = ($ShellScript::Env::utok || 0);
return $self;
}
# deletes a variable.
sub unset {
my $self = shift;
my $env = shift;
# Remove one element from a list. Isn't there a cool way to do this
# with map?
my @rebuild;
for (@{$self->{'order'}}) {
if ($_ ne $env) {
push @rebuild, $_;
}
}
@{$self->{'order'}} = @rebuild;
delete $self->{'env'}->{$env};
delete $self->{'utok'}->{$env};
return $self;
}
# Returns 0 if there are no errors.
sub save {
my $self = shift;
my $error = 0;
my $csh_file = "$self->{prefix}.csh";
if (open(CSH, ">$csh_file")) {
print "Writing $csh_file\n";
print CSH $self->csh();
close(CSH);
} else {
++$error;
warn "Can't write $csh_file: $!";
}
my $sh_file = "$self->{prefix}sh";
if (open(SH, ">$sh_file")) {
print "Writing $sh_file.\n";
print SH $self->sh();
close(SH);
} else {
++$error;
warn "Can't write $sh_file: $!";
}
return $error;
}
##################
# functions to generate shell scripts, these are considered public
# too. Are there other common shells that arn't compatible with C or
# Bourne Shell?
# output Bourne Shell.
sub sh {
my $self = shift;
my $output = '';
my $export = 'export ';
for (@{$self->{'order'}}) {
$export .= "$_ ";
if ($self->{'utok'}->{$_}) {
$output .= "$_=`utok ";
} else {
$output .= "$_=";
}
for (@{$self->{'env'}->{$_}}) {
$output .= "$_:";
}
if ($self->{'utok'}->{$_}) {
$output =~ s/:$/\`\n/;
} else {
$output =~ s/:$/\n/;
}
}
if ($export ne 'export ') {
$output .= $export;
}
$output =~ s/\ $/\n/;
return $output;
}
# output C Shell.
sub csh {
my $self = shift;
my $output = '';
for (@{$self->{'order'}}) {
my $delimiter = ':';
# I hate how C Shell set every variable one way, and PATH another.
# It bugs me to no end.
if ($_ eq 'PATH') {
$delimiter = ' ';
$output .= "set path = (";
} else {
$output .= "setenv $_ ";
}
if ($self->{'utok'}->{$_}) {
if ($delimiter ne ':') {
$output .= "`utok -s '$delimiter' ";
} else {
$output .= '`utok ';
}
}
my $item;
foreach $item (@{$self->{'env'}->{$_}}) {
if (($_ eq 'PATH') && ($item eq '$PATH')) {
$output .= "\$path$delimiter";
} else {
$output .= "$item$delimiter";
}
}
$delimiter = quotemeta($delimiter);
if ($self->{'utok'}->{$_}) {
$output =~ s/$delimiter$/\`\n/;
} else {
$output =~ s/$delimiter$/\n/;
}
if ($_ eq 'PATH') {
$output =~ s/\n$/\)\n/;
}
}
return $output;
}
#####################
# Private functions.
# I really wish File::Find's find returned an array. I also wish it
# worked while tainted. oh well.
sub dir_find {
my $self = shift;
@ShellScript::Env::find = @_;
undef @ShellScript::Env::found;
@ShellScript::Env::skip = @{$self->{'skip_dirs'}};
my @output;
if (-l $self->{'prefix'}) {
my $newdir = $self->{'prefix'};
$newdir =~ s<[^/]*$><>;
chdir($newdir);
my $prefix = readlink($self->{'prefix'});
find(\&wanted, $prefix);
$prefix = quotemeta($prefix);
@output = map(s/^$prefix/$self->{'prefix'}/g && $_,
@ShellScript::Env::found);
} else {
find(\&wanted, $self->{'prefix'});
@output = @ShellScript::Env::found;
}
undef @ShellScript::Env::skip;
undef @ShellScript::Env::found;
undef $ShellScript::Env::find;
return @output;
}
# Only used for call to find.
sub wanted {
foreach my $find (@ShellScript::Env::find) {
$find = quotemeta($find);
if (m/^$find$/ && -d $_) {
for (@ShellScript::Env::skip) {
my $skip = quotemeta($_);
if ($File::Find::name =~ m</$skip/>) {
return 0;
}
}
push @ShellScript::Env::found, $File::Find::name;
return 1;
}
}
return 0;
}
###################
# bye, bye.
return 1;