#!/usr/bin/perl
#<buildenv: creates Bourne/C-shell files to set environment variables

use Modern::Perl;
use Getopt::Long qw(GetOptions);
use File::Basename;

use subs qw(manpage myuuid older susage version where);
local $ENV{'PATH'} = join ":", qw(/bin /usr/bin /usr/local/bin);

my %meta;
my $myname = basename($0);
$myname =~ s/\.\w*$//x;    # strip any extension

# Command-line options.
my %options;
my @getopt_args = (
    'h|?',    # print usage
    'f=s',    # set rcfile
    'm',      # print manpage
    'u',      # print UUID
    'v',      # print version
    'w',      # print source location
    );

Getopt::Long::config("noignorecase", "bundling");
susage unless GetOptions(\%options, @getopt_args);

manpage if $options{'m'};
myuuid  if $options{'u'};
version if $options{'v'};
where   if $options{'w'};
susage  if $options{'h'};

# Real work starts here.
my $bourne;    # output file holding Bourne-shell env variables.
my $csh;       # output file holding C-shell env variables.
my $first;     # first word in env setting line; env variable name.
my $rc;        # input file holding env variable settings.
my @line;      # remaining words in env setting line; env value.

# Find the environment settings file.
if ($options{'f'}) {
    $rc = $options{'f'};
}
else {
    if (defined $ENV{'XDG_CONFIG_HOME'}) {
        $rc = $ENV{'XDG_CONFIG_HOME'} . '/envrc';
    }
    else {
        die "XDG_CONFIG_HOME not defined, can't find settings file.";
    }
}

die "$rc: settings file not found: $!\n" unless -f "$rc";

# Read settings file, write Bourne-shell equivalents.
$bourne = $rc . ".sh";

if (older($bourne, $rc)) {
    print "Rebuilding $bourne ...\n";
    open(my $ifh, '<', "$rc")     || die "$rc: can't read $!\n";
    open(my $ofh, '>', "$bourne") || die "$bourne: can't write: $!\n";

    while (<$ifh>) {
        chomp;
        if (/^#|^[ \t]*$/) {
            print $ofh "$_\n";
        }
        else {
            @line  = split;
            $first = shift(@line);
            print $ofh "$first=@line; export $first\n";
        }
    }

    close($ifh);
    close($ofh);
}

# Read settings file, write C-shell equivalents.
$csh = $rc . ".csh";

if (older($csh, $rc)) {
    print "Rebuilding $csh ...\n";
    open(my $ifh, '<', "$rc")  || die "$rc: can't read: $!\n";
    open(my $ofh, '>', "$csh") || die "$csh: can't write: $!\n";

    while (<$ifh>) {
        chomp;
        if (/^#|^[ \t]*$/) {
            print $ofh "$_\n";
        }
        else {    # $(cmd) won't work in TCSH.
            if (/\$\(/) {
                s/\$\(/`/g;
                s/\)/`/g;
            }
            print $ofh "setenv $_\n";
        }
    }

    close($ifh);
    close($ofh);
}

exit(0);

#---------------------------------------------------------------------
# Print a short usage message and exit.

sub susage {
    my ($emsg) = @_;
    warn "$emsg\n" if defined $emsg;

    print <<"EndHelp";

$myname reads "\$XDG_CONFIG_HOME/envrc" and creates equivalent Bourne-shell
and C-shell files for setting environment variables.  Run "$myname -m" to
see an example.

"\$XDG_CONFIG_HOME" is usually set to "\$HOME/.config"

Options:
  -f FILE   Read settings from FILE instead of default
  -h        Print a brief help message and exit.
  -m        Print the manual page and exit.
  -u        Print the script UUID and exit.
  -v        Print the version and exit.
  -w        Print the canonical source location and exit.

EndHelp
    exit(1);
}

# Print a longer manpage.

sub manpage {
    my @args = ("perldoc", "$0");
    exec {$args[0]} @args;    # safe even with one-arg list
    die("should not get here\n");
}

#---------------------------------------------------------------------
# Return a true value if the first file entered is older than
# or the same age as the second file.

sub older {
    my ($file1, $file2) = @_;
    my ($mtime1) = (stat($file1))[9];
    my ($mtime2) = (stat($file2))[9];
    if ($mtime1 && $mtime2) { return ($mtime1 <= $mtime2); }
    return (1);
}

#---------------------------------------------------------------------
# Print the UUID, current version, or source location.
# Store version info, etc. so we can use it in more than one place.
# In the examples below, replace underscores with dollar signs.
#
# Put [-2..-1] in date if you use RCS:
#    (qw_Date: 2000-01-01 00:00:00-05 _)[-2 .. -1]),
# Put [-5..-1] in date if you use Mercurial:
#    (qw_Date: Sat, 1 Jan 2000 00:00:00 -0500 _)[-5 .. -1]),

BEGIN {
    no warnings 'qw';    ## no critic (TestingAndDebugging::ProhibitNoWarnings)

    %meta = (
        'date' =>
            join(" ", (qw$Date: Sun, 31 Mar 2024 20:18:32 -0400 $)[-5..-1]),
        'host' => (qw$Host: furbag.my.domain $)[-1],
        'source' => (
            qw$Source: /doc/html/htdocs/dotfiles/bin/buildenv $
          )[-1],
        'uuid'    => (qw$UUID: 1670d98f-2968-33d2-ac33-6ae8b9949d53 $)[-1],
        'version' => (qw$Revision: d7156e5109b9 $)[-1]
        );
}

sub myuuid  { print "$meta{'uuid'}\n";                           exit(0); }
sub version { print "$myname $meta{'version'} $meta{'date'}\n";  exit(0); }
sub where   { print "file://$meta{'host'}", "$meta{'source'}\n"; exit(0); }

#---------------------------------------------------------------------
__END__

=head1 NAME

buildenv - reads environment settings, writes equivalent Bourne/C-shell files.

=head1 SYNOPSIS

buildenv [-f file] [-hmuvw]

=head1 OPTIONS

=over 4

=item B<-h>

Print a brief help message and exit.

=item B<-f> FILE

Reads settings from FILE instead of default.

=item B<-m>

Print the manual page and exit.

=item B<-u>

Print the script UUID and exit.

=item B<-v>

Print the version and exit.

=item B<-w>

Print the canonical source location and exit.

=back

=head1 DESCRIPTION

B<buildenv> reads the B<envrc> file in $XDG_CONFIG_HOME and creates
equivalent Bourne-shell and C-shell files for setting environment variables.

$XDG_CONFIG_HOME is usually set to the $HOME/.config directory.

(T)csh doesn't understand using $(...) to run an inline command,
so it's changed to `...` in the C-shell settings file.

=head1 EXAMPLE

 me% cat in
 # Pager
 PAGER "less"
 # X-windows
 HOST "`uname -n`"
 DISPLAY "$HOST:0.0"

 me% buildenv -f in
 Rebuilding in.sh ...
 Rebuilding in.csh ...

 me% cat in.sh
 # Pager
 PAGER="less"; export PAGER
 # X-windows
 HOST="`uname -n`"; export HOST
 DISPLAY="$HOST:0.0"; export DISPLAY

 me% cat in.csh
 # Pager
 setenv PAGER "less"
 # X-windows
 setenv HOST "`uname -n`"
 setenv DISPLAY "$HOST:0.0"

=head1 AUTHOR

 Karl Vogel <vogelke+unix@pobox.com>
 Nobody, Inc

=cut
