#!/usr/bin/perl
my
$Program
= basename($0);
__PACKAGE__->run(
args
=> \
@ARGV
)
unless
caller
;
sub
run {
my
$class
=
shift
;
my
%args
=
@_
;
my
$args
=
delete
$args
{args};
my
$self
=
$class
->new( {
args
=> dclone(
$args
),
%args
} )->process_options;
$self
->error(
"$Program: -P ignored\n"
)
if
$self
->is_overwrite;
unless
( () =
$self
->files ) {
exit
(EX_SUCCESS)
if
$self
->is_force;
$self
->error(
"$Program: missing argument\n"
);
usage();
}
my
$errors
=
grep
{
$self
->process_file(
$_
) }
$self
->files;
exit
(
$errors
? EX_FAILURE : EX_SUCCESS );
}
sub
new {
my
(
$class
,
$args
) =
@_
;
bless
{
$class
->defaults,
%$args
},
$class
;
}
sub
defaults {
my
%hash
= (
args
=> [],
error_fh
=> \
*STDERR
,
output_fh
=> \
*STDOUT
,
);
}
sub
files {
my
$self
=
shift
; @{
$self
->{files} } }
sub
is_force {
my
$self
=
shift
;
$self
->{options}{f} }
sub
is_interactive {
my
$self
=
shift
;
$self
->{options}{i} }
sub
is_overwrite {
my
$self
=
shift
;
$self
->{options}{P} }
sub
is_recursive {
my
$self
=
shift
;
$self
->{options}{R} ||
$self
->{options}{r} }
sub
is_verbose {
my
$self
=
shift
;
$self
->{options}{v} }
sub
options {
my
$self
=
shift
;
$self
->{options} }
sub
preprocess_options {
my
(
$self
) =
@_
;
my
@new_args
= @{
$self
->{args} };
my
%args
=
map
{
$new_args
[
$_
],
$_
} 0 ..
$#new_args
;
my
@rest
;
if
(
exists
$args
{
'--'
} ) {
@rest
=
@new_args
[
$args
{
'--'
} ..
$#new_args
];
@new_args
=
@new_args
[0 .. (
$args
{
'--'
} - 1)];
}
foreach
(
@new_args
) {
if
(m/\A\-\-/) {
warn
"unknown option: '$_'\n"
;
usage();
}
}
@new_args
=
map
{
if
( /\A\-(.+)/ ) {
my
$cluster
= $1;
map
{
"-$_"
}
split
//,
$cluster
;
}
else
{
$_
;
}
}
@new_args
;
if
(
exists
$args
{
'-f'
} &&
exists
$args
{
'-i'
} ) {
my
$last
;
foreach
(
reverse
@new_args
) {
next
unless
/\A-[fi]\z/;
$last
=
$_
;
last
;
}
@new_args
=
map
{
(
(
$last
eq
'-f'
and
$_
eq
'-i'
)
||
(
$last
ne
'-f'
and
$_
eq
'-f'
)
) ? () :
$_
;
}
@new_args
;
}
$self
->{original_args} =
$self
->{args};
$self
->{args} =
$self
->{preprocessed_args} = [
@new_args
,
@rest
];
return
$self
;
}
sub
process_options {
my
(
$self
) =
@_
;
$self
->preprocess_options;
my
%opts
;
my
$ret
= Getopt::Long::GetOptionsFromArray(
$self
->{args},
'f'
=> \
$opts
{
'f'
},
'i'
=> \
$opts
{
'i'
},
'P'
=> \
$opts
{
'P'
},
'R'
=> \
$opts
{
'R'
},
'r'
=> \
$opts
{
'r'
},
'v'
=> \
$opts
{
'v'
},
);
usage()
unless
$ret
;
$self
->{options} = {
map
{
defined
$_
?
$_
: 0 }
%opts
};
$self
->{files} =
$self
->{args};
return
$self
;
}
sub
process_file {
my
(
$self
,
$filename
) =
@_
;
my
$method
=
do
{
if
( -d
$filename
) {
if
( !
$self
->is_recursive ) {
$self
->error(
"$Program: '$filename': is a directory\n"
)
unless
$self
->is_force;
return
$self
->is_force ? OP_SUCCEEDED : OP_FAILED;
}
'remove_directory'
;
}
else
{
'remove_file'
;
}
};
my
$result
=
$self
->
$method
(
$filename
);
return
$self
->is_force ? OP_SUCCEEDED :
$result
;
}
sub
remove_directory {
my
(
$self
,
$dirname
) =
@_
;
my
$dh
;
unless
(
opendir
(
$dh
,
$dirname
) ) {
$self
->error(
"$Program: cannot open '$dirname': $!\n"
)
unless
$self
->is_force;
return
$self
->is_force ? OP_SUCCEEDED : OP_FAILED;
}
foreach
my
$file
(
readdir
(
$dh
) ) {
next
if
$file
eq
'.'
||
$file
eq
'..'
;
my
$path
= catfile(
$dirname
,
$file
);
my
$method
= -d
$path
?
'remove_directory'
:
'remove_file'
;
my
$result
=
$self
->
$method
(
$path
);
}
closedir
$dh
;
unless
(
rmdir
$dirname
) {
$self
->error(
"$Program: cannot remove directory '$dirname': $!\n"
)
unless
$self
->is_force;
return
$self
->is_force ? OP_SUCCEEDED : OP_FAILED;
}
$self
->message(
"$dirname\n"
)
if
$self
->is_verbose;
return
OP_SUCCEEDED;
}
sub
remove_file {
my
(
$self
,
$filename
) =
@_
;
if
(
$self
->is_interactive ) {
$self
->message(
"$filename: ? "
);
return
OP_SUCCEEDED
if
<STDIN> =~ /^[Nn]/;
}
elsif
( !
$self
->is_force && ! -w
$filename
) {
$self
->message(
"$filename: Read-only ? "
);
return
OP_SUCCEEDED
if
<STDIN> =~ /^[Nn]/;
}
unless
(
unlink
$filename
) {
$self
->error(
"$Program: cannot remove '$filename': $!\n"
)
unless
$self
->is_force;
return
$self
->is_force ? OP_SUCCEEDED : OP_FAILED;
}
$self
->message(
"$filename\n"
)
if
$self
->is_verbose;
return
OP_SUCCEEDED;
}
sub
usage {
Pod::Usage::pod2usage({
-exitval
=> EX_USAGE,
-verbose
=> 1,
});
}
sub
error_fh {
my
$self
=
shift
;
$self
->{error_fh} }
sub
error {
my
$self
=
shift
;
print
{
$self
->error_fh ||
*STDERR
}
@_
;
}
sub
output_fh {
my
$self
=
shift
;
$self
->{output_fh} }
sub
message {
my
$self
=
shift
;
print
{
$self
->output_fh ||
*STDOUT
}
@_
;
}
__PACKAGE__;