line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Zoidberg; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
our $VERSION = '0.981'; |
4
|
|
|
|
|
|
|
our $LONG_VERSION = "Zoidberg $VERSION |
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
Copyright (c) 2011 Jaap G Karssenberg and Joel Berger. All rights reserved. |
7
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or |
8
|
|
|
|
|
|
|
modify it under the same terms as Perl. |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful, |
11
|
|
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
12
|
|
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
http://github.com/jberger/Zoidberg"; |
15
|
|
|
|
|
|
|
|
16
|
29
|
|
|
29
|
|
3145
|
use strict; |
|
29
|
|
|
|
|
146
|
|
|
29
|
|
|
|
|
1791
|
|
17
|
23
|
|
|
23
|
|
206
|
use vars qw/$AUTOLOAD/; |
|
23
|
|
|
|
|
103
|
|
|
23
|
|
|
|
|
2154
|
|
18
|
|
|
|
|
|
|
#use warnings; |
19
|
|
|
|
|
|
|
#no warnings 'uninitialized'; # yes, undefined == '' == 0 |
20
|
19
|
|
|
19
|
|
121
|
no warnings; # I am leaving this, because I don't totally understand how warnings propagate through -- Joel |
|
19
|
|
|
|
|
46
|
|
|
19
|
|
|
|
|
1853
|
|
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
require Cwd; |
23
|
|
|
|
|
|
|
require File::Glob; |
24
|
19
|
|
|
19
|
|
8834
|
use File::ShareDir qw/dist_dir/; |
|
19
|
|
|
|
|
31554
|
|
|
19
|
|
|
|
|
2013
|
|
25
|
19
|
|
|
19
|
|
20991
|
use File::Copy qw/copy/; |
|
19
|
|
|
|
|
65146
|
|
|
19
|
|
|
|
|
1387
|
|
26
|
19
|
|
|
19
|
|
18382
|
use File::Spec::Functions qw/catfile/; |
|
19
|
|
|
|
|
17886
|
|
|
19
|
|
|
|
|
1755
|
|
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
require Zoidberg::Contractor; |
29
|
|
|
|
|
|
|
require Zoidberg::Shell; |
30
|
|
|
|
|
|
|
require Zoidberg::PluginHash; |
31
|
|
|
|
|
|
|
require Zoidberg::StringParser; |
32
|
|
|
|
|
|
|
|
33
|
19
|
|
|
19
|
|
12232
|
use Zoidberg::DispatchTable; |
|
19
|
|
|
|
|
58
|
|
|
19
|
|
|
|
|
188
|
|
34
|
|
|
|
|
|
|
use Zoidberg::Utils |
35
|
19
|
|
|
19
|
|
954
|
qw/:error :output :fs read_data_file merge_hash regex_glob getopt/; |
|
19
|
|
|
|
|
24
|
|
|
19
|
|
|
|
|
161
|
|
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
our @ISA = qw/Zoidberg::Contractor Zoidberg::Shell/; |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
=head1 NAME |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
Zoidberg - A modular perl shell |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
=head1 SYNOPSIS |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
You should use the B system command to start the Zoidberg shell. |
46
|
|
|
|
|
|
|
To embed the Zoidberg shell in another perl program use the L |
47
|
|
|
|
|
|
|
module. |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
=head1 DESCRIPTION |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
I |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
This module contains the core dispatch and event logic of the Zoidberg shell. |
54
|
|
|
|
|
|
|
Also it is used as a 'main object' so other objects can find each other here; |
55
|
|
|
|
|
|
|
all other objects are nested below this object. |
56
|
|
|
|
|
|
|
Also it contains some parser code. |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
This object inherits from both L |
59
|
|
|
|
|
|
|
and L. |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
=head1 METHODS |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
=over 4 |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
=cut |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
our %OBJECTS; # used to store refs to ALL Zoidberg objects in a process |
68
|
|
|
|
|
|
|
our $CURRENT; # current Zoidberg object |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
our $_base_dir; # relative path for some settings |
71
|
|
|
|
|
|
|
our @_parser_settings = qw/ |
72
|
|
|
|
|
|
|
split_script split_words |
73
|
|
|
|
|
|
|
parse_env parse_fd parse_aliases parse_def_contexts |
74
|
|
|
|
|
|
|
expand_comm expand_param expand_path |
75
|
|
|
|
|
|
|
/; |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
our %_settings = ( # default settings |
78
|
|
|
|
|
|
|
output => { error => 'red', debug => 'yellow' }, |
79
|
|
|
|
|
|
|
clothes => { |
80
|
|
|
|
|
|
|
keys => [qw/settings commands aliases events error/], |
81
|
|
|
|
|
|
|
subs => [qw/shell alias unalias setting set source mode plug unplug/], |
82
|
|
|
|
|
|
|
}, |
83
|
|
|
|
|
|
|
perl => { |
84
|
|
|
|
|
|
|
keywords => [qw/ |
85
|
|
|
|
|
|
|
if unless for foreach while until |
86
|
|
|
|
|
|
|
print |
87
|
|
|
|
|
|
|
push shift unshift pop splice |
88
|
|
|
|
|
|
|
delete |
89
|
|
|
|
|
|
|
do eval |
90
|
|
|
|
|
|
|
tie untie |
91
|
|
|
|
|
|
|
my our use no sub package |
92
|
|
|
|
|
|
|
import bless |
93
|
|
|
|
|
|
|
/], |
94
|
|
|
|
|
|
|
namespace => 'Zoidberg::Eval', |
95
|
|
|
|
|
|
|
opts => 'Z', |
96
|
|
|
|
|
|
|
}, |
97
|
|
|
|
|
|
|
hide_private_method => 1, |
98
|
|
|
|
|
|
|
hide_hidden_files => 1, |
99
|
|
|
|
|
|
|
naked_zoid => 0, |
100
|
|
|
|
|
|
|
( map {($_ => 1)} @_parser_settings ), |
101
|
|
|
|
|
|
|
##Insert defaults here## |
102
|
|
|
|
|
|
|
rcfiles => [ |
103
|
|
|
|
|
|
|
( $ENV{PAR_TEMP} ? "$ENV{PAR_TEMP}/inc/etc/zoidrc" : '/etc/zoidrc' ), |
104
|
|
|
|
|
|
|
"$ENV{HOME}/.zoidrc", |
105
|
|
|
|
|
|
|
"$ENV{HOME}/.zoid/zoidrc", |
106
|
|
|
|
|
|
|
], |
107
|
|
|
|
|
|
|
data_dirs => [ |
108
|
|
|
|
|
|
|
"$ENV{HOME}/.zoid", |
109
|
|
|
|
|
|
|
( $ENV{PAR_TEMP} ? "$ENV{PAR_TEMP}/inc/share" : ( qw# /usr/local/share/zoid /usr/share/zoid # ) ), |
110
|
|
|
|
|
|
|
dist_dir('Zoidberg'), |
111
|
|
|
|
|
|
|
], |
112
|
|
|
|
|
|
|
); |
113
|
|
|
|
|
|
|
our %_grammars = ( # default grammar |
114
|
|
|
|
|
|
|
_base_gram => { |
115
|
|
|
|
|
|
|
esc => '\\', |
116
|
|
|
|
|
|
|
quotes => { |
117
|
|
|
|
|
|
|
'"' => '"', |
118
|
|
|
|
|
|
|
"'" => "'", |
119
|
|
|
|
|
|
|
'`' => '`', |
120
|
|
|
|
|
|
|
}, |
121
|
|
|
|
|
|
|
nests => { |
122
|
|
|
|
|
|
|
'{' => '}', |
123
|
|
|
|
|
|
|
'(' => ')', |
124
|
|
|
|
|
|
|
}, |
125
|
|
|
|
|
|
|
}, |
126
|
|
|
|
|
|
|
script_gram => { |
127
|
|
|
|
|
|
|
tokens => [ |
128
|
|
|
|
|
|
|
[ ';', 'EOS' ], |
129
|
|
|
|
|
|
|
[ "\n", 'EOL' ], |
130
|
|
|
|
|
|
|
[ '&&', 'AND' ], |
131
|
|
|
|
|
|
|
[ '||', 'OR' ], |
132
|
|
|
|
|
|
|
[ '|', '_CUT' ], |
133
|
|
|
|
|
|
|
[ qr/(?])&/ , 'EOS_BG' ], |
134
|
|
|
|
|
|
|
[ '==>', 'XFW' ], |
135
|
|
|
|
|
|
|
[ '<==', 'XBW' ], |
136
|
|
|
|
|
|
|
], |
137
|
|
|
|
|
|
|
}, |
138
|
|
|
|
|
|
|
word_gram => qr/\s/, |
139
|
|
|
|
|
|
|
redirect_gram => { |
140
|
|
|
|
|
|
|
s_esc => qr/[\\\-\=]/, |
141
|
|
|
|
|
|
|
tokens => [ |
142
|
|
|
|
|
|
|
[ qr/<\S+>/, '_SELF' ], |
143
|
|
|
|
|
|
|
[ '>&', 'DUP_OUT' ], |
144
|
|
|
|
|
|
|
[ '>|', 'CLOB_OUT' ], |
145
|
|
|
|
|
|
|
[ '>!', 'CLOB_OUT' ], |
146
|
|
|
|
|
|
|
[ '>>', 'APP_OUT' ], |
147
|
|
|
|
|
|
|
[ '<&', 'DUP_IN' ], |
148
|
|
|
|
|
|
|
[ '<<', 'ERROR' ], |
149
|
|
|
|
|
|
|
[ '<>', 'RW' ], |
150
|
|
|
|
|
|
|
[ '>', 'OUT' ], |
151
|
|
|
|
|
|
|
[ '<', 'IN' ], |
152
|
|
|
|
|
|
|
], |
153
|
|
|
|
|
|
|
}, |
154
|
|
|
|
|
|
|
dezoid_gram => { |
155
|
|
|
|
|
|
|
tokens => [ |
156
|
|
|
|
|
|
|
[ qr/->/, 'ARR' ], # ARRow |
157
|
|
|
|
|
|
|
[ qr/[\$\@][A-Za-z_][\w\-]*(?
|
158
|
|
|
|
|
|
|
], |
159
|
|
|
|
|
|
|
quotes => { "'" => "'" }, # interpolate also between '"' |
160
|
|
|
|
|
|
|
nests => {}, |
161
|
|
|
|
|
|
|
}, |
162
|
|
|
|
|
|
|
expand_comm_gram => { |
163
|
|
|
|
|
|
|
tokens => { |
164
|
|
|
|
|
|
|
'$(' => { |
165
|
|
|
|
|
|
|
token => 'COMM', |
166
|
|
|
|
|
|
|
tokens => {')' => '_CUT'}, |
167
|
|
|
|
|
|
|
}, |
168
|
|
|
|
|
|
|
'`' => { |
169
|
|
|
|
|
|
|
token => 'COMM', |
170
|
|
|
|
|
|
|
tokens => {'`' => '_CUT'}, |
171
|
|
|
|
|
|
|
} |
172
|
|
|
|
|
|
|
}, |
173
|
|
|
|
|
|
|
}, |
174
|
|
|
|
|
|
|
# expand_braces_gram => { |
175
|
|
|
|
|
|
|
# tokens => { |
176
|
|
|
|
|
|
|
# '{' => { |
177
|
|
|
|
|
|
|
# token => 'BRACE', |
178
|
|
|
|
|
|
|
# tokens => { '}' => '_CUT' }, |
179
|
|
|
|
|
|
|
# }, |
180
|
|
|
|
|
|
|
# }, |
181
|
|
|
|
|
|
|
# }, |
182
|
|
|
|
|
|
|
); |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
=item C |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
Initialize secondary objects and sets config. |
187
|
|
|
|
|
|
|
C<%attr> contains non-default attributes and is used to set runtime settings. |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
You probably don't want to use this to construct a new Zoidberg shell object, |
190
|
|
|
|
|
|
|
better use L. |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
=cut |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
sub new { # FIXME maybe rename this to init ? |
195
|
16
|
|
|
16
|
1
|
47
|
my $class = shift; |
196
|
16
|
50
|
|
|
|
113
|
my $self = @_ ? { @_ } : {}; |
197
|
16
|
|
100
|
|
|
448
|
$$self{$_} ||= {} for qw/settings commands aliases events objects/; |
198
|
16
|
|
50
|
|
|
143
|
$$self{no_words} ||= []; |
199
|
16
|
|
|
|
|
32
|
push @{$$self{no_words}}, qw/PERL SUBZ/; # parser stuff |
|
16
|
|
|
|
|
64
|
|
200
|
16
|
|
|
|
|
33
|
$$self{round_up}++; |
201
|
16
|
|
50
|
|
|
143
|
$$self{topic} ||= ''; |
202
|
|
|
|
|
|
|
|
203
|
16
|
|
|
|
|
32
|
bless($self, $class); |
204
|
|
|
|
|
|
|
|
205
|
16
|
|
|
|
|
208
|
$OBJECTS{"$self"} = $self; |
206
|
16
|
50
|
|
|
|
65
|
$CURRENT = $self unless ref( $CURRENT ) eq $class; # could be autovivicated |
207
|
16
|
|
|
|
|
48
|
$self->{shell} = $self; # for Contractor |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
## settings |
210
|
16
|
|
|
|
|
128
|
$$self{_settings} = merge_hash(\%_settings, $$self{settings}); |
211
|
16
|
50
|
|
|
|
94
|
$$self{_settings}{data_dirs} |
212
|
|
|
|
|
|
|
|| error 'You should at least set a config value for \'data_dirs\''; |
213
|
|
|
|
|
|
|
|
214
|
16
|
|
|
|
|
34
|
my %set; |
215
|
16
|
|
|
|
|
113
|
tie %set, 'Zoidberg::SettingsHash', $$self{_settings}, $self; |
216
|
16
|
|
|
|
|
34
|
$$self{settings} = \%set; |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
## commands |
219
|
16
|
|
|
|
|
291
|
$$self{commands} = Zoidberg::DispatchTable->new( |
220
|
|
|
|
|
|
|
$self, { |
221
|
|
|
|
|
|
|
exit => '->exit', |
222
|
|
|
|
|
|
|
plug => '->plug', |
223
|
|
|
|
|
|
|
unplug => '->unplug', |
224
|
|
|
|
|
|
|
mode => '->mode', |
225
|
|
|
|
|
|
|
readline => "->stdin('zoid-$VERSION\$ ')", |
226
|
|
|
|
|
|
|
readmore => "->stdin('> ')", |
227
|
|
|
|
|
|
|
builtin => '->builtin', |
228
|
|
|
|
|
|
|
command => '->command', |
229
|
16
|
|
|
|
|
80
|
( %{$$self{commands}} ) |
230
|
|
|
|
|
|
|
} |
231
|
|
|
|
|
|
|
); |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
## events |
234
|
16
|
|
|
|
|
129
|
$$self{events} = Zoidberg::DispatchTable->new($self, $$self{events}); |
235
|
|
|
|
|
|
|
$$self{events}{envupdate} = sub { |
236
|
295
|
|
|
295
|
|
3364499
|
my $pwd = Cwd::cwd(); |
237
|
295
|
50
|
|
|
|
13895
|
return if $pwd eq $ENV{PWD}; |
238
|
0
|
|
|
|
|
0
|
@ENV{qw/OLDPWD PWD/} = ($ENV{PWD}, $pwd); |
239
|
0
|
|
|
|
|
0
|
$self->broadcast('newpwd'); |
240
|
0
|
0
|
|
|
|
0
|
$self->builtin('log', $pwd, 'pwd') if $$self{settings}{interactive}; |
241
|
16
|
|
|
|
|
194
|
}; |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
## parser |
244
|
16
|
|
|
|
|
113
|
$$self{parser} = Zoidberg::DispatchTable->new($self, $$self{parser}); |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
## stringparser |
247
|
16
|
|
50
|
|
|
236
|
$$self{grammars} ||= \%_grammars; |
248
|
16
|
|
|
|
|
191
|
$$self{stringparser} = Zoidberg::StringParser->new( |
249
|
|
|
|
|
|
|
$$self{grammars}{_base_gram}, $$self{grammars}, |
250
|
|
|
|
|
|
|
{allow_broken => 1, no_esc_rm => 1} ); |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
## initialize contractor |
253
|
16
|
|
|
|
|
160
|
$self->shell_init; |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
## plugins |
256
|
16
|
|
|
|
|
32
|
my %objects; |
257
|
16
|
|
|
|
|
159
|
tie %objects, 'Zoidberg::PluginHash', $self; |
258
|
16
|
|
|
|
|
67
|
$self->{objects} = \%objects; |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
# autoloading of contexts after plugin loading |
261
|
|
|
|
|
|
|
# because of bootstrapping issues |
262
|
|
|
|
|
|
|
$$self{parser}{_AUTOLOAD} = sub { |
263
|
32
|
|
|
32
|
|
62
|
my $c = shift; |
264
|
32
|
|
|
|
|
339
|
debug "trying to autoload $c"; |
265
|
32
|
50
|
|
|
|
422
|
if ($c =~ /::/) { |
266
|
0
|
|
|
|
|
0
|
$c =~ m#(.*?)(::|->)$#; |
267
|
0
|
|
|
|
|
0
|
my ($class, $type) = ($1, $2); |
268
|
0
|
|
|
|
|
0
|
debug "loading class $class"; |
269
|
0
|
|
|
|
|
0
|
$$self{parser}{$c} = {}; |
270
|
|
|
|
|
|
|
$$self{parser}{$c}{handler} = sub { |
271
|
0
|
|
|
|
|
0
|
my (undef, $sub, @args) = @{ shift() }; |
|
0
|
|
|
|
|
0
|
|
272
|
0
|
0
|
|
|
|
0
|
unshift @args, $class if $type eq '->'; |
273
|
19
|
|
|
19
|
|
32856
|
no strict 'refs'; |
|
19
|
|
|
|
|
54
|
|
|
19
|
|
|
|
|
2193
|
|
274
|
0
|
|
|
|
|
0
|
$sub = $class.'::'.$sub; |
275
|
0
|
|
|
|
|
0
|
$sub->(@args); |
276
|
0
|
|
|
|
|
0
|
}; |
277
|
|
|
|
|
|
|
$$self{parser}{$c}{intel} = sub { |
278
|
0
|
|
|
|
|
0
|
my $block = shift; |
279
|
0
|
0
|
|
|
|
0
|
return undef if @$block > 2; |
280
|
19
|
|
|
19
|
|
110
|
no strict 'refs'; |
|
19
|
|
|
|
|
38
|
|
|
19
|
|
|
|
|
266089
|
|
281
|
0
|
|
|
|
|
0
|
my @p = grep m/^$$block[1]/, |
282
|
0
|
|
|
|
|
0
|
grep defined *{$class.'::'.$_}{CODE}, keys %{$class.'::'}; |
|
0
|
|
|
|
|
0
|
|
283
|
0
|
0
|
|
|
|
0
|
push @p, grep m/^$$block[1]/, keys %{$$self{aliases}{'mode_'.$c}} |
|
0
|
|
|
|
|
0
|
|
284
|
|
|
|
|
|
|
if exists $$self{aliases}{'mode_'.$c}; |
285
|
0
|
|
|
|
|
0
|
$$block[0]{poss} = \@p; |
286
|
0
|
|
|
|
|
0
|
return $block; |
287
|
0
|
|
|
|
|
0
|
}; |
288
|
|
|
|
|
|
|
} |
289
|
32
|
|
|
|
|
365
|
else { eval { $self->plug($c) } } |
|
32
|
|
|
|
|
496
|
|
290
|
32
|
50
|
|
|
|
230
|
debug 'did you know 5.6.2 sucks ?' if $] < 5.008; # don't ask ... i suspect another vivication bug |
291
|
32
|
50
|
|
|
|
175
|
return exists($$self{parser}{$c}) ? $$self{parser}{$c} : undef ; |
292
|
16
|
|
|
|
|
267
|
}; |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
## let's load the rcfiles |
295
|
|
|
|
|
|
|
$$self{events}{loadrc} = sub { |
296
|
|
|
|
|
|
|
#check for existant rcfiles in the known locations |
297
|
16
|
|
|
16
|
|
33
|
my @rcfiles = grep {-f $_} @{$$self{_settings}{rcfiles}}; |
|
16
|
|
|
|
|
435
|
|
|
16
|
|
|
|
|
97
|
|
298
|
|
|
|
|
|
|
#if no zoidrc file is found, create one from the template in the dist_dir |
299
|
16
|
50
|
|
|
|
81
|
unless (@rcfiles) { |
300
|
0
|
|
|
|
|
0
|
my $rc_template = catfile(dist_dir('Zoidberg'), "zoidrc.example"); |
301
|
0
|
|
|
|
|
0
|
my $new_rc = catfile($ENV{HOME}, ".zoidrc"); |
302
|
0
|
|
|
|
|
0
|
warn "### No zoidrc file was found. A new zoidrc file will be created for you at $new_rc. If you really intend to use without a zoidrc file, simply create an empty zoidrc file in that location or at /etc/zoidrc\n\n"; |
303
|
0
|
0
|
|
|
|
0
|
if( copy( $rc_template, $new_rc) ) { |
304
|
0
|
|
|
|
|
0
|
push @rcfiles, $new_rc; |
305
|
|
|
|
|
|
|
} else { |
306
|
0
|
|
|
|
|
0
|
warn "### Could not copy $rc_template to $new_rc\n\n"; |
307
|
|
|
|
|
|
|
} |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
} |
310
|
16
|
|
|
|
|
229
|
$self->source(@rcfiles); |
311
|
16
|
|
|
|
|
162
|
}; |
312
|
16
|
|
|
|
|
83
|
$self->broadcast('loadrc'); |
313
|
|
|
|
|
|
|
|
314
|
16
|
|
|
|
|
124
|
$self->broadcast('envupdate'); # set/log pwd and maybe init other env stuff |
315
|
|
|
|
|
|
|
|
316
|
16
|
|
|
|
|
1374
|
return $self; |
317
|
|
|
|
|
|
|
} |
318
|
|
|
|
|
|
|
|
319
|
19
|
50
|
|
19
|
|
2628
|
sub import { bug "You should use Zoidberg::Shell to import from" if @_ > 1 } |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
# hooks overloading Contracter # FIXME these are not used !? |
322
|
|
|
|
|
|
|
*pre_job = \&parse_block; |
323
|
|
|
|
|
|
|
*post_job = \&broadcast; |
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
# ############ # |
326
|
|
|
|
|
|
|
# Main routine # |
327
|
|
|
|
|
|
|
# ############ # |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
=item C |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
Spans interactive shell reading from a secondary ReadLine object or from STDIN. |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
To quit this loop the routine C of this package should be called. |
334
|
|
|
|
|
|
|
Most common way to do this is pressing ^D. |
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
=cut |
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
sub main_loop { |
339
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
340
|
|
|
|
|
|
|
|
341
|
0
|
|
|
|
|
0
|
$$self{_continue} = 1; |
342
|
0
|
|
|
|
|
0
|
while ($$self{_continue}) { |
343
|
0
|
|
|
|
|
0
|
$self->reap_jobs(); |
344
|
0
|
|
|
|
|
0
|
$self->broadcast('prompt'); |
345
|
0
|
|
|
|
|
0
|
my ($cmd) = $self->builtin('readline'); |
346
|
0
|
0
|
|
|
|
0
|
if ($@) { |
347
|
0
|
|
|
|
|
0
|
complain "\nInput routine died. (You can interrupt zoid NOW)"; |
348
|
0
|
|
|
|
|
0
|
local $SIG{INT} = 'DEFAULT'; |
349
|
0
|
|
|
|
|
0
|
sleep 1; # infinite loop protection |
350
|
|
|
|
|
|
|
} |
351
|
|
|
|
|
|
|
else { |
352
|
0
|
|
|
|
|
0
|
$self->reap_jobs(); |
353
|
|
|
|
|
|
|
|
354
|
0
|
0
|
0
|
|
|
0
|
unless (defined $cmd || $$self{_settings}{ignoreeof}) { |
355
|
0
|
|
|
|
|
0
|
debug 'readline returned undef .. exiting'; |
356
|
0
|
|
|
|
|
0
|
$self->exit(); |
357
|
|
|
|
|
|
|
} |
358
|
0
|
|
|
|
|
0
|
else { $$self{_warned_bout_jobs} = 0 } |
359
|
|
|
|
|
|
|
|
360
|
0
|
0
|
|
|
|
0
|
last unless $$self{_continue}; |
361
|
|
|
|
|
|
|
|
362
|
0
|
0
|
|
|
|
0
|
$self->shell_string({interactive => 1}, $cmd) if length $cmd; |
363
|
|
|
|
|
|
|
} |
364
|
|
|
|
|
|
|
} |
365
|
|
|
|
|
|
|
} |
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
# ############ # |
368
|
|
|
|
|
|
|
# Parser stuff # |
369
|
|
|
|
|
|
|
# ############ # |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
sub shell_string { |
372
|
110
|
|
|
110
|
0
|
567
|
my ($self, $meta, $string) = @_; |
373
|
110
|
50
|
|
|
|
737
|
($meta, $string) = ({}, $meta) unless ref($meta) eq 'HASH'; |
374
|
110
|
|
|
|
|
568
|
local $CURRENT = $self; |
375
|
|
|
|
|
|
|
|
376
|
110
|
50
|
|
|
|
33037
|
PARSE_STRING: |
377
|
|
|
|
|
|
|
my @list = $$self{_settings}{split_script} |
378
|
|
|
|
|
|
|
? ($$self{stringparser}->split('script_gram', $string)) : ($string) ; |
379
|
110
|
50
|
33
|
|
|
1896
|
my $b = $$self{stringparser}{broken} ? 1 |
|
|
50
|
|
|
|
|
|
380
|
|
|
|
|
|
|
: (@list and ! ref $list[-1] and $list[-1] !~ /^EO/) ? 2 : 0 ; |
381
|
110
|
50
|
33
|
|
|
704
|
if ($b and ! $$self{_settings}{interactive}) { # FIXME should be STDIN on non interactive |
|
|
50
|
|
|
|
|
|
382
|
0
|
0
|
|
|
|
0
|
error qq#Operator at end of input# if $b == 2; |
383
|
0
|
|
|
|
|
0
|
my $gram = $$self{stringparser}{broken}[1]; |
384
|
0
|
|
|
|
|
0
|
error qq#Unmatched $$gram{_open}[1] at end of input: $$gram{_open}[0]#; |
385
|
|
|
|
|
|
|
} |
386
|
|
|
|
|
|
|
elsif ($b) { |
387
|
0
|
|
|
|
|
0
|
($string) = $self->builtin('readmore'); |
388
|
0
|
|
|
|
|
0
|
debug "\n\ngot $string\n\n\n"; |
389
|
0
|
0
|
|
|
|
0
|
if ($@) { |
390
|
0
|
|
|
|
|
0
|
complain "\nInput routine died.\n$@"; |
391
|
0
|
|
|
|
|
0
|
return; |
392
|
|
|
|
|
|
|
} |
393
|
0
|
|
|
|
|
0
|
goto PARSE_STRING; |
394
|
|
|
|
|
|
|
} |
395
|
|
|
|
|
|
|
|
396
|
110
|
50
|
|
|
|
400
|
if ($$meta{interactive}) { |
397
|
0
|
|
|
|
|
0
|
$self->broadcast('cmd', $string); |
398
|
0
|
|
|
|
|
0
|
$$self{previous_cmd} = $string; |
399
|
0
|
0
|
|
|
|
0
|
print STDERR $string if $$self{_settings}{verbose}; # posix spec |
400
|
|
|
|
|
|
|
} |
401
|
|
|
|
|
|
|
|
402
|
110
|
|
|
|
|
866
|
debug 'block list: ', \@list; |
403
|
110
|
|
33
|
|
|
613
|
$$self{fg_job} ||= $self; |
404
|
110
|
|
|
|
|
1050
|
$$self{fg_job}->shell_list($meta, @list); # calling a contractor |
405
|
|
|
|
|
|
|
} |
406
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
sub prepare_block { |
408
|
225
|
|
|
225
|
0
|
682
|
my ($self, $block) = @_; |
409
|
225
|
|
|
|
|
1043
|
my $t = ref $block; |
410
|
225
|
100
|
|
|
|
1313
|
if ($t eq 'SCALAR') { $block = [{env => {pwd => $ENV{PWD}}}, $$block] } |
|
141
|
50
|
|
|
|
1294
|
|
411
|
|
|
|
|
|
|
elsif ($t eq 'ARRAY') { |
412
|
84
|
100
|
66
|
|
|
549
|
if (ref($$block[0]) eq 'HASH') { $$block[0]{env}{pwd} ||= $ENV{PWD} } |
|
26
|
|
|
|
|
313
|
|
413
|
58
|
|
|
|
|
988
|
else { unshift @$block, {env => {pwd => $ENV{PWD}}} } |
414
|
|
|
|
|
|
|
} |
415
|
|
|
|
|
|
|
else { |
416
|
0
|
0
|
|
|
|
0
|
bug $t ? "prepare_block can't handle type $t" |
417
|
|
|
|
|
|
|
: "block ain't a ref !??" ; |
418
|
|
|
|
|
|
|
} |
419
|
225
|
|
|
|
|
1151
|
return $block; |
420
|
|
|
|
|
|
|
} |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
sub parse_block { # call as late as possible before execution |
423
|
|
|
|
|
|
|
# FIXME can this be more optimised for builtin() call ? |
424
|
200
|
|
|
200
|
0
|
654
|
my $self = shift; |
425
|
200
|
50
|
|
|
|
873
|
my $meta = (ref($_[0]) eq 'HASH') ? shift : {}; |
426
|
200
|
|
|
|
|
443
|
my $block = shift; |
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
# check settings |
429
|
200
|
|
|
|
|
989
|
$$meta{$_} = $$self{_settings}{$_} for grep {! defined $$meta{$_}} @_parser_settings; |
|
1800
|
|
|
|
|
10620
|
|
430
|
|
|
|
|
|
|
# FIXME mode settings, uc || lc ? |
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
# decipher block |
433
|
334
|
|
|
|
|
635
|
PARSE_BLOCK: |
434
|
|
|
|
|
|
|
my @words; |
435
|
334
|
|
|
|
|
928
|
my $t = ref $block; |
436
|
334
|
100
|
66
|
|
|
2844
|
if (!$t or $t eq 'SCALAR') { |
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
437
|
134
|
50
|
|
|
|
159
|
($meta, @words) = @{ $self->parse_env([$meta, $t ? $$block : $block]) }; |
|
134
|
|
|
|
|
948
|
|
438
|
134
|
50
|
0
|
|
|
1727
|
++$$meta{no_mode} and (length $words[0] or shift @words) if @words && $words[0] =~ s/^\!\s*//; |
|
|
|
0
|
|
|
|
|
|
|
|
66
|
|
|
|
|
439
|
|
|
|
|
|
|
} |
440
|
|
|
|
|
|
|
elsif ($t eq 'ARRAY') { |
441
|
200
|
50
|
|
|
|
1378
|
$meta = { %$meta, %{shift @$block} } if ref($$block[0]) eq 'HASH'; |
|
200
|
|
|
|
|
1898
|
|
442
|
200
|
100
|
66
|
|
|
2317
|
unless (@$block > 1 or $$meta{plain_words}) { |
443
|
134
|
|
|
|
|
611
|
debug "block aint a word block"; |
444
|
134
|
|
|
|
|
405
|
$block = shift @$block; |
445
|
134
|
|
|
|
|
2305
|
goto PARSE_BLOCK; |
446
|
|
|
|
|
|
|
} |
447
|
66
|
|
|
|
|
847
|
@words = @$block; |
448
|
66
|
50
|
0
|
|
|
1501
|
++$$meta{no_mode} and shift @words if @words && $words[0] eq '!'; |
|
|
|
33
|
|
|
|
|
449
|
|
|
|
|
|
|
} |
450
|
0
|
|
|
|
|
0
|
elsif ($t eq 'CODE') { return [{context => 'PERL', %$meta}, $block] } |
451
|
0
|
|
|
|
|
0
|
else { bug "parse tree contains $t reference" } |
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
# do aliases |
454
|
200
|
|
|
|
|
1712
|
debug 'meta: ', $meta; # , 'words: ', [[@words]]; |
455
|
200
|
50
|
66
|
|
|
3807
|
if (@words and ! $$meta{pretend} and $$meta{parse_aliases}) { |
|
|
|
66
|
|
|
|
|
456
|
195
|
|
|
|
|
1689
|
my @blocks = $self->parse_aliases($meta, @words); |
457
|
195
|
50
|
|
|
|
1711
|
if (@blocks > 1) { return @blocks } # probably an alias contained pipe or logic operator |
|
0
|
50
|
|
|
|
0
|
|
458
|
0
|
|
|
|
|
0
|
elsif (! @blocks) { return undef } |
459
|
|
|
|
|
|
|
else { |
460
|
195
|
|
|
|
|
355
|
($meta, @words) = @{ shift(@blocks) }; |
|
195
|
|
|
|
|
2207
|
|
461
|
|
|
|
|
|
|
} |
462
|
|
|
|
|
|
|
} |
463
|
|
|
|
|
|
|
# post alias stuff |
464
|
200
|
|
|
|
|
1586
|
$$meta{zoidcmd} = join ' ', @words; # unix haters guide pdf page 60 |
465
|
|
|
|
|
|
|
#FIXME how does this hadle escaped whitespacec ? |
466
|
200
|
50
|
|
|
|
849
|
$$meta{no_mode}++ if $words[0] eq 'mode'; # explicitly after alias expansion .. ! is before alias expansion |
467
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
# check custom filters |
469
|
200
|
|
|
|
|
3363
|
for my $sub ($$self{parser}->stack('filter')) { |
470
|
0
|
|
|
|
|
0
|
my $r = $sub->([$meta, @words]); |
471
|
0
|
0
|
|
|
|
0
|
($meta, @words) = @$r if $r; # skip on undef |
472
|
|
|
|
|
|
|
} |
473
|
200
|
100
|
66
|
|
|
1841
|
return undef unless $$meta{context} or @words; |
474
|
|
|
|
|
|
|
|
475
|
195
|
50
|
|
|
|
1302
|
$$meta{context} = 'SUBZ' if $$meta{zoidcmd} =~ /^\s*\(.*\)\s*$/s; # check for subshell |
476
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
# check builtin contexts/filters |
478
|
195
|
50
|
33
|
|
|
12400
|
unless ($$meta{context} or ! $$meta{parse_def_contexts}) { |
479
|
195
|
|
|
|
|
932
|
debug 'trying builtin contexts'; |
480
|
195
|
|
|
|
|
539
|
my $perl_regexp = join '|', @{$self->{_settings}{perl}{keywords}}; |
|
195
|
|
|
|
|
2476
|
|
481
|
195
|
100
|
33
|
|
|
11920
|
if ( |
|
|
50
|
66
|
|
|
|
|
|
|
50
|
66
|
|
|
|
|
|
|
100
|
|
|
|
|
|
482
|
|
|
|
|
|
|
$$meta{zoidcmd} =~ s/^\s*(\w*){(.*)}(\w*)\s*$/$2/s or $$meta{pretend} and |
483
|
|
|
|
|
|
|
$$meta{zoidcmd} =~ s/^\s*(\w*){(.*)$/$2/s |
484
|
|
|
|
|
|
|
) { # all kinds of blocks with { ... } |
485
|
36
|
50
|
50
|
|
|
237
|
unless (length $1) { @$meta{qw/context opts/} = ('PERL', $3 || '') } |
|
36
|
0
|
|
|
|
525
|
|
|
0
|
|
|
|
|
0
|
|
486
|
|
|
|
|
|
|
elsif (grep {$_ eq $1} qw/s m tr y/) { |
487
|
0
|
|
|
|
|
0
|
$$meta{zoidcmd} = $1.'{'.$$meta{zoidcmd}.'}'.$3; # always the exceptions |
488
|
0
|
0
|
|
|
|
0
|
@$meta{qw/context opts/} = ('PERL', ($1 eq 'm') ? 'g' : 'p') |
489
|
|
|
|
|
|
|
} |
490
|
|
|
|
|
|
|
else { |
491
|
0
|
|
0
|
|
|
0
|
@$meta{qw/context opts/} = (uc($1), $3 || ''); |
492
|
0
|
|
|
|
|
0
|
@words = $$self{stringparser}->split('word_gram', $$meta{zoidcmd}); |
493
|
|
|
|
|
|
|
} |
494
|
|
|
|
|
|
|
} |
495
|
|
|
|
|
|
|
elsif ($$meta{zoidcmd} =~ s/^\s*(\w+):\s+//) { # little bit o psh2 compat |
496
|
0
|
|
|
|
|
0
|
$$meta{context} = uc $1; |
497
|
0
|
|
|
|
|
0
|
shift @words; |
498
|
|
|
|
|
|
|
} |
499
|
0
|
|
|
|
|
0
|
elsif (@words == 1 and $words[0] =~ /^%/) { unshift @words, 'fg' } # and another exception |
500
|
|
|
|
|
|
|
elsif ($words[0] =~ /^\s*(->|[\$\@\%\&\*\xA3]\S|\w+::|\w+[\(\{]|($perl_regexp)\b)/s) { |
501
|
10
|
|
|
|
|
102
|
$$meta{context} = 'PERL'; |
502
|
|
|
|
|
|
|
} |
503
|
|
|
|
|
|
|
} |
504
|
|
|
|
|
|
|
|
505
|
195
|
|
|
|
|
982
|
$$meta{env}{ZOIDCMD} = $$meta{zoidcmd}; # unix haters guide, pdf page 60 |
506
|
195
|
50
|
33
|
|
|
860
|
if ($$self{_settings}{mode} and ! $$meta{no_mode}) { |
507
|
0
|
|
|
|
|
0
|
my $m = $$self{_settings}{mode}; |
508
|
0
|
0
|
0
|
|
|
0
|
$$meta{context} ||= ($m =~ /::/) ? $m : uc($m); |
509
|
|
|
|
|
|
|
} |
510
|
|
|
|
|
|
|
|
511
|
195
|
50
|
33
|
|
|
646
|
return [$meta, @words] if $$meta{pretend} and @words == 1; |
512
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
# check custom contexts |
514
|
195
|
100
|
|
|
|
733
|
unless ($$meta{context}) { |
515
|
149
|
|
|
|
|
962
|
debug 'trying custom contexts'; |
516
|
149
|
|
|
|
|
820
|
for my $pair ($$self{parser}->stack('word_list', 'TAGS')) { |
517
|
0
|
|
|
|
|
0
|
my $r = $$pair[0]->([$meta, @words]); |
518
|
0
|
0
|
|
|
|
0
|
unless ($r) { next } |
|
0
|
0
|
|
|
|
0
|
|
519
|
0
|
|
|
|
|
0
|
elsif (ref $r) { ($meta , @words) = @$r } |
520
|
0
|
0
|
|
|
|
0
|
else { $$meta{context} = length($r) > 1 ? $r : $$pair[1] } |
521
|
0
|
0
|
|
|
|
0
|
last if $$meta{context}; |
522
|
|
|
|
|
|
|
} |
523
|
|
|
|
|
|
|
} |
524
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
# use default builtin context |
526
|
195
|
100
|
66
|
|
|
1926
|
unless ($$meta{context} or ! $$meta{parse_def_contexts}) { |
527
|
149
|
|
|
|
|
593
|
debug 'using default context'; |
528
|
149
|
|
|
|
|
739
|
$$meta{context} = 'CMD'; |
529
|
|
|
|
|
|
|
} |
530
|
|
|
|
|
|
|
|
531
|
195
|
50
|
66
|
|
|
2440
|
if ( |
|
|
100
|
33
|
|
|
|
|
|
|
50
|
|
|
|
|
|
532
|
|
|
|
|
|
|
exists $$self{parser}{$$meta{context}} and |
533
|
|
|
|
|
|
|
exists $$self{parser}{$$meta{context}}{parser} |
534
|
390
|
|
|
|
|
2780
|
) { # custom parser |
535
|
0
|
|
|
|
|
0
|
($meta, @words) = @{ $$self{parser}{$$meta{context}}{parser}->([$meta, @words]) }; |
|
0
|
|
|
|
|
0
|
|
536
|
|
|
|
|
|
|
} |
537
|
195
|
|
|
|
|
771
|
elsif (grep {$$meta{context} eq $_} @{$$self{no_words}}) { # no words |
538
|
46
|
50
|
|
|
|
257
|
@words = $$meta{pretend} |
539
|
|
|
|
|
|
|
? $$self{stringparser}->split('word_gram', $$meta{zoidcmd}) |
540
|
|
|
|
|
|
|
: ( $$meta{zoidcmd} ) ; |
541
|
46
|
50
|
|
|
|
305
|
$$meta{fork_job} = 1 if $$meta{context} eq 'SUBZ'; |
542
|
46
|
50
|
33
|
|
|
893
|
($meta, @words) = @{ $self->parse_perl([$meta, @words]) } |
|
46
|
|
|
|
|
524
|
|
543
|
|
|
|
|
|
|
if ! $$meta{pretend} and $$meta{context} eq 'PERL'; |
544
|
|
|
|
|
|
|
} |
545
|
|
|
|
|
|
|
elsif (@words and ! $$meta{pretend}) { # expand and set topic |
546
|
149
|
50
|
|
|
|
636
|
($meta, @words) = @{ $self->parse_words([$meta, @words]) } unless $$meta{plain_words}; |
|
149
|
|
|
|
|
1461
|
|
547
|
149
|
100
|
66
|
|
|
2476
|
$$self{topic} = |
548
|
|
|
|
|
|
|
# FIXME exists($$meta{fd}{0}) ? $$meta{fd}{0}[0] : |
549
|
|
|
|
|
|
|
(@words > 1 and $words[-1] !~ /^-/) ? $words[-1] : $$self{topic}; |
550
|
149
|
100
|
33
|
|
|
2759
|
$$meta{fork_job} = 1 if $$meta{context} eq 'CMD' and |
|
|
|
66
|
|
|
|
|
551
|
|
|
|
|
|
|
$$meta{cmdtype} ne 'builtin' and ! exists $$self{commands}{$words[0]}; |
552
|
|
|
|
|
|
|
} |
553
|
195
|
|
|
|
|
1970
|
return [$meta, @words]; |
554
|
|
|
|
|
|
|
} |
555
|
|
|
|
|
|
|
|
556
|
|
|
|
|
|
|
our %_redir_ops = ( |
557
|
|
|
|
|
|
|
IN => '<', OUT => '>', |
558
|
|
|
|
|
|
|
CLOB_OUT => '>!', APP_OUT => '>>', |
559
|
|
|
|
|
|
|
RW => '+<', DUP_OUT => '>&', DUP_IN => '<&' |
560
|
|
|
|
|
|
|
); |
561
|
|
|
|
|
|
|
|
562
|
|
|
|
|
|
|
sub parse_env { |
563
|
134
|
|
|
134
|
0
|
306
|
my ($self, $block) = @_; |
564
|
134
|
|
|
|
|
500
|
my ($meta, @words) = @$block; |
565
|
|
|
|
|
|
|
|
566
|
134
|
50
|
33
|
|
|
1301
|
if (@words > 1 or ! $$meta{split_words}) { |
567
|
0
|
|
|
|
|
0
|
$$meta{string} = join ' ', @words; |
568
|
|
|
|
|
|
|
} |
569
|
|
|
|
|
|
|
else { |
570
|
134
|
|
|
|
|
439
|
$$meta{string} = $words[0]; |
571
|
134
|
|
|
|
|
1011
|
@words = $$self{stringparser}->split('word_gram', $words[0]) |
572
|
|
|
|
|
|
|
} |
573
|
|
|
|
|
|
|
# FIXME parse word_gram and redir_gram at same time |
574
|
|
|
|
|
|
|
|
575
|
|
|
|
|
|
|
# parse environment |
576
|
134
|
50
|
|
|
|
758
|
if ($$meta{parse_env}) { |
577
|
134
|
|
|
|
|
443
|
my $_env = delete $$meta{env}; # PWD and SHELL |
578
|
134
|
|
|
|
|
1200
|
while ($words[0] =~ /^(\w[\w\-]*)=(.*)/s) { |
579
|
6
|
|
|
|
|
78
|
$$meta{compl} = shift @words; |
580
|
6
|
|
|
|
|
96
|
$$meta{env}{$1} = $2 |
581
|
|
|
|
|
|
|
} |
582
|
134
|
50
|
66
|
|
|
1008
|
if (! @words and $$meta{env}) { # special case |
|
|
100
|
|
|
|
|
|
583
|
0
|
|
|
|
|
0
|
@words = ('export', map $_.'='.$$meta{env}{$_}, keys %{$$meta{env}}); |
|
0
|
|
|
|
|
0
|
|
584
|
0
|
|
|
|
|
0
|
delete $$meta{env}; # duplicate would make var local |
585
|
|
|
|
|
|
|
} |
586
|
|
|
|
|
|
|
elsif ($$meta{env}) { |
587
|
6
|
|
|
|
|
60
|
delete $$meta{compl}; # @words > 0 |
588
|
6
|
|
|
|
|
1224
|
for (keys %{$$meta{env}}) { |
|
6
|
|
|
|
|
42
|
|
589
|
6
|
|
|
|
|
48
|
my (undef, @w) = @{ $self->parse_words([$meta, $$meta{env}{$_}]) }; |
|
6
|
|
|
|
|
162
|
|
590
|
6
|
|
|
|
|
30
|
$$meta{env}{$_} = join ':', @w; |
591
|
|
|
|
|
|
|
} |
592
|
|
|
|
|
|
|
} |
593
|
134
|
|
|
|
|
506
|
for (keys %$_env) { |
594
|
134
|
50
|
|
|
|
1183
|
$$meta{env}{$_} = $$_env{$_} unless defined $$meta{env}{$_}; |
595
|
|
|
|
|
|
|
} |
596
|
|
|
|
|
|
|
} |
597
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
# parse redirections |
599
|
134
|
50
|
|
|
|
636
|
return [$meta, @words] unless $$meta{parse_fd}; |
600
|
134
|
|
|
|
|
796
|
my @s_words = map [ $$self{stringparser}->split('redirect_gram', $_) ], @words; |
601
|
134
|
50
|
|
|
|
633
|
return [$meta, @words] if ! grep {! ref $_} map @$_, @s_words; |
|
428
|
|
|
|
|
2122
|
|
602
|
0
|
|
0
|
|
|
0
|
$$meta{fd} ||= []; |
603
|
0
|
|
|
|
|
0
|
my @re; |
604
|
|
|
|
|
|
|
|
605
|
0
|
|
|
|
|
0
|
PARSE_REDIR_S_WORD: |
606
|
0
|
|
|
|
|
0
|
my @parts = @{shift @s_words}; |
607
|
0
|
|
|
|
|
0
|
my $last = $#parts; # length of @parts changes later on |
608
|
0
|
|
|
|
|
0
|
for (0 .. $#parts) { |
609
|
0
|
0
|
0
|
|
|
0
|
next unless defined $parts[$_] and ! ref $parts[$_]; |
610
|
0
|
|
|
|
|
0
|
my $op = delete $parts[$_]; |
611
|
0
|
0
|
|
|
|
0
|
if ($op =~ /[^A-Z_]/) { # _SELF escape for "" |
|
|
0
|
|
|
|
|
|
612
|
0
|
|
|
|
|
0
|
$parts[$_] = \$op; |
613
|
0
|
|
|
|
|
0
|
next; |
614
|
|
|
|
|
|
|
} |
615
|
|
|
|
|
|
|
elsif ($op eq 'ERROR') { |
616
|
0
|
0
|
|
|
|
0
|
error 'redirection operation not supported' |
617
|
|
|
|
|
|
|
unless $$meta{pretend}; |
618
|
|
|
|
|
|
|
} |
619
|
|
|
|
|
|
|
|
620
|
0
|
|
|
|
|
0
|
my ($n, $word); |
621
|
0
|
0
|
0
|
|
|
0
|
if ($_ > 0 and ref $parts[$_-1]) { # find file descriptor number |
622
|
0
|
0
|
|
|
|
0
|
if (${$parts[$_-1]} =~ /^\d+$/) { $n = ${delete $parts[$_-1]} } |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
623
|
|
|
|
|
|
|
else { |
624
|
0
|
0
|
|
|
|
0
|
${$parts[$_-1]} =~ s/(\\\\)|(\\\d+)$|(\d+)$/$1 || $2/eg; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
625
|
0
|
|
|
|
|
0
|
$n = $3; |
626
|
|
|
|
|
|
|
} |
627
|
|
|
|
|
|
|
} |
628
|
|
|
|
|
|
|
|
629
|
0
|
0
|
0
|
|
|
0
|
if ($_ < $#parts and ref $parts[$_+1]) { # find argument |
|
|
0
|
0
|
|
|
|
|
630
|
0
|
|
|
|
|
0
|
$word = ${ delete $parts[$_+1] }; |
|
0
|
|
|
|
|
0
|
|
631
|
0
|
0
|
0
|
|
|
0
|
$$meta{compl} = $word if $_+1 == $last and ! @s_words; # complete last word |
632
|
|
|
|
|
|
|
} |
633
|
|
|
|
|
|
|
elsif (@s_words and ref $s_words[0][0]) { |
634
|
0
|
|
|
|
|
0
|
$word = ${ delete $s_words[0][0] }; |
|
0
|
|
|
|
|
0
|
|
635
|
0
|
0
|
0
|
|
|
0
|
$$meta{compl} = $word if @s_words == 1 and ! @{$s_words[0]}; |
|
0
|
|
|
|
|
0
|
|
636
|
|
|
|
|
|
|
} |
637
|
|
|
|
|
|
|
else { |
638
|
0
|
0
|
0
|
|
|
0
|
error 'redirection needs argument' |
639
|
|
|
|
|
|
|
unless $op =~ /^DUP/ or $$meta{pretend}; |
640
|
0
|
|
|
|
|
0
|
$$meta{compl} = ''; |
641
|
|
|
|
|
|
|
} |
642
|
|
|
|
|
|
|
|
643
|
0
|
0
|
|
|
|
0
|
unless ($$meta{pretend}) { |
644
|
0
|
0
|
0
|
|
|
0
|
$n ||= ($op =~ /OUT$/) ? 1 : 0; |
645
|
0
|
|
|
|
|
0
|
my (undef, @w) = @{ $self->parse_words([$meta, $word]) }; |
|
0
|
|
|
|
|
0
|
|
646
|
0
|
0
|
|
|
|
0
|
if (@w == 1) { push @{$$meta{fd}}, $n.$_redir_ops{$op}.$w[0] } |
|
0
|
0
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
647
|
0
|
|
|
|
|
0
|
elsif (@w > 1) { error 'redirection argument expands to multiple words' } |
648
|
0
|
|
|
|
|
0
|
else { error 'redirection needs argument' } # @w < 1 |
649
|
|
|
|
|
|
|
} |
650
|
|
|
|
|
|
|
} |
651
|
0
|
|
|
|
|
0
|
push @re, map $$_, @parts; |
652
|
0
|
0
|
|
|
|
0
|
goto PARSE_REDIR_S_WORD if @s_words; |
653
|
|
|
|
|
|
|
|
654
|
0
|
|
|
|
|
0
|
return [$meta, @re]; |
655
|
|
|
|
|
|
|
} |
656
|
|
|
|
|
|
|
|
657
|
|
|
|
|
|
|
sub parse_aliases { # recursive sub (aliases are 3 way recursive, 2 ways are in this sub) |
658
|
208
|
|
|
208
|
0
|
903
|
my ($self, $meta, @words) = @_; |
659
|
208
|
50
|
33
|
|
|
3261
|
my $aliases = ($$self{_settings}{mode} && ! $$meta{no_mode}) |
660
|
|
|
|
|
|
|
? $$self{aliases}{'mode_'.$$self{_settings}{mode}} |
661
|
|
|
|
|
|
|
: $$self{aliases}; |
662
|
208
|
100
|
66
|
|
|
3247
|
return [$meta, @words] unless ref $aliases and exists $$aliases{$words[0]}; |
663
|
13
|
|
50
|
|
|
351
|
$$meta{alias_stack} ||= []; |
664
|
13
|
50
|
|
|
|
26
|
return [$meta, @words] if grep {$_ eq $words[0]} @{$$meta{alias_stack}}; |
|
0
|
|
|
|
|
0
|
|
|
13
|
|
|
|
|
143
|
|
665
|
13
|
|
|
|
|
39
|
push @{$$meta{alias_stack}}, $words[0]; |
|
13
|
|
|
|
|
78
|
|
666
|
|
|
|
|
|
|
|
667
|
13
|
|
|
|
|
143
|
my $string = $$aliases{$words[0]}; |
668
|
13
|
|
|
|
|
143
|
debug "$words[0] is aliased to: $string"; |
669
|
13
|
|
|
|
|
91
|
shift @words; |
670
|
|
|
|
|
|
|
|
671
|
|
|
|
|
|
|
=cut |
672
|
|
|
|
|
|
|
|
673
|
|
|
|
|
|
|
# saving code for later usage in pipelines |
674
|
|
|
|
|
|
|
# this is not the right place for it |
675
|
|
|
|
|
|
|
|
676
|
|
|
|
|
|
|
{ # variable substitution in the macro |
677
|
|
|
|
|
|
|
local @_ = @words; |
678
|
|
|
|
|
|
|
my $n = ( $string =~ s# (?
|
679
|
|
|
|
|
|
|
if ($1) { $words[$1] } |
680
|
|
|
|
|
|
|
elsif ($2) { eval "join ' ', \@_[$2]" } |
681
|
|
|
|
|
|
|
else { join ' ', @words } |
682
|
|
|
|
|
|
|
#xge ); |
683
|
|
|
|
|
|
|
@words = () if $n; |
684
|
|
|
|
|
|
|
} |
685
|
|
|
|
|
|
|
|
686
|
|
|
|
|
|
|
=cut |
687
|
|
|
|
|
|
|
|
688
|
13
|
|
|
|
|
130
|
my @as = @{$$meta{alias_stack}}; # force copy |
|
13
|
|
|
|
|
52
|
|
689
|
13
|
50
|
|
|
|
260
|
my @l = map { |
690
|
13
|
50
|
|
|
|
351
|
ref($_) ? [ |
691
|
|
|
|
|
|
|
{ alias_stack => [@as] }, |
692
|
|
|
|
|
|
|
$$self{stringparser}->split('word_gram', $$_) |
693
|
|
|
|
|
|
|
] : $_ |
694
|
|
|
|
|
|
|
} $$meta{split_script} ? ($$self{stringparser}->split('script_gram', $string)) : ($string); |
695
|
|
|
|
|
|
|
|
696
|
13
|
50
|
|
|
|
130
|
if ( my ($firstref) = grep ref($_), @l ) { |
697
|
13
|
|
|
|
|
78
|
$$firstref[0] = $meta; # re-insert %meta |
698
|
13
|
50
|
0
|
|
|
247
|
++$$meta{no_mode} and (length $$firstref[1] or delete $$firstref[1]) |
|
|
|
0
|
|
|
|
|
|
|
|
33
|
|
|
|
|
699
|
|
|
|
|
|
|
if @$firstref > 1 and $$firstref[1] =~ s/^\!\s*//; # check mode |
700
|
|
|
|
|
|
|
} |
701
|
|
|
|
|
|
|
|
702
|
13
|
50
|
|
|
|
741
|
if ($string =~ /\s$/) { # recurs for 2nd word - see posix spec |
|
|
50
|
|
|
|
|
|
703
|
0
|
|
|
|
|
0
|
my @l1 = $self->parse_aliases({}, @words); # recurs |
704
|
0
|
0
|
0
|
|
|
0
|
push @{$l[-1]}, splice(@{ shift(@l1) }, 1) if ref $l[-1] and ref $l1[0]; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
705
|
0
|
|
|
|
|
0
|
push @l, @l1; |
706
|
|
|
|
|
|
|
} |
707
|
13
|
|
|
|
|
39
|
elsif (@l == 1) { return $self->parse_aliases(@{$l[0]}, @words) } # recurs |
|
13
|
|
|
|
|
182
|
|
708
|
|
|
|
|
|
|
else { |
709
|
0
|
0
|
|
|
|
0
|
if (ref $l[-1]) { push @{$l[-1]}, @words } |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
710
|
0
|
|
|
|
|
0
|
else { push @l, \@words } |
711
|
|
|
|
|
|
|
} |
712
|
|
|
|
|
|
|
|
713
|
0
|
|
|
|
|
0
|
return @l; |
714
|
|
|
|
|
|
|
} |
715
|
|
|
|
|
|
|
|
716
|
|
|
|
|
|
|
sub parse_words { # expand words etc. |
717
|
164
|
|
|
164
|
0
|
515
|
my ($self, $block) = @_; |
718
|
|
|
|
|
|
|
|
719
|
|
|
|
|
|
|
# custom stack |
720
|
164
|
|
|
|
|
850
|
for ($$self{parser}->stack('word_expansion')) { |
721
|
0
|
|
|
|
|
0
|
my $re = $_->($block); |
722
|
0
|
0
|
|
|
|
0
|
$block = $re if $re; |
723
|
|
|
|
|
|
|
} |
724
|
|
|
|
|
|
|
|
725
|
|
|
|
|
|
|
# default expansions |
726
|
|
|
|
|
|
|
# expand_comm resets zoidcmd, all other stuff is left for appliction level re-parsing |
727
|
|
|
|
|
|
|
@$block = $self->$_(@$block) |
728
|
164
|
|
|
|
|
2246
|
for grep $$block[0]{$_}, qw/expand_param expand_comm expand_path/; |
729
|
|
|
|
|
|
|
|
730
|
|
|
|
|
|
|
# remove quote |
731
|
164
|
|
|
|
|
646
|
my ($meta, @words) = @$block; |
732
|
164
|
|
|
|
|
475
|
for (@words) { |
733
|
758
|
100
|
|
|
|
5064
|
if (/^([\/\w]+=)?(['"])(.*)\2$/s) { |
734
|
|
|
|
|
|
|
# quote removal and escape removal within quotes |
735
|
39
|
|
|
|
|
343
|
$_ = $1.$3; |
736
|
39
|
100
|
|
|
|
271
|
if ($2 eq '\'') { $_ =~ s/\\([\\'])/$1/ge } |
|
29
|
|
|
|
|
116
|
|
|
1
|
|
|
|
|
5
|
|
737
|
10
|
|
|
|
|
144
|
else { $_ =~ s/\\(.)/$1/ge } |
|
0
|
|
|
|
|
0
|
|
738
|
|
|
|
|
|
|
} |
739
|
|
|
|
|
|
|
# FIXME also do escape removal here |
740
|
|
|
|
|
|
|
# is now done by File::Glob |
741
|
|
|
|
|
|
|
} |
742
|
|
|
|
|
|
|
|
743
|
164
|
|
|
|
|
1336
|
return [$meta, @words]; |
744
|
|
|
|
|
|
|
} |
745
|
|
|
|
|
|
|
|
746
|
|
|
|
|
|
|
=cut |
747
|
|
|
|
|
|
|
|
748
|
|
|
|
|
|
|
# so far no luck of getting this to work - maybe combine intgrate |
749
|
|
|
|
|
|
|
# this with stringparser some how :S |
750
|
|
|
|
|
|
|
|
751
|
|
|
|
|
|
|
our $_IFS = [undef, qr/\s+/, qr/\s+/]; |
752
|
|
|
|
|
|
|
sub _split_on_IFS { # bloody heavy routine for such a simple parsing rule |
753
|
|
|
|
|
|
|
my $self = shift; |
754
|
|
|
|
|
|
|
unless ($ENV{IFS} eq $$_IFS[0]) { |
755
|
|
|
|
|
|
|
debug "generating new IFS regexes"; |
756
|
|
|
|
|
|
|
if (! defined $ENV{IFS}) { $_IFS = [undef, qr/\s+/, qr/\s+/] } |
757
|
|
|
|
|
|
|
elsif ($ENV{IFS} eq '') { $_IFS = [''] } |
758
|
|
|
|
|
|
|
else { |
759
|
|
|
|
|
|
|
my $ifs_white = join '', ($ENV{IFS} =~ m/(\s)/g); |
760
|
|
|
|
|
|
|
my $ifs_char = join '', ($ENV{IFS} =~ m/(\S)/g); |
761
|
|
|
|
|
|
|
$_IFS = [ $ENV{IFS}, qr/[$ifs_white]+/, |
762
|
|
|
|
|
|
|
qr/[$ifs_white]*[$ifs_char][$ifs_white]*|[$ifs_white]+/ ]; |
763
|
|
|
|
|
|
|
} |
764
|
|
|
|
|
|
|
debug "IFS = ['$ENV{IFS}', $$_IFS[1], $$_IFS[2]]"; |
765
|
|
|
|
|
|
|
} |
766
|
|
|
|
|
|
|
debug "IFS = ['$ENV{IFS}', $$_IFS[1], $$_IFS[2]]"; |
767
|
|
|
|
|
|
|
return @_ if defined $$_IFS[0] and $$_IFS[0] eq ''; |
768
|
|
|
|
|
|
|
return map { |
769
|
|
|
|
|
|
|
$_ =~ s/(\\\\)|^$$_IFS[1]|(?
|
770
|
|
|
|
|
|
|
$$self{stringparser}->split($$_IFS[2], $_) |
771
|
|
|
|
|
|
|
} @_; |
772
|
|
|
|
|
|
|
} |
773
|
|
|
|
|
|
|
|
774
|
|
|
|
|
|
|
=cut |
775
|
|
|
|
|
|
|
|
776
|
|
|
|
|
|
|
=cut |
777
|
|
|
|
|
|
|
|
778
|
|
|
|
|
|
|
sub expand_braces { |
779
|
|
|
|
|
|
|
my ($self, $meta, @words) = @_; |
780
|
|
|
|
|
|
|
my @re; |
781
|
|
|
|
|
|
|
for (@words) { |
782
|
|
|
|
|
|
|
my @parts = $$self{stringparser}->split('expand_braces_gram', $_); |
783
|
|
|
|
|
|
|
error $$self{stringparser}{broken} if $$self{stringparser}{broken}; |
784
|
|
|
|
|
|
|
# FIXME let stringparser do the error throwing ? |
785
|
|
|
|
|
|
|
unless (@parts > 1) { |
786
|
|
|
|
|
|
|
push @re, $_; |
787
|
|
|
|
|
|
|
next; |
788
|
|
|
|
|
|
|
} |
789
|
|
|
|
|
|
|
for (0 .. $#parts) { |
790
|
|
|
|
|
|
|
if ($parts[$_] eq 'BRACE') { |
791
|
|
|
|
|
|
|
my $braced = delete $parts[$_+1]; |
792
|
|
|
|
|
|
|
|
793
|
|
|
|
|
|
|
} |
794
|
|
|
|
|
|
|
elsif (ref $parts[$_]) { $parts[$_] = ${$parts[$_]} } |
795
|
|
|
|
|
|
|
} |
796
|
|
|
|
|
|
|
push @re, join '', map {ref($_) ? (@$_) : $_} @parts; |
797
|
|
|
|
|
|
|
} |
798
|
|
|
|
|
|
|
return ($meta, @re); |
799
|
|
|
|
|
|
|
} |
800
|
|
|
|
|
|
|
|
801
|
|
|
|
|
|
|
=cut |
802
|
|
|
|
|
|
|
|
803
|
|
|
|
|
|
|
sub expand_param { |
804
|
|
|
|
|
|
|
# make sure $() and @() remain untouched ... `` are considered quotes |
805
|
19
|
|
|
19
|
|
297
|
no strict 'refs'; |
|
19
|
|
|
|
|
71
|
|
|
19
|
|
|
|
|
9273725
|
|
806
|
164
|
|
|
164
|
0
|
1401
|
my ($self, $meta, @words) = @_; |
807
|
164
|
|
|
|
|
244
|
my ($e); |
808
|
|
|
|
|
|
|
|
809
|
164
|
|
|
|
|
733
|
my $class = $$self{_settings}{perl}{namespace}; |
810
|
|
|
|
|
|
|
@words = map { # substitute vars |
811
|
743
|
100
|
|
|
|
1944
|
if (/^([\/\w]+=)?'.*'$/s) { $_ }# skip quoted words |
|
29
|
|
|
|
|
316
|
|
812
|
|
|
|
|
|
|
else { |
813
|
714
|
|
|
|
|
958
|
my $old = $_; |
814
|
714
|
0
|
|
|
|
1641
|
s{(?
|
|
0
|
0
|
|
|
|
0
|
|
815
|
714
|
|
|
|
|
3045
|
s{ (?
|
816
|
32
|
|
33
|
|
|
614
|
my ($w, $i) = ($1 || $2, $3); |
817
|
32
|
50
|
0
|
|
|
298
|
$e ||= "no advanced expansion for \$\{$w\}" if $w =~ /[^\w-]/; |
818
|
32
|
0
|
33
|
|
|
794
|
if ($w eq '_') { $w = $$self{topic} } |
|
0
|
50
|
|
|
|
0
|
|
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
819
|
0
|
|
|
|
|
0
|
elsif (exists $$meta{env}{$w} or exists $ENV{$w}) { |
820
|
32
|
50
|
|
|
|
220
|
$w = exists( $$meta{env}{$w} ) ? $$meta{env}{$w} : $ENV{$w} ; |
821
|
32
|
100
|
|
|
|
225
|
$w = $i ? (split /:/, $w)[$i] : $w; |
822
|
|
|
|
|
|
|
} |
823
|
0
|
|
|
|
|
0
|
elsif ($i ? defined(*{$class.'::'.$w}{ARRAY}) : defined(*{$class.'::'.$w}{SCALAR})) { |
824
|
0
|
0
|
|
|
|
0
|
$w = $i ? ${$class.'::'.$w}[$i] : ${$class.'::'.$w}; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
825
|
|
|
|
|
|
|
} |
826
|
0
|
|
|
|
|
0
|
else { $w = '' } |
827
|
32
|
|
|
|
|
189
|
$w =~ s/\\/\\\\/g; # literal backslashes |
828
|
32
|
|
|
|
|
167
|
$w; |
829
|
|
|
|
|
|
|
}exg; |
830
|
714
|
100
|
100
|
|
|
2206
|
if ($_ eq $old or $_ =~ /^".*"$/) { $_ } |
|
691
|
|
|
|
|
1889
|
|
831
|
23
|
|
|
|
|
2093
|
else { $$self{stringparser}->split('word_gram', $_) } |
832
|
|
|
|
|
|
|
# TODO honour IFS here -- POSIX tells us so |
833
|
|
|
|
|
|
|
} |
834
|
|
|
|
|
|
|
} |
835
|
|
|
|
|
|
|
|
836
|
|
|
|
|
|
|
@words = map { # substitute arrays |
837
|
164
|
100
|
|
|
|
390
|
if (m/^ \@ (?: \{ (.*?) \} | ([\w-]+) ) $/x) { |
|
741
|
|
|
|
|
1414
|
|
838
|
1
|
|
33
|
|
|
64
|
my $w = $1 || $2; |
839
|
1
|
50
|
0
|
|
|
15
|
$e ||= "no advanced expansion for \@\{$w\}" if $w =~ /[^\w-]/; |
840
|
1
|
50
|
0
|
|
|
15
|
$e ||= '@_ is reserved for future syntax usage' if $2 eq '_'; |
841
|
1
|
50
|
33
|
|
|
27
|
if (exists $$meta{env}{$w} or exists $ENV{$w}) { |
|
0
|
0
|
|
|
|
0
|
|
842
|
1
|
50
|
|
|
|
8
|
$w = (exists $$meta{env}{$w}) ? $$meta{env}{$w} : $ENV{$w}; |
843
|
1
|
|
|
|
|
10
|
map {s/\\/\\\\/g; $_} split /:/, $w; |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
12
|
|
844
|
|
|
|
|
|
|
} |
845
|
|
|
|
|
|
|
elsif (defined *{$class.'::'.$w}{ARRAY}) { |
846
|
0
|
|
|
|
|
0
|
map {s/\\/\\\\/g; $_} @{$class.'::'.$w}; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
847
|
|
|
|
|
|
|
} |
848
|
0
|
|
|
|
|
0
|
else { () } |
849
|
|
|
|
|
|
|
} |
850
|
740
|
|
|
|
|
2225
|
else { $_ } |
851
|
|
|
|
|
|
|
} @words; |
852
|
164
|
50
|
|
|
|
670
|
error $e if $e; # "Attempt to free unreferenced scalar" when dying inside the map !? |
853
|
164
|
|
|
|
|
2158
|
return ($meta, @words); |
854
|
|
|
|
|
|
|
} |
855
|
|
|
|
|
|
|
|
856
|
|
|
|
|
|
|
sub expand_comm { |
857
|
164
|
|
|
164
|
0
|
767
|
my ($self, $meta, @words) = @_; |
858
|
164
|
|
|
|
|
234
|
my @re; |
859
|
164
|
|
|
|
|
901
|
my $m = {capture => 1, env => $$meta{env}}; |
860
|
164
|
|
|
|
|
431
|
for (@words) { |
861
|
755
|
100
|
|
|
|
3200
|
if (/^([\/\w]+=)?'.*'$/s) { |
|
|
50
|
|
|
|
|
|
862
|
29
|
|
|
|
|
188
|
push @re, $_; |
863
|
|
|
|
|
|
|
} |
864
|
|
|
|
|
|
|
elsif (/^\@\((.*?)\)$/s) { |
865
|
0
|
|
|
|
|
0
|
debug "\@() subz: $1"; |
866
|
0
|
|
|
|
|
0
|
push @re, $self->shell($m, $1); # list context |
867
|
|
|
|
|
|
|
} |
868
|
|
|
|
|
|
|
else { |
869
|
726
|
100
|
|
|
|
2501
|
my $quote = $1 if s/^(")(.*)\1$/$2/s; |
870
|
726
|
|
|
|
|
4690
|
my @parts = $$self{stringparser}->split('expand_comm_gram', $_); |
871
|
726
|
50
|
|
|
|
2602
|
error $$self{stringparser}{broken} if $$self{stringparser}{broken}; |
872
|
|
|
|
|
|
|
# FIXME let stringparser do the error throwing ? |
873
|
726
|
50
|
|
|
|
1868
|
unless (@parts > 1) { |
874
|
726
|
100
|
|
|
|
11080
|
push @re, $quote ? $quote.$_.$quote : $_; |
875
|
726
|
|
|
|
|
2090
|
next; |
876
|
|
|
|
|
|
|
} |
877
|
0
|
|
|
|
|
0
|
for (0 .. $#parts) { |
878
|
0
|
0
|
|
|
|
0
|
if ($parts[$_] eq 'COMM') { |
|
|
0
|
|
|
|
|
|
879
|
0
|
|
|
|
|
0
|
debug '$() subz: '.$parts[$_+1]; |
880
|
0
|
|
|
|
|
0
|
$parts[$_] = $self->shell($m, ${delete $parts[$_+1]}); # scalar context |
|
0
|
|
|
|
|
0
|
|
881
|
0
|
0
|
0
|
|
|
0
|
if ($_ < $#parts-1 and ${$parts[$_+2]} =~ s/^\[(\d*)\]//) { |
|
0
|
|
|
|
|
0
|
|
882
|
0
|
|
|
|
|
0
|
$parts[$_] = $parts[$_][$1]; |
883
|
0
|
|
|
|
|
0
|
chomp $parts[$_]; |
884
|
|
|
|
|
|
|
} |
885
|
0
|
|
|
|
|
0
|
else { $parts[$_] = "$parts[$_]" } # just to be sure bout overload |
886
|
|
|
|
|
|
|
} |
887
|
0
|
|
|
|
|
0
|
elsif (ref $parts[$_]) { $parts[$_] = ${$parts[$_]} } |
|
0
|
|
|
|
|
0
|
|
888
|
|
|
|
|
|
|
} |
889
|
0
|
|
|
|
|
0
|
my $word = join '', @parts; # map {ref($_) ? (@$_) : $_} @parts; |
890
|
0
|
0
|
|
|
|
0
|
if ($quote) { push @re, $quote.$word.$quote } |
|
0
|
|
|
|
|
0
|
|
891
|
0
|
|
|
|
|
0
|
else { push @re, $$self{stringparser}->split('word_gram', $word) } |
892
|
|
|
|
|
|
|
# TODO honour IFS here - POSIX says so |
893
|
|
|
|
|
|
|
} |
894
|
|
|
|
|
|
|
} |
895
|
164
|
|
|
|
|
1833
|
$$meta{env}{ZOIDCMD} = $$meta{zoidcmd} = join ' ', @re; |
896
|
164
|
|
|
|
|
2131
|
return $meta, @re; |
897
|
|
|
|
|
|
|
} |
898
|
|
|
|
|
|
|
|
899
|
|
|
|
|
|
|
# See File::Glob for explanation of behaviour |
900
|
|
|
|
|
|
|
our $_GLOB_OPTS = File::Glob::GLOB_TILDE() | File::Glob::GLOB_QUOTE() | File::Glob::GLOB_BRACE(); |
901
|
|
|
|
|
|
|
our $_NC_GLOB_OPTS = $_GLOB_OPTS | File::Glob::GLOB_NOCHECK(); |
902
|
|
|
|
|
|
|
|
903
|
|
|
|
|
|
|
sub expand_path { # path expansion |
904
|
|
|
|
|
|
|
# FIXME add 'failglob' setting (useful in scripts) |
905
|
164
|
|
|
164
|
0
|
569
|
my ($self, $meta, @files) = @_; |
906
|
164
|
50
|
|
|
|
4465
|
return $meta, @files if $$self{_settings}{noglob}; |
907
|
164
|
50
|
|
|
|
807
|
my $opts = $$self{_settings}{nullglob} ? $_GLOB_OPTS : $_NC_GLOB_OPTS; |
908
|
164
|
50
|
|
|
|
643
|
$opts |= File::Glob::GLOB_NOCASE() if $$self{_settings}{nocaseglob}; |
909
|
|
|
|
|
|
|
return $meta, map { |
910
|
164
|
100
|
|
|
|
395
|
if (/^([\/\w]+=)?(['"])/) { $_ } # quoted |
|
755
|
50
|
|
|
|
6638
|
|
|
39
|
100
|
|
|
|
731
|
|
911
|
|
|
|
|
|
|
elsif (/^m\{(.*)\}([imsx]*)$/) { # regex globs |
912
|
0
|
|
|
|
|
0
|
my @r = regex_glob($1, $2); |
913
|
0
|
0
|
|
|
|
0
|
if (@r) { @r } |
|
0
|
|
|
|
|
0
|
|
914
|
0
|
0
|
|
|
|
0
|
else { $_ =~ s/\\\\|\\(.)/$1||'\\'/eg; $_ } |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
915
|
|
|
|
|
|
|
} |
916
|
|
|
|
|
|
|
elsif (/^~|[*?\[\]{}]/) { # normal globs |
917
|
|
|
|
|
|
|
# TODO: {x..y} brace expansion |
918
|
11
|
50
|
|
|
|
168
|
$_ =~ s#(\\\\)|(?
|
|
4
|
50
|
|
|
|
60
|
|
919
|
|
|
|
|
|
|
unless $$self{_settings}{voidbraces}; # brace pre-parsing |
920
|
11
|
|
|
|
|
626
|
my @r = File::Glob::bsd_glob($_, $opts); |
921
|
11
|
|
|
|
|
242
|
debug "glob: $_ ==> ".join(', ', @r); |
922
|
11
|
50
|
|
|
|
81
|
($_ !~ /^-/) ? (grep {$_ !~ /^-/} @r) : (@r); |
|
14
|
|
|
|
|
153
|
|
923
|
|
|
|
|
|
|
# protect against implict switches as file names |
924
|
|
|
|
|
|
|
} |
925
|
705
|
50
|
|
|
|
2030
|
else { $_ =~ s/\\\\|\\(.)/$1||'\\'/eg; $_ } # remove escapes # FIXME should be done in parse_words like quote removal |
|
1
|
|
|
|
|
8
|
|
|
705
|
|
|
|
|
4707
|
|
926
|
|
|
|
|
|
|
} @files ; |
927
|
|
|
|
|
|
|
} |
928
|
|
|
|
|
|
|
|
929
|
|
|
|
|
|
|
sub parse_perl { # parse switches |
930
|
46
|
|
|
46
|
0
|
126
|
my ($self, $block) = @_; |
931
|
46
|
|
|
|
|
109
|
my ($meta, $string) = @$block; |
932
|
46
|
|
|
|
|
1014
|
my %opts = map {($_ => 1)} split '', $$self{_settings}{perl}{opts}; |
|
46
|
|
|
|
|
391
|
|
933
|
46
|
50
|
|
|
|
389
|
$opts{z} = 0 if delete $opts{Z}; |
934
|
46
|
|
|
|
|
222
|
$opts{$_}++ for split '', $$meta{opts}; |
935
|
46
|
50
|
|
|
|
177
|
$opts{z} = 0 if delete $opts{Z}; |
936
|
46
|
|
|
|
|
244
|
debug 'perl block options: ', \%opts; |
937
|
|
|
|
|
|
|
|
938
|
46
|
50
|
|
|
|
373
|
($meta, $string) = $self->_expand_zoid($meta, $string) unless $opts{z}; |
939
|
|
|
|
|
|
|
|
940
|
46
|
50
|
|
|
|
295
|
if ($opts{g}) { $string = "\nwhile () {\n\tif (eval {".$string."}) { print \$_; }\n}" } |
|
0
|
50
|
|
|
|
0
|
|
|
|
50
|
|
|
|
|
|
941
|
0
|
|
|
|
|
0
|
elsif ($opts{p}) { $string = "\nwhile () {\n\t".$string.";\n\tprint \$_\n}" } |
942
|
0
|
|
|
|
|
0
|
elsif ($opts{n}) { $string = "\nwhile () {\n\t".$string.";\n}" } |
943
|
|
|
|
|
|
|
|
944
|
46
|
50
|
|
|
|
245
|
$string = "no strict;\n".$string unless $opts{z}; |
945
|
|
|
|
|
|
|
|
946
|
46
|
|
|
|
|
253
|
return [$meta, $string]; |
947
|
|
|
|
|
|
|
} |
948
|
|
|
|
|
|
|
|
949
|
|
|
|
|
|
|
sub _expand_zoid { |
950
|
53
|
|
|
53
|
|
16198
|
my ($self, $meta, $code) = @_; |
951
|
|
|
|
|
|
|
|
952
|
53
|
|
|
|
|
289
|
my @parts = $$self{stringparser}->split('dezoid_gram', $code); |
953
|
53
|
|
|
|
|
189
|
my @idx = grep {! ref $parts[$_]} 0 .. $#parts; |
|
148
|
|
|
|
|
377
|
|
954
|
53
|
100
|
|
|
|
122
|
@parts = map {ref($_) ? $$_ : $_} @parts; |
|
148
|
|
|
|
|
451
|
|
955
|
|
|
|
|
|
|
|
956
|
53
|
|
|
|
|
119
|
my $pre = ''; |
957
|
53
|
|
|
|
|
162
|
for (@idx) { # probably could be done much cleaner |
958
|
56
|
|
|
|
|
126
|
my $token = delete $parts[$_]; |
959
|
56
|
50
|
|
|
|
236
|
my $next = ($_ < $#parts) ? $parts[$_+1] : ''; |
960
|
56
|
100
|
|
|
|
282
|
my $prev = $_ ? $parts[$_-1] : ''; |
961
|
|
|
|
|
|
|
|
962
|
56
|
|
|
|
|
197
|
my $class = $$self{_settings}{perl}{namespace}; |
963
|
56
|
100
|
|
|
|
431
|
if ($token =~ /^([\@\$])(\w+)/) { |
|
|
100
|
|
|
|
|
|
964
|
49
|
|
|
|
|
149
|
my ($sigil, $name) = ($1, $2); |
965
|
49
|
100
|
66
|
|
|
462
|
if ( # global, reserved or non-env var |
|
|
100
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
66
|
|
|
|
|
966
|
294
|
|
|
|
|
1223
|
$next =~ /^::/ |
967
|
|
|
|
|
|
|
or grep {$name eq $_} qw/_ ARGV ENV SIG INC JOBS/ |
968
|
|
|
|
|
|
|
or ! exists $ENV{$name} and ! exists $$meta{env}{$name} |
969
|
41
|
|
|
|
|
151
|
) { $parts[$_] = $token } |
970
|
|
|
|
|
|
|
elsif ($sigil eq '@' or $next =~ /^\[/) { # array |
971
|
19
|
|
|
19
|
|
258
|
no strict 'refs'; |
|
19
|
|
|
|
|
39
|
|
|
19
|
|
|
|
|
44166
|
|
972
|
1
|
|
|
|
|
33
|
$pre .= "Env->import('$token');\n" |
973
|
1
|
50
|
33
|
|
|
11
|
unless defined *{$class.'::'.$name}{ARRAY} and @{$class.'::'.$name}; |
|
0
|
|
|
|
|
0
|
|
974
|
1
|
|
|
|
|
7
|
$parts[$_] = $token; |
975
|
|
|
|
|
|
|
} |
976
|
7
|
|
|
|
|
65
|
else { $parts[$_] = '$ENV{'.$name.'}' } # scalar |
977
|
|
|
|
|
|
|
} |
978
|
|
|
|
|
|
|
# else token eq 'ARR' |
979
|
1
|
|
|
|
|
4
|
elsif ($prev =~ /[\w\}\)\]]$/) { $parts[$_] = '->' } |
980
|
6
|
|
|
|
|
17
|
else { $parts[$_] = '$shell->' } |
981
|
|
|
|
|
|
|
} |
982
|
|
|
|
|
|
|
|
983
|
53
|
|
|
|
|
533
|
return $meta, $pre . join '', grep defined($_), @parts; |
984
|
|
|
|
|
|
|
} |
985
|
|
|
|
|
|
|
|
986
|
|
|
|
|
|
|
# ########## # |
987
|
|
|
|
|
|
|
# Exec stuff # |
988
|
|
|
|
|
|
|
# ########## # |
989
|
|
|
|
|
|
|
|
990
|
|
|
|
|
|
|
sub eval_block { # real exec code |
991
|
90
|
|
|
90
|
0
|
196
|
my ($self, $ref) = @_; |
992
|
90
|
|
|
|
|
394
|
my $context = $$ref[0]{context}; |
993
|
|
|
|
|
|
|
|
994
|
90
|
50
|
|
|
|
898
|
if ($$self{parser}{$context}{handler}) { |
|
|
50
|
|
|
|
|
|
995
|
0
|
|
|
|
|
0
|
debug "going to call handler for context: $context"; |
996
|
0
|
|
|
|
|
0
|
$$self{parser}{$context}{handler}->($ref); |
997
|
|
|
|
|
|
|
} |
998
|
|
|
|
|
|
|
elsif ($self->can('_do_'.lc($context))) { |
999
|
90
|
|
|
|
|
248
|
my $sub = '_do_'.lc($context); |
1000
|
90
|
|
|
|
|
469
|
debug "going to call sub: $sub"; |
1001
|
90
|
|
|
|
|
637
|
$self->$sub(@$ref); |
1002
|
|
|
|
|
|
|
} |
1003
|
|
|
|
|
|
|
else { |
1004
|
0
|
0
|
|
|
|
0
|
$context |
1005
|
|
|
|
|
|
|
? error "No handler defined for context $context" |
1006
|
|
|
|
|
|
|
: bug 'No context defined !' |
1007
|
|
|
|
|
|
|
} |
1008
|
|
|
|
|
|
|
} |
1009
|
|
|
|
|
|
|
|
1010
|
|
|
|
|
|
|
# FIXME FIXME remove _do_* subs below and store them in {parser} |
1011
|
|
|
|
|
|
|
|
1012
|
|
|
|
|
|
|
sub _do_subz { # sub shell, forked if all is well |
1013
|
0
|
|
|
0
|
|
0
|
my ($self, $meta) = @_; |
1014
|
0
|
|
|
|
|
0
|
my $cmd = $$meta{zoidcmd}; |
1015
|
0
|
0
|
|
|
|
0
|
$cmd = $1 if $cmd =~ /^\s*\((.*)\)\s*$/s; |
1016
|
0
|
|
|
|
|
0
|
%$meta = map {($_ => $$meta{$_})} qw/env/; # FIXME also add parser opts n stuff |
|
0
|
|
|
|
|
0
|
|
1017
|
|
|
|
|
|
|
# FIXME reset mode n stuff ? |
1018
|
0
|
|
|
|
|
0
|
$self->shell_string($meta, $cmd); |
1019
|
0
|
0
|
|
|
|
0
|
error $$self{error} if $$self{error}; # forward the error |
1020
|
|
|
|
|
|
|
} |
1021
|
|
|
|
|
|
|
|
1022
|
|
|
|
|
|
|
sub _do_cmd { |
1023
|
44
|
|
|
44
|
|
605
|
my ($self, $meta, $cmd, @args) = @_; |
1024
|
|
|
|
|
|
|
# exec = exexvp which checks $PATH for us |
1025
|
|
|
|
|
|
|
# the block syntax to force use of execvp, not shell for one argument list |
1026
|
|
|
|
|
|
|
# If a command is not found, the exit status shall be 127. If the command name is found, |
1027
|
|
|
|
|
|
|
# but it is not an executable utility, the exit status shall be 126. |
1028
|
44
|
|
50
|
|
|
1896
|
$$meta{cmdtype} ||= ''; |
1029
|
44
|
100
|
66
|
|
|
1437
|
if ($cmd =~ m|/|) { # executable file |
|
|
100
|
|
|
|
|
|
1030
|
1
|
50
|
|
|
|
16
|
error 'builtin should not contain a "/"' if $$meta{cmdtype} eq 'builtin'; |
1031
|
1
|
50
|
|
|
|
60
|
error {exit_status => 127}, $cmd.': No such file or directory' unless -e $cmd; |
1032
|
1
|
50
|
|
|
|
8
|
error {exit_status => 126}, $cmd.': is a directory' if -d _; |
1033
|
1
|
50
|
|
|
|
7
|
error {exit_status => 126}, $cmd.': Permission denied' unless -x _; |
1034
|
1
|
|
|
|
|
21
|
debug 'going to exec file: ', join ', ', $cmd, @args; |
1035
|
1
|
0
|
|
|
|
10
|
exec {$cmd} $cmd, @args or error {exit_status => 127}, $cmd.': command not found'; |
|
1
|
|
|
|
|
0
|
|
1036
|
|
|
|
|
|
|
} |
1037
|
|
|
|
|
|
|
elsif ($$meta{cmdtype} eq 'builtin' or exists $$self{commands}{$cmd}) { # built-in, not forked I hope |
1038
|
30
|
50
|
|
|
|
198
|
error {exit_status => 127}, $cmd.': no such builtin' unless exists $$self{commands}{$cmd}; |
1039
|
30
|
|
|
|
|
254
|
debug 'going to do built-in: ', join ', ', $cmd, @args; |
1040
|
30
|
|
|
|
|
308
|
local $Zoidberg::Utils::Error::Scope = $cmd; |
1041
|
30
|
|
|
|
|
196
|
$$self{commands}{$cmd}->(@args); |
1042
|
|
|
|
|
|
|
} |
1043
|
|
|
|
|
|
|
else { # command in path ? |
1044
|
13
|
|
|
|
|
717
|
debug 'going to exec: ', join ', ', $cmd, @args; |
1045
|
13
|
0
|
|
|
|
156
|
exec {$cmd} $cmd, @args or error {exit_status => 127}, $cmd.': command not found'; |
|
13
|
|
|
|
|
0
|
|
1046
|
|
|
|
|
|
|
} |
1047
|
|
|
|
|
|
|
} |
1048
|
|
|
|
|
|
|
|
1049
|
|
|
|
|
|
|
sub _do_perl { |
1050
|
46
|
|
|
46
|
|
93
|
my ($shell, $_Meta, $_Code) = @_; |
1051
|
46
|
|
50
|
|
|
261
|
my $_Class = $$shell{_settings}{perl}{namespace} || 'Zoidberg::Eval'; |
1052
|
46
|
50
|
|
|
|
282
|
$_Code .= ";\n\$_Class = __PACKAGE__;" if $_Code =~ /package/; |
1053
|
46
|
|
|
|
|
166
|
$_Code = "package $_Class;\n$_Code"; |
1054
|
46
|
|
|
|
|
100
|
undef $_Class; |
1055
|
46
|
|
|
|
|
245
|
debug "going to eval perl code: << '...'\n$_Code\n..."; |
1056
|
|
|
|
|
|
|
|
1057
|
46
|
|
|
|
|
533
|
local $Zoidberg::Utils::Error::Scope = ['zoid', 0]; |
1058
|
46
|
|
|
|
|
237
|
$_ = $$shell{topic}; |
1059
|
46
|
50
|
|
|
|
198
|
$? = $$shell{error}{exit_status} if ref $$shell{error}; |
1060
|
46
|
50
|
|
16
|
|
9630
|
ref($_Code) ? eval { $_Code->() } : eval $_Code; |
|
0
|
|
|
16
|
|
0
|
|
|
16
|
|
|
|
|
224
|
|
|
16
|
|
|
|
|
32
|
|
|
16
|
|
|
|
|
1101
|
|
|
16
|
|
|
|
|
215
|
|
|
16
|
|
|
|
|
23
|
|
|
16
|
|
|
|
|
166160
|
|
1061
|
46
|
50
|
|
|
|
1260
|
if ($@) { # post parse errors |
1062
|
0
|
0
|
|
|
|
0
|
die if ref $@; # just propagate the exception |
1063
|
0
|
|
|
|
|
0
|
$@ =~ s/ at \(eval \d+\) line (\d+)(\.|,.*\.)$/ at line $1/; |
1064
|
0
|
|
|
|
|
0
|
error { string => $@, scope => [] }; |
1065
|
|
|
|
|
|
|
} |
1066
|
|
|
|
|
|
|
else { |
1067
|
46
|
|
|
|
|
278
|
$$shell{topic} = $_; |
1068
|
46
|
50
|
|
|
|
198
|
$$shell{settings}{perl}{namespace} = $_Class if $_Class; |
1069
|
46
|
50
|
|
|
|
533
|
print "\n" if $$shell{_settings}{interactive}; # ugly hack |
1070
|
|
|
|
|
|
|
} |
1071
|
|
|
|
|
|
|
} |
1072
|
|
|
|
|
|
|
|
1073
|
|
|
|
|
|
|
# ############## # |
1074
|
|
|
|
|
|
|
# some functions # |
1075
|
|
|
|
|
|
|
# ############## # |
1076
|
|
|
|
|
|
|
|
1077
|
|
|
|
|
|
|
=item mode [mode] |
1078
|
|
|
|
|
|
|
|
1079
|
|
|
|
|
|
|
Without arguments prints the current mode. |
1080
|
|
|
|
|
|
|
With arguments sets the mode. |
1081
|
|
|
|
|
|
|
|
1082
|
|
|
|
|
|
|
=cut |
1083
|
|
|
|
|
|
|
|
1084
|
|
|
|
|
|
|
sub mode { |
1085
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
1086
|
0
|
0
|
|
|
|
0
|
unless (@_) { |
1087
|
0
|
0
|
|
|
|
0
|
output $$self{_settings}{mode} if $$self{_settings}{mode}; |
1088
|
0
|
|
|
|
|
0
|
return; |
1089
|
|
|
|
|
|
|
} |
1090
|
0
|
|
|
|
|
0
|
my $mode = shift; |
1091
|
0
|
0
|
0
|
|
|
0
|
if ($mode eq '-' or $mode eq 'default') { |
1092
|
0
|
|
|
|
|
0
|
$$self{settings}{mode} = undef; |
1093
|
|
|
|
|
|
|
} |
1094
|
|
|
|
|
|
|
else { |
1095
|
0
|
0
|
|
|
|
0
|
my $m = ($mode =~ /::/) ? $mode : uc($mode); |
1096
|
0
|
|
|
|
|
0
|
error $mode.': No such context defined' |
1097
|
0
|
0
|
0
|
|
|
0
|
unless grep {lc($mode) eq $_} qw/perl cmd sh/ |
1098
|
|
|
|
|
|
|
or $$self{parser}{$m}{handler} ; # allow for autoloading |
1099
|
0
|
|
|
|
|
0
|
$$self{settings}{mode} = $mode; |
1100
|
|
|
|
|
|
|
} |
1101
|
|
|
|
|
|
|
} |
1102
|
|
|
|
|
|
|
|
1103
|
|
|
|
|
|
|
=item plug |
1104
|
|
|
|
|
|
|
|
1105
|
|
|
|
|
|
|
TODO |
1106
|
|
|
|
|
|
|
|
1107
|
|
|
|
|
|
|
=cut |
1108
|
|
|
|
|
|
|
|
1109
|
|
|
|
|
|
|
sub plug { |
1110
|
32
|
|
|
32
|
1
|
70
|
my $self = shift; |
1111
|
32
|
|
|
|
|
509
|
my ($opts, $args) = getopt 'list,l verbose,v @', @_; |
1112
|
32
|
50
|
|
|
|
137
|
if ($$opts{list}) { # list info |
1113
|
0
|
|
|
|
|
0
|
my @items = keys %{$$self{objects}}; |
|
0
|
|
|
|
|
0
|
|
1114
|
0
|
0
|
|
|
|
0
|
if (@$args) { |
1115
|
0
|
|
|
|
|
0
|
my $re = join '|', @$args; |
1116
|
0
|
|
|
|
|
0
|
@items = grep m/$re/i, @items; |
1117
|
|
|
|
|
|
|
} |
1118
|
0
|
0
|
|
|
|
0
|
if ($$opts{verbose}) { # FIXME nicer PLuginHash interface for this |
1119
|
0
|
|
|
|
|
0
|
my ($raw, $meta) = @{ tied( %{$$self{objects}} ) }; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
1120
|
0
|
0
|
|
|
|
0
|
@items = map { |
1121
|
0
|
|
|
|
|
0
|
$_ .' '. $$meta{$_}{module} |
1122
|
|
|
|
|
|
|
. (exists($$raw{$_}) ? ' (loaded)' : '') |
1123
|
|
|
|
|
|
|
} @items; |
1124
|
|
|
|
|
|
|
} |
1125
|
0
|
|
|
|
|
0
|
output \@items; |
1126
|
|
|
|
|
|
|
} |
1127
|
|
|
|
|
|
|
else { # load plugin |
1128
|
32
|
50
|
|
|
|
106
|
error 'usage: plug name [args]' unless @$args; |
1129
|
32
|
50
|
|
|
|
632
|
error $$args[0].': no such plugin' |
1130
|
|
|
|
|
|
|
unless exists $$self{objects}{ $$args[0] }; |
1131
|
0
|
|
|
|
|
0
|
tied( %{$$self{objects}} )->load(@$args); |
|
0
|
|
|
|
|
0
|
|
1132
|
|
|
|
|
|
|
} |
1133
|
|
|
|
|
|
|
} |
1134
|
|
|
|
|
|
|
|
1135
|
|
|
|
|
|
|
=item unplug |
1136
|
|
|
|
|
|
|
|
1137
|
|
|
|
|
|
|
TODO |
1138
|
|
|
|
|
|
|
|
1139
|
|
|
|
|
|
|
=cut |
1140
|
|
|
|
|
|
|
|
1141
|
|
|
|
|
|
|
sub unplug { |
1142
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
1143
|
0
|
|
|
|
|
0
|
my ($opt, $args) = getopt 'all,a @', @_; |
1144
|
0
|
0
|
|
|
|
0
|
if ($$opt{all}) { tied( %{$$self{objects}} )->CLEAR() } |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
1145
|
|
|
|
|
|
|
else { |
1146
|
0
|
0
|
|
|
|
0
|
error "usage: unplug name" unless @$args == 1; |
1147
|
0
|
|
|
|
|
0
|
delete $$self{objects}{$$args[0]}; |
1148
|
|
|
|
|
|
|
} |
1149
|
|
|
|
|
|
|
} |
1150
|
|
|
|
|
|
|
|
1151
|
0
|
|
|
0
|
0
|
0
|
sub dev_null {} # does absolutely nothing |
1152
|
|
|
|
|
|
|
|
1153
|
|
|
|
|
|
|
sub stdin { # stub STDIN input |
1154
|
0
|
|
|
0
|
0
|
0
|
my (undef, $prompt, $preput) = @_; |
1155
|
0
|
|
|
|
|
0
|
local $/ = "\n"; |
1156
|
0
|
0
|
|
|
|
0
|
print $prompt if length $prompt; |
1157
|
0
|
0
|
|
|
|
0
|
my $string = length($preput) ? $preput . : ; |
1158
|
0
|
|
|
|
|
0
|
output $string; |
1159
|
|
|
|
|
|
|
}; |
1160
|
|
|
|
|
|
|
|
1161
|
|
|
|
|
|
|
sub list_clothes { |
1162
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
1163
|
0
|
|
|
|
|
0
|
my @return = map {'{'.$_.'}'} sort @{$self->{_settings}{clothes}{keys}}; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
1164
|
0
|
|
|
|
|
0
|
push @return, sort @{$self->{_settings}{clothes}{subs}}; |
|
0
|
|
|
|
|
0
|
|
1165
|
0
|
|
|
|
|
0
|
return [@return]; |
1166
|
|
|
|
|
|
|
} |
1167
|
|
|
|
|
|
|
|
1168
|
|
|
|
|
|
|
# ########### # |
1169
|
|
|
|
|
|
|
# Event logic # |
1170
|
|
|
|
|
|
|
# ########### # |
1171
|
|
|
|
|
|
|
|
1172
|
|
|
|
|
|
|
sub broadcast { # eval to be sure we return |
1173
|
1375367
|
|
|
1375367
|
0
|
6054496
|
my ($self, $event) = (shift(), shift()); |
1174
|
1375367
|
100
|
|
|
|
16034998
|
return unless exists $self->{events}{$event}; |
1175
|
311
|
|
|
|
|
5099
|
debug "Broadcasting event: $event"; |
1176
|
311
|
|
|
|
|
2832
|
for my $sub ($$self{events}->stack($event)) { |
1177
|
327
|
|
|
|
|
995
|
eval { $sub->($event, @_) }; |
|
327
|
|
|
|
|
2833
|
|
1178
|
327
|
50
|
|
|
|
15886
|
complain("$sub died on event $event ($@)") if $@; |
1179
|
|
|
|
|
|
|
} |
1180
|
|
|
|
|
|
|
} |
1181
|
|
|
|
|
|
|
|
1182
|
0
|
|
|
0
|
0
|
0
|
sub call { bug 'deprecated routine used' } |
1183
|
|
|
|
|
|
|
|
1184
|
|
|
|
|
|
|
# ########### # |
1185
|
|
|
|
|
|
|
# auto loader # |
1186
|
|
|
|
|
|
|
# ########### # |
1187
|
|
|
|
|
|
|
|
1188
|
|
|
|
|
|
|
our $ERROR_CALLER; |
1189
|
|
|
|
|
|
|
|
1190
|
|
|
|
|
|
|
sub AUTOLOAD { |
1191
|
9
|
|
|
9
|
|
2428
|
my $self = shift; |
1192
|
9
|
|
|
|
|
109
|
my $call = (split/::/,$AUTOLOAD)[-1]; |
1193
|
|
|
|
|
|
|
|
1194
|
9
|
|
|
|
|
81
|
local $ERROR_CALLER = 1; |
1195
|
9
|
50
|
|
|
|
84
|
error "Undefined subroutine &Zoidberg::$call called" unless ref $self; |
1196
|
9
|
|
|
|
|
212
|
debug "Zoidberg::AUTOLOAD got $call"; |
1197
|
|
|
|
|
|
|
|
1198
|
9
|
50
|
|
|
|
207
|
if (exists $self->{objects}{$call}) { |
1199
|
19
|
|
|
19
|
|
148
|
no strict 'refs'; |
|
19
|
|
|
|
|
54
|
|
|
19
|
|
|
|
|
17365
|
|
1200
|
0
|
|
|
0
|
|
0
|
*{ref($self).'::'.$call} = sub { return $self->{objects}{$call} }; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
1201
|
0
|
|
|
|
|
0
|
goto \&{$call}; |
|
0
|
|
|
|
|
0
|
|
1202
|
|
|
|
|
|
|
} |
1203
|
|
|
|
|
|
|
else { # Shell like behaviour |
1204
|
9
|
|
|
|
|
108
|
debug "No such method or object: '$call', trying to shell() it"; |
1205
|
9
|
|
|
|
|
69
|
@_ = ([$call, @_]); # force words parsing |
1206
|
9
|
|
|
|
|
103
|
goto \&Zoidberg::Shell::shell; |
1207
|
|
|
|
|
|
|
} |
1208
|
|
|
|
|
|
|
} |
1209
|
|
|
|
|
|
|
|
1210
|
|
|
|
|
|
|
# ############# # |
1211
|
|
|
|
|
|
|
# Exit routines # |
1212
|
|
|
|
|
|
|
# ############# # |
1213
|
|
|
|
|
|
|
|
1214
|
|
|
|
|
|
|
=item C |
1215
|
|
|
|
|
|
|
|
1216
|
|
|
|
|
|
|
Called by plugins to exit zoidberg -- this ends a interactive C |
1217
|
|
|
|
|
|
|
loop. This does not clean up or destroy any objects, C can be |
1218
|
|
|
|
|
|
|
called again to restart it. |
1219
|
|
|
|
|
|
|
|
1220
|
|
|
|
|
|
|
=cut |
1221
|
|
|
|
|
|
|
|
1222
|
|
|
|
|
|
|
sub exit { |
1223
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
1224
|
0
|
0
|
0
|
|
|
0
|
if (@{$$self{jobs}} and ! $$self{_warned_bout_jobs}) { |
|
0
|
|
|
|
|
0
|
|
1225
|
0
|
|
|
|
|
0
|
complain "There are unfinished jobs"; |
1226
|
0
|
|
|
|
|
0
|
$$self{_warned_bout_jobs}++; |
1227
|
|
|
|
|
|
|
} |
1228
|
|
|
|
|
|
|
else { |
1229
|
0
|
|
|
|
|
0
|
message join ' ', @_; |
1230
|
0
|
|
|
|
|
0
|
$self->{_continue} = 0; |
1231
|
|
|
|
|
|
|
} |
1232
|
|
|
|
|
|
|
# FIXME this should force ReadLine to quit |
1233
|
|
|
|
|
|
|
} |
1234
|
|
|
|
|
|
|
|
1235
|
|
|
|
|
|
|
=item C |
1236
|
|
|
|
|
|
|
|
1237
|
|
|
|
|
|
|
This method should be called to clean up the shell objects. |
1238
|
|
|
|
|
|
|
A C method will be called recursively for all secondairy objects. |
1239
|
|
|
|
|
|
|
|
1240
|
|
|
|
|
|
|
=cut |
1241
|
|
|
|
|
|
|
|
1242
|
|
|
|
|
|
|
sub round_up { |
1243
|
2
|
|
|
2
|
1
|
533
|
my $self = shift; |
1244
|
2
|
|
|
|
|
33
|
$self->broadcast('exit'); |
1245
|
2
|
50
|
|
|
|
35
|
if ($self->{round_up}) { |
1246
|
2
|
|
|
|
|
13
|
tied( %{$$self{objects}} )->round_up(); # round up loaded plugins |
|
2
|
|
|
|
|
50
|
|
1247
|
2
|
|
|
|
|
20
|
Zoidberg::Contractor::round_up($self); |
1248
|
2
|
|
|
|
|
0
|
undef $self->{round_up}; |
1249
|
|
|
|
|
|
|
} |
1250
|
|
|
|
|
|
|
} |
1251
|
|
|
|
|
|
|
|
1252
|
|
|
|
|
|
|
sub DESTROY { |
1253
|
1
|
|
|
1
|
|
472
|
my $self = shift; |
1254
|
1
|
50
|
|
|
|
5
|
if ($$self{round_up}) { |
1255
|
0
|
|
|
|
|
0
|
warn "Zoidberg was not properly cleaned up.\n"; |
1256
|
0
|
|
|
|
|
0
|
$self->round_up; |
1257
|
|
|
|
|
|
|
} |
1258
|
1
|
|
|
|
|
123
|
delete $OBJECTS{"$self"}; |
1259
|
|
|
|
|
|
|
} |
1260
|
|
|
|
|
|
|
|
1261
|
|
|
|
|
|
|
package Zoidberg::SettingsHash; |
1262
|
|
|
|
|
|
|
|
1263
|
|
|
|
|
|
|
our $VERSION = '0.981'; |
1264
|
|
|
|
|
|
|
|
1265
|
|
|
|
|
|
|
sub TIEHASH { |
1266
|
16
|
|
|
16
|
|
47
|
my ($class, $ref, $shell) = @_; |
1267
|
16
|
|
|
|
|
276
|
bless [$ref, $shell], $class; |
1268
|
|
|
|
|
|
|
} |
1269
|
|
|
|
|
|
|
|
1270
|
|
|
|
|
|
|
sub STORE { |
1271
|
103
|
|
|
103
|
|
2786
|
my ($self, $key, $val) = @_; |
1272
|
103
|
|
|
|
|
315
|
my $old = $$self[0]{$key}; |
1273
|
103
|
|
|
|
|
504
|
$$self[0]{$key} = $val; |
1274
|
103
|
|
|
|
|
915
|
$$self[1]->broadcast('set_'.$key, $val, $old); # new, old |
1275
|
103
|
|
|
|
|
455
|
1; |
1276
|
|
|
|
|
|
|
} |
1277
|
|
|
|
|
|
|
|
1278
|
|
|
|
|
|
|
#sub set_default { |
1279
|
|
|
|
|
|
|
# my ($self, $key, @list) = @_; |
1280
|
|
|
|
|
|
|
# $$self[0]{_SettingsHash_def}{$key} = \@list; |
1281
|
|
|
|
|
|
|
#} |
1282
|
|
|
|
|
|
|
|
1283
|
|
|
|
|
|
|
sub DELETE { |
1284
|
0
|
|
|
0
|
|
0
|
my ($self, $key) = @_; |
1285
|
0
|
|
|
|
|
0
|
my $val = delete $$self[0]{$key}; |
1286
|
0
|
|
|
|
|
0
|
$$self[1]->broadcast('set_'.$key, undef, $val); # new, old |
1287
|
0
|
|
|
|
|
0
|
return $val; |
1288
|
|
|
|
|
|
|
} |
1289
|
|
|
|
|
|
|
|
1290
|
0
|
|
|
0
|
|
0
|
sub CLEAR { $_[0]->DELETE($_) for keys %{$_[0][0]} } |
|
0
|
|
|
|
|
0
|
|
1291
|
|
|
|
|
|
|
|
1292
|
|
|
|
|
|
|
sub FETCH { |
1293
|
8149
|
|
|
8149
|
|
97984
|
return $_[0][0]{$_[1]} |
1294
|
|
|
|
|
|
|
# unless !defined $_[0][0]{$_[1]} |
1295
|
|
|
|
|
|
|
# and exists $_[0][0]{_SettingsHash_def}{$_[1]}; |
1296
|
|
|
|
|
|
|
# check for default (environment) values |
1297
|
|
|
|
|
|
|
# for my $def (@{$_[0][0]{_SettingsHash_def}{$_[1]}}) { |
1298
|
|
|
|
|
|
|
# $def = $ENV{$1} if $def =~ /^\$(.*)/; |
1299
|
|
|
|
|
|
|
# return $def if defined $def; |
1300
|
|
|
|
|
|
|
# } |
1301
|
|
|
|
|
|
|
} |
1302
|
|
|
|
|
|
|
|
1303
|
0
|
|
|
0
|
|
0
|
sub EXISTS { exists $_[0][0]{$_[1]} } |
1304
|
|
|
|
|
|
|
|
1305
|
0
|
|
|
0
|
|
0
|
sub FIRSTKEY { my $a = scalar keys %{$_[0][0]}; each %{$_[0][0]} } |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
1306
|
|
|
|
|
|
|
|
1307
|
0
|
|
|
0
|
|
0
|
sub NEXTKEY { each %{$_[0][0]} } |
|
0
|
|
|
|
|
0
|
|
1308
|
|
|
|
|
|
|
|
1309
|
|
|
|
|
|
|
package Zoidberg::Eval; |
1310
|
|
|
|
|
|
|
|
1311
|
|
|
|
|
|
|
our $VERSION = '0.981'; |
1312
|
|
|
|
|
|
|
|
1313
|
|
|
|
|
|
|
# included to bootstrap a bit of default environment |
1314
|
|
|
|
|
|
|
# for the perl syntax |
1315
|
|
|
|
|
|
|
|
1316
|
19
|
|
|
19
|
|
334
|
use strict; |
|
19
|
|
|
|
|
39
|
|
|
19
|
|
|
|
|
925
|
|
1317
|
19
|
|
|
19
|
|
131
|
use vars qw/$AUTOLOAD/; |
|
19
|
|
|
|
|
40
|
|
|
19
|
|
|
|
|
1729
|
|
1318
|
|
|
|
|
|
|
|
1319
|
19
|
|
|
19
|
|
25991
|
use Data::Dumper; |
|
19
|
|
|
|
|
144935
|
|
|
19
|
|
|
|
|
1465
|
|
1320
|
19
|
|
|
19
|
|
2123
|
use Zoidberg::Shell qw/:all/; |
|
19
|
|
|
|
|
42
|
|
|
19
|
|
|
|
|
229
|
|
1321
|
19
|
|
|
19
|
|
5956
|
use Zoidberg::Utils qw/:error :output :fs regex_glob/; |
|
19
|
|
|
|
|
38
|
|
|
19
|
|
|
|
|
135
|
|
1322
|
|
|
|
|
|
|
require Env; |
1323
|
|
|
|
|
|
|
|
1324
|
|
|
|
|
|
|
$| = 1; |
1325
|
|
|
|
|
|
|
$Data::Dumper::Sortkeys = 1; |
1326
|
|
|
|
|
|
|
|
1327
|
|
|
|
|
|
|
sub pp { # pretty print |
1328
|
0
|
0
|
|
0
|
|
0
|
local $Data::Dumper::Maxdepth = shift if $_[0] =~ /^\d+$/; |
1329
|
0
|
0
|
|
|
|
0
|
if (wantarray) { return Dumper @_ } |
|
0
|
|
|
|
|
0
|
|
1330
|
0
|
|
|
|
|
0
|
else { print Dumper @_ } |
1331
|
|
|
|
|
|
|
} |
1332
|
|
|
|
|
|
|
|
1333
|
|
|
|
|
|
|
{ |
1334
|
19
|
|
|
19
|
|
6664
|
no warnings; |
|
19
|
|
|
|
|
53
|
|
|
19
|
|
|
|
|
4536
|
|
1335
|
|
|
|
|
|
|
sub AUTOLOAD { |
1336
|
|
|
|
|
|
|
## Code inspired by Shell.pm ## |
1337
|
0
|
|
|
0
|
|
0
|
my $cmd = (split/::/, $AUTOLOAD)[-1]; |
1338
|
0
|
0
|
|
|
|
0
|
return undef if $cmd eq 'DESTROY'; |
1339
|
0
|
0
|
|
|
|
0
|
shift if ref($_[0]) eq __PACKAGE__; |
1340
|
0
|
|
|
|
|
0
|
debug "Zoidberg::Eval::AUTOLOAD got $cmd"; |
1341
|
0
|
|
|
|
|
0
|
@_ = ([$cmd, @_]); # force words |
1342
|
0
|
0
|
|
|
|
0
|
unshift @{$_[0]}, '!' |
|
0
|
|
|
|
|
0
|
|
1343
|
|
|
|
|
|
|
if lc( $Zoidberg::CURRENT->{settings}{mode} ) eq 'perl'; |
1344
|
0
|
|
|
|
|
0
|
goto \&shell; |
1345
|
|
|
|
|
|
|
} |
1346
|
|
|
|
|
|
|
} |
1347
|
|
|
|
|
|
|
|
1348
|
|
|
|
|
|
|
1; |
1349
|
|
|
|
|
|
|
|
1350
|
|
|
|
|
|
|
__END__ |