File Coverage

blib/lib/Text/ParseWords.pm
Criterion Covered Total %
statement 71 79 89.8
branch 38 44 86.3
condition 17 21 80.9
subroutine 9 9 100.0
pod 0 5 0.0
total 135 158 85.4


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