267 lines
5.8 KiB
Perl
267 lines
5.8 KiB
Perl
package Win32::ShellQuote;
|
|
use strict;
|
|
use warnings FATAL => 'all';
|
|
use base 'Exporter';
|
|
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
|