#!/usr/bin/perl
#<setperm: restore owner, group, modtime, and permissions for files.

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

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

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

my %options;
my @getopt_args = (
    'h|?',    # print usage
    'm',      # print manpage
    'u',      # print UUID
    'v',      # print version
    'w',      # print source location
    );

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

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

# Cache results from password/group file lookups.

my ($file, $mode, $uid, $usr, $gid, $grp, $mtime);

memoize('saveuid');
memoize('savegid');

sub saveuid {
    my $u = shift;
    return $u if $u =~ /^\d\d*$/;
    return getpwnam($u);
}

sub savegid {
    my $g = shift;
    return $g if $g =~ /^\d\d*$/;
    return getgrnam($g);
}

# Read "user|group|mode|mtime|file", split into fields.
# Set user, group, mode, and modtime.

while (<>) {
    chomp;
    ($usr, $grp, $mode, $mtime, $file) = split(/\|/, $_, 5);
    $mode = oct($mode);    # read in as a string!

    $uid = saveuid($usr);
    unless (defined $uid) {
        warn "$myname: can't find uid for '$usr', line $.:\n[$_]\n";
        next;
    }

    $gid = savegid($grp);
    unless (defined $gid) {
        warn "$myname: can't find gid for '$grp', line $.:\n[$_]\n";
        next;
    }

    # URL-decode the filename.  Do this inline since we do it a lot,
    # and the characters we encode aren't the same as URI::Escape.
    $file =~ s/\%([A-Fa-f0-9]{2})/pack('C', hex($1))/seg;

    chown($uid, $gid, $file)     or warn "chown $file: $!\n";
    chmod($mode, $file)          or warn "chmod $file: $!\n";
    utime($mtime, $mtime, $file) or warn "utime $file: $!\n";
}


exit(0);

#---------------------------------------------------------------------
# Print a usage message from the comments and exit.

sub usage {
    my ($emsg) = @_;
    use Pod::Usage qw(pod2usage);
    warn "$emsg\n" if defined $emsg;
    pod2usage(-verbose => 99, -sections => "NAME|SYNOPSIS|OPTIONS");
    return;
}

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

#---------------------------------------------------------------------
# Print the UUID, current version, or source location.

sub myuuid {
    my $UUID = $1
        if q$UUID: 781fbf6b-5607-396f-8e53-65c0c90ee407 $ =~ /UUID: (.*) /;
    print "$UUID\n";
    exit(0);
}

sub version {
    use version; our $VERSION = qv( (qw$Revision: 2.2 $)[-1] );
    my $DATE = $1 if q$Date: 2023-08-16 03:40:12-04 $ =~ /Date: (.*) /;
    print "$myname $VERSION $DATE\n";
    exit(0);
}

sub where {
    my $SOURCE = $1
        if q$Source: /home/vogelke/bin/RCS/setperm,v $ =~
          /Source: (.*) /;
    my $HOST = $1 if q$Host: furbag.my.domain $ =~ /Host: (.*) /;
    print "file://[$HOST]", "[$SOURCE]\n";
    exit(0);
}

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

=head1 NAME

setperm - restore UID, GID, modtime and filemode for files

=head1 SYNOPSIS

setperm [-hmuvw]

setperm < /tmp/perm

=head1 OPTIONS

=over 4

=item B<-h>

Print a brief help message and exit.

=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<setperm> will restore owner and permissions settings for supplied files.
Intended for use with companion program B<getperm>.

Filenames can be URL-encoded to safely deal with character like spaces, etc.
that don't work and play nicely with Unix.  Do this inline since we do it
a lot; the characters encoded are a subset of those used in URI::Escape.

These characters are left alone:  A-Z a-z 0-9 / - _ . ! ~ , ( ) +

=head1 AUTHOR

 Karl Vogel <vogelke+unix@pobox.com>
 Array Information Technology

=cut
