summaryrefslogtreecommitdiff
path: root/dot/local/bin/safeunrar
blob: 506145be12765217bc0ac016beac180b95163880 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#!/usr/bin/perl -w
########################################################################
# safeunrar, safeunzip, safeuntar -- Extract archives, ensuring that   #
# all files in each archive remain in a single subdirectory, without   #
# overwriting anything.                                                #
########################################################################
# The decompression program is called once for each argument.  The     #
# program to use is guessed, first by extension, and failing that      #
# by the name of this program (which, in that case, must be one of     #
# safeunrar, safeunzip, safeuntar).                                    #
########################################################################
use File::Temp qw(tempdir);
use File::Basename;
use Cwd;

my %extension_map = (
  '(tar\.gz|tgz|taz)' 	    => 'tar -zxf',
  '(tar\.Z|taZ)'            => 'tar -Zxf',
  '(tar\.bz2|tz2|tbz2|tbz)' => 'tar -jxf',
  '(tar\.lzma|tlz)'         => 'tar --use-compress-program=lzma -xf',
  '(zip|xpi|jar)'	    => 'unzip',
  'rar'			    => 'unrar x',
  'tar'			    => 'tar -xf',
);

$cwd = getcwd or die "getcwd error: $!";
for $archive (@ARGV) {
  chdir $cwd or die "chdir error: $!";

  my ($base, @cmd);
  keys %extension_map; # resets "each"
  while (($rx, $cmd) = each %extension_map) {
    $_ = basename $archive;
    if (m/^(.*)\.$rx$/) {
      @cmd = split / /, $cmd;
      $base = $1;
      last;
    }
  }
  unless (@cmd) {
    for (basename $0) {
      $_ eq 'safeunzip' && (@cmd = qw'unzip')
      || $_ eq 'safeunrar' && (@cmd = qw'unrar x')
# don't use this because the current gnu tar's --auto-compress is
# guaranteed to fail if the current %extension_map failed.
#     || $_ eq 'safeuntar' && (@cmd = qw'tar --auto-compress -xf')
      || $_ eq 'safeuntar' && (@cmd = qw'tar -zxf')
      || die;
    }
    ($base = basename $archive) =~ s/\..{2,4}$//;
  }

  $tempdir = tempdir("$base.XXXXXX", DIR => ".")
    or warn("tempdir error: $!; skipping $archive"), next;
  chmod 0777 & ~ umask, $tempdir;

  $ltd = "leaving temporary directory '$tempdir'";

  # extract the files
  chdir $tempdir
    or warn("chdir error: $!; $ltd"), next;
  $archive =~ s#^(?!/)#../#;
  system(@cmd, $archive) == 0
    or warn("$cmd[0] error: $?/$!; $ltd"), next;

  # count the files
  opendir(DIR, '.')
    or warn("opendir error: $!; $ltd"), next;
  @f = readdir(DIR)
    or warn("readdir error: $!; $ltd"), next;
  closedir(DIR)
    or warn("closedir error: $!; ignoring");

  # if only one file, delete the dir
  @f = grep { ! m/^\.\.?$/ } @f;
  if (1 == @f) {
    ! -e "$cwd/$f[0]"
      or warn("not overwriting $f[0]; $ltd"), next;
    rename $f[0], "$cwd/$f[0]"
      or warn("rename error: $!; $ltd"), next;
    chdir $cwd
      or warn("chdir error: $!; $ltd"), next;
    rmdir $tempdir
      or warn("rmdir error: $!; $ltd"), next;
  # if multiple files, rename the dir
  } else {
    rename "$cwd/$tempdir", "$cwd/$base"
      or warn("rename error: $!; $ltd"), next;
  }
}