—package
Win32::ShellQuote;
use
strict;
use
Carp;
our
$VERSION
=
'0.003001'
;
$VERSION
=
eval
$VERSION
;
our
@EXPORT_OK
=
qw(
quote_native
quote_cmd
quote_system_list
quote_system_string
quote_system
quote_system_cmd
quote_literal
cmd_escape
unquote_native
cmd_unescape
)
;
our
%EXPORT_TAGS
= (
all
=> [
@EXPORT_OK
]);
sub
quote_native {
return
join
q{ }
, quote_system_list(
@_
);
}
sub
quote_cmd {
return
cmd_escape(quote_native(
@_
));
}
sub
quote_system_list {
# have to force quoting, or perl might try to use cmd anyway
return
map
{ quote_literal(
$_
, 1) }
@_
;
}
sub
quote_system_string {
my
$args
= quote_native(
@_
);
if
(_has_shell_metachars(
$args
)) {
$args
= cmd_escape(
$args
);
}
return
$args
;
}
sub
quote_system {
if
(
@_
> 1) {
return
quote_system_list(
@_
);
}
else
{
return
quote_system_string(
@_
);
}
}
sub
quote_system_cmd {
# force cmd, even when running through system
my
$args
= quote_native(
@_
);
if
(! _has_shell_metachars(
$args
)) {
# IT BURNS LOOK AWAY
return
'%PATH:~0,0%'
. cmd_escape(
$args
);
}
return
cmd_escape(
$args
);
}
sub
cmd_escape {
my
$string
=
shift
;
if
(
$string
=~ /[\r\n\0]/) {
croak
"can't quote newlines to pass through cmd.exe"
;
}
$string
=~ s/([()%!^"<>&|])/^$1/g;
return
$string
;
}
sub
quote_literal {
my
(
$text
,
$force
) =
@_
;
# basic argument quoting. uses backslashes and quotes to escape
# everything.
if
(!
$force
&&
$text
ne
''
&&
$text
!~ /[ \t\n\x0b"]/) {
# no quoting needed
}
else
{
$text
=~ s{(\\*)(?="|\z)}{$1$1}g;
$text
=~ s{
"}{\\"
}g;
$text
=
qq{"$text"}
;
}
return
$text
;
}
# derived from rules in code in win32.c
sub
_has_shell_metachars {
my
$string
=
shift
;
return
1
if
$string
=~ /%/;
$string
=~ s/(['"]).*?(\1|\z)//sg;
return
$string
=~ /[<>|]/;
}
sub
unquote_native {
local
(
$_
) =
@_
;
my
@argv
;
my
$length
=
length
or
return
@argv
;
m/\G\s*/gc;
ARGS:
until
(
pos
==
$length
) {
my
$quote_mode
;
my
$arg
=
''
;
CHARS:
until
(
pos
==
$length
) {
if
( m/\G((?:\\\\)+)(?=\\?(")?)/gc ) {
if
(
defined
$2) {
$arg
.=
'\\'
x (
length
($1) / 2);
}
else
{
$arg
.= $1;
}
}
elsif
( m/\G\\"/gc ) {
$arg
.=
'"'
;
}
elsif
( m/\G"/gc ) {
if
(
$quote_mode
&& m/\G"/gc ) {
$arg
.=
'"'
;
}
$quote_mode
= !
$quote_mode
;
}
elsif
( !
$quote_mode
&& m/\G\s+/gc ) {
last
;
}
elsif
( m/\G(.)/sgc ) {
$arg
.= $1;
}
}
push
@argv
,
$arg
;
}
return
@argv
;
}
sub
cmd_unescape {
my
(
$string
) =
@_
;
no
warnings
'uninitialized'
;
$string
=~ s/\^(.?)|([^^
"]+)|("
[^
"]*(?:"
|\z))/$1$2$3/gs;
return
$string
;
}
1;
__END__
=head1 NAME
Win32::ShellQuote - Quote argument lists for Win32
=head1 SYNOPSIS
use Win32::ShellQuote qw(:all);
system quote_system('program.exe', '--switch', 'argument with spaces or other special characters');
=head1 DESCRIPTION
Quotes argument lists to be used in Win32 in several different
situations.
Windows passes its arguments as a single string instead of an array
as other platforms do. In almost all cases, the standard Win32
L<CommandLineToArgvW|http://msdn.microsoft.com/en-us/library/ms647232.aspx>
function is used to parse this string. F<cmd.exe> has different
rules for handling quoting, so extra work has to be done if it is
involved. It isn't possible to consistantly create a single string
that will be handled the same by F<cmd.exe> and the stardard parsing
rules.
Perl will try to detect if you need the shell by detecting shell
metacharacters. The routine that checks that uses different quoting
rules from both F<cmd.exe> and the native Win32 parsing. Extra
work must therefore be done to protect against this autodetection.
=head1 SUBROUTINES
=head2 quote_native
Quotes as a string to pass directly to a program using native methods
like L<Win32::Spawn()|Win32>. This is the safest option to use if
possible.
=head2 quote_cmd
Quotes as a string to be run through F<cmd.exe>, such as in a batch file.
=head2 quote_system_list
Quotes as a list to be passed to L<system|perlfunc/system> or
L<exec|perlfunc/exec>. This is equally as safe as L</quote_native>,
but you must ensure you have more than one item being quoted for
the list to be usable with system.
=head2 quote_system_string
Like L</quote_system_list>, but returns a single string. Some
argument lists cannot be properly quoted using this function.
=head2 quote_system
Switches between L</quote_system_list> and L</quote_system_string>
based on the number of items quoted.
=head2 quote_system_cmd
Quotes as a single string that will always be run with F<cmd.exe>.
=head2 quote_literal
Quotes a single parameter in native form.
=head2 cmd_escape
Escapes a string to be passed untouched by F<cmd.exe>.
=head1 CAVEATS
=over
=item *
Newlines (\n or \r) and null (\0) can't be properly quoted when
running through F<cmd.exe>.
=item *
This module re-implements some under-specified part of the perl
internals to accurately perform its work.
=back
=head1 AUTHOR
haarg - Graham Knop (cpan:HAARG) <haarg@haarg.org>
=head1 CONTRIBUTORS
=over 8
=item * Mithaldu - Christian Walde (cpan:MITHALDU) <walde.christian@googlemail.com>
=back
=head1 COPYRIGHT AND LICENSE
Copyright (c) 2012 the L</AUTHOR> and L</CONTRIBUTORS>
as listed above.
This is free software; you can redistribute it and/or modify it
under the same terms as the Perl 5 programming language system
itself.
=cut