File Coverage

blib/lib/Win32/ParseWords.pm
Criterion Covered Total %
statement 61 81 75.3
branch 29 44 65.9
condition 17 21 80.9
subroutine 10 11 90.9
pod 0 6 0.0
total 117 163 71.7


line stmt bran cond sub pod time code
1             package Win32::ParseWords;
2             $Win32::ParseWords::VERSION = '0.01';
3 1     1   652 use strict;
  1         1  
  1         26  
4 1     1   4 use warnings;
  1         1  
  1         27  
5              
6 1     1   10 use Carp;
  1         1  
  1         361  
7              
8             require Exporter;
9             our @ISA = qw(Exporter);
10             our %EXPORT_TAGS = ();
11             our @EXPORT = qw(shellwords quotewords nested_quotewords parse_line);
12             our @EXPORT_OK = qw(old_shellwords set_perl_squote);
13              
14             my $PERL_SINGLE_QUOTE = 0;
15              
16             sub set_perl_squote {
17 1     1 0 233 $PERL_SINGLE_QUOTE = $_[0];
18             }
19              
20             sub shellwords {
21 4     4 0 1938 my (@lines) = @_;
22 4         6 my @allwords;
23              
24 4         8 foreach my $line (@lines) {
25 6         13 $line =~ s/^\s+//;
26 6         10 my @words = parse_line('\s+', 0, $line);
27 6 100 100     23 pop @words if (@words and !defined $words[-1]);
28 6 100 66     25 return() unless (@words || !length($line));
29 4         9 push(@allwords, @words);
30             }
31 2         8 return(@allwords);
32             }
33              
34             sub quotewords {
35 4     4 0 1666 my($delim, $keep, @lines) = @_;
36 4         4 my($line, @words, @allwords);
37              
38 4         6 foreach $line (@lines) {
39 4         7 @words = parse_line($delim, $keep, $line);
40 4 100 66     13 return() unless (@words || !length($line));
41 3         7 push(@allwords, @words);
42             }
43 3         12 return(@allwords);
44             }
45              
46             sub nested_quotewords {
47 2     2 0 580 my($delim, $keep, @lines) = @_;
48 2         2 my($i, @allwords);
49              
50 2         6 for ($i = 0; $i < @lines; $i++) {
51 4         6 @{$allwords[$i]} = parse_line($delim, $keep, $lines[$i]);
  4         10  
52 4 100 66     5 return() unless (@{$allwords[$i]} || !length($lines[$i]));
  4         17  
53             }
54 1         5 return(@allwords);
55             }
56              
57             sub parse_line {
58 25     25 0 2662 my($delimiter, $keep, $line) = @_;
59 25         20 my($word, @pieces);
60              
61 1     1   4 no warnings 'uninitialized'; # we will be testing undef strings
  1         1  
  1         255  
62              
63 25         47 while (length($line)) {
64             # This pattern is optimised to be stack conservative on older perls.
65             # Do not refactor without being careful and testing it on very long strings.
66             # See Perl bug #42980 for an example of a stack busting input.
67 105 100       1226 $line =~ s/^
68             (?:
69             # double quoted string
70             (") # $quote
71             ((?>[^\^"]*(?:\^.[^\^"]*)*))" # $quoted
72             | # --OR--
73             # singe quoted string
74             (') # $quote
75             ((?>[^\^']*(?:\^.[^\^']*)*))' # $quoted
76             | # --OR--
77             # unquoted string
78             ( # $unquoted
79             (?:\^.|[^\^"'])*?
80             )
81             # followed by
82             ( # $delim
83             \Z(?!\n) # EOL
84             | # --OR--
85             (?-x:$delimiter) # delimiter
86             | # --OR--
87             (?!^)(?=["']) # a quote
88             )
89             )//xs or return; # extended layout
90 100 100       242 my ($quote, $quoted, $unquoted, $delim) = (($1 ? ($1,$2) : ($3,$4)), $5, $6);
91              
92 100 50 100     298 return() unless( defined($quote) || length($unquoted) || length($delim));
      66        
93              
94 100 100       96 if ($keep) {
95 27         28 $quoted = "$quote$quoted$quote";
96             }
97             else {
98 73         91 $unquoted =~ s/\^(.)/$1/sg;
99 73 100       104 if (defined $quote) {
100 17 100       37 $quoted =~ s/\^(.)/$1/sg if ($quote eq '"');
101 17 100 100     49 $quoted =~ s/\^([\^'])/$1/g if ( $PERL_SINGLE_QUOTE && $quote eq "'");
102             }
103             }
104 100         102 $word .= substr($line, 0, 0); # leave results tainted
105 100 100       103 $word .= defined $quote ? $quoted : $unquoted;
106            
107 100 100       115 if (length($delim)) {
108 55         54 push(@pieces, $word);
109 55 100       78 push(@pieces, $delim) if ($keep eq 'delimiters');
110 55         40 undef $word;
111             }
112 100 100       189 if (!length($line)) {
113 20         47 push(@pieces, $word);
114             }
115             }
116 20         61 return(@pieces);
117             }
118              
119             sub old_shellwords {
120             # Usage:
121             # use ParseWords;
122             # @words = old_shellwords($line);
123             # or
124             # @words = old_shellwords(@lines);
125             # or
126             # @words = old_shellwords(); # defaults to $_ (and clobbers it)
127              
128 1     1   3 no warnings 'uninitialized'; # we will be testing undef strings
  1         2  
  1         292  
129 0 0   0 0   local *_ = \join('', @_) if @_;
130 0           my (@words, $snippet);
131              
132 0           s/\A\s+//;
133 0           while ($_ ne '') {
134 0           my $field = substr($_, 0, 0); # leave results tainted
135 0           for (;;) {
136 0 0         if (s/\A"(([^"\^]|\^.)*)"//s) {
    0          
    0          
    0          
    0          
    0          
137 0           ($snippet = $1) =~ s#\^(.)#$1#sg;
138             }
139             elsif (/\A"/) {
140 0           Carp::carp("Unmatched double quote: $_");
141 0           return();
142             }
143             elsif (s/\A'(([^'\^]|\^.)*)'//s) {
144 0           ($snippet = $1) =~ s#\^(.)#$1#sg;
145             }
146             elsif (/\A'/) {
147 0           Carp::carp("Unmatched single quote: $_");
148 0           return();
149             }
150             elsif (s/\A\^(.?)//s) {
151 0           $snippet = $1;
152             }
153             elsif (s/\A([^\s\^'"]+)//) {
154 0           $snippet = $1;
155             }
156             else {
157 0           s/\A\s+//;
158 0           last;
159             }
160 0           $field .= $snippet;
161             }
162 0           push(@words, $field);
163             }
164 0           return @words;
165             }
166              
167             1;
168              
169             __END__