Commit | Line | Data |
---|---|---|
86530b38 AT |
1 | package Psh::Strategy::Perlfunc_heavy; |
2 | ||
3 | =item * C<perlfunc_heavy> | |
4 | ||
5 | Tries to detect perl builtins - this is helpful if you e.g. have | |
6 | a print command on your system. | |
7 | ||
8 | =cut | |
9 | ||
10 | require Psh::Strategy; | |
11 | ||
12 | use vars qw($builtins $packages $expand_arguments); | |
13 | ||
14 | $builtins=0; | |
15 | $packages=1; | |
16 | ||
17 | @Psh::Strategy::Perlfunc_heavy::ISA=('Psh::Strategy'); | |
18 | ||
19 | ||
20 | sub new { Psh::Strategy::new(@_) } | |
21 | ||
22 | sub consumes { | |
23 | return Psh::Strategy::CONSUME_TOKENS; | |
24 | } | |
25 | ||
26 | sub runs_before { | |
27 | return qw(perlscript auto_resume executable); | |
28 | } | |
29 | ||
30 | # | |
31 | # TODO: Is there a better way to detect Perl built-in-functions and | |
32 | # keywords than the following? Surprisingly enough, | |
33 | # defined(&CORE::abs) does not work, i.e., it returns false. | |
34 | # | |
35 | # If a value is anything > 1, then it's the minimum number of | |
36 | # arguments for that function | |
37 | # | |
38 | ||
39 | my %perl_builtins = qw( -X 1 abs 1 accept 1 alarm 1 atan2 1 bind 1 | |
40 | binmode 1 bless 1 caller 1 chdir 1 chmod 3 chomp 1 chop 1 chown 3 chr | |
41 | 1 chroot 1 close 1 closedir 1 connect 3 continue 1 cos 1 crypt 1 | |
42 | dbmclose 1 dbmopen 1 defined 1 delete 1 die 1 do 1 dump 1 each 1 | |
43 | endgrent 1 endhostent 1 endnetent 1 endprotoent 1 endpwent 1 | |
44 | endservent 1 eof 1 eval 1 exec 3 exists 1 exit 1 exp 1 fcntl 1 fileno | |
45 | 1 flock 1 for 1 foreach 1 fork 1 format 1 formline 1 getc 1 getgrent 1 | |
46 | getgrgid 1 getgrnam 1 gethostbyaddr 1 gethostbyname 1 gethostent 1 | |
47 | getlogin 1 getnetbyaddr 1 getnetbyname 1 getnetent 1 getpeername 1 | |
48 | getpgrp 1 getppid 1 getpriority 1 getprotobyname 1 getprotobynumber 1 | |
49 | getprotoent 1 getpwent 1 getpwnam 1 getpwuid 1 getservbyname 1 | |
50 | getservbyport 1 getservent 1 getsockname 1 getsockopt 1 glob 1 gmtime | |
51 | 1 goto 1 grep 3 hex 1 import 1 if 1 int 1 ioctl 1 join 1 keys 1 kill 1 | |
52 | last 1 lc 1 lcfirst 1 length 1 link 1 listen 1 local 1 localtime 1 log | |
53 | 1 lstat 1 m// 1 map 1 mkdir 3 msgctl 1 msgget 1 msgrcv 1 msgsnd 1 my 1 | |
54 | next 1 no 1 oct 1 open 1 opendir 1 ord 1 pack 1 package 1 pipe 1 pop 1 | |
55 | pos 1 print 1 printf 1 prototype 1 push 1 q/STRING/ 1 qq/STRING/ 1 | |
56 | quotemeta 1 qw/STRING/ 1 qx/STRING/ 1 rand 1 read 1 readdir 1 readlink | |
57 | 1 recv 1 redo 1 ref 1 rename 1 require 1 reset 1 return 1 reverse 1 | |
58 | rewinddir 1 rindex 1 rmdir 1 s/// 1 scalar 1 seek 1 seekdir 1 select 1 | |
59 | semctl 1 semget 1 semop 1 send 1 setgrent 1 sethostent 1 setnetent 1 | |
60 | setpgrp 1 setpriority 1 setprotoent 1 setpwent 1 setservent 1 | |
61 | setsockopt 1 shift 1 shmctl 1 shmget 1 shmread 1 shmwrite 1 shutdown 1 | |
62 | sin 1 sleep 1 socket 1 socketpair 1 sort 1 splice 1 split 1 sprintf 1 | |
63 | sqrt 1 srand 1 stat 1 study 1 sub 1 substr 1 symlink 1 syscall 1 | |
64 | sysread 1 system 1 syswrite 1 tell 1 telldir 1 tie 1 time 1 times 1 | |
65 | tr/// 1 truncate 1 uc 1 ucfirst 1 umask 1 undef 1 unless 1 unlink 1 | |
66 | unpack 1 unshift 1 untie 1 until 1 use 1 utime 1 values 1 vec 1 wait 1 | |
67 | waitpid 1 wantarray 1 warn 1 while 1 write 1 y/// 1 ); | |
68 | ||
69 | ||
70 | # | |
71 | # The following hash contains names where the arguments should never | |
72 | # undergo expansion in the sense of | |
73 | # $Psh::perlfunc_expand_arguments. For example, any perl keyword where | |
74 | # an argument is interpreted literally by Perl anyway (such as "use": | |
75 | # use $yourpackage; is a syntax error) should be on this | |
76 | # list. Flow-control keywords should be here too. | |
77 | # | |
78 | # TODO: Is this list complete ? | |
79 | # | |
80 | ||
81 | %perl_builtins_noexpand = qw( continue 1 do 1 for 1 foreach 1 goto 1 if 1 last 1 local 1 my 1 next 1 package 1 redo 1 sub 1 until 1 use 1 while 1); | |
82 | ||
83 | ||
84 | sub applies { | |
85 | my $firstword = @{$_[2]}->[0]; | |
86 | my $copy = ${$_[1]}; | |
87 | ||
88 | my $fnname = $firstword; | |
89 | my $parenthesized = 0; | |
90 | # catch "join(':',@foo)" here as well: | |
91 | if ($firstword =~ m/\(/) { | |
92 | $parenthesized = 1; | |
93 | $fnname = (split('\(', $firstword))[0]; | |
94 | } | |
95 | my $qPerlFunc = 0; | |
96 | if ( $builtins && | |
97 | exists($perl_builtins{$fnname})) { | |
98 | my $needArgs = $perl_builtins{$fnname}; | |
99 | if ($needArgs > 0 | |
100 | and ($parenthesized | |
101 | or scalar(@{$_[2]}) >= $needArgs)) { | |
102 | $qPerlFunc = 1; | |
103 | } | |
104 | } elsif( $packages && | |
105 | $fnname =~ /^([a-zA-Z0-9_]+)\:\:([a-zA-Z0-9_:]+)$/) { | |
106 | if( $1 eq 'CORE') { | |
107 | my $needArgs = $perl_builtins{$2}; | |
108 | if ($needArgs > 0 | |
109 | and ($parenthesized or scalar(@{$_[2]}) >= $needArgs)) { | |
110 | $qPerlFunc = 1; | |
111 | } | |
112 | } else { | |
113 | $qPerlFunc = (Psh::PerlEval::protected_eval("defined(&{'$fnname'})"))[0]; | |
114 | } | |
115 | } elsif( $fnname =~ /^[a-zA-Z0-9_]+$/) { | |
116 | $qPerlFunc = (Psh::PerlEval::protected_eval("defined(&{'$fnname'})"))[0]; | |
117 | } | |
118 | if ( $qPerlFunc ) { | |
119 | ||
120 | # | |
121 | # remove braces containing no whitespace | |
122 | # and at least one comma in checking, | |
123 | # since they might be for brace expansion | |
124 | # | |
125 | ||
126 | $copy =~ s/{\S*,\S*}//g; | |
127 | ||
128 | if (!$expand_arguments | |
129 | or exists($perl_builtins_noexpand{$fnname}) or | |
130 | ($Psh::current_options and | |
131 | $Psh::current_options->{noexpand}) or | |
132 | or $fnname ne $firstword | |
133 | or $copy =~ m/[(){},]/) { | |
134 | return ${$_[1]}; | |
135 | } else { # no parens, braces, or commas, so do expansion | |
136 | my $ampersand = ''; | |
137 | my $lastword = pop @{$_[2]}; | |
138 | ||
139 | if ($lastword eq '&') { $ampersand = '&'; } | |
140 | else { push @{$_[2]}, $lastword; } | |
141 | ||
142 | shift @{$_[2]}; # OK to destroy command line since we matched | |
143 | ||
144 | # | |
145 | # No need to do variable expansion, because the whole thing | |
146 | # will be evaluated later. | |
147 | # | |
148 | my @args; | |
149 | if (!$Psh::current_options and | |
150 | !$Psh::current_options->{noglob}) { | |
151 | @args = Psh::Parser::glob_expansion($_[2]); | |
152 | ||
153 | # | |
154 | # But we will quote barewords, expressions involving | |
155 | # $variables, filenames, and the like: | |
156 | # | |
157 | ||
158 | foreach (@args) { | |
159 | if (&Psh::Parser::needs_double_quotes($_)) { | |
160 | $_ = "\"$_\""; | |
161 | } | |
162 | } | |
163 | } else { | |
164 | @args= @{$_[2]}; | |
165 | } | |
166 | ||
167 | my $possible_proto = ''; | |
168 | ||
169 | if (defined($perl_builtins{$fnname})) { | |
170 | $possible_proto = prototype("CORE::$fnname"); | |
171 | } else { | |
172 | $possible_proto = prototype($fnname); | |
173 | } | |
174 | ||
175 | # | |
176 | # TODO: Can we use the prototype more fully here? | |
177 | # | |
178 | my $command = ''; | |
179 | ||
180 | if (defined($possible_proto) and $possible_proto ne '@') { | |
181 | # | |
182 | # if it's not just a list operator, better not put in | |
183 | # parens, because they could change the semantics | |
184 | # | |
185 | $command = "$fnname " . join(",",@args); | |
186 | } else { | |
187 | # | |
188 | # Otherwise put in the parens to avoid any ambiguity: we | |
189 | # want to pass the given list of args to the function. It | |
190 | # would be better in perlfunc eval to get a reference to | |
191 | # the function and simply pass the args to it, but I | |
192 | # couldn't find any way to make that work with perl | |
193 | # builtins. You can't take a reference to CODE::sort, for | |
194 | # example. | |
195 | # | |
196 | $command .= "$fnname(" . join(",",@args) . ')'; | |
197 | } | |
198 | ||
199 | return $command . $ampersand; } | |
200 | } | |
201 | ||
202 | return ''; | |
203 | } | |
204 | ||
205 | sub execute { | |
206 | $_[4]=undef; | |
207 | return Psh::Strategy::Eval(@_); | |
208 | } | |
209 | ||
210 | 1; |