3
# atool - A script for managing file archives of various types.
5
# Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2008,
8
# This program is free software; you can redistribute it and/or modify
9
# it under the terms of the GNU General Public License as published by
10
# the Free Software Foundation; either version 2 of the License, or
11
# (at your option) any later version.
13
# This program is distributed in the hope that it will be useful,
14
# but WITHOUT ANY WARRANTY; without even the implied warranty of
15
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
# GNU General Public License for more details.
18
# You should have received a copy of the GNU General Public License along
19
# with this program; if not, write to the Free Software Foundation,
20
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22
# See the atool(1) manual page for usage details.
24
# This file uses tab stops with a length of two.
27
# XXX: We could use -CLSDA but 5.10.0 has a bug which prevents us from
28
# specifying this with shebang. Thanks to some helpful dude on #perl
31
use Encode qw(decode_utf8);
32
binmode($_, ':encoding(UTF-8)') for \*STDIN, \*STDOUT, \*STDERR;
33
$_ = decode_utf8($_) for @ARGV, values %ENV;
43
# Subroutine prototypes (needed for perl 5.6)
46
sub multiarchivecmd($$$$@);
47
sub singlearchivecmd($$$$$@);
48
sub maketarcmd($$$$@);
64
sub unlink_directory($);
65
sub find_comparable_file($);
70
sub handle_empty_add(@);
71
sub issingleformat($);
72
sub repack_archive($$$$);
73
sub set_config_option($$$);
75
$::SYSCONFDIR = '/usr/local/etc'; # This line is automatically updated by make
76
$::PACKAGE = 'atool'; # This line is automatically updated by make
77
$::VERSION = '0.37.0'; # This line is automatically updated by make
78
$::BUG_EMAIL = 'oskar@osk.mine.nu'; # This line is automatically updated by make
79
$::PROGRAM = $::PACKAGE;
81
# Configuration options and their built-in defaults
82
$::cfg_args_diff = '-ru'; # arguments to pass to diff program
83
$::cfg_decompress_to_cwd = 1; # decompress to current directory
84
$::cfg_default_verbosity = 1; # default verbosity level
85
$::cfg_extract_deb_control = 1; # extract DEBIAN control dir from .deb packages?
86
$::cfg_keep_compressed = 1; # keep compressed file after pack/unpack
87
$::cfg_path_7z = '7z'; # 7z program
88
$::cfg_path_ar = 'ar'; # ar program
89
$::cfg_path_arc = 'arc'; # arc program
90
$::cfg_path_arj = 'arj'; # arj program
91
$::cfg_path_bzip = 'bzip'; # bzip program
92
$::cfg_path_bzip2 = 'bzip2'; # bzip2 program
93
$::cfg_path_cabextract = 'cabextract'; # cabextract program
94
$::cfg_path_cat = 'cat'; # cat program
95
$::cfg_path_compress = 'compress'; # compress program
96
$::cfg_path_cpio = 'cpio'; # cpio program
97
$::cfg_path_diff = 'diff'; # diff program
98
$::cfg_path_dpkg_deb = 'dpkg-deb'; # dpkg-deb program
99
$::cfg_path_file = 'file'; # file program
100
$::cfg_path_find = 'find'; # find program
101
$::cfg_path_gzip = 'gzip'; # gzip program
102
$::cfg_path_jar = 'jar'; # jar program
103
$::cfg_path_lha = 'lha'; # lha program
104
$::cfg_path_lrzip = 'lrzip'; # lrzip program
105
$::cfg_path_lzip = 'lzip'; # lzip program
106
$::cfg_path_lzma = 'lzma'; # lzma program
107
$::cfg_path_lzop = 'lzop'; # lzop program
108
$::cfg_path_nomarch = 'nomarch'; # nomarch program
109
$::cfg_path_pager = 'pager'; # pager program
110
$::cfg_path_pbzip2 = 'pbzip2'; # pbzip2 program
111
$::cfg_path_rar = 'rar'; # rar program
112
$::cfg_path_rpm = 'rpm'; # rpm program
113
$::cfg_path_rpm2cpio = 'rpm2cpio'; # rpm2cpio program
114
$::cfg_path_rzip = 'rzip'; # rzip program
115
$::cfg_path_syscfg = File::Spec->catfile($::SYSCONFDIR, $::PROGRAM.'.conf'); # system-wide configuration file
116
$::cfg_path_tar = 'tar'; # tar program
117
$::cfg_path_unace = 'unace'; # unace program
118
$::cfg_path_unalz = 'unalz'; # unalz program
119
$::cfg_path_unarj = 'unarj'; # unarj program
120
$::cfg_path_unrar = 'unrar'; # unrar program
121
$::cfg_path_unzip = 'unzip'; # unzip program
122
$::cfg_path_usercfg = '.'.$::PROGRAM.'rc'; # user configuration file
123
$::cfg_path_xargs = 'xargs'; # xargs program
124
$::cfg_path_xz = 'xz'; # xz program
125
$::cfg_path_zip = 'zip'; # zip program
126
$::cfg_show_extracted = 1; # always show extracted file/directory
127
$::cfg_strip_unknown_ext = 1; # strip unknown extensions
128
$::cfg_tmpdir_name = 'Unpack-%04d'; # extraction directory name
129
$::cfg_tmpfile_name = 'Pack-%04d'; # temporary file used during packing
130
$::cfg_use_arc_for_unpack = 0; # use arc to unpack arc files?
131
$::cfg_use_arj_for_unpack = 0; # use arj to unpack arj files?
132
$::cfg_use_file = 1; # use file(1) for unknown extensions?
133
$::cfg_use_file_always = 0; # always use file to identify archives (ignore extension)
134
$::cfg_use_find_cpio_print0 = 1; # use -print0/-0 find/cpio options?
135
$::cfg_use_gzip_for_z = 1; # use gzip to decompress .Z files?
136
$::cfg_use_jar = 0; # use jar or zip for .jar archives?
137
$::cfg_use_pbzip2 = 0; # use pbzip2 instead of bzip2
138
$::cfg_use_rar_for_unpack = 0; # use rar to unpack rar files?
139
$::cfg_use_tar_bzip2_option = 1; # does tar support --bzip2?
140
$::cfg_use_tar_lzma_option = 1; # does tar support --lzma?
141
$::cfg_use_tar_lzop_option = 0; # does tar support --lzop?
142
$::cfg_use_tar_xv_option = 0; # does tar support --xv?
143
$::cfg_use_tar_z_option = 1; # does tar support -z?
146
$::basename = quote(File::Basename::basename($0));
148
$::up = File::Spec->updir();
149
$::cur = File::Spec->curdir();
153
Getopt::Long::config('bundling');
154
Getopt::Long::GetOptions(
155
'l|list' => \$::opt_cmd_list,
156
'x|extract' => \$::opt_cmd_extract,
157
'X|extract-to=s' => \$::opt_cmd_extract_to,
158
'a|add' => \$::opt_cmd_add,
159
'c|cat' => \$::opt_cmd_cat,
160
'd|diff' => \$::opt_cmd_diff,
161
'r|repack' => \$::opt_cmd_repack,
162
'q|quiet' => sub { $::opt_verbosity--; },
163
'v|verbose' => sub { $::opt_verbosity++; },
164
'V|verbosity=i' => \$::opt_verbosity,
165
'config=s' => \$::opt_config,
166
'o|option=s' => sub { push @::opt_options, $_[1] },
167
'help' => \$::opt_cmd_help,
168
'version' => \$::opt_cmd_version,
169
'F|format=s' => \$::opt_format,
170
'f|force' => \$::opt_force,
171
'p|page' => \$::opt_use_pager,
172
'e|each' => \$::opt_each,
173
'E|explain' => \$::opt_explain,
174
'S|simulate' => \$::opt_simulate,
175
'save-outdir=s' => \$::opt_save_outdir,
176
'D|subdir' => \$::opt_extract_subdir,
177
'0|null' => \$::opt_null,
181
if ($::opt_cmd_version) {
182
print $::PACKAGE.' '.$::VERSION."\
183
Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2008 Oskar Liljeblad\
184
This is free software. You may redistribute copies of it under the terms of
185
the GNU General Public License <http://www.gnu.org/licenses/gpl.html>.
186
There is NO WARRANTY, to the extent permitted by law.
188
Written by Oskar Liljeblad.\n";
193
if ($::opt_cmd_help) {
194
print "Usage: $::PROGRAM [OPTION]... ARCHIVE [FILE]...\n";
195
print " $::PROGRAM -e [OPTION]... [ARCHIVE]...\n";
196
print "Manage file archives of various types.\
199
-l, --list list files in archive (als)\
200
-x, --extract extract files from archive (aunpack)\
201
-X, --extract-to=PATH extract archive to specified directory\
202
-a, --add create archive (apack)\
203
-c, --cat extract file to standard out (acat)\
204
-d, --diff generate a diff between two archives (adiff)\
205
-r, --repack repack archives to a different format (arepack)\
206
--help display this help and exit\
207
--version output version information and exit\
210
-e, --each execute command above for each file specified
211
-F, --format=EXT override archive format (see below)\
212
-D, --subdir always create subdirectory when extracting\
213
-f, --force allow overwriting of local files\
214
-q, --quiet decrease verbosity level by one\
215
-v, --verbose increase verbosity level by one\
216
-V, --verbosity=LEVEL specify verbosity (0, 1 or 2)\
217
-p, --page send output through pager\
218
-0, --null filenames from standard in are null-byte separated\
219
-E, --explain explain what is being done by ".$::PROGRAM."\
220
-S, --simulate simulation mode - no filesystem changes are made\
221
-o, --option=KEY=VALUE override a configuration option\
222
--config=FILE load configuration defaults from file\
224
Archive format (for --format) may be specified either as a\
225
file extension (\"tar.gz\") or as \"tar+gzip\".\
227
Report bugs to Oskar Liljeblad <".$::BUG_EMAIL.">.\
232
# Read configuration files
233
if (defined $::opt_config) {
234
readconfig($::opt_config, 0);
236
readconfig($::cfg_path_syscfg, 1);
237
if ($::cfg_path_usercfg !~ /^\//) {
238
readconfig(File::Spec->catfile($ENV{HOME}, $::cfg_path_usercfg), 1);
240
readconfig($::cfg_path_usercfg, 1);
243
foreach my $opt (@::opt_options) {
244
my ($var,$val) = ($opt =~ /^([^=]+)=(.*)$/);
245
die "$::basename: invalid value for --option: $opt\n" if !defined $val;
246
set_config_option($var, $val, '');
249
# Verify option integrity
250
$::opt_verbosity += $::cfg_default_verbosity;
251
if ($::opt_explain && $::opt_simulate) {
252
die "$::basename: --explain and --simulate options are mutually exclusive\n"; #OK
255
my $mode = getmode();
257
if (defined $::opt_save_outdir && $mode eq 'extract-to') {
258
die "$::basename: --save-outdir cannot be used in extract-to mode\n";
260
if ($::opt_extract_subdir && $mode ne 'extract') {
261
die "$::basename: --subdir can only be used in extract mode\n";
264
if ($mode eq 'diff') {
265
die "$::basename: missing archive argument\n" if (@ARGV < 2); #OK
266
my $use_pager = $::opt_use_pager;
268
$::opt_use_pager = 0;
270
my $outfile1 = makeoutdir() || exit 1;
271
my $outfile2 = makeoutdir() || exit 1;
272
$::opt_cmd_extract_to = $outfile1;
273
$::opt_cmd_extract_to_type = 'f';
274
exit 1 if (!runcmds('extract-to', undef, $ARGV[0]));
275
$::opt_cmd_extract_to = $outfile2;
276
$::opt_cmd_extract_to_type = 'f';
277
exit 1 if (!runcmds('extract-to', undef, $ARGV[1]));
279
my $match1 = find_comparable_file($outfile1);
280
my $match2 = find_comparable_file($outfile2);
282
my @cmd = ($::cfg_path_diff, split(/ /, $::cfg_args_diff), $match1, $match2);
283
push @cmd, ['|'], get_pager_program() if $use_pager;
284
my $allok = cmdexec(1, @cmd);
286
foreach my $file ($outfile1,$outfile2) {
287
warn 'rm -r ',quote($file),"\n" if $::opt_simulate;
288
if (-e $file && -d $file) {
290
#print "$::basename: remove `$file'? ";
291
#select((select(STDOUT), $| = 1)[0]);
293
#if (defined $line && $line =~ /^y/) {
295
warn 'rm -r ',quote($file),"\n" if $::opt_explain;
296
unlink_directory($file) if !$::opt_simulate;
304
exit ($allok ? 0 : 1);
306
elsif ($mode eq 'repack') {
309
if (!defined $::opt_format) {
310
die "$::basename: specify a format with -F when using --each in repack mode\n";
312
my $fmt2 = findformat($::opt_format, 1);
313
for (my $c = 0; $c < @ARGV; $c++) {
314
my $fmt1 = findformat($ARGV[$c], 0);
315
if (!issingleformat($fmt1) && issingleformat($fmt2)) {
316
warn "$::basename: format $fmt1 is cannot be repacked into format $fmt2\n";
317
warn "skipping ", quote($ARGV[$c]), "\n";
320
if ($fmt1 eq $fmt2) {
321
warn "$::basename: will not repack to same archive type\n";
322
warn "skipping ", quote($ARGV[$c]), "\n";
325
my $newname = stripext($ARGV[$c]).formatext($fmt2);
327
warn "$::basename: ".quote($newname).": destination file exists\n";
328
warn "skipping ", quote($ARGV[$c]), "\n";
331
repack_archive($ARGV[$c], $newname, $fmt1, $fmt2);
332
my $diff = ($::opt_simulate ? 0 : -s $ARGV[$c] - -s $newname);
334
if ($::opt_verbosity >= 1) {
335
print quote($newname), ': ',
336
($diff >= 0 ? 'saved '.$diff : 'grew '.-$diff),' ',
337
($diff == 1 ? 'byte':'bytes'), "\n";
340
if ($::opt_verbosity >= 1) {
341
print $totaldiff >= 0 ? 'saved '.$totaldiff : 'grew '.-$totaldiff, ' ',
342
$totaldiff == 1 ? 'byte':'bytes', " in total\n";
345
die "$::basename: missing archive arguments\n" if (@ARGV < 1); #OK
346
die "$::basename: missing archive argument\n" if (@ARGV < 2); #OK
347
die "$::basename: will not repack to same archive file\n"
348
if ($ARGV[0] eq $ARGV[1] || File::Spec->canonpath($ARGV[0]) eq File::Spec->canonpath($ARGV[1]));
349
die "$::basename: ".quote($ARGV[1]).": destination file exists\n" if -e $ARGV[1];
350
my $fmt1 = findformat($ARGV[0], 0);
351
my $fmt2 = findformat($ARGV[1], 0);
352
die "$::basename: format $fmt1 is cannot be repacked into format $fmt1\n"
353
if (!issingleformat($fmt1) && issingleformat($fmt2));
354
die "$::basename: will not repack to same archive type\n" if $fmt1 eq $fmt2;
355
repack_archive($ARGV[0], $ARGV[1], $fmt1, $fmt2);
356
my $diff = ($::opt_simulate ? 0 : (-s $ARGV[0]) - (-s $ARGV[1]));
357
if ($::opt_verbosity >= 1) {
358
print quote($ARGV[1]), ': ',
359
($diff >= 0 ? 'saved '.$diff : 'grew '.-$diff),' ',
360
($diff == 1 ? 'byte':'bytes'), "\n";
364
elsif ($::opt_each) {
366
if ($mode eq 'cat') {
367
die "$::basename: --each can not be used with cat or add command\n"; #OK
369
if ($mode eq 'add') {
370
if (!defined $::opt_format) {
371
die "$::basename: specify a format with -F when using --each in add mode\n";
373
my $format = findformat($::opt_format, 1);
374
for (my $c = 0; $c < @ARGV; $c++) {
375
my $archive = File::Spec->canonpath($ARGV[$c]) . formatext($format);
376
warn quote($archive).":\n" if $::opt_verbosity > 1;
377
runcmds('add', $format, $archive, $ARGV[$c]) or $allok = 0;
380
for (my $c = 0; $c < @ARGV; $c++) {
381
warn quote($ARGV[$c]).":\n" if $::opt_verbosity > 1;
382
runcmds($mode, undef, $ARGV[$c]) or $allok = 0;
385
exit ($allok ? 0 : 1);
388
die "$::basename: missing archive argument\n" if (@ARGV == 0); #OK
389
runcmds($mode, undef, shift @ARGV, @ARGV) || exit 1;
392
# runcmds(mode, format, archive, args)
393
# Execute an atool command. This is where it all happens.
394
# If mode is 'extract', returns the directory (or only file)
395
# which was extracted.
396
# If forceformat is undef, the format will be detected from
397
# $::opt_format or the filename.
399
my ($mode, $format, $archive, @args) = @_;
401
if (!defined $format) {
402
if (defined $::opt_format) {
403
$format = findformat($::opt_format, 1);
405
$format = findformat($archive, 0);
407
return undef if !defined $format;
412
if ($format eq 'tar+bzip2') {
413
return undef if ($mode eq 'extract' && !defined ($outdir = makeoutdir()));
414
if ($::cfg_use_tar_bzip2_option) {
415
push @cmd, maketarcmd($archive, $outdir, $mode, 'f', '--bzip2'), @args;
416
} elsif ($::cfg_use_pbzip2) {
417
if ($mode eq 'add') {
418
# Unfortunately pbzip2 cannot read from standard in
419
my $tmpname = makeoutfile($::cfg_tmpfile_name);
420
push @cmd, maketarcmd($tmpname, $outdir, $mode, 'f'), @args;
421
push @cmd, [';'], $::cfg_path_pbzip2, '-c', $tmpname, ['>'], $archive;
422
push @cmd, [';'], 'rm', $tmpname;
424
push @cmd, $::cfg_path_pbzip2, '-cd', $archive, ['|'];
425
push @cmd, maketarcmd('-', $outdir, $mode, 'f'), @args;
428
push @cmd, $::cfg_path_bzip2, '-cd', $archive, ['|'] if $mode ne 'add';
429
push @cmd, maketarcmd('-', $outdir, $mode, 'f'), @args;
430
push @cmd, ['|'], $::cfg_path_bzip2, '-c', ['>'], $archive if $mode eq 'add';
432
@cmd = handle_empty_add(@cmd) if ($mode eq 'add' && @args == 0);
433
return multiarchivecmd($archive, $outdir, $mode, 1, 0, \@args, @cmd);
435
elsif ($format eq 'tar+gzip') {
436
return undef if ($mode eq 'extract' && !defined ($outdir = makeoutdir()));
437
if ($::cfg_use_tar_z_option) {
438
push @cmd, maketarcmd($archive, $outdir, $mode, 'zf'), @args;
440
push @cmd, $::cfg_path_gzip, '-cd', $archive, ['|'] if $mode ne 'add';
441
push @cmd, maketarcmd('-', $outdir, $mode, 'f'), @args;
442
push @cmd, ['|'], $::cfg_path_gzip, '-c', ['>'], $archive if $mode eq 'add';
444
@cmd = handle_empty_add(@cmd) if ($mode eq 'add' && @args == 0);
445
return multiarchivecmd($archive, $outdir, $mode, 1, 0, \@args, @cmd);
447
elsif ($format eq 'tar+bzip') {
448
return undef if ($mode eq 'extract' && !defined ($outdir = makeoutdir()));
449
push @cmd, $::cfg_path_bzip, '-cd', $archive, ['|'] if $mode ne 'add';
450
push @cmd, maketarcmd('-', $outdir, $mode, 'f'), @args;
451
push @cmd, ['|'], $::cfg_path_bzip, '-c', ['>'], $archive if $mode eq 'add';
452
@cmd = handle_empty_add(@cmd) if ($mode eq 'add' && @args == 0);
453
return multiarchivecmd($archive, $outdir, $mode, 1, 0, \@args, @cmd);
455
elsif ($format eq 'tar+compress') {
456
return undef if ($mode eq 'extract' && !defined ($outdir = makeoutdir()));
457
if ($::cfg_use_gzip_for_z) {
458
push @cmd, $::cfg_path_gzip, '-cd', $archive, ['|'] if $mode ne 'add';
460
push @cmd, $::cfg_path_compress, '-cd', $archive, ['|'] if $mode ne 'add';
462
push @cmd, maketarcmd('-', $outdir, $mode, 'f'), @args;
463
push @cmd, ['|'], $::cfg_path_compress, '-c', ['>'], $archive if $mode eq 'add';
464
@cmd = handle_empty_add(@cmd) if ($mode eq 'add' && @args == 0);
465
return multiarchivecmd($archive, $outdir, $mode, 1, 0, \@args, @cmd);
467
elsif ($format eq 'tar+lzop') {
468
return undef if ($mode eq 'extract' && !defined ($outdir = makeoutdir()));
469
if ($::cfg_use_tar_lzop_option) {
470
push @cmd, maketarcmd($archive, $outdir, $mode, 'f', '--lzop'), @args;
472
push @cmd, $::cfg_path_lzop, '-cd', $archive, ['|'] if $mode ne 'add';
473
push @cmd, maketarcmd('-', $outdir, $mode, 'f'), @args;
474
push @cmd, ['|'], $::cfg_path_lzop, '-c', ['>'], $archive if $mode eq 'add';
476
@cmd = handle_empty_add(@cmd) if ($mode eq 'add' && @args == 0);
477
return multiarchivecmd($archive, $outdir, $mode, 1, 0, \@args, @cmd);
479
elsif ($format eq 'tar+lzip') {
480
return undef if ($mode eq 'extract' && !defined ($outdir = makeoutdir()));
481
push @cmd, $::cfg_path_lzip, '-cd', $archive, ['|'] if $mode ne 'add';
482
push @cmd, maketarcmd('-', $outdir, $mode, 'f'), @args;
483
push @cmd, ['|'], $::cfg_path_lzip, '-c', ['>'], $archive if $mode eq 'add';
484
@cmd = handle_empty_add(@cmd) if ($mode eq 'add' && @args == 0);
485
return multiarchivecmd($archive, $outdir, $mode, 1, 0, \@args, @cmd);
487
elsif ($format eq 'tar+xz') {
488
return undef if ($mode eq 'extract' && !defined ($outdir = makeoutdir()));
489
if ($::cfg_use_tar_xv_option) {
490
push @cmd, maketarcmd($archive, $outdir, $mode, 'f', '--xv'), @args;
492
push @cmd, $::cfg_path_xz, '-cd', $archive, ['|'] if $mode ne 'add';
493
push @cmd, maketarcmd('-', $outdir, $mode, 'f'), @args;
494
push @cmd, ['|'], $::cfg_path_xz, '-c', ['>'], $archive if $mode eq 'add';
496
@cmd = handle_empty_add(@cmd) if ($mode eq 'add' && @args == 0);
497
return multiarchivecmd($archive, $outdir, $mode, 1, 0, \@args, @cmd);
499
elsif ($format eq 'tar+7z') {
500
return undef if ($mode eq 'extract' && !defined ($outdir = makeoutdir()));
501
push @cmd, $::cfg_path_7z, 'x', '-so', $archive, ['|'] if $mode ne 'add';
502
push @cmd, maketarcmd('-', $outdir, $mode, 'f'), @args;
503
push @cmd, ['|'], $::cfg_path_7z, 'a', '-si', $archive if $mode eq 'add';
504
@cmd = handle_empty_add(@cmd) if ($mode eq 'add' && @args == 0);
505
return multiarchivecmd($archive, $outdir, $mode, 1, 0, \@args, @cmd);
507
elsif ($format eq 'tar+lzma') {
508
return undef if ($mode eq 'extract' && !defined ($outdir = makeoutdir()));
509
if ($::cfg_use_tar_lzma_option) {
510
push @cmd, maketarcmd($archive, $outdir, $mode, 'f', '--lzma'), @args;
512
push @cmd, $::cfg_path_lzma, '-cd', $archive, ['|'] if $mode ne 'add';
513
push @cmd, maketarcmd('-', $outdir, $mode, 'f'), @args;
514
push @cmd, ['|'], $::cfg_path_lzma, '-c', ['>'], $archive if $mode eq 'add';
516
@cmd = handle_empty_add(@cmd) if ($mode eq 'add' && @args == 0);
517
return multiarchivecmd($archive, $outdir, $mode, 1, 0, \@args, @cmd);
519
elsif ($format eq 'tar') {
520
return undef if ($mode eq 'extract' && !defined ($outdir = makeoutdir()));
521
push @cmd, maketarcmd($archive, $outdir, $mode, 'f'), @args;
522
@cmd = handle_empty_add(@cmd) if ($mode eq 'add' && @args == 0);
523
return multiarchivecmd($archive, $outdir, $mode, 1, 0, \@args, @cmd);
525
elsif ($format eq 'jar' && $::cfg_use_jar) {
526
return undef if ($mode eq 'extract' && !defined ($outdir = makeoutdir()));
528
if ($mode eq 'add') {
529
warn "$::basename: ".quote($archive).": $mode command not supported for $format archives\n";
532
$opts .= 'v' if $::opt_verbosity >= 1;
533
push @cmd, $::cfg_path_jar;
534
push @cmd, "x$opts", '-C', $outdir if $mode eq 'extract';
535
push @cmd, "x$opts", '-C', $::opt_cmd_extract_to if $mode eq 'extract-to';
536
push @cmd, "t$opts" if $mode eq 'list';
537
push @cmd, "c$opts" if $mode eq 'add';
538
push @cmd, $archive, @args;
539
@cmd = handle_empty_add(@cmd) if ($mode eq 'add' && @args == 0);
540
return multiarchivecmd($archive, $outdir, $mode, 1, 0, \@args, @cmd);
542
elsif ($format eq 'jar' || $format eq 'zip') {
543
return undef if ($mode eq 'extract' && !defined ($outdir = makeoutdir()));
544
if ($mode eq 'add') {
545
push @cmd, $::cfg_path_zip, '-r';
547
push @cmd, $::cfg_path_unzip;
548
push @cmd, '-p' if $mode eq 'cat';
549
push @cmd, '-l' if $mode eq 'list';
550
push @cmd, '-d', $outdir if $mode eq 'extract';
551
push @cmd, '-d', $::opt_cmd_extract_to if $mode eq 'extract-to';
553
push @cmd, '-v' if $::opt_verbosity > 1;
554
push @cmd, '-qq' if $::opt_verbosity < 0;
555
push @cmd, '-q' if $::opt_verbosity == 0;
556
push @cmd, $archive, @args;
557
@cmd = handle_empty_add(@cmd) if ($mode eq 'add' && @args == 0);
558
return multiarchivecmd($archive, $outdir, $mode, 0, 0, \@args, @cmd);
560
elsif ($format eq 'rar') {
561
return undef if ($mode eq 'extract' && !defined ($outdir = makeoutdir()));
562
if ($mode eq 'add' || $::cfg_use_rar_for_unpack) {
563
push @cmd, $::cfg_path_rar;
565
push @cmd, $::cfg_path_unrar;
567
push @cmd, 'a' if $mode eq 'add';
568
push @cmd, 'vt' if $mode eq 'list' && $::opt_verbosity >= 3;
569
push @cmd, 'v' if $mode eq 'list' && $::opt_verbosity == 2;
570
push @cmd, 'l' if $mode eq 'list' && $::opt_verbosity <= 1;
571
push @cmd, 'x' if ($mode eq 'extract' || $mode eq 'extract-to');
572
push @cmd, '-ierr', 'p' if $mode eq 'cat';
573
push @cmd, '-r0' if ($mode eq 'add');
574
push @cmd, $archive, @args;
575
push @cmd, tailslash($outdir) if $mode eq 'extract';
576
push @cmd, tailslash($::opt_cmd_extract_to) if $mode eq 'extract-to';
577
@cmd = handle_empty_add(@cmd) if ($mode eq 'add' && @args == 0);
578
return multiarchivecmd($archive, $outdir, $mode, 0, 0, \@args, @cmd);
580
elsif ($format eq '7z') {
581
# 7z has the -so option for writing data to stdout, but it doesn't
582
# write data to terminal even if the file is designed to be
583
# read in a terminal...
584
return undef if ($mode eq 'extract' && !defined ($outdir = makeoutdir()));
585
#if ($mode eq 'cat') {
586
# warn "$::basename: ".quote($archive).": $mode command not supported for $format archives\n";
589
push @cmd, $::cfg_path_7z;
590
push @cmd, 'a' if $mode eq 'add';
591
push @cmd, 'l' if $mode eq 'list';
592
push @cmd, 'x', '-so' if $mode eq 'cat';
593
push @cmd, 'x', '-o'.$outdir if $mode eq 'extract';
594
push @cmd, 'x', '-o'.$::opt_cmd_extract_to if $mode eq 'extract-to';
595
push @cmd, $archive, @args;
596
return multiarchivecmd($archive, $outdir, $mode, 1, 0, \@args, @cmd);
598
elsif ($format eq 'cab') {
599
return undef if ($mode eq 'extract' && !defined ($outdir = makeoutdir()));
600
if ($mode eq 'add') {
601
warn "$::basename: ".quote($archive).": $mode command not supported for $format archives\n";
604
push @cmd, $::cfg_path_cabextract;
605
push @cmd, '--single';
606
push @cmd, '--directory', $outdir if $mode eq 'extract';
607
push @cmd, '--directory', $::opt_cmd_extract_to if $mode eq 'extract-to';
608
push @cmd, '--pipe' if $mode eq 'cat';
609
push @cmd, '--list' if $mode eq 'list';
611
push @cmd, '--filter';
613
return multiarchivecmd($archive, $outdir, $mode, 0, 0, \@args, @cmd);
615
elsif ($format eq 'alzip') {
616
if ($mode eq 'cat' || $mode eq 'add' || $mode eq 'list') {
617
warn "$::basename: ".quote($archive).": $mode command not supported for $format archives\n";
620
return undef if ($mode eq 'extract' && !defined ($outdir = makeoutdir()));
621
push @cmd, $::cfg_path_unalz;
623
push @cmd, $outdir if $mode eq 'extract';
624
push @cmd, $::opt_cmd_extract_to if $mode eq 'extract-to';
625
return multiarchivecmd($archive, $outdir, $mode, 0, 0, \@args, @cmd);
627
elsif ($format eq 'lha') {
628
return undef if ($mode eq 'extract' && !defined ($outdir = makeoutdir()));
629
push @cmd, $::cfg_path_lha;
630
push @cmd, 'a' if $mode eq 'add';
631
push @cmd, 'v' if $mode eq 'list' && $::opt_verbosity >= 3;
632
push @cmd, 'l' if $mode eq 'list' && $::opt_verbosity == 2;
633
push @cmd, 'lq' if $mode eq 'list' && $::opt_verbosity <= 1;
634
push @cmd, 'xw='.tailslash($outdir) if $mode eq 'extract';
635
push @cmd, 'xw='.tailslash($::opt_cmd_extract_to) if $mode eq 'extract-to';
636
push @cmd, 'p' if $mode eq 'cat';
637
push @cmd, $archive, @args;
638
@cmd = handle_empty_add(@cmd) if ($mode eq 'add' && @args == 0);
639
return multiarchivecmd($archive, $outdir, $mode, 0, 0, \@args, @cmd);
641
elsif ($format eq 'ace') {
642
return undef if ($mode eq 'extract' && !defined ($outdir = makeoutdir()));
643
push @cmd, $::cfg_path_unace;
644
if ($mode eq 'add' || $mode eq 'cat') {
645
warn "$::basename: ".quote($archive).": $mode command not supported for $format archives\n";
648
push @cmd, 'v', '-c' if $mode eq 'list' && $::opt_verbosity >= 3;
649
push @cmd, 'v' if $mode eq 'list' && $::opt_verbosity == 2;
650
push @cmd, 'l' if $mode eq 'list' && $::opt_verbosity <= 1;
651
push @cmd, 'x' if ($mode eq 'extract' || $mode eq 'extract-to');
652
push @cmd, $archive, @args;
653
push @cmd, tailslash($outdir) if $mode eq 'extract';
654
push @cmd, tailslash($::opt_cmd_extract_to) if $mode eq 'extract-to';
655
@cmd = handle_empty_add(@cmd) if ($mode eq 'add' && @args == 0);
656
return multiarchivecmd($archive, $outdir, $mode, 0, 0, \@args, @cmd);
658
elsif ($format eq 'arj') {
659
return undef if ($mode eq 'extract' && !defined ($outdir = makeoutdir()));
660
if ($mode eq 'cat') {
661
warn "$::basename: ".quote($archive).": $mode command not supported for $format archives\n";
664
if ($mode eq 'add' || $::cfg_use_arj_for_unpack) {
665
push @cmd, $::cfg_path_arj;
666
push @cmd, 'a' if $mode eq 'add';
667
push @cmd, 'v' if $mode eq 'list' && $::opt_verbosity == 2;
668
push @cmd, 'l' if $mode eq 'list' && $::opt_verbosity <= 1;
669
push @cmd, 'x' if ($mode eq 'extract' || $mode eq 'extract-to');
670
push @cmd, $archive, @args;
671
push @cmd, tailslash($outdir) if $mode eq 'extract';
672
push @cmd, tailslash($::opt_cmd_extract_to) if $mode eq 'extract-to';
673
@cmd = handle_empty_add(@cmd) if ($mode eq 'add' && @args == 0);
674
return multiarchivecmd($archive, $outdir, $mode, 0, 0, \@args, @cmd);
676
push @cmd, $::cfg_path_unarj;
677
# XXX: cat mode might work for arj archives, but it extract to stderr!
678
push @cmd, 'v' if $mode eq 'list' && $::opt_verbosity == 2;
679
push @cmd, 'l' if $mode eq 'list' && $::opt_verbosity <= 1;
680
push @cmd, 'x' if ($mode eq 'extract' || $mode eq 'extract-to');
681
push @cmd, $archive if ($mode ne 'extract' && $mode ne 'extract-to');;
682
# we call makeabsolute here because needcwd=1 to the multiarchivecmd call
683
push @cmd, makeabsolute($archive) if ($mode eq 'extract' || $mode eq 'extract-to');
685
@cmd = handle_empty_add(@cmd) if ($mode eq 'add' && @args == 0);
686
return multiarchivecmd($archive, $outdir, $mode, 0, 1, \@args, @cmd);
689
elsif ($format eq 'arc') {
690
return undef if ($mode eq 'extract' && !defined ($outdir = makeoutdir()));
691
if ($mode eq 'add' || $::cfg_use_arc_for_unpack) {
692
push @cmd, $::cfg_path_arc;
693
push @cmd, 'a' if $mode eq 'add';
694
push @cmd, 'v' if $mode eq 'list' && $::opt_verbosity >= 3;
695
push @cmd, 'l' if $mode eq 'list' && $::opt_verbosity == 2;
696
push @cmd, 'ln' if $mode eq 'list' && $::opt_verbosity <= 1;
697
push @cmd, 'x' if ($mode eq 'extract' || $mode eq 'extract-to');
698
push @cmd, 'p' if $mode eq 'cat';
700
push @cmd, $::cfg_path_nomarch;
701
push @cmd, '-lvU' if $mode eq 'list' && $::opt_verbosity >= 2;
702
push @cmd, '-lU' if $mode eq 'list' && $::opt_verbosity <= 1;
703
push @cmd, '-p' if $mode eq 'cat';
705
push @cmd, $archive if ($mode ne 'extract' && $mode ne 'extract-to');
706
# we call makeabsolute here because needcwd=1 to the multiarchivecmd call
707
push @cmd, makeabsolute($archive) if ($mode eq 'extract' || $mode eq 'extract-to');
709
@cmd = handle_empty_add(@cmd) if ($mode eq 'add' && @args == 0);
710
return multiarchivecmd($archive, $outdir, $mode, 0, 1, \@args, @cmd);
712
elsif ($format eq 'rpm') {
713
return undef if ($mode eq 'extract' && !defined ($outdir = makeoutdir()));
714
if ($mode eq 'list') {
715
push @cmd, $::cfg_path_rpm;
717
push @cmd, '-v' if $::opt_verbosity >= 1;
718
push @cmd, $archive, @args;
719
return multiarchivecmd($archive, $outdir, $mode, 0, 0, \@args, @cmd);
721
elsif ($mode eq 'extract' || $mode eq 'extract-to') {
722
push @cmd, $::cfg_path_rpm2cpio;
723
push @cmd, makeabsolute($archive);
725
push @cmd, $::cfg_path_cpio, '-imd', '--quiet', @args;
726
return multiarchivecmd($archive, $outdir, $mode, 0, 1, \@args, @cmd);
729
# FIXME: I guess cat could work too, but it would require that we
730
# extracted to a temporary dir, read and printed it, then removed it.
731
warn "$::basename: ".quote($archive).": $mode command not supported for $format archives\n";
735
elsif ($format eq 'deb') {
736
return undef if ($mode eq 'extract' && !defined ($outdir = makeoutdir()));
737
if ($mode eq 'cat') {
738
push @cmd, $::cfg_path_dpkg_deb, '--fsys-tarfile', makeabsolute($archive), ['|'];
739
push @cmd, $::cfg_path_tar, '-xO', @args;
740
} elsif ($mode eq 'list' || $mode eq 'extract' || $mode eq 'extract-to') {
741
push @cmd, $::cfg_path_dpkg_deb;
742
push @cmd, '--contents' if $mode eq 'list';
743
if ($mode eq 'extract' || $mode eq 'extract-to') {
744
push @cmd, '--extract' if $::opt_verbosity <= 0;
745
push @cmd, '--vextract' if $::opt_verbosity > 0;
748
push @cmd, $outdir if $mode eq 'extract';
749
push @cmd, $::opt_cmd_extract_to if $mode eq 'extract-to';
751
if ($::cfg_extract_deb_control && ($mode eq 'extract' || $mode eq 'extract-to')) {
753
push @cmd, $::cfg_path_dpkg_deb;
754
push @cmd, '--control';
756
push @cmd, File::Spec->catdir($outdir, 'DEBIAN') if $mode eq 'extract';
757
push @cmd, File::Spec->catdir($::opt_cmd_extract_to, 'DEBIAN') if $mode eq 'extract-to';
760
return multiarchivecmd($archive, $outdir, $mode, 0, 0, \@args, @cmd);
762
elsif ($format eq 'ar') {
763
return undef if ($mode eq 'extract' && !defined ($outdir = makeoutdir()));
764
my $v = ($::opt_verbosity >= 1 ? 'v' : '');
765
push @cmd, $::cfg_path_ar;
766
push @cmd, 'rc'.$v if $mode eq 'add';
767
push @cmd, 'x'.$v if ($mode eq 'extract' || $mode eq 'extract-to');
768
push @cmd, 't'.$v if $mode eq 'list';
769
# Don't use v(erbose) with cat command because ar would add "\n<member data>\n\n" to output
770
push @cmd, 'p' if $mode eq 'cat';
771
push @cmd, makeabsolute($archive), @args;
772
return multiarchivecmd($archive, $outdir, $mode, 1, 1, \@args, @cmd);
774
elsif ($format eq 'cpio') {
775
return undef if ($mode eq 'extract' && !defined ($outdir = makeoutdir()));
776
if ($mode eq 'list') {
777
push @cmd, $::cfg_path_cat, $archive, ['|'];
778
push @cmd, $::cfg_path_cpio, '-t';
779
push @cmd, '-v' if $::opt_verbosity >= 1;
780
return multiarchivecmd($archive, $outdir, $mode, 0, 0, \@args, @cmd);
782
elsif ($mode eq 'extract' || $mode eq 'extract-to') {
783
push @cmd, $::cfg_path_cat, makeabsolute($archive), ['|'];
784
push @cmd, $::cfg_path_cpio, '-i';
785
push @cmd, '-v' if $::opt_verbosity >= 1;
786
return multiarchivecmd($archive, $outdir, $mode, 0, 1, \@args, @cmd);
788
elsif ($mode eq 'add') {
790
push @cmd, $::cfg_path_cpio;
791
push @cmd, '-0' if $::opt_null;
793
push @cmd, '-v' if $::opt_verbosity >= 1;
794
push @cmd, ['>'], $archive;
796
push @cmd, $::cfg_path_find, @args;
797
push @cmd, '-print0' if $::cfg_use_find_cpio_print0;
798
push @cmd, ['|'], $::cfg_path_cpio;
799
push @cmd, '-0' if $::cfg_use_find_cpio_print0;
801
push @cmd, '-v' if $::opt_verbosity >= 1;
802
push @cmd, ['>'], $archive;
804
return multiarchivecmd($archive, $outdir, $mode, 1, 1, \@args, @cmd);
807
warn "$::basename: ".quote($archive).": $mode command not supported for $format archives\n";
811
elsif ($format eq 'bzip2') {
812
return singlearchivecmd($archive, $::cfg_use_pbzip2 ? $::cfg_path_pbzip2 : $::cfg_path_bzip2, $format, $mode, 1, @args);
814
elsif ($format eq 'bzip') {
815
return singlearchivecmd($archive, $::cfg_path_bzip, $format, $mode, 1, @args);
817
elsif ($format eq 'gzip') {
818
return singlearchivecmd($archive, $::cfg_path_gzip, $format, $mode, 1, @args);
820
elsif ($format eq 'compress') {
821
if ($::cfg_use_gzip_for_z && $mode ne 'add') {
822
return singlearchivecmd($archive, $::cfg_path_gzip, $format, $mode, 1, @args);
824
return singlearchivecmd($archive, $::cfg_path_compress, $format, $mode, 1, @args);
827
elsif ($format eq 'lzma') {
828
return singlearchivecmd($archive, $::cfg_path_lzma, $format, $mode, 1, @args);
830
elsif ($format eq 'lzop') {
831
return singlearchivecmd($archive, $::cfg_path_lzop, $format, $mode, 0, @args);
833
elsif ($format eq 'lzip') {
834
return singlearchivecmd($archive, $::cfg_path_lzip, $format, $mode, 1, @args);
836
elsif ($format eq 'xz') {
837
return singlearchivecmd($archive, $::cfg_path_xz, $format, $mode, 1, @args);
839
elsif ($format eq 'rzip') {
840
return singlearchivecmd($archive, $::cfg_path_rzip, $format, $mode, 0, @args);
842
elsif ($format eq 'lrzip') {
843
return singlearchivecmd($archive, $::cfg_path_lrzip, $format, $mode, 0, @args);
850
# Return 1 if value defined and is non-zero, 0 otherwise.
853
return defined $value && $value ? 1 : 0;
857
# Identify the execution mode, and return it.
858
# Possible modes are 'cat', 'extract', 'list', 'add' or 'extract-to'.
861
if (de($::opt_cmd_list)
863
+ de($::opt_cmd_extract)
865
+ de($::opt_cmd_extract_to)
866
+ de($::opt_cmd_diff)
867
+ de($::opt_cmd_repack) > 1) {
868
die "$::basename: only one command may be specified\n"; #OK
870
$mode = 'cat' if ($::basename eq 'acat');
871
$mode = 'extract' if ($::basename eq 'aunpack');
872
$mode = 'list' if ($::basename eq 'als');
873
$mode = 'add' if ($::basename eq 'apack');
874
$mode = 'diff' if ($::basename eq 'adiff');
875
$mode = 'repack' if ($::basename eq 'arepack');
876
$mode = 'add' if ($::opt_cmd_add);
877
$mode = 'cat' if ($::opt_cmd_cat);
878
$mode = 'list' if ($::opt_cmd_list);
879
$mode = 'extract' if ($::opt_cmd_extract);
880
$mode = 'extract-to' if ($::opt_cmd_extract_to);
881
$mode = 'diff' if ($::opt_cmd_diff);
882
$mode = 'repack' if ($::opt_cmd_repack);
883
if (!defined $mode) {
884
die "$::basename: no command specified\nTry `$::basename --help' for more information.\n"; #OK
889
# singlearchivecmd(archive, command, format, mode, args)
890
# Execute a command for single-file archives.
891
# The command parameter specifies what command to execute.
892
# If mode is 'extract-to', returns the directory (or only file)
893
# which was extracted.
894
sub singlearchivecmd($$$$$@) {
895
my ($archive, $cmd, $format, $mode, $can_do_c, @args) = @_;
900
push @cmd, '-v' if $::opt_verbosity > 1;
902
if ($mode eq 'list') {
903
warn "$::basename: ".quote($archive).": $mode command not supported for $format archives\n";
906
elsif ($mode eq 'cat') {
908
warn "$::basename: ".quote($archive).": $mode command not supported for $format archives\n";
911
push @cmd, '-c', '-d', $archive, @args;
912
$outfile = $archive; # Just so that we don't return undef
914
elsif ($mode eq 'add') {
916
warn "$::basename: cannot add more than one file with this format\n";
919
if (!$::opt_force && (-e $archive || -l $archive)) {
920
warn "$::basename: ".quote($archive).": refusing to overwrite existing file\n";
923
#if (!$::cfg_keep_compressed && stripext($archive) ne $args[0]) {
924
# warn "$::basename: ".quote($archive).": cannot create a $format archive with this name (use -X)\n";
928
push @cmd, '-c', @args, ['>'], $archive;
930
push @cmd, '-o', $archive, @args;
932
$outfile = $archive; # Just so that we don't return undef
934
elsif ($mode eq 'extract') {
935
$outfile = stripext($archive);
936
if ($::cfg_decompress_to_cwd) {
937
$outfile = basename($outfile);
940
$outfile = makeoutfile($::cfg_tmpdir_name);
941
$reason = 'local file exists';
944
push @cmd, '-c', '-d', $archive, @args, ['>'], $outfile;
946
push @cmd, '-o', $outfile, '-d', $archive, @args;
949
elsif ($mode eq 'extract-to') {
950
$outfile = $::opt_cmd_extract_to;
951
if ($::opt_simulate ? $::opt_cmd_extract_to_type eq 'd' : -d $outfile) {
952
my $base = File::Basename::basename($archive);
953
$outfile = File::Spec->catfile($outfile, stripext($base));
956
push @cmd, '-c', '-d', $archive, @args, ['>'], $outfile;
958
push @cmd, '-o', $outfile, '-d', $archive, @args;
962
push @cmd, ['|'], get_pager_program() if $::opt_use_pager;
963
cmdexec(0, @cmd) || return undef;
965
if ($mode eq 'extract' || $mode eq 'extract-to') {
966
if ($::cfg_show_extracted && !$::opt_simulate) {
967
my $archivebase = File::Basename::basename($archive);
968
my $rmsg = defined $reason ? " ($reason)" : '';
969
warn quote($archivebase).": extracted to `".quote($outfile)."'$rmsg\n";
973
if (!$::cfg_keep_compressed) {
974
if ($mode eq 'extract') {
975
warn 'unlink ', quote($archive), "\n" if ($::opt_explain || $::opt_simulate);
976
if (!$::opt_simulate) {
977
unlink($archive) || warn "$::basename: ".quote($archive).": cannot remove - $!\n";
980
elsif ($mode eq 'add') {
981
warn 'unlink ', quote($args[0]), "\n" if ($::opt_explain || $::opt_simulate);
982
if (!$::opt_simulate) {
983
unlink($args[0]) || warn "$::basename: ".quote($args[0]).": cannot remove - $!\n";
992
# Create (partial) command line arguments for a tar command.
993
# The parameter opts specifies additional arguments to add.
994
sub maketarcmd($$$$@) {
995
my ($archive, $outdir, $mode, $opts, @rest) = @_;
996
$opts = 'v'.$opts if $::opt_verbosity >= 1;
997
my @cmd = ($::cfg_path_tar);
998
push @cmd, "xO$opts" if $mode eq 'cat';
999
push @cmd, "x$opts" if ($mode eq 'extract' || $mode eq 'extract-to');
1000
push @cmd, "t$opts" if $mode eq 'list';
1001
push @cmd, "c$opts" if $mode eq 'add';
1002
push @cmd, $archive if defined $archive;
1003
push @cmd, '-C', $outdir if $mode eq 'extract';
1004
push @cmd, '-C', $::opt_cmd_extract_to if $mode eq 'extract-to';
1009
# cmdexec(ignore_return, cmdspec)
1010
# Execute a command specification.
1011
# The cmdspec parameter is a list of string arguments building
1012
# the command line. If there's a list reference instead of a
1013
# string, it is a shell meta character/string which shouldn't
1016
my ($ignret, @cmd) = @_;
1018
if ($::opt_explain || $::opt_simulate) {
1019
my $spec = join(' ', map { ref $_ ? @{$_} : shquotemeta $_ } @cmd);
1020
explain quote($spec)."\n";
1021
return 1 if ($::opt_simulate);
1024
my $cmds = makespec(@cmd);
1025
if (!shell_execute(@cmd)) {
1026
warn "$::basename: ".quote($cmds).": cannot execute - $::errmsg\n";
1030
if ($? & 0xFF != 0) {
1031
warn "$::basename: ".quote($cmds).": abnormal exit (exit code $?)\n";
1035
if (!$ignret && $? >> 8 != 0) {
1036
warn "$::basename: ".quote($cmds).": non-zero return-code\n";
1044
# Make a command specification when printing errors.
1047
my $spec = $cmd[0].' ...';
1049
foreach (@cmd, '') {
1051
$spec .= " | $_ ...";
1054
$lastref = 1 if (ref);
1059
# makeoutfile(template)
1060
# Make a unique output file for extraction command.
1061
sub makeoutfile($) {
1062
my ($template) = @_;
1065
$file = sprintf $template, int rand 10000;
1071
# Make a temporary (unique) output directory for extraction command.
1075
$dir = sprintf $::cfg_tmpdir_name, int rand 10000;
1078
warn 'mkdir ', $dir, "\n" if $::opt_simulate || $::opt_explain;
1079
if (!$::opt_simulate) {
1080
if (!mkdir($dir, 0700)) {
1081
warn "$::basename: ".quote($dir).": cannot create directory - $!\n";
1084
push @::rmdirs, $dir;
1090
# Print on screen if $::opt_explain is true.
1093
print STDERR $msg if ($::opt_explain || $::opt_simulate);
1097
# If specified filename does not end with a slash,
1098
# add one and return the new filename.
1101
return ($file =~ /\/$/ ? $file : "$file/");
1105
# A more sophisticated quotemeta for bourne shells.
1106
# (This should be used for printing only.)
1107
sub shquotemeta($) {
1109
$str =~ s/([^A-Za-z0-9_.+,\/:=@%^-])/\\$1/g;
1113
# multiarchivecmd(archive, outdir, mode, create, needcwd, argref, cmdspec)
1114
# Execute a command for multi-file archives.
1115
# The `create' argument controls whether the archive
1116
# will be created (1) or just added to (0) if mode is "add".
1117
# If mode is 'extract', returns the directory (or only file)
1118
# which was extracted.
1119
# If needcwd is true, the outdir must be changed to.
1120
sub multiarchivecmd($$$$@) {
1121
my ($archive, $outdir, $mode, $create, $needcwd, $argref, @cmd) = @_;
1122
my @args = @{$argref};
1124
if ($mode eq 'cat' && @args == 0) {
1125
die "$::basename: missing file argument\n"; #OK
1128
if ($mode eq 'add' && $create && !$::opt_force && (-e $archive || -l $archive)) {
1129
warn "$::basename: ".quote($archive).": refusing to overwrite existing file\n";
1133
push @cmd, ['|'], get_pager_program() if $::opt_use_pager;
1138
if ($mode eq 'extract') {
1139
warn "cd ", quote($outdir), "\n" if $::opt_explain || $::opt_simulate;
1140
if (!$::opt_simulate && !chdir($outdir)) {
1141
warn "$::basename: ".quote($outdir).": cannot change to - $!\n";
1145
if ($mode eq 'extract-to') {
1146
warn "cd ", quote($::opt_cmd_extract_to), "\n" if $::opt_explain || $::opt_simulate;
1147
if (!$::opt_simulate && !chdir($::opt_cmd_extract_to)) {
1148
warn "$::basename: ".quote($::opt_cmd_extract_to).": cannot change to - $!\n";
1154
if ($mode ne 'extract') {
1155
cmdexec(0, @cmd) || return undef;
1156
if (defined $olddir) {
1157
warn "cd ", quote($olddir), "\n" if $::opt_explain || $::opt_simulate;
1158
if (!$::opt_simulate && !chdir($olddir)) {
1159
warn "$::basename: ".quote($olddir).": cannot change to - $!\n";
1163
# XXX: can't save outdir with extract-to.
1167
if (!cmdexec(0, @cmd)) {
1168
if (defined $olddir) {
1169
warn "cd ", quote($olddir), "\n" if $::opt_explain || $::opt_simulate;
1170
if (!$::opt_simulate && !chdir($olddir)) {
1171
warn "$::basename: ".quote($olddir).": cannot change to - $!\n";
1177
if (defined $olddir) {
1178
warn "cd ", quote($olddir), "\n" if $::opt_explain || $::opt_simulate;
1179
if (!$::opt_simulate && !chdir($olddir)) {
1180
warn "$::basename: ".quote($olddir).": cannot change to - $!\n";
1185
return undef if $::opt_simulate;
1187
if (!opendir(DIR, $outdir)) {
1188
warn "$::basename: ".quote($outdir).": cannot list - $!\n";
1191
my @files = grep !/^\.\.?$/, readdir DIR;
1194
my $archivebase = File::Basename::basename($archive);
1198
warn quote($archivebase).": archive is empty\n";
1201
} elsif ($::opt_extract_subdir) {
1203
} elsif (@files == 1) {
1204
my $fromfile = File::Spec->catfile($outdir, $files[0]);
1205
if ($::opt_force || (!-l $files[0] && !-e $files[0])) {
1207
# If the file is a directory, it can only be moved if writable
1208
my $oldmode = undef;
1209
if (!-l $fromfile && -d $fromfile) {
1210
my @statinfo = stat($fromfile);
1212
warn quote($fromfile).": cannot get file info - $!\n";
1215
$oldmode = $statinfo[2];
1216
if (!chmod(0700, $fromfile)) {
1217
warn quote($fromfile).": cannot change mode - $!\n";
1222
if (!rename $fromfile, $files[0]) {
1223
warn quote($fromfile).": cannot rename - $!\n";
1228
# If we changed mode previously, restore that mode now
1229
if (defined $oldmode) {
1230
if (!chmod($oldmode, $files[0])) {
1231
warn quote($files[0]).": cannot change mode - $!\n";
1236
if ($::cfg_show_extracted) {
1237
my $file = ($files[0] =~ /\// ? dirname($files[0]) : $files[0]);
1238
warn quote($archivebase).": extracted to `".quote($file)."'\n" ;
1241
save_outdir($files[0]);
1244
$reason = 'local file exists';
1245
$adddir = 1 if (!-l $files[0] && -d $files[0]);
1247
$reason = 'multiple files in root';
1250
my $localoutdir = stripext($archivebase);
1251
if (!-e $localoutdir) {
1252
if (!rename $outdir, $localoutdir) {
1253
warn quote($outdir).": cannot rename - $!\n";
1256
$outdir = $localoutdir;
1259
warn quote($archivebase).": extracted to `".quote($outdir)."' ($reason)\n";
1260
save_outdir($adddir ? File::Spec->catfile($outdir, $files[0]) : $outdir);
1265
# Strip extension from the specified file.
1268
return $file if ($file =~ s/(\.tar\.bz2|\.tbz2)$//);
1269
return $file if ($file =~ s/(\.tar\.bz|\.tbz)$//);
1270
return $file if ($file =~ s/(\.tar\.gz|\.tgz)$//);
1271
return $file if ($file =~ s/(\.tar\.Z|\.tZ)$//);
1272
return $file if ($file =~ s/(\.tar\.7z|\.t7z)$//);
1273
return $file if ($file =~ s/(\.tar\.lzma|\.tlzma)$//);
1274
return $file if ($file =~ s/(\.tar\.lzo|\.lzo)$//);
1275
return $file if ($file =~ s/(\.tar\.lz|\.lz)$//);
1276
return $file if ($file =~ s/\.tar$//);
1277
return $file if ($file =~ s/\.bz2$//);
1278
return $file if ($file =~ s/\.bz$//);
1279
return $file if ($file =~ s/\.lz$//);
1280
return $file if ($file =~ s/\.gz$//);
1281
return $file if ($file =~ s/\.zip$//);
1282
return $file if ($file =~ s/\.7z$//);
1283
return $file if ($file =~ s/\.alz$//);
1284
return $file if ($file =~ s/\.jar$//);
1285
return $file if ($file =~ s/\.war$//);
1286
return $file if ($file =~ s/\.Z$//);
1287
return $file if ($file =~ s/\.rar$//);
1288
return $file if ($file =~ s/\.(lha|lzh)$//);
1289
return $file if ($file =~ s/\.ace$//);
1290
return $file if ($file =~ s/\.arj$//);
1291
return $file if ($file =~ s/\.a$//);
1292
return $file if ($file =~ s/\.lzma$//);
1293
return $file if ($file =~ s/\.rpm$//);
1294
return $file if ($file =~ s/\.deb$//);
1295
return $file if ($file =~ s/\.cpio$//);
1296
return $file if ($file =~ s/\.cab$//);
1297
return $file if ($::cfg_strip_unknown_ext && $file =~ s/\.[^.]+$//);
1302
# Return the usual extension for the specified file format
1305
return '.tar.bz2' if $format eq 'tar+bzip2';
1306
return '.tar.gz' if $format eq 'tar+gzip';
1307
return '.tar.bz' if $format eq 'tar+bzip';
1308
return '.tar.7z' if $format eq 'tar+7z';
1309
return '.tar.lzo' if $format eq 'tar+lzop';
1310
return '.tar.lzma' if $format eq 'tar+lzma';
1311
return '.tar.lz' if $format eq 'tar+lzip';
1312
return '.tar.xz' if $format eq 'tar+xz';
1313
return '.tar.Z' if $format eq 'tar+compress';
1314
return '.tar' if $format eq 'tar';
1315
return '.bz2' if $format eq 'bzip2';
1316
return '.lzma' if $format eq 'lzma';
1317
return '.7z' if $format eq '7z';
1318
return '.alz' if $format eq 'alzip';
1319
return '.bz' if $format eq 'bzip';
1320
return '.gz' if $format eq 'gzip';
1321
return '.lzo' if $format eq 'lzop';
1322
return '.lz' if $format eq 'lzip';
1323
return '.xz' if $format eq 'xzip';
1324
return '.rz' if $format eq 'rzip';
1325
return '.lrz' if $format eq 'lrzip';
1326
return '.zip' if $format eq 'zip';
1327
return '.jar' if $format eq 'jar';
1328
return '.Z' if $format eq 'compress';
1329
return '.rar' if $format eq 'rar';
1330
return '.ace' if $format eq 'ace';
1331
return '.a' if $format eq 'ar';
1332
return '.arj' if $format eq 'arj';
1333
return '.lha' if $format eq 'lha';
1334
return '.rpm' if $format eq 'rpm';
1335
return '.deb' if $format eq 'deb';
1336
return '.cpio' if $format eq 'cpio';
1337
return '.cab' if $format eq 'cab';
1338
die "$::basename: ".quote($format).": don't know file extension for format\n";
1341
# issingleformat(fmt)
1342
# fmt is a file specification as returned by findformat.
1343
# This function returns true if fmt is a single file archive (gzip etc)
1344
# for certain. This means that 7zip is not a single file archive format,
1345
# although it can be used in this way.
1346
sub issingleformat($) {
1348
return 1 if $fmt eq 'bzip2';
1349
return 1 if $fmt eq 'gzip';
1350
return 1 if $fmt eq 'bzip';
1351
return 1 if $fmt eq 'compress';
1352
return 1 if $fmt eq 'lzma';
1353
return 1 if $fmt eq 'lzop';
1354
return 1 if $fmt eq 'lzip';
1355
return 1 if $fmt eq 'xz';
1356
return 1 if $fmt eq 'rzip';
1357
return 1 if $fmt eq 'lrzip';
1361
# findformat(spec, manual)
1362
# Figure out format from specified file/string.
1363
# If manual is 0, spec is a filename, otherwise
1364
# it is a format description string.
1365
sub findformat($$) {
1366
my ($file, $manual) = @_;
1367
my $spec = lc $file;
1369
['tar+bzip2', qr/^(GNU|POSIX) tar archive \(bzip2 compressed data(\W|$)/],
1370
['tar+gzip', qr/^(GNU|POSIX) tar archive \(gzip compressed data(\W|$)/],
1371
['tar+bzip', qr/^(GNU|POSIX) tar archive \(bzip compressed data(\W|$)/],
1372
['tar+compress', qr/^(GNU|POSIX) tar archive \(compress'd data(\W|$)/],
1373
['tar', qr/^(GNU|POSIX) tar archive(\W|$)/],
1374
['zip', qr/ \(Zip archive data[^)]+\)$/],
1375
['zip', qr/^Zip archive data(\W|$)/],
1376
['zip', qr/^MS-DOS executable (.*), ZIP self-extracting archive(\W|$)/],
1377
['rar', qr/^RAR archive data(\W|$)/],
1378
['lha', qr/^LHa \(2\.x\) archive data /],
1379
['lha', qr/^LHa 2\.x\? archive data /],
1380
['lha', qr/^LHarc 1\.x archive data /],
1381
['lha', qr/^MS-DOS executable .*, LHA's SFX$/],
1382
['7z', qr/^7z archive data, version .*$/],
1383
['ar', qr/^current ar archive(\W|$)/],
1384
['arj', qr/^ARJ archive data(\W|$)/],
1385
['arc', qr/^ARC archive data(\W|$)/],
1386
['cpio', qr/^cpio archive$/],
1387
['cpio', qr/^ASCII cpio archive /],
1388
['rpm', qr/^RPM v/],
1389
['cab', qr/^Microsoft Cabinet archive data\W/],
1390
['cab', qr/^PE executable for MS Windows /],
1391
['deb', qr/^Debian binary package(\W|$)/],
1392
['bzip2', qr/ \(bzip2 compressed data(\W|$)/],
1393
['bzip', qr/ \(bzip compressed data(\W|$)/],
1394
['gzip', qr/ \(gzip compressed data(\W|$)/],
1395
['compress', qr/ \(compress'd data(\W|$)/],
1396
['lzma', qr/^lzma compressed data /], # Not in my magic
1397
['lzop', qr/^lzop compressed data /],
1398
['lzip', qr/^lzip compressed data /], # Not in my magic
1399
['xz', qr/^xz compressed data /], # Not in my magic
1400
['rzip', qr/^rzip compressed data /],
1401
['lrzip', qr/^lrzip compressed data /], # Not in my magic
1402
['bzip2', qr/^bzip2 compressed data(\W|$)/],
1403
['bzip', qr/^bzip compressed data(\W|$)/],
1404
['gzip', qr/^gzip compressed data(\W|$)/],
1405
['compress', qr/^compress'd data(\W|$)/],
1407
my @fileextensions = (
1408
['tar+7z', qr/(\.tar\.7z|\.t7z)$/],
1409
['tar+bzip', qr/(\.tar\.bz|\.tbz)$/],
1410
['tar+bzip2', qr/(\.tar\.bz2|\.tbz2)$/],
1411
['tar+compress', qr/(\.tar\.[zZ]|\.t[zZ])$/],
1412
['tar+gzip', qr/(\.tar\.gz|\.tgz)$/],
1413
['tar+lzip', qr/(\.tar\.lz|\.tlz)$/],
1414
['tar+lzma', qr/(\.tar\.lzma|\.tlzma)$/],
1415
['tar+lzop', qr/(\.tar\.lzo|\.tzo)$/],
1416
['tar+xz', qr/(\.tar\.xz|\.txz)$/],
1419
['ace', qr/\.ace$/],
1420
['alzip', qr/\.alz$/],
1422
['arc', qr/\.arc$/],
1423
['arj', qr/\.arj$/],
1424
['bzip', qr/\.bz$/],
1425
['bzip2', qr/\.bz2$/],
1426
['cab', qr/\.cab$/],
1427
['compress', qr/\.[zZ]$/],
1428
['cpio', qr/\.cpio$/],
1429
['deb', qr/\.deb$/],
1430
['gzip', qr/\.gz$/],
1431
['jar', qr/\.(jar|war)$/],
1432
['lha', qr/\.(lha|lzh)$/],
1433
['lrzip', qr/\.lrz$/],
1434
['lzip', qr/\.lz$/],
1435
['lzma', qr/\.lzma$/],
1436
['lzop', qr/\.lzo$/],
1437
['rar', qr/\.rar$/],
1438
['rpm', qr/\.rpm$/],
1439
['rzip', qr/\.rz$/],
1440
['tar', qr/\.tar$/],
1442
['zip', qr/\.zip$/],
1447
$spec =~ s/^\.*/\./;
1448
$spec =~ s/lzop/lzo/;
1449
$spec =~ s/lzip/lz/;
1450
$spec =~ s/rzip/rz/;
1451
$spec =~ s/lrzip/lrz/;
1452
$spec =~ s/bzip2/bz2/;
1453
$spec =~ s/bzip/bz/;
1454
$spec =~ s/gzip/gz/;
1455
$spec =~ s/7zip/7z/;
1456
$spec =~ s/alzip/alz/;
1457
$spec =~ s/compress/Z/;
1460
if (!$::cfg_use_file_always) {
1461
foreach my $formatinfo (@fileextensions) {
1462
my ($format, $regex) = @{$formatinfo};
1463
return $format if ($spec =~ $regex);
1466
if (!$manual && $::cfg_use_file) {
1468
warn "$::basename: ".quote($file).": no such file and cannot identify format from extension\n";
1471
if (!sysopen(TMP, $file, O_RDONLY)) {
1472
warn "$::basename: ".quote($file).": cannot open - $!\n";
1477
warn "$::basename: ".quote($file).": not a regular file\n";
1480
if ($::opt_verbosity >= 1) {
1481
if ($::cfg_use_file_always) {
1482
warn "$::basename: ".quote($file).": identifying format using file\n";
1484
warn "$::basename: ".quote($file).": format not known, identifying using file\n";
1487
my @cmd = ($::cfg_path_file, '-b', '-L', '-z', '--', $file);
1488
$spec = backticks(@cmd);
1489
if (!defined $spec) {
1490
warn "$::basename: $::errmsg\n";
1493
if ($? & 0xFF != 0) {
1494
warn "$::basename: ".quote($::cfg_path_file).": abnormal exit\n";
1498
warn "$::basename: ".quote($file).": unknown file format\n";
1502
foreach my $formatinfo (@fileoutput) {
1503
my ($format, $regex) = @{$formatinfo};
1504
if ($spec =~ $regex) {
1505
warn "$::basename: ".quote($file).": format is `$format'\n" if $::opt_verbosity >= 1;
1509
warn "$::basename: ".quote($file).": unsupported file format `$spec'\n";
1512
warn "$::basename: ".quote($file).": unrecognized file format\n";
1516
# backticks(cmdargs, ..)
1517
# An implementation of the backtick (qx//) operator.
1518
# The difference is that command STDERR output will still
1519
# be printed on STDERR, and the shell isn't used to parse
1522
if (!pipe(IN,OUT)) {
1523
$::errmsg = "pipe failed - $!";
1527
if (!defined $child) {
1528
$::errmsg = "fork failed - $!";
1533
close STDOUT || exit 1;
1534
open(STDOUT, '>&OUT') || exit 1;
1535
close OUT || exit 1;
1536
$SIG{__WARN__} = sub {};
1540
my $text = join('', <IN>);
1542
if (waitpid($child,0) != $child && $^O ne 'MSWin32') {
1543
$::errmsg = "waitpid failed - $!";
1549
# set_config_option(variable, value)
1550
# Set a configuration option.
1551
sub set_config_option($$$) {
1552
my ($var, $val, $context) = @_;
1554
'args_diff' => [ 'option', \$::cfg_args_diff, qr/.*/ ],
1555
'decompress_to_cwd' => [ 'option', \$::cfg_decompress_to_cwd, qr/^(0|1)$/ ],
1556
'default_verbosity' => [ 'option', \$::cfg_default_verbosity, qr/^\d+$/ ],
1557
'extract_deb_control' => [ 'option', \$::cfg_extract_deb_control, qr/^(0|1)$/ ],
1558
'keep_compressed' => [ 'option', \$::cfg_keep_compressed, qr/^(0|1)$/ ],
1559
'path_7z' => [ 'option', \$::cfg_path_7z, qr/.*/ ],
1560
'path_ar' => [ 'option', \$::cfg_path_ar, qr/.*/ ],
1561
'path_arc' => [ 'option', \$::cfg_path_arc, qr/.*/ ],
1562
'path_arj' => [ 'option', \$::cfg_path_arj, qr/.*/ ],
1563
'path_bzip' => [ 'option', \$::cfg_path_bzip, qr/.*/ ],
1564
'path_bzip2' => [ 'option', \$::cfg_path_bzip2, qr/.*/ ],
1565
'path_cabextract' => [ 'option', \$::cfg_path_cabextract, qr/.*/ ],
1566
'path_cat' => [ 'option', \$::cfg_path_cat, qr/.*/ ],
1567
'path_compress' => [ 'option', \$::cfg_path_compress, qr/.*/ ],
1568
'path_cpio' => [ 'option', \$::cfg_path_cpio, qr/.*/ ],
1569
'path_diff' => [ 'option', \$::cfg_path_diff, qr/.*/ ],
1570
'path_dpkg_deb' => [ 'option', \$::cfg_path_dpkg_deb, qr/.*/ ],
1571
'path_file' => [ 'option', \$::cfg_path_file, qr/.*/ ],
1572
'path_find' => [ 'option', \$::cfg_path_find, qr/.*/ ],
1573
'path_gzip' => [ 'option', \$::cfg_path_gzip, qr/.*/ ],
1574
'path_jar' => [ 'option', \$::cfg_path_jar, qr/.*/ ],
1575
'path_lha' => [ 'option', \$::cfg_path_lha, qr/.*/ ],
1576
'path_lrzip' => [ 'option', \$::cfg_path_lrzip, qr/.*/ ],
1577
'path_lzip' => [ 'option', \$::cfg_path_lzip, qr/.*/ ],
1578
'path_lzma' => [ 'option', \$::cfg_path_lzma, qr/.*/ ],
1579
'path_lzop' => [ 'option', \$::cfg_path_lzop, qr/.*/ ],
1580
'path_nomarch' => [ 'option', \$::cfg_path_nomarch, qr/.*/ ],
1581
'path_pager' => [ 'option', \$::cfg_path_pager, qr/.*/ ],
1582
'path_pbzip2' => [ 'option', \$::cfg_path_pbzip2, qr/.*/ ],
1583
'path_rar' => [ 'option', \$::cfg_path_rar, qr/.*/ ],
1584
'path_rpm' => [ 'option', \$::cfg_path_rpm, qr/.*/ ],
1585
'path_rpm2cpio' => [ 'option', \$::cfg_path_rpm2cpio, qr/.*/ ],
1586
'path_rzip' => [ 'option', \$::cfg_path_rzip, qr/.*/ ],
1587
'path_tar' => [ 'option', \$::cfg_path_tar, qr/.*/ ],
1588
'path_unace' => [ 'option', \$::cfg_path_unace, qr/.*/ ],
1589
'path_unalz' => [ 'option', \$::cfg_path_unalz, qr/.*/ ],
1590
'path_unarj' => [ 'option', \$::cfg_path_unarj, qr/.*/ ],
1591
'path_unrar' => [ 'option', \$::cfg_path_unrar, qr/.*/ ],
1592
'path_unzip' => [ 'option', \$::cfg_path_unzip, qr/.*/ ],
1593
'path_usercfg' => [ 'option', \$::cfg_path_usercfg, qr/.*/ ],
1594
'path_xargs' => [ 'option', \$::cfg_path_xargs, qr/.*/ ],
1595
'path_xz' => [ 'option', \$::cfg_path_xz, qr/.*/ ],
1596
'path_zip' => [ 'option', \$::cfg_path_zip, qr/.*/ ],
1597
'show_extracted' => [ 'option', \$::cfg_show_extracted, qr/^(0|1)$/ ],
1598
'strip_unknown_ext' => [ 'option', \$::cfg_strip_unknown_ext, qr/^(0|1)$/ ],
1599
'tmpdir_name' => [ 'option', \$::cfg_tmpdir_name, qr/.*/ ],
1600
'tmpfile_name' => [ 'option', \$::cfg_tmpfile_name, qr/.*/ ],
1601
'use_arc_for_unpack' => [ 'option', \$::cfg_use_arc_for_unpack, qr/^(0|1)$/ ],
1602
'use_arj_for_unpack' => [ 'option', \$::cfg_use_arj_for_unpack, qr/^(0|1)$/ ],
1603
'use_file' => [ 'option', \$::cfg_use_file, qr/^(0|1)$/ ],
1604
'use_file_always' => [ 'option', \$::cfg_use_file_always, qr/^(0|1)$/ ],
1605
'use_find_cpio_print0' => [ 'option', \$::cfg_use_find_cpio_print0, qr/^(0|1)$/ ],
1606
'use_gzip_for_z' => [ 'option', \$::cfg_use_gzip_for_z, qr/^(0|1)$/ ],
1607
'use_jar' => [ 'option', \$::cfg_use_jar, qr/^(0|1)$/ ],
1608
'use_pbzip2' => [ 'option', \$::cfg_use_pbzip2, qr/^(0|1)$/ ],
1609
'use_rar_for_unpack' => [ 'option', \$::cfg_use_rar_for_unpack, qr/^(0|1)$/ ],
1610
'use_rar_for_unrar' => [ 'obsolete', 'use_rar_for_unpack' ],
1611
'use_tar_bzip2_option' => [ 'option', \$::cfg_use_tar_bzip2_option, qr/^(0|1)$/ ],
1612
'use_tar_lzma_option' => [ 'option', \$::cfg_use_tar_lzma_option, qr/^(0|1)$/ ],
1613
'use_tar_lzop_option' => [ 'option', \$::cfg_use_tar_lzop_option, qr/^(0|1)$/ ],
1614
'use_tar_xv_option' => [ 'option', \$::cfg_use_tar_xv_option, qr/^(0|1)$/ ],
1615
'use_tar_j_option' => [ 'obsolete', 'use_tar_bzip2_option' ],
1616
'use_tar_z_option' => [ 'option', \$::cfg_use_tar_z_option, qr/^(0|1)$/ ],
1618
die $::basename,': ',$context,'unrecognized directive `',$var,"'\n" if !exists $optionmap{$var};
1619
return 0 if !exists $optionmap{$var};
1620
my ($type) = @{$optionmap{$var}};
1621
if ($type eq 'obsolete') {
1622
warn $context.$var.' is obsolete - use '.$optionmap{$var}->[1].')'."\n";
1623
$var = $optionmap{$var}->[1];
1625
my ($varref,$check) = @{$optionmap{$var}}[1,2];
1626
die $::basename,': ',$context,'invalid value for `',$var,"'\n" if $val !~ $check;
1632
# Read and parse the specified configuration file.
1633
# If the file does not exist, just return.
1634
# If there is an error in the configuration file,
1635
# the program will be terminated. This could be a
1636
# problem when there are errors in the system-wide
1637
# configuration file.
1638
sub readconfig($$) {
1639
my ($file, $failok) = @_;
1640
return if ($failok && !-e $file);
1641
sysopen(FILE, $file, O_RDONLY) || die "$::basename: ".quote($file).": cannot open for reading - $!\n"; #OK
1644
next if /^\s*(#(.*))?$/;
1645
my ($var,$val) = /^(.*?)\s+([^\s].*)$/; # joe markup bug -> ]]
1646
set_config_option($var, $val, quote($file).':'.$..': ');
1651
# Remove a directory recursively. This function used to change
1652
# the mode on the directories is traverses, but I now consider
1653
# that to be unsafe (what if there's a bug in atool and it
1654
# removes a file it shouldn't?).
1655
sub unlink_directory($) {
1657
die "$::basename: internal error 1 - please report this bug\n"
1658
if ($dir eq '/' || $dir eq $ENV{HOME});
1659
# chmod 0700, $dir || die "$::basename: cannot chmod `".quote($dir)."': $!\n";
1660
chdir $dir || die "$::basename: ".quote($dir).": cannot change to - $!\n";
1661
opendir(DIR, $::cur) || die "$::basename: ".quote($dir).": cannot list - $!\n";
1662
my @files = readdir(DIR);
1664
foreach my $file (@files) {
1665
next if $file eq $::cur || $file eq $::up;
1666
if (-d $file && !-l $file) {
1667
unlink_directory($file);
1669
unlink $file || die "$::basename: ".quote($file).": cannot remove - $!\n";
1672
chdir $::up || die "$::basename: $::up: cannot change to - $!\n";
1673
rmdir $dir || die "$::basename: ".quote($dir).": cannot remove - $!\n";
1676
# find_comparable_file(dir)
1677
# Assuming that the contents of some archive has been extracted to dir,
1678
# this function will determine the main file or directory in this
1679
# archive - the file or directory which will be compared when this
1680
# archive is compared to some other.
1681
sub find_comparable_file($) {
1684
if (opendir(my $dh, $dir)) {
1687
my $file = readdir($dh);
1688
last if !defined $file;
1689
next if $file eq '.' || $file eq '..';
1693
$result = File::Spec->catfile($dir, $files[0]) if @files == 1;
1698
# makeabsolute(file)
1699
# Return the absolute version of file.
1700
sub makeabsolute($) {
1702
return $file if (substr($file, 0, 1) eq '/');
1703
return File::Spec->catfile(getcwd(), $file);
1707
# Quote a style like the GNU fileutils would do (`locale'
1712
for (my $c = 0; $c < length($in); $c++) {
1713
my $ch = substr($in, $c, 1);
1716
} elsif ($ch eq "\f") {
1718
} elsif ($ch eq "\n") {
1720
} elsif ($ch eq "\r") {
1722
} elsif ($ch eq "\t") {
1724
} elsif (ord($ch) == 11) { # Vertical Tab, \v
1726
} elsif ($ch eq "\\") {
1728
} elsif ($ch eq "'") {
1730
} elsif ($ch !~ /[[:print:]]/) {
1731
$out .= sprintf('\\%03o', ord($ch));
1740
# Execute a command with pipes and output redirection like the
1741
# shell does. Only difference is we do it without the shell.
1742
# This reason for this is because we don't have to quote
1743
# meta-characters - some meta-characters like LF and DEL are
1745
sub shell_execute(@) {
1749
for ($c = 0; $c < @cmdspec; $c++) {
1750
if (ref $cmdspec[$c] && ${$cmdspec[$c]}[0] eq ';') {
1751
return 0 if !shell_execute_single_statement(@cmdspec[$start..$c-1]);
1756
return 0 if !shell_execute_single_statement(@cmdspec[$start..$c-1]);
1761
sub shell_execute_single_statement(@) {
1764
while (@cmdspec > 0) {
1767
my $redir_out = undef;
1770
for ($c = 0; $c < @cmdspec; $c++) {
1771
if (ref $cmdspec[$c]) {
1772
push @cmds, [ @cmdspec[$start..$c-1] ];
1773
if (${$cmdspec[$c]}[0] eq '>') {
1774
$redir_out = $cmdspec[$c+1];
1777
#} elsif (${$cmdspec[$c]}[0] eq ';') {
1782
} elsif (${$cmdspec[$c]}[0] eq '|') {
1787
push @cmds, [ @cmdspec[$start..$c-1] ] if $start < $c;
1788
#for (my $x = 0; $x < @cmds; $x++) {
1789
# print $x, ': ', join(':',@{$cmds[$x]}), "\n";
1791
splice @cmdspec,0,$c;
1793
$SIG{INT} = 'IGNORE';
1798
for (my $c = 0; $c <= $#cmds; $c++) {
1800
@op = reverse POSIX::pipe();
1801
if (!@op || !defined $op[0] || !defined $op[1]) {
1802
$::errmsg = "pipe failed - $!";
1806
if ($c == $#cmds && defined $redir_out) {
1807
@_ = (); # XXX: necessary to overcome POSIX autoload bug!
1808
@op = (POSIX::open($redir_out, &POSIX::O_WRONLY | &POSIX::O_CREAT));
1809
if (!@op || !defined $op[0]) {
1810
$::errmsg = quote($redir_out).": cannot open for writing - $!";
1815
die "fork failed - $!\n" if !defined $pid;
1819
die "dup2 failed - $!\n" if POSIX::dup2($ip[1], 0) < 0;
1820
POSIX::close($_) foreach (@ip);
1823
die "dup2 failed - $!\n" if POSIX::dup2($op[0], 1) < 0;
1824
POSIX::close($_) foreach (@op);
1826
exec(@{$cmds[$c]}) || die ${$cmds[$c]}[0].": cannot execute - $!\n";
1828
POSIX::close($op[0]) if ($c == $#cmds && defined $redir_out);
1829
POSIX::close($_) foreach (@ip);
1832
push @children, $pid;
1835
foreach (@children) {
1836
if (waitpid($_,0) < 0 && $^O ne 'MSWin32') {
1837
$::errmsg = "waitpid failed - $!";
1847
# Write dir to file indicated by $::opt_save_outdir.
1849
sub save_outdir($) {
1851
if (defined $::opt_save_outdir && !-l $dir && -d $dir) {
1852
if (!sysopen(TMP, $::opt_save_outdir, O_WRONLY)) {
1853
warn die "$::basename: ".quote($::opt_save_outdir).": cannot open for writing - $!\n";
1855
print TMP $dir, "\n";
1861
# Somewhat stupid subroutine to add xargs to the command line.
1863
sub handle_empty_add(@) {
1866
unshift @cmd, '-0' if ($::opt_null);
1867
unshift @cmd, $::cfg_path_xargs;
1871
# Return a suitable pager command
1873
sub get_pager_program {
1874
return $ENV{PAGER} if (exists $ENV{PAGER});
1875
return $::cfg_path_pager;
1878
# repack_archive(srcfile,dstfile,srcfmt,dstfmt)
1879
# Repack an archive from a file to another (that shouldn't exist).
1880
sub repack_archive($$$$) {
1881
my ($file1,$file2,$fmt1,$fmt2) = @_;
1883
# Special cases for tar-based archives (single file archives).
1884
if ($fmt1 =~ /^tar\+/ && $fmt2 =~ /^tar$/) {
1885
$fmt1 =~ s/^tar\+//;
1886
$::opt_cmd_extract_to = $file2; # XXX: would like to get rid of these
1887
$::opt_cmd_extract_to_type = 'f'; # XXX: would like to get rid of these
1888
exit 1 if (!runcmds('extract-to', $fmt1, $file1));
1890
} elsif ($fmt1 =~ /^tar$/ && $fmt2 =~ /^tar\+/) {
1891
$fmt2 =~ s/^tar\+//;
1892
exit 1 if (!runcmds('add', $fmt2, $file2, $file1));
1896
if ($fmt1 =~ /^tar\+/ && $fmt2 =~ /^tar\+/) {
1897
$fmt1 =~ s/^tar\+//;
1898
$fmt2 =~ s/^tar\+//;
1902
if (File::Spec->file_name_is_absolute($file2)) {
1903
$newarchive = $file2;
1905
$newarchive = File::Spec->catdir($::up, $file2);
1909
$outdir = makeoutdir() || exit 1;
1910
$::opt_cmd_extract_to = $outdir;
1911
$::opt_cmd_extract_to_type = 'd';
1912
exit 1 if !runcmds('extract-to', $fmt1, $file1);
1913
warn 'cd ',quote($outdir),"\n" if $::opt_explain || $::opt_simulate;
1914
if (!$::opt_simulate) {
1915
chdir($outdir) || die "$::basename: ".quote($outdir).": cannot change to - $!\n";
1917
if (issingleformat($fmt2)) {
1918
# Preferrably we would like to find out what file it was
1919
# extracted to from the above execute-to command.
1920
#my $oldfile = stripext_exactly(basename($file1), $fmt1);
1921
my $oldfile = find_comparable_file($::cur); # FIXME: won't work in simulate mode
1922
exit 1 if !runcmds('add', $fmt2, $newarchive, $oldfile);
1924
exit 1 if !runcmds('add', $fmt2, $newarchive, $::cur);
1926
warn 'cd ',quote($::up),"\n" if $::opt_explain || $::opt_simulate;
1927
if (!$::opt_simulate) {
1928
chdir($::up) || die "$::basename: ".$::up.": cannot change to - $!\n"; #OK?????
1930
warn 'rm -r ',quote($outdir),"\n" if $::opt_explain || $::opt_simulate;
1931
if (!$::opt_simulate) {
1932
unlink_directory($outdir);
1937
map (rmdir, @::rmdirs) if !$::opt_simulate; # Errors are ignored