File Coverage

blib/lib/App/sh2p/Builtins.pm
Criterion Covered Total %
statement 15 620 2.4
branch 0 270 0.0
condition 0 125 0.0
subroutine 5 40 12.5
pod 0 35 0.0
total 20 1090 1.8


line stmt bran cond sub pod time code
1             package App::sh2p::Builtins;
2              
3 1     1   1038 use strict;
  1         2  
  1         43  
4 1     1   2752 use Getopt::Std;
  1         64  
  1         87  
5 1     1   787 use App::sh2p::Utils;
  1         3  
  1         251  
6 1     1   969 use App::sh2p::Parser;
  1         3  
  1         30  
7 1     1   8 use App::sh2p::Here;
  1         2  
  1         8103  
8              
9             our $VERSION = '0.06';
10             sub App::sh2p::Parser::convert(\@\@);
11              
12             my %g_shell_options;
13             my %g_file_handles;
14              
15             ########################################################
16             #
17             # Note for developers:
18             # There are a lot of functions in here,
19             # try to keep them in alphabetic order
20             #
21             ########################################################
22             # For builtins/functionality that cannot be implemented
23             sub not_implemented {
24 0     0 0   error_out "The following line cannot be translated:";
25 0           error_out "@_";
26              
27 0           return scalar(@_);
28             }
29              
30             ########################################################
31              
32             sub one4one {
33              
34 0     0 0   my ($func, @args) = @_;
35 0           my $new_func = (App::sh2p::Parser::get_perl_builtin($func))[1];
36            
37 0           error_out ("$func replaced by Perl built-in $new_func\n".
38             "# Check arguments and return value");
39            
40 0           return general_arg_list($new_func, @args);
41             }
42              
43             ########################################################
44              
45             sub general_arg_list {
46 0     0 0   my ($cmd, @args) = @_;
47 0           my $ntok = 1;
48 0           my $last = '';
49            
50             #{local $" = '|';print STDERR "general_arg_list: <@args>\n";}
51            
52             # Is final token a comment?
53 0 0         if (substr($args[-1],0,1) eq '#') {
54 0           $last = pop @args;
55 0           $ntok++;
56             }
57            
58 0           my $semi = '';
59 0 0         $semi = ';' if query_semi_colon();
60            
61             # Parse arguments
62 0           for my $arg (@args) {
63            
64 0 0         last if is_break($arg); # 0.05
65 0 0         last if $arg eq ';'; # January 2009
66 0           $ntok++;
67            
68             # Here doc January 2009
69 0 0         if ( $arg eq '<<' ) {
70 0           my $heredoc = App::sh2p::Here::get_last_here_doc();
71            
72 0 0         if (defined $heredoc) {
73 0           my $filename = App::sh2p::Here::gen_filename($heredoc);
74 0           App::sh2p::Here::abandon_sh2p_here_subs ();
75 0           error_out("Heredoc replaced by simple redirection");
76 0           $arg = "< $filename";
77             }
78 0           next; # Yuk!
79             }
80              
81             # Wrap quotes around it:
82             # if it is not a digit && it doesn't already have quotes &&
83             # it isn't a glob constuct
84 0 0 0       if ($arg !~ /^\d+$/ && $arg !~ /^([\'\"]).*\1$/ && $arg !~ /\[|\*|\?/) {
      0        
85             # Escape embedded quotes
86 0           $arg =~ s/\"/\\\"/g;
87             #"help syntax highlighter
88 0           $arg = "\"$arg\"";
89             }
90             }
91              
92             #{local $" = '|';print STDERR "general_arg_list: <@args>\n";}
93 0           iout "$cmd (";
94 0           App::sh2p::Parser::join_parse_tokens (',', @args);
95 0           out ")$semi $last";
96            
97 0 0         iout "\n" if query_semi_colon();
98            
99 0           return $ntok;
100              
101             }
102              
103             ########################################################
104              
105             sub advise {
106              
107 0     0 0   my $func = shift;
108            
109 0           my $advise = (App::sh2p::Parser::get_perl_builtin($func))[1];
110            
111 0           error_out ("$func should be replaced by something like $advise");
112            
113 0           my @args;
114            
115             # Pipeline?
116 0           for my $arg (@_) {
117 0 0         last if ($arg eq '|');
118 0 0         push @args, $arg if $arg;
119             # print STDERR "advise: <$arg>\n";
120             }
121            
122 0           return general_arg_list($func, @args);
123             }
124              
125             ########################################################
126              
127             sub do_autoload {
128              
129 0     0 0   my ($cmd, @functions) = @_;
130 0           my $ntok = 1;
131            
132 0           for my $func (@functions) {
133 0           my $first_char = substr($func,0,1);
134 0 0 0       last if is_break($func) || $func eq ';' || $first_char eq '#';
      0        
135              
136 0 0         if ($first_char eq '$') {
137             # $cmd used - this might be called from typedef
138 0           error_out "$cmd '$func' ignored";
139             }
140             else {
141 0           set_user_function ($func);
142             }
143            
144 0           $ntok++;
145             }
146            
147 0           return $ntok;
148             }
149              
150             ########################################################
151              
152             sub do_break {
153              
154 0     0 0   my (undef, $level) = @_;
155 0           my $ntok = 1;
156            
157 0           iout 'last';
158            
159 0 0         if (query_semi_colon()) {
160 0           out ";\n";
161             }
162              
163 0 0 0       if (defined $level && $level =~ /^\d+$/) {
164 0           error_out "Multiple levels in 'break $level' not supported";
165 0           $ntok++;
166             }
167              
168 0           return $ntok;
169             }
170              
171             ########################################################
172              
173             sub do_colon {
174              
175 0     0 0   my ($colon) = @_;
176            
177 0 0         if (!query_semi_colon()) {
178             # Probably in a conditional
179 0           out '(1)';
180             }
181             else {
182 0           iout '';
183             }
184            
185 0           return 1;
186             }
187              
188             ########################################################
189              
190             sub do_continue {
191              
192 0     0 0   my (undef, $level) = @_;
193 0           my $ntok = 1;
194            
195 0           iout 'next';
196            
197 0 0         if (query_semi_colon()) {
198 0           out ";\n";
199             }
200              
201 0 0 0       if (defined $level && $level =~ /^\d+$/) {
202 0           error_out "Multiple levels in 'continue $level' not supported";
203 0           $ntok++;
204             }
205              
206 0           return $ntok;
207             }
208              
209             ########################################################
210             # 0.04 - removed quote handling
211             sub do_cd {
212              
213 0     0 0   my (undef, @args) = @_;
214 0           my $ntok = 1;
215 0           my $comment = "\n";
216            
217 0 0         pop @args if !$args[-1];
218            
219 0           iout 'chdir (';
220            
221 0           for (my $i=0; $i < @args; $i++) {
222            
223 0           $ntok++;
224            
225 0 0         if (substr ($args[$i],0,1) eq '#') {
226 0           my @comment = splice (@args,$i);
227 0           $comment = "@comment";
228            
229             # remove trailing comment from previous item
230 0 0         $args[$i-1] =~ s/\.$// if $i > 0;
231             last
232 0           }
233            
234             # Wrap quotes around it:
235 0 0 0       if ($args[$i] !~ /^\d+$/ && # if it is not a digit
      0        
236             $args[$i] !~ /^\".*\"$/ && # it doesn't already have quotes
237             $args[$i] !~ /\[|\*|\?/) { # it isn't a glob constuct
238             # Escape embedded quotes
239 0           $args[$i] =~ s/\"/\\\"/g;
240             #"help syntax highlighter
241 0           $args[$i] = "\"$args[$i]\"";
242             }
243            
244 0 0         $args[$i] .= '.' if $i < $#args;
245             }
246            
247             # $ntok += App::sh2p::Parser::join_parse_tokens ('.', @args);
248 0           App::sh2p::Parser::join_parse_tokens ('.', @args);
249            
250 0           out ')';
251              
252 0 0         if (query_semi_colon()) {
253 0           out "; $comment";
254             }
255              
256 0           return $ntok;
257             }
258              
259             ########################################################
260             # TODO: comma separated groups
261             sub chmod_text_permissions {
262              
263 0     0 0   my ($in, $file) = @_;
264            
265 0           iout "# chmod $in $file\n";
266            
267             # Remove any surrounding quotes 0.06
268 0           $file =~ s/^\"(.*)\"$/$1/;
269            
270 0           my $stat = "{ my \$perm = (stat \"$file\")[2] & 07777;\n";
271            
272             # numbers are base 10: I'm constructing a string, not an octal int
273 0           my %classes = ( u => 100, g => 10, o => 1);
274 0           my %access = ( x => 1, w => 2, r => 4);
275            
276             # Linux man page [ugoa]*([-+=]([rwxXst]*|[ugo]))+
277 0           my ($class, $op, $access) = $in =~ /^([ugoa]*)([-=+])([rwx]+)?$/;
278            
279 0           my $mask = 0;
280 0           my $perms = 0;
281            
282 0 0 0       $class = 'ugo' if $class eq 'a' or !$class;
283 0 0         $access = 0 if !$access;
284              
285 0           for (split('', $access)) {$mask += $access{$_}}
  0            
286 0           for (split('', $class)) {$perms += $mask * $classes{$_}}
  0            
287            
288 0           $perms = sprintf ("0%03d", $perms);
289            
290 0           iout "$stat ";
291              
292 0 0         if ($op eq '=') {
    0          
293 0           my $mask = 0;
294 0           for (split('', $class)) {$mask += 7 * $classes{$_}}
  0            
295 0           $mask = sprintf ("0%03d", $mask);
296              
297 0           out "\$perm &= ~0$mask;";
298 0           out "chmod(\$perm,\"$file\");chmod(\$perm|$perms"
299             }
300             elsif ($op eq '+') {
301 0           out "chmod (\$perm | $perms";
302             }
303             else {
304 0           out "chmod (\$perm & ~$perms";
305             }
306              
307 0           out ", \"$file\")}\n";
308             }
309              
310             ########################################################
311             # also used by umask
312             sub do_chmod {
313            
314 0     0 0   my ($cmd) = shift;
315 0           my ($opt) = shift;
316 0           my $perms;
317 0           my $ntok = 2;
318              
319 0 0         if (substr($opt,0,1) eq '-') {
320 0           error_out ("$cmd options not yet supported");
321 0           $perms = shift;
322 0           $ntok++;
323             }
324             else {
325 0           $perms = $opt;
326 0           $opt = '';
327             }
328            
329 0           my @args = @_;
330              
331 0           my $comment = '';
332 0           my $text = '';
333            
334 0 0         if ( $perms !~ /^\d+$/ ) {
335 0           for my $file (@args) {
336 0           chmod_text_permissions ($perms, $file);
337 0           $ntok++;
338             }
339 0           return $ntok;
340             }
341              
342 0           iout "$cmd ";
343            
344 0 0         if (defined $perms) {
345             #$ntok++; 0.06
346            
347 0 0         if ($cmd eq 'chmod') {
    0          
348 0           out "0$perms,";
349             }
350             elsif ($cmd eq 'umask') {
351 0           out "0$perms";
352             }
353             else {
354 0           out "$perms,";
355             }
356            
357 0 0 0       if (@args && $cmd ne 'umask') {
358            
359 0           for (my $i=0; $i < @args; $i++) {
360            
361 0           $ntok++;
362 0 0         if (substr ($args[$i],0,1) eq '#') {
363 0           my @comment = splice (@args,$i);
364 0           $comment = "@comment";
365            
366             # remove trailing comment from previous item
367 0 0         $args[$i-1] =~ s/,$// if $i > 0;
368             last
369 0           }
370            
371             # Remove any surrounding quotes 0.06
372 0           $args[$i] =~ s/^\"(.*)\"$/$1/;
373            
374             # Escape embedded quotes
375             #$args[$i] =~ s/\"/\\\"/g; # commented out 0.06
376             #"help syntax highlighter
377            
378 0           $args[$i] = "\"$args[$i]\"";
379 0 0         $args[$i] .= ',' if $i < $#args;
380             }
381            
382 0           App::sh2p::Handlers::interpolation ("@args");
383             }
384             }
385 0           out "; $comment\n";
386            
387 0           return $ntok;
388             }
389              
390             ########################################################
391              
392             sub do_chown {
393            
394 0     0 0   my ($cmd) = shift;
395 0           my ($opt) = shift;
396 0           my $ugrp;
397 0           my $ntok = 1;
398              
399 0 0         if (substr($opt,0,1) eq '-') {
400 0           error_out ("$cmd options not yet supported");
401 0           $ugrp = shift;
402 0           $ntok++;
403             }
404             else {
405 0           $ugrp = $opt;
406 0           $opt = '';
407             }
408            
409 0           my @args = @_;
410              
411 0           my $comment = '';
412 0           my $text = '';
413            
414 0 0         if (defined $ugrp) {
415 0           $ntok++;
416 0 0         if ($cmd eq 'chown') {
417 0           iout "{my(\$uid,\$gid)=(getpwnam(\"$ugrp\"))[2,3];";
418             }
419             else { # chgrp
420 0           iout "{my (\$name,undef,\$gid)=getgrname(\"$ugrp\");\n";
421 0           out " my \$uid=(getpwnam(\$name))[2];\n ";
422             }
423 0           out "chown \$uid, \$gid,"; # There is no chgrp
424             }
425             else {
426 0           error_out ("No user/group supplied for $cmd");
427 0           iout $cmd;
428             }
429            
430 0 0         if (@args) {
431            
432 0           for (my $i=0; $i < @args; $i++) {
433            
434 0           $ntok++;
435            
436 0 0         if (substr ($args[$i],0,1) eq '#') {
437 0           my @comment = splice (@args,$i);
438 0           $comment = "@comment";
439            
440             # remove trailing comment from previous item
441 0 0         $args[$i-1] =~ s/,$// if $i > 0;
442             last
443 0           }
444            
445             # Escape embedded quotes
446 0           $args[$i] =~ s/\"/\\\"/g;
447             #"help syntax highlighter
448 0           $args[$i] = "\"$args[$i]\"";
449 0 0         $args[$i] .= ',' if $i < $#args;
450             }
451            
452 0           App::sh2p::Handlers::interpolation ("@args");
453              
454             }
455 0           out "}; $comment\n";
456            
457 0           return $ntok;
458             }
459              
460             ########################################################
461            
462             sub do_exec {
463              
464 0     0 0   my (undef, @rest) = @_;
465 0           my $ntok = 1;
466            
467 0 0         if ($rest[0] =~ /^\d$/) {
468 0           error_out ("Warning: file descriptors are not supported");
469 0           my ($fd, $access, $filename) = @rest;
470 0           iout "open(my \$sh2p_handle$fd, '$access', \"$filename\") or ".
471             "die \"Unable to open $filename: \$!\";\n";
472 0           $ntok += 3;
473 0           $g_file_handles{$fd} = $filename;
474             }
475             else {
476 0           $ntok = general_arg_list('exec', @rest);
477             }
478            
479 0           return $ntok;
480             }
481              
482             ########################################################
483              
484             sub do_exit {
485              
486 0     0 0   my ($cmd, $arg) = @_;
487 0           my $ntok = 1;
488            
489 0 0         if (defined $arg) {
490 0           iout ("exit ($arg);\n");
491 0           $ntok++;
492             }
493             else {
494 0           iout ("exit;\n");
495             }
496            
497 0           return $ntok;
498             }
499              
500             ########################################################
501              
502             sub do_export {
503              
504 0     0 0   my $ntok = @_;
505             # First argument should be 'export'
506 0           shift @_;
507            
508             # TODO - other export arguments
509            
510 0           for my $env (@_) {
511 0 0         if ($env =~ /^(\w+)=(.*)$/) {
512 0           $env = $1;
513 0           my $value = $2;
514            
515             # 0.06
516 0 0 0       if ($value =~ /^\$/) {
    0          
517 0           my $special = get_special_var($value);
518 0 0         $value = $special if defined $special;
519 0           iout "\$ENV{$env} = $value;\n";
520             }
521             elsif ($value =~ /^\$/ || $value =~ /^([\"\']).*\1/) {
522 0           iout "\$ENV{$env} = $value;\n";
523             }
524             else {
525 0           iout "\$ENV{$env} = \"$value\";\n";
526             }
527             }
528             else {
529 0           iout "\$ENV{$env} = \$$env;\n";
530 0           iout "undef \$$env;\n";
531 0           Delete_variable ($env);
532             }
533 0           Register_env_variable ($env);
534             }
535            
536 0           return $ntok;
537             }
538              
539             ########################################################
540              
541             sub do_expr {
542            
543             # $cmd should be expr
544 0     0 0   my ($cmd, @rest) = @_;
545 0           my $ntok = 1;
546            
547             # temporary fix
548 0           error_out ('Suspious conversion from expr');
549            
550 0 0         if (query_semi_colon()) {
551 0           iout ("@rest");
552             }
553             else {
554 0           out ("@rest");
555             }
556            
557 0           return $ntok;
558             }
559              
560             ########################################################
561              
562             sub do_functions {
563              
564 0     0 0   my ($func) = @_;
565            
566 0           iout 'print map {"sub $_\n" if defined &{$_}} keys %main::;';
567            
568 0           return 1;
569              
570             }
571              
572             ########################################################
573              
574             sub do_integer {
575            
576             # $cmd should be 'integer'
577 0     0 0   my ($cmd, $action, @rest) = @_;
578            
579 0           my $var = $action;
580            
581             # Remove any assignment from the name
582 0           $var =~ s/=.*//;
583            
584 0 0         if (Register_variable ("\$$var", 'int') ) {
585 0           iout 'my ';
586             }
587            
588 0           my $retn = App::sh2p::Handlers::Handle_assignment ($action, @rest);
589            
590 0           $retn++; # $cmd
591 0           return $retn;
592             }
593              
594             ########################################################
595              
596             sub do_kill {
597 0     0 0   my ($cmd, @rest) = @_;
598 0           my $signal;
599 0           my $ntok = 0;
600            
601             # Remove the hyphen - it has a different meaning in Perl!
602 0 0         if ($rest[0] =~ s/^-//) {
603 0           $signal = shift @rest;
604             }
605             else {
606 0           $signal = 'TERM'; # default signal
607            
608             # 0.06 Hack because this is an inserted token and
609             # general_arg_list will include this in its count
610 0           $ntok--;
611             }
612              
613             #print STDERR "do_kill: <@rest>\n";
614              
615 0           $ntok += general_arg_list ($cmd, $signal, @rest);
616            
617 0           return $ntok;
618             }
619              
620             ########################################################
621              
622             sub do_let {
623              
624 0     0 0   my ($cmd, @rest) = @_;
625 0           my $ntok = 1;
626            
627             # Find any comment - this should go first
628 0 0         if (substr($rest[-1],0,1) eq '#') {
629 0           $ntok++;
630 0           iout $rest[-1]; # Write the comment out
631             pop @rest
632 0           }
633            
634 0           for my $token (@rest) {
635             # strip quotes
636 0           $token =~ s/[\'\"]//g;
637              
638             # Get variable name
639 0           $token =~ /^(.*?)=/;
640 0           my $var = "\$$1";
641 0 0         if (Register_variable($var, int)) {
642 0           iout "my $var;\n"; # 0.05 added leading $
643             }
644            
645 0           App::sh2p::Compound::arith ($token);
646 0           $ntok++;
647             }
648            
649 0           return $ntok;
650             }
651              
652             ########################################################
653             # Also does for echo
654             sub do_print {
655              
656 0     0 0   my $ntok = 1;
657 0           my ($name, @args) = @_;
658 0           my $newline = 1;
659 0           my $handle = '';
660              
661 0           my $opt_u;
662             my %options;
663 0           local @ARGV;
664              
665 0           my $redirection = '';
666 0           my $file = '';
667 0           my $from_fd = ''; # TODO - not currently supported
668            
669             # Move the comment to before the statement
670 0 0         if ( substr($args[-1],0,1) eq '#' ) {
671 0           my $comment = pop @args;
672 0           out "\n";
673 0           iout $comment;
674 0           $ntok++;
675             }
676            
677 0           for my $arg (@args) {
678 0 0 0       last if is_break($arg) || $arg eq ';';
679 0           my $in_redirection_token = 0;
680            
681             # This is so a > inside a string is not seen as redirection
682 0 0         if ($arg =~ /^([\"\']).*?\1/) {
683 0           set_in_quotes();
684             }
685            
686             # This should also strip out the redirection
687 0 0 0       if (!query_in_quotes() && $arg =~ s/(\>{1,2})//) {
688            
689 0           $ntok++;
690 0           $redirection = $1;
691 0           $in_redirection_token = 1;
692            
693 0 0         if ($ARGV[-1] =~ /\d/) {
694 0           $from_fd = pop @ARGV;
695 0           error_out ("dup file descriptors ($from_fd>&n) not currently supported");
696 0           $ntok++;
697             }
698             }
699            
700 0 0 0       if ($arg && $redirection && (! $file)) {
      0        
701 0           $arg =~ s/(\S+)//;
702 0           $file = $1;
703 0 0         $ntok++ unless $in_redirection_token;
704             }
705            
706 0           unset_in_quotes();
707 0 0         push @ARGV, $arg if $arg;
708             #$ntok++; 0.05 commented out
709             }
710            
711 0 0         if ($redirection) {
    0          
712            
713             #print STDERR "do_print redirection file <$file>\n";
714             # January 2009
715 0 0         if ( $file =~ /^\&(\d+)$/ ) {
716 0           my $fd = $1;
717 0 0         if ($fd == 1) {
    0          
718 0           $handle = 'STDOUT ';
719             }
720             elsif ($fd == 2) {
721 0           $handle = 'STDERR ';
722             }
723             else {
724 0           error_out ('file descriptors not currently supported');
725 0           $handle = "$fd "; # Just to show something
726             }
727 0           $redirection = 0; # Avoid the close
728             }
729             else {
730 0           App::sh2p::Handlers::Handle_open_redirection ($redirection, $file);
731 0           $handle = '$sh2p_handle ';
732             }
733             }
734             elsif (App::sh2p::Handlers::Query_redirection('w')) {
735             # Redirection may have been done in sh2p
736 0           $redirection = 1;
737 0           $handle = '$sh2p_handle ';
738             }
739            
740 0           my $ARGV_length = @ARGV; # 0.05
741 0           getopts ('nEepu:', \%options);
742            
743             # How many tokens have I processed?
744 0           $ntok += $ARGV_length - @ARGV; # 0.05
745            
746             # Ignore -e and -E options (from echo)
747 0 0         if (exists $options{n}) {
748 0           $newline = 0;
749             }
750            
751 0 0         if ($name eq 'print') {
752 0 0         if (exists $options{p}) {
753 0           error_out ('Pipes/co-processes are not supported, use open');
754             }
755            
756 0 0 0       if (exists $options{u} && defined $options{u}) {
757 0           my @handles = ('', 'STDOUT ', 'STDERR ');
758 0 0         if ($options{u} > $#handles) {
759 0           error_out ('file descriptors not currently supported');
760 0           $handle = "$options{u} "; # Just to show something
761             }
762             else {
763 0           $handle = $handles[$options{u}];
764             }
765             }
766             }
767            
768 0           iout ("print $handle");
769            
770 0           @args = @ARGV; # Removed the 'my' 0.05
771            
772             # Is final token a comment?
773 0 0         pop @args if substr($args[-1],0,1) eq '#';
774              
775 0           my $string = '';
776 0           my @trailing_tokens;
777            
778             # C style for loop because I need to check the position
779 0           for (my $i = 0; $i < @args; $i++) {
780            
781             # Strip out existing quotes
782 0 0         if ($args[$i] =~ s/^([\"])(.*)\1(.*)$/$2$3/) {
783 0           set_in_quotes();
784             }
785            
786 0           my @tokens = ($args[$i]);
787 0           my @types = App::sh2p::Parser::identify (2, @tokens);
788              
789             #print_types_tokens(\@types, \@tokens);
790            
791 0 0 0       if ($types[0][0] eq 'UNKNOWN' ||
    0 0        
792             $types[0][0] eq 'SINGLE_DELIMITER' ||
793             $types[0][0] eq 'TWO_CHAR_DELIMITER') { # 0.05
794            
795 0           $string .= "$tokens[0]";
796            
797             # append with a space for print/echo
798 0 0         $string .= ' ' if $i < $#args;
799             }
800             elsif ($types[0][0] eq 'OPERATOR') { # 0.05
801 0           @trailing_tokens = splice (@args, $i);
802 0           last;
803             }
804             else {
805            
806 0 0         if ($string) {
807 0           App::sh2p::Handlers::interpolation ($string);
808 0           $string = ' '; # Add a space between args
809 0           out ',';
810             }
811            
812 0           App::sh2p::Parser::convert (@tokens, @types);
813 0 0         out ',' if $i < $#args;
814             }
815            
816 0           $ntok++; # 0.05 (moved)
817             }
818            
819 0 0 0       if ($string && $string ne ' ') {
    0          
820 0 0         if ($newline) {
821 0           $string .= "\\n"
822             }
823              
824 0           App::sh2p::Handlers::interpolation ($string);
825             }
826             elsif ($newline) {
827 0           out ",\"\\n\""
828             }
829            
830 0 0         if (@trailing_tokens) { # 0.05
831 0           out " "; # cosmetic
832 0           $ntok += @trailing_tokens;
833 0           my @trailing_types = App::sh2p::Parser::identify (1, @trailing_tokens);
834 0           App::sh2p::Parser::convert (@trailing_tokens, @trailing_types);
835             }
836             else {
837 0           out ";\n";
838             }
839            
840             # An ugly hack, but necessary where the first arg is parenthesised
841 0           fix_print_arg();
842            
843 0 0         App::sh2p::Handlers::Handle_close_redirection('w') if $redirection;
844              
845 0           return $ntok;
846            
847             } # do_print
848              
849             ########################################################
850              
851             sub do_read {
852 0     0 0   my %args;
853 0           my $prompt = 'undef';
854 0           my $ntok;
855 0           local @ARGV;
856              
857             # First argument is 'read'
858 0           shift @_;
859 0           $ntok++;
860            
861             # Find end of statement
862 0           for my $arg (@_) {
863 0 0 0       last if is_break($arg) || $arg eq ';'; # Inserted in sh2p loop
864 0           push @ARGV, $arg;
865 0           $ntok++;
866             }
867            
868 0           getopts ('p:rsu:nAa', \%args);
869            
870 0 0 0       if (exists $args{p} && which_shell() eq 'bash') {
    0          
871             # Bash syntax for prompt
872 0           $prompt = $args{p}
873             }
874             elsif ($ARGV[0] =~ /^(\w*)\?(.*)$/) { # ksh syntax for prompt
875            
876 0   0       $ARGV[0] = $1 || 'REPLY';
877 0           $prompt = $2;
878             }
879              
880             # Default variable
881 0 0         @ARGV = ('REPLY') if ! @ARGV;
882              
883             # Add $ prefix to variable names
884             # Do I need to pre-define them?
885 0           for (my $i = 0; $i < @ARGV; $i++) {
886              
887 0 0 0       if (exists $args{a} || exists $args{A}) {
    0          
888 0           $ARGV[$i] = "\@$ARGV[$i]";
889 0 0         if (Register_variable($ARGV[$i], '@')) {
890 0           pre_out "my $ARGV[$i];\n";
891             }
892             }
893             elsif ($ARGV[$i] =~ s/^
894 0           my $filename;
895 0 0 0       if (defined $ARGV[$i] && $ARGV[$i]) {
896 0           $filename = $ARGV[$i];
897             }
898             else {
899 0           $filename = $ARGV[$i+1];
900             }
901 0           pop @ARGV;
902 0 0         pop @ARGV if $i == $#ARGV;
903            
904 0           App::sh2p::Handlers::Handle_open_redirection('<', $filename);
905            
906             }
907             else {
908 0           $ARGV[$i] = "\$$ARGV[$i]";
909 0 0         if (Register_variable($ARGV[$i], '$')) {
910 0           pre_out "my $ARGV[$i];\n";
911             }
912             }
913             }
914            
915 0 0 0       if (exists $args{p} && which_shell() eq 'ksh') {
916             # ksh syntax for pipes
917 0           error_out "read through ksh pipes is not supported";
918 0           iout "read @_;\n";
919 0           return $ntok;
920             }
921            
922 0           my $heredoc = App::sh2p::Here::get_last_here_doc();
923            
924 0 0         if (defined $heredoc) {
925 0           my $filename = App::sh2p::Here::gen_filename($heredoc);
926 0 0         if (Register_variable('$IFS', '$')) {
927 0           pre_out "my \$IFS=".get_special_var('IFS').";\n";
928             }
929 0           iout "sh2p_read_from_file ('$filename', \"\$IFS\", $prompt, ".
930             '\\'.(join ',\\', @ARGV).")";
931 0           App::sh2p::Here::store_sh2p_here_subs();
932             }
933             else {
934 0 0 0       if (exists $args{u} && $args{u} ne 0) {
935 0           my $fd = $args{u};
936            
937 0           iout "$ARGV[0] = <\$sh_handle$fd>";
938            
939 0 0         if (@ARGV > 1) {
940 0           iout "(".(join ',', @ARGV).") = split /\$IFS/, $ARGV[0];"
941             }
942             }
943             else {
944 0 0         if (Register_variable('$IFS', '$')) {
945 0           pre_out "my \$IFS=".get_special_var('IFS').";\n";
946             }
947            
948 0           my $filename = App::sh2p::Handlers::Query_redirection('r');
949            
950 0 0         if (defined $filename) {
951 0           iout 'sh2p_read_from_handle ($sh2p_handle,"$IFS",'."$prompt,".
952             '\\'.(join ',\\', @ARGV).")";
953             }
954             else {
955 0           iout "sh2p_read_from_stdin (\"\$IFS\", $prompt, ".
956             '\\'.(join ',\\', @ARGV).")";
957             }
958            
959 0 0         if (!App::sh2p::Compound::get_context()) {
960 0           out ";\n";
961 0           App::sh2p::Handlers::Handle_close_redirection('r');
962             }
963              
964 0           App::sh2p::Here::store_sh2p_here_subs();
965             }
966             }
967            
968 0           return $ntok;
969            
970             } # do_read
971              
972             ########################################################
973              
974             sub do_return {
975              
976 0     0 0   my ($name, $arg) = @_;
977 0           my $ntok = 1;
978              
979 0           iout $name;
980            
981 0 0 0       if (defined $arg &&
      0        
982             substr($arg,0,1) ne '#' &&
983             substr($arg,0,1) ne ';' ) {
984            
985 0           out ' ';
986 0           my @tokens = ($arg);
987 0           my @types = App::sh2p::Parser::identify (1, @tokens);
988            
989 0           App::sh2p::Parser::convert (@tokens, @types);
990 0           $ntok++;
991             }
992              
993 0           out ";\n";
994 0           return $ntok;
995             }
996              
997             ########################################################
998              
999             sub do_shift {
1000              
1001 0     0 0   my (undef, $level) = @_;
1002 0           my $ntok = 1;
1003            
1004 0 0 0       if (defined $level && $level =~ /^\d+$/ && !is_break($level)) {
      0        
1005 0           $ntok++;
1006             }
1007             else {
1008 0           $level = 1;
1009             }
1010              
1011 0           iout (('shift;' x $level)."\n"); # 0.04
1012            
1013 0           return $ntok;
1014              
1015             }
1016              
1017             ########################################################
1018              
1019             sub do_shopt {
1020              
1021 0     0 0   my (undef, $switch, @rest) = @_;
1022 0           my $ntok = 2;
1023 0           my @options;
1024            
1025 0           for my $option (@rest) {
1026 0 0 0       last if is_break($option) || $option eq ';' || substr($option,0,1) eq '#';
      0        
1027 0           push @options, $option;
1028 0           $ntok++;
1029             }
1030            
1031 0           error_out ("Shell option @options being set");
1032 0 0         if ($switch eq '-s') {
    0          
1033 0           @g_shell_options{@options} = undef;
1034             }
1035             elsif ($switch eq '+s') {
1036 0           delete @g_shell_options{@options};
1037             }
1038             else {
1039 0           error_out ("Unrecognised shopt argument: <$switch>");
1040             }
1041            
1042 0           return $ntok;
1043            
1044             }
1045              
1046             ########################################################
1047              
1048             sub do_source {
1049              
1050 0     0 0   my (undef, @tokens) = @_;
1051 0           my $ntok = 1;
1052            
1053 0           error_out ();
1054 0           error_out "sourced file should also be converted";
1055            
1056             # Removed enclosing " in 0.06
1057 0           iout 'do ';
1058            
1059 0           no_semi_colon();
1060            
1061 0           $ntok += App::sh2p::Parser::join_parse_tokens ('.', @tokens);
1062            
1063 0           reset_semi_colon();
1064 0           out ';';
1065            
1066 0           return $ntok;
1067             }
1068              
1069             ########################################################
1070              
1071             sub do_touch {
1072 0     0 0   my $ntok = @_;
1073 0           my $cmd = shift;
1074 0           local @ARGV = @_;
1075              
1076 0           my %args;
1077 0           getopts ('acdfmr:t', \%args);
1078 0 0         if (keys %args) {
1079 0           error_out "$cmd options not currently supported";
1080             }
1081              
1082 0           my $text = "# $cmd @_\n";
1083            
1084 0           for my $file (@ARGV) {
1085 0 0         if (substr ($file,0,1) eq '#') {
1086 0           iout "$file\n"; # Output comment first
1087             }
1088            
1089             # Remove surrounding quotes
1090 0           $file =~ s/^([\'\"])(.*)\1/$2/;
1091            
1092 0           $text .= << "END"
1093             if (-e \"$file\") {
1094             # update access and modification times, requires perl 5.8
1095             utime undef, undef, \"$file\";
1096             }
1097             else {
1098             open(my \$fh,'>',\"$file\") or warn \"$file:\$!\";
1099             }
1100              
1101             END
1102             }
1103              
1104 0           iout $text;
1105              
1106 0           return $ntok;
1107             }
1108              
1109             ########################################################
1110              
1111             sub do_tr {
1112              
1113 0     0 0   my ($cmd, @args) = @_;
1114 0           my $ntok = 1;
1115 0           my %args;
1116            
1117 0           local @ARGV = @args;
1118 0           getopts ('cCsd', \%args);
1119 0 0         if (keys %args) {
1120 0           error_out "$cmd options not currently supported";
1121             }
1122            
1123 0           $ntok = @_ - @ARGV;
1124            
1125 0 0         return $ntok if !@ARGV;
1126            
1127 0           my $from = shift @ARGV;
1128 0           $ntok++;
1129            
1130 0           my $to;
1131 0 0         if (@ARGV) {
1132 0           $to = shift @ARGV;
1133 0           $ntok++;
1134             }
1135            
1136             # Strip quotes if there are any
1137 0           $from =~ s/^\'(.*)\'/$1/g;
1138 0           $to =~ s/^\'(.*)\'/$1/g;
1139            
1140             # common case
1141 0 0 0       if (($from eq '[a-z]' || $from eq '[:lower:]') &&
    0 0        
      0        
      0        
      0        
      0        
1142             ($to eq '[A-Z]' || $to eq '[:upper:]')) {
1143 0           iout 'uc ';
1144             }
1145             elsif (($from eq '[A-Z]' || $from eq '[:upper:]') &&
1146             ($to eq '[a-z]' || $to eq '[:lower:]')) {
1147 0           iout 'lc ';
1148             }
1149             else {
1150             # Convert patterns TODO
1151 0           iout "tr/$from/$to/";
1152             }
1153            
1154 0           return $ntok;
1155             }
1156              
1157             ########################################################
1158             # typeset [[+-Ulprtux] [-L[n]] [-R[n]] [-Z[n]] [-i[n]] | -f [-tux]]
1159             # [name[=value] ...]
1160             # Needs more work!
1161             sub do_typeset {
1162            
1163 0     0 0   my $ntok = @_;
1164 0           my %args;
1165            
1166             #print STDERR "do_typeset: $_[0]\n";
1167             # First argument should be 'typeset' or 'declare'
1168 0           shift @_;
1169            
1170 0           local @ARGV = @_;
1171            
1172 0           getopts ('UPRTUXLRZ:iftux', \%args);
1173            
1174 0           my %type = (i => 'int',
1175             l => 'lc',
1176             u => 'uc',
1177             Z => '%0nd',
1178             L => '%-s',
1179             R => '%s',
1180             X => '%X',
1181             x => '%x');
1182            
1183 0           my $type = '$';
1184 0           my @opt = grep {$args{$_}} keys %args;
  0            
1185            
1186 0 0         if (exists $type{$opt[0]}) {
1187 0           $type = $type{$opt[0]};
1188             }
1189              
1190             # These types are not yet supported by other functions
1191 0 0         if (@opt > 1) {
1192 0 0 0       if ( $args{Z} && defined $args{Z}) {
    0          
1193 0           $type =~ s/n/$args{Z}/;
1194             }
1195             elsif ( $args{f} ) {
1196 0 0         if ($args{u}) {
1197 0           $ntok += do_autoload ('typeset -fu',@ARGV);
1198 0           $ntok--; # artificial 1st argument
1199             }
1200 0           return $ntok;
1201             }
1202             else {
1203 0           error_out "Only one option supported for typedef or declare";
1204             }
1205             }
1206              
1207 0           my $var = $ARGV[0];
1208              
1209             # Remove any assignment for the name
1210 0           $var =~ s/=.*//;
1211            
1212 0 0         if (Register_variable ("\$$var", $type) ) {
1213 0           iout 'my ';
1214             }
1215            
1216             #$ntok += January 2009
1217 0           App::sh2p::Handlers::Handle_assignment (@ARGV);
1218            
1219 0           return $ntok;
1220             }
1221              
1222             ########################################################
1223             # Need getopt here, but it can't deal with +
1224             # set [+-abCefhkmnpsuvxX] [+-o [option]] [+-A name] [--] [arg ...]
1225              
1226             sub do_set {
1227            
1228 0     0 0   my $ntok = 1;
1229            
1230             # First argument is 'set'
1231 0           shift @_;
1232 0           my @values;
1233            
1234 0           for my $option (@_) {
1235 0           my $act = substr($option, 0, 1);
1236              
1237 0 0 0       if ($act eq '+' || $act eq '-') {
    0          
1238 0           my $set = substr($option, 1);
1239            
1240 0 0         if ( $set eq 'A') {
1241 0 0         if ($act eq '-') {
1242 0           initialise_array (@_);
1243             }
1244             else {
1245 0           overwrite_array (@_);
1246             }
1247            
1248 0           $ntok += @_; # Added 0.05
1249 0           last;
1250             }
1251             }
1252             elsif (is_break($option)) {
1253             last
1254 0           }
1255             else {
1256 0           push @values, $option;
1257             }
1258 0           $ntok++;
1259             }
1260            
1261 0 0         if (@values) {
1262 0           my $IFS = get_special_var('IFS');
1263            
1264 0           iout "\@ARGV = (";
1265 0           for (my $i=0; $i < @values;$i++) {
1266 0           my @tokens = ($values[$i]);
1267             #print STDERR "do_set: <$values[$i]>\n";
1268            
1269 0           my @types = App::sh2p::Parser::identify (2,@tokens);
1270 0 0 0       if ($values[$i] =~ /^[\"\']*\$/ && !get_special_var($values[$i])) {
1271 0           out "(split /[$IFS]/,";
1272 0           App::sh2p::Parser::convert (@tokens, @types);
1273 0           out ")";
1274             }
1275             else {
1276 0           App::sh2p::Parser::convert (@tokens, @types);
1277             }
1278 0 0         out ',' if $i < $#values;
1279             }
1280             #App::sh2p::Parser::join_parse_tokens (',', @values);
1281            
1282 0           out ");\n";
1283             }
1284            
1285 0           return $ntok;
1286            
1287             } # do_set
1288              
1289             # set -A
1290             sub initialise_array {
1291 0     0 0   my ($nu, $array, @values) = @_;
1292            
1293 0           iout "my \@$array = qw(@values);\n";
1294             }
1295              
1296             # set +A
1297             sub overwrite_array {
1298 0     0 0   my ($nu, $array, @values) = @_;
1299            
1300             # Fix for 5.6 01/09/2008
1301 0           iout "my \@${array}\[0..". $#values ."\] = qw(@values);\n";
1302             }
1303              
1304             ########################################################
1305              
1306             sub do_true {
1307              
1308 0     0 0   my ($name, $rest) = @_;
1309 0           my $ntok = 1;
1310            
1311 0 0         if (App::sh2p::Compound::get_context()) {
1312             # Inside a conditional
1313 0           out ' 1 ';
1314             }
1315             else {
1316 0           iout '$? = 0;';
1317              
1318 0 0         if (!defined $rest) {
1319 0           out "\n";
1320             }
1321             }
1322            
1323 0           return 1;
1324             }
1325              
1326             sub do_false {
1327              
1328 0     0 0   my ($name, $rest) = @_;
1329 0           my $ntok = 1;
1330            
1331 0 0         if (App::sh2p::Compound::get_context()) {
1332             # Inside a conditional
1333 0           out ' 0 ';
1334             }
1335             else {
1336 0           iout '$? = 1;';
1337            
1338 0 0         if (!defined $rest) {
1339 0           out "\n";
1340             }
1341             }
1342              
1343 0           return 1;
1344             }
1345              
1346             ########################################################
1347              
1348             sub do_unset {
1349            
1350 0     0 0   my (undef, $var, @rest) = @_;
1351 0           my $ntok = 1;
1352            
1353 0 0         if (substr($var,0,1) eq '-') {
1354 0           my $option = $var;
1355            
1356 0           $var = $rest[0];
1357 0           $ntok++;
1358            
1359             # unset only supports two options (POSIX)
1360             # -v has the same effect as not being there
1361            
1362 0 0         if ($option eq '-f') {
1363 0           unset_user_function ($var);
1364 0           $ntok++;
1365 0           return $ntok;
1366             }
1367            
1368             }
1369            
1370 0           iout 'undef ';
1371              
1372 0 0 0       if (defined $var && substr($var,0,1) ne '#') {
1373            
1374 0           my $type = '$';
1375            
1376 0 0         if (get_special_var($var,0)) {
1377 0           set_special_var(undef);
1378             }
1379             else {
1380 0           $type = get_variable_type($var);
1381 0           Delete_variable ($var);
1382             }
1383            
1384 0           $var = $type.$var;
1385            
1386 0           my @tokens = ($var);
1387 0           my @types = App::sh2p::Parser::identify (1, @tokens);
1388            
1389 0           App::sh2p::Parser::convert (@tokens, @types);
1390 0           $ntok++;
1391            
1392             }
1393 0           out ";\n";
1394            
1395 0           return $ntok;
1396             }
1397              
1398             ########################################################
1399              
1400             1;
1401              
1402             __END__