File Coverage

blib/lib/App/sh2p/Parser.pm
Criterion Covered Total %
statement 15 247 6.0
branch 0 162 0.0
condition 0 99 0.0
subroutine 5 12 41.6
pod 0 7 0.0
total 20 527 3.8


line stmt bran cond sub pod time code
1             package App::sh2p::Parser;
2              
3 1     1   7 use strict;
  1         2  
  1         43  
4 1     1   6 use warnings;
  1         3  
  1         33  
5              
6 1     1   526 use App::sh2p::Compound;
  1         4  
  1         38  
7 1     1   9 use App::sh2p::Trap;
  1         2  
  1         18  
8 1     1   5 use App::sh2p::Utils;
  1         3  
  1         4231  
9              
10             sub convert(\@\@);
11              
12             our $VERSION = '0.06';
13             our $DEBUG = 0;
14              
15             ###########################################################
16              
17             my %icompound =
18             ( 'case' => \&App::sh2p::Compound::Handle_case,
19             'do' => \&App::sh2p::Compound::Handle_do,
20             'done' => \&App::sh2p::Compound::Handle_done,
21             'elif' => \&App::sh2p::Compound::Handle_elif,
22             'else' => \&App::sh2p::Compound::Handle_else,
23             'esac' => \&App::sh2p::Compound::Handle_esac,
24             'fi' => \&App::sh2p::Compound::Handle_fi,
25             'for' => \&App::sh2p::Compound::Handle_for,
26             'function' => \&App::sh2p::Compound::Handle_function,
27             'if' => \&App::sh2p::Compound::Handle_if,
28             'in' => \&App::sh2p::Compound::Ignore,
29             'select' => \&App::sh2p::Compound::Handle_for,
30             'then' => \&App::sh2p::Compound::Handle_then,
31             'time' => 5,
32             'until' => \&App::sh2p::Compound::Handle_until,
33             'while' => \&App::sh2p::Compound::Handle_while,
34             '!' => \&App::sh2p::Compound::Handle_not,
35             '{' => \&App::sh2p::Compound::open_brace,
36             '}' => \&App::sh2p::Compound::close_brace,
37             );
38            
39             my %ioperator =
40             ( '&&' => \&App::sh2p::Operators::shortcut,
41             '||' => \&App::sh2p::Operators::shortcut,
42             '|&' => 3,
43             #'&' => 4, January 2009
44             );
45            
46             my %idelimiter =
47             ( '\'' => \&App::sh2p::Handlers::Handle_delimiter,
48             '"' => \&App::sh2p::Handlers::Handle_delimiter,
49             '`' => \&App::sh2p::Handlers::Handle_delimiter,
50             '$(' => \&App::sh2p::Handlers::Handle_2char_qx,
51             '${' => \&App::sh2p::Handlers::Handle_expansion, # Problems, do specific testing?
52             '(' => \&App::sh2p::Handlers::Handle_delimiter,
53             ')' => \&App::sh2p::Handlers::Handle_delimiter,
54             '[' => \&App::sh2p::Compound::sh_test,
55             '#' => \&App::sh2p::Handlers::Handle_delimiter, # 'COMMENT',
56             ';' => \&App::sh2p::Handlers::Handle_delimiter,
57             '|' => \&App::sh2p::Handlers::Handle_pipe,
58             '[[' => \&App::sh2p::Compound::ksh_test,
59             '((' => \&App::sh2p::Compound::arith,
60             '$((' => \&App::sh2p::Compound::arith,
61             );
62            
63             my %ibuiltins =
64             ( ':' => \&App::sh2p::Builtins::do_colon,
65             '.' => \&App::sh2p::Builtins::do_source,
66             'alias' => 2,
67             'autoload' => \&App::sh2p::Builtins::do_autoload,
68             'bg' => 3,
69             'bind' => 4,
70             'break' => \&App::sh2p::Builtins::do_break,
71             'builtin' => 6,
72             'cd' => \&App::sh2p::Builtins::do_cd,
73             'command' => 8,
74             'continue' => \&App::sh2p::Builtins::do_continue,
75             'echo' => \&App::sh2p::Builtins::do_print,
76             'eval' => 2,
77             'exec' => \&App::sh2p::Builtins::do_exec,
78             'exit' => \&App::sh2p::Builtins::do_exit,
79             'export' => \&App::sh2p::Builtins::do_export,
80             'false' => \&App::sh2p::Builtins::do_false,
81             'fc' => 7,
82             'fg' => 8,
83             'functions'=> \&App::sh2p::Builtins::do_functions,
84             'getopts' => 9,
85             'integer' => \&App::sh2p::Builtins::do_integer,
86             'hash' => 10,
87             'jobs' => 11,
88             'kill' => \&App::sh2p::Builtins::do_kill,
89             'let' => \&App::sh2p::Builtins::do_let,
90             'print' => \&App::sh2p::Builtins::do_print,
91             'read' => \&App::sh2p::Builtins::do_read,
92             'readonly' => 7,
93             'return' => \&App::sh2p::Builtins::do_return,
94             'set' => \&App::sh2p::Builtins::do_set,
95             'shift' => \&App::sh2p::Builtins::do_shift,
96             'test' => \&App::sh2p::Compound::sh_test,
97             '[' => \&App::sh2p::Compound::sh_test,
98             'time' => 12,
99             'times' => 13,
100             'tr' => \&App::sh2p::Builtins::do_tr,
101             'trap' => \&App::sh2p::Trap::do_trap,
102             'true' => \&App::sh2p::Builtins::do_true,
103             'typeset' => \&App::sh2p::Builtins::do_typeset,
104             'ulimit' => 17,
105             'umask' => \&App::sh2p::Builtins::do_chmod,
106             'unalias' => 19,
107             'unset' => \&App::sh2p::Builtins::do_unset,
108             'wait' => 21,
109             'whence' => 22,
110             # Bash specifics
111             'declare' => \&App::sh2p::Builtins::do_typeset,
112             'local' => \&App::sh2p::Builtins::do_typeset,
113             'shopt' => \&App::sh2p::Builtins::do_shopt,
114             'source' => \&App::sh2p::Builtins::do_source,
115             );
116              
117             my %perl_builtins =
118             ( 'awk' => [\&App::sh2p::Builtins::advise,'Perl code, often split'],
119             'basename'=> [\&App::sh2p::Builtins::advise,'File::Basename::basename'],
120             'cat' => [\&App::sh2p::Builtins::advise,'ExtUtils::Command::cat'],
121             'chmod' => [\&App::sh2p::Builtins::do_chmod],
122             'chown' => [\&App::sh2p::Builtins::do_chown],
123             'chgrp' => [\&App::sh2p::Builtins::do_chown],
124             'cp' => [\&App::sh2p::Builtins::advise,'File::Copy'],
125             'cut' => [\&App::sh2p::Builtins::advise,'split'],
126             'date' => [\&App::sh2p::Builtins::advise,'localtime or POSIX::strftime'],
127             'df' => [\&App::sh2p::Builtins::advise,'Filesys::Df'],
128             'diff' => [\&App::sh2p::Builtins::advise,'File::Compare'],
129             'dirname' => [\&App::sh2p::Builtins::advise,'File::Basename::dirname'],
130             'egrep' => [\&App::sh2p::Builtins::advise,'while(<>){print if /re/} or perl grep'],
131             'eval' => [\&App::sh2p::Builtins::one4one,'eval'],
132             'exec' => [\&App::sh2p::Builtins::advise,'exec or pipe (co-processes) or open (file descriptors)'],
133             'expr' => [\&App::sh2p::Builtins::do_expr],
134             'find' => [\&App::sh2p::Builtins::advise,'File::Find'],
135             'file' => [\&App::sh2p::Builtins::advise,'File::Type'],
136             'ftp' => [\&App::sh2p::Builtins::advise,'Net::Ftp'],
137             'grep' => [\&App::sh2p::Builtins::advise,'while(<>){print if /re/} or perl grep'],
138             'ln' => [\&App::sh2p::Builtins::one4one,'link'],
139             'ln -s' => [\&App::sh2p::Builtins::one4one,'symlink'],
140             'ls' => [\&App::sh2p::Builtins::advise,'glob or opendir/readdir/closedir or stat/lstat'],
141             'mkdir' => [\&App::sh2p::Builtins::one4one,'mkdir'],
142             'mkpath' => [\&App::sh2p::Builtins::advise,'ExtUtils::Command::mkpath'],
143             'mv' => [\&App::sh2p::Builtins::one4one,'rename'],
144             'od' => [\&App::sh2p::Builtins::advise,'ord or printf'],
145             'printf' => [\&App::sh2p::Builtins::one4one,'printf'],
146             'pwd' => [\&App::sh2p::Builtins::advise,'Cwd::getcwd'],
147             'rand' => [\&App::sh2p::Builtins::one4one,'rand'],
148             'rm' => [\&App::sh2p::Builtins::one4one,'unlink'],
149             'rm -f' => [\&App::sh2p::Builtins::advise,'ExtUtils::Command::rm_rf'],
150             'sed' => [\&App::sh2p::Builtins::advise,'s/// (usually)'],
151             'select' => [\&App::sh2p::Builtins::advise,'Shell::POSIX::select'],
152             'sleep' => [\&App::sh2p::Builtins::one4one,'sleep'],
153             'sort' => [\&App::sh2p::Builtins::one4one,'sort'],
154             'tail' => [\&App::sh2p::Builtins::advise,'File::Tail'],
155             'telnet' => [\&App::sh2p::Builtins::advise,'Net::Telnet'],
156             'touch' => [\&App::sh2p::Builtins::do_touch],
157             );
158             ###########################################################
159             # $ibuiltins added 0.04
160             sub get_perl_builtin {
161 0     0 0   my $func = shift;
162            
163            
164 0 0         if (defined $perl_builtins{$func}) {
    0          
165 0           return @{$perl_builtins{$func}};
  0            
166             }
167             elsif (defined $ibuiltins{$func}) {
168 0           return ($ibuiltins{$func}, $func);
169             }
170             else {
171 0           return ();
172             }
173             }
174              
175             ###########################################################
176              
177             sub tokenise {
178 0     0 0   my @tokens;
179 0           my $index = 0;
180 0           my $q = 0;
181 0           my $qq = 0;
182 0           my $qx = 0;
183 0           my $qp = 0; # ()
184 0           my $qs = 0; # []
185 0           my $br = 0; # {}
186 0           my $esc = 0; # \
187 0           my $comment = 0;
188 0           my $heredoc = 0;
189 0           my $variable = 0;
190            
191 0           my ($line) = @_;
192            
193 0           for my $char (split '', $line) {
194            
195 0 0         if ($comment) {
196 0           $tokens[$index] .= $char;
197             next
198 0           }
199            
200 0 0         if ($heredoc) {
201             #$g_herelabel .= $char;
202 0           $tokens[$index] .= $char;
203 0           next;
204             }
205            
206 0 0         if ($esc) {
207 0           $tokens[$index] .= $char;
208 0           $esc = 0;
209 0           next;
210             }
211            
212 0 0         if ($variable) {
213 0 0         if ($char =~ /[^A-Z0-9#@*\$\-!\{\}\[\]]/i) {
214 0           $variable = 0;
215             }
216             }
217            
218 0 0 0       if ($char eq '$') {
    0 0        
    0          
    0          
    0          
    0          
    0          
    0          
    0          
219 0           $variable = 1;
220             }
221             elsif ($char eq '\'') {
222 0 0         $q = $q?0:1;
223             }
224             elsif ($char eq '`') {
225 0 0         $qx = $qx?0:1;
226             }
227             elsif ($char eq '"') {
228 0 0         $qq = $qq?0:1;
229             }
230             elsif ($char eq '[') { # Take into account nested []
231 0           $qs++;
232             }
233             elsif ($qs && $char eq ']') {
234 0           $qs--
235             }
236             elsif ($char eq '{') { # Take into account nested {}
237 0           $br++;
238             }
239             # Tried, but had unexpected side-effects
240             # elsif ($br && $char eq '}' && !$q && !$qq && !$qx && !$qp && !$qs) {
241             # $tokens[$index] .= $char;
242             # $index++;
243             # $br--;
244             # next;
245             # }
246             # Modification of above
247             elsif ($br && $char eq '}') {
248 0           $br--;
249             }
250             elsif ($char eq '\\') {
251 0           $tokens[$index] .= $char;
252 0           $esc = 1;
253 0           next;
254             }
255            
256             # Take into account nested ()
257 0 0 0       if ($char eq '(') {
    0          
258 0           $qp++
259             }
260             elsif ($qp && $char eq ')') {
261 0           $qp--
262             }
263              
264             # Not inside a delimiter
265 0 0 0       if (!$q && !$qq && !$qx && !$qp && !$qs && !$br) {
266 0 0 0       if ($char eq '#' && !$variable) {
267 0           $comment = 1
268             }
269            
270 0 0 0       if ($char =~ /\s/ && !$comment) {
    0 0        
    0 0        
    0 0        
271 0 0         $index++ if defined $tokens[$index];
272             }
273             elsif ($char eq ';' && !$comment) {
274 0 0         $index++ if defined $tokens[$index];
275 0           $tokens[$index] .= $char;
276 0           $index++;
277             }
278             elsif ($char eq '<' && !$comment) {
279             # Here doc?
280 0 0         if (defined $tokens[$index]) {
281 0 0         if ($tokens[$index] ne '<') {
282 0 0         $index++ if defined $tokens[$index];
283 0           $tokens[$index] .= $char;
284             }
285             else {
286 0           $heredoc = 1;
287 0           $tokens[$index] .= $char;
288 0           $index++;
289             }
290             }
291             else {
292 0           $tokens[$index] .= $char;
293             }
294             }
295             elsif ($char eq '>' && !$comment) {
296 0 0 0       if (defined $tokens[$index] && $tokens[$index] ne '>') { # Append?
297 0 0         $index++ if defined $tokens[$index];
298 0           $tokens[$index] .= $char;
299             }
300             else {
301 0           $tokens[$index] .= $char;
302 0           $index++;
303             }
304             }
305             else {
306 0           $tokens[$index] .= $char;
307             }
308             }
309             else {
310 0           $tokens[$index] .= $char;
311             }
312             }
313            
314 0 0         $tokens[$index] .= "\n" if $comment;
315            
316             return @tokens
317 0           }
318              
319             ###########################################################
320             # First argument is used to identify external program calls
321             # nested = 0 - call is not nested, first argument may be an external program
322             # nested = 1 - call is not nested, first argument is not an external program
323             # nested = 2 - as 1, plus call is as a list
324              
325             sub identify {
326 0     0 0   my ($nested, @in) = @_;
327 0           my @out;
328 0           my $first = $in[0];
329            
330 0 0         if (!@in) {
331 0           print STDERR "+++ Internal error: Empty input array to identify\n";
332 0           my @caller = caller();
333 0           die "@caller\n";
334             }
335            
336             #print STDERR "identify first <$first>\n";
337             # Special processing for the first token
338            
339 0 0 0       if ($first =~ /^\w+\+?=/) {
    0          
    0          
    0          
340 0           $out[0] = [('ASSIGNMENT',
341             \&App::sh2p::Handlers::Handle_assignment)];
342             shift @in
343 0           }
344             elsif ($first =~ /^\w+\[.*\]=/) {
345 0           $out[0] = [('ARRAY_ASSIGNMENT',
346             \&App::sh2p::Handlers::Handle_array_assignment)];
347             shift @in
348 0           }
349             elsif (is_break($first)) {
350 0           $out[0] = [('BREAK',
351             \&App::sh2p::Handlers::Handle_break)];
352             shift @in
353 0           }
354             elsif (!$nested && $first =~ /^([\"]?)\$[A-Z0-9#@*{}\[\]]+\1/i) { # Optional " added January 2009
355             # Not a variable, but a call (variable contains call name)
356 0           $out[0] = [('EXTERNAL',
357             \&App::sh2p::Handlers::Handle_external)];
358 0           shift @in;
359             }
360              
361             # Now process the rest
362            
363 0           for my $token (@in) {
364            
365             #print STDERR "Identify token: <$token> <$nested>\n";
366            
367 0           my $type = 'UNKNOWN';
368 0           my $sub = \&App::sh2p::Handlers::Handle_unknown;
369              
370 0 0 0       if (ref($token) eq 'CODE') {
    0 0        
    0 0        
    0          
    0          
    0          
    0          
371 0           $sub = $token;
372 0           $type = 'INTERNAL';
373             }
374             elsif ($token =~ /^\w+=/) {
375 0           $sub = \&App::sh2p::Handlers::Handle_assignment;
376 0           $type = 'ASSIGNMENT';
377             }
378             elsif ($token =~ /^\w+\[.*\]=/) {
379 0           $sub = \&App::sh2p::Handlers::Handle_array_assignment;
380 0           $type = 'ARRAY_ASSIGNMENT';
381             }
382             elsif (exists $icompound{$token}) {
383 0           $sub = $icompound{$token};
384 0           $type = 'COMPOUND';
385             }
386             elsif (exists $ioperator{$token} && $nested < 2) {
387 0           $sub = $ioperator{$token};
388 0           $type = 'OPERATOR';
389             # Shortcut, next is another command
390             }
391             elsif (exists $ibuiltins{$token} && $nested < 2) {
392 0           $sub = $ibuiltins{$token};
393 0           $type = 'BUILTIN'
394             }
395             elsif (exists $perl_builtins{$token} && $nested < 2) {
396 0           $sub = $perl_builtins{$token}[0];
397 0           $type = 'PERL_BUILTIN'
398             }
399             else {
400 0           my $first_char = '';
401 0           my $two_chars = '';
402 0           my $three_chars = '';
403            
404 0           $first_char = substr($token, 0, 1);
405 0 0         $two_chars = substr($token, 0, 2) if length($token) > 1;
406 0 0         $three_chars = substr($token, 0, 3) if length($token) > 2;
407            
408 0 0 0       if (exists $idelimiter{$three_chars}) {
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0          
    0          
    0          
409 0           $type = 'THREE_CHAR_DELIMITER';
410 0           $sub = $idelimiter{$three_chars};
411             }
412             elsif (exists $idelimiter{$two_chars}) {
413             # Special hack for variables
414 0 0 0       if ( $two_chars eq '${' && (!@out || ($out[-1]->[0] eq 'BREAK')) &&
      0        
      0        
      0        
415             !$nested && !is_break($first_char)) { # Must be first token
416 0           $type = 'EXTERNAL';
417 0           $sub = \&App::sh2p::Handlers::Handle_external;
418             }
419             else {
420 0           $type = 'TWO_CHAR_DELIMITER';
421 0           $sub = $idelimiter{$two_chars};
422             }
423             }
424             elsif (exists $idelimiter{$first_char}) { # January 2009
425 0 0 0       if ( $first_char eq '"' && (!@out || ($out[-1]->[0] eq 'BREAK')) &&
      0        
      0        
      0        
426             !$nested && !is_break($first_char)) { # Must be first token
427 0           $type = 'EXTERNAL';
428 0           $sub = \&App::sh2p::Handlers::Handle_external;
429             }
430             else {
431 0           $type = 'SINGLE_DELIMITER';
432 0           $sub = $idelimiter{$first_char};
433             }
434             }
435             elsif ($first_char eq '~') {
436 0           $type = 'GLOB';
437 0           $sub = \&App::sh2p::Handlers::Handle_Glob;
438             }
439             elsif ( (!@out || ($out[-1]->[0] eq 'BREAK')) &&
440             !$nested && !is_break($first_char)) { # Must be first token
441 0           $type = 'EXTERNAL';
442 0           $sub = \&App::sh2p::Handlers::Handle_external;
443             }
444             # January 2009 This test must come after the 'EXTERNAL' test,
445             # otherwise a bare variable is not seen as an external call
446             elsif ($first_char eq '$' && $token =~ /^\$[A-Z0-9\#\@\*\?\{\}\[\]]+$/i) {
447 0           $type = 'VARIABLE';
448 0           $sub = \&App::sh2p::Handlers::Handle_variable
449             }
450             elsif (is_break($token)) { # 0.06
451 0           $type = 'BREAK';
452 0           $sub = \&App::sh2p::Handlers::Handle_break;
453             }
454             elsif (exists $ioperator{$two_chars} && $nested) {
455 0           $sub = $ioperator{$two_chars};
456 0           $type = 'OPERATOR'
457             }
458             elsif (exists $ioperator{$first_char} && $nested) {
459 0           $sub = $ioperator{$first_char};
460 0           $type = 'OPERATOR'
461             }
462             elsif ($token =~ /\[|\*|\?/ && !query_in_quotes()) {
463             # No globbing inside quotes
464 0           $sub = \&App::sh2p::Handlers::Handle_Glob;
465 0           $type = 'GLOB';
466             }
467              
468             }
469 0           push @out, [($type, $sub)];
470             }
471            
472 0           return @out;
473            
474             }
475              
476             ###########################################################
477              
478             sub convert (\@\@) {
479 0     0 0   my ($rtok, $rtype) = @_;
480            
481 0 0         if ( $DEBUG ) {
482 0           my @caller = caller();
483 0           print STDERR "\nconvert called from @caller\n";
484 0           local $" = '|';
485 0           print STDERR "convert:@$rtok\nconvert: ";
486 0           print STDERR (map {"$_->[0] "} @$rtype),"\n";
  0            
487             }
488              
489 0 0         if (@$rtok != @$rtype ) {
490 0           print STDERR "+++ Internal Error rtok: <@$rtok>, rtype: <@$rtype>\n";
491 0           die "Parser::convert: token and type arrays uneven\n"
492             }
493            
494 0 0         pop @$rtok if (is_break($rtok->[-1]));
495 0           my $tokens_processed = 0;
496            
497             #print_types_tokens ($rtype, $rtok);
498            
499 0           while (@$rtok) {
500            
501 0           my $type = $rtype->[0][0];
502 0           my $sub = $rtype->[0][1];
503            
504             #print STDERR "tokens: <@$rtok> type: $type, sub: $sub\n";
505 0 0         if (ref($sub) eq 'CODE' ) {
506            
507 0 0         if ($type eq 'COMPOUND') {
508 0           test_for_redirection($rtok, $rtype);
509             }
510            
511 0           $tokens_processed = &$sub(@$rtok);
512            
513 0 0         if ($tokens_processed > @$rtok) {
514 0           error_out "Internal error: Token count wrong! Was: $tokens_processed, max: ".scalar(@$rtok);
515 0           error_out "Type: $rtype->[0][0], tokens: @$rtok";
516             }
517             }
518             else {
519 0           error_out ("No conversion routine for $type $rtok->[0]");
520 0           out "$rtok->[0]\n";
521 0           $tokens_processed = 1;
522             }
523            
524 0 0         if ($tokens_processed) {
525             # Remove tokens already processed
526 0           splice (@$rtok, 0, $tokens_processed);
527 0           splice (@$rtype, 0, $tokens_processed);
528             }
529             }
530            
531             }
532              
533             ########################################################
534             # Called by convert
535             sub test_for_redirection {
536            
537 0     0 0   my ($rtok, $rtype) = @_;
538            
539 0           my $next_type = $rtype->[1][0];
540            
541            
542 0 0 0       return 0 if !defined $next_type || $next_type ne 'BUILTIN';
543            
544             #print_types_tokens($rtype, $rtok);
545            
546 0           for (my $i = 2; $i < @$rtok; $i++) {
547 0 0 0       if ($rtok->[$i] eq '<' || $rtok->[$i] eq '>' || $rtok->[$i] eq '>>') {
      0        
548            
549 0 0         if ( !defined $rtok->[$i+1] ) {
550 0           die "*** Malformed redirection (no file)\n";
551             }
552            
553 0           my $redirection_file = $rtok->[$i+1];
554 0           $redirection_file =~ s/^\s+//;
555 0           App::sh2p::Handlers::Handle_open_redirection ($rtok->[$i],
556             $redirection_file);
557             # Remove tokens processed
558 0           splice (@$rtok, $i, 2);
559 0           splice (@$rtype, $i, 2);
560            
561 0           return 2;
562             }
563             }
564             }
565              
566             ########################################################
567              
568             sub join_parse_tokens {
569              
570 0     0 0   my ($sep, @args) = @_;
571 0           my $ntok = 0;
572              
573             # C style for loop because I need to check the position
574 0           for (my $i = 0; $i < @args; $i++) {
575            
576 0           my @tokens = ($args[$i]);
577 0           my @types = identify (2, @tokens);
578            
579             #print_types_tokens(\@types, \@tokens);
580            
581 0           convert (@tokens, @types);
582 0           $ntok++;
583            
584             # Look ahead to see if we are at end
585 0 0         if ($i < $#args) {
586 0 0         last if substr($args[$i+1],0,1) eq '#';
587 0 0         last if is_break($args[$i+1]);
588 0 0         last if $args[$i+1] eq ';'; # January 2009
589 0           out $sep;
590             }
591            
592             }
593              
594 0           return $ntok;
595             }
596              
597             ###########################################################
598              
599             sub analyse_pipeline {
600 0     0 0   my @args = @_;
601 0           my $ntok = @args;
602 0           my $end_value = '';
603            
604 0           error_out ();
605 0           error_out "Pipeline '@args' detected";
606            
607             #my @caller = caller();
608             #print STDERR "analyse_pipeline: <@args><@caller>\n";
609            
610             # Get commands, sometimes the | is separate, sometimes not
611 0           @args = split /\|/, "@args";
612            
613 0           App::sh2p::Handlers::no_semi_colon();
614            
615             # Let's make a guess. echo or print at the front usually means
616             # that the command which follows wants a string
617 0 0         if ($args[0] =~ s/^(echo |print )//) {
618 0           $end_value = shift @args;
619             }
620            
621 0           for (my $i = 0; $i < @args; $i++) {
622 0           $args[$i] =~ s/^\s+//; # Strip leading whitespace
623 0           $args[$i] =~ s/\s+$//; # Strip trailing whitespace
624            
625 0 0         if (! $args[$i] ) {
626             # Blank line - remove it
627 0           splice (@args, $i, 1);
628 0           $i--; # to counteract the ++
629 0           next;
630             }
631            
632 0           my @tokens = tokenise ($args[$i]);
633 0           my @types = identify (0, @tokens);
634            
635             # We are delimited by |, so get the arguments as well
636             # external call is not the last in the pipe, change to back-ticks
637 0 0 0       if ( $types[0][0] eq 'EXTERNAL' && $i < $#args) {
638            
639 0           @types = (['DELIMITER',\&App::sh2p::Handlers::Handle_2char_qx]);
640 0           @tokens = ("\$(@tokens)");
641            
642 0 0         if ($args[$i+1] =~ /^\s*grep/) {
643             # Switch next command around with this
644 0           $i++;
645 0           $args[$i] =~ s/^\s+//;
646 0           $args[$i] =~ s/\s+$//;
647              
648 0           my @next_tokens = tokenise ($args[$i]);
649 0           my @next_types = identify (0, @next_tokens);
650 0           convert (@next_tokens, @next_types);
651             }
652             }
653              
654             #print_types_tokens (\@types, \@tokens);
655            
656 0           convert (@tokens, @types);
657 0 0         out '|' if $i < $#args;
658             }
659 0           out "$end_value";
660 0 0         out "\n" if !App::sh2p::Compound::get_context();
661            
662 0           App::sh2p::Handlers::reset_semi_colon();
663 0           error_out ();
664            
665 0           return $ntok;
666             }
667              
668             ###########################################################
669              
670             1;