line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Zoidberg::Fish::Commands; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
our $VERSION = '0.981'; |
4
|
|
|
|
|
|
|
|
5
|
17
|
|
|
17
|
|
1211
|
use strict; |
|
17
|
|
|
|
|
20
|
|
|
17
|
|
|
|
|
616
|
|
6
|
|
|
|
|
|
|
#use AutoLoader 'AUTOLOAD'; |
7
|
17
|
|
|
17
|
|
103
|
use Cwd; |
|
17
|
|
|
|
|
34
|
|
|
17
|
|
|
|
|
1286
|
|
8
|
17
|
|
|
17
|
|
118
|
use Env qw/@CDPATH @DIRSTACK/; |
|
17
|
|
|
|
|
34
|
|
|
17
|
|
|
|
|
155
|
|
9
|
17
|
|
|
17
|
|
3790
|
use base 'Zoidberg::Fish'; |
|
17
|
|
|
|
|
34
|
|
|
17
|
|
|
|
|
10358
|
|
10
|
17
|
|
|
17
|
|
121
|
use Zoidberg::Utils qw/:default path getopt usage path2hashref/; |
|
17
|
|
|
|
|
35
|
|
|
17
|
|
|
|
|
93
|
|
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
# FIXME what to do with commands that use block input ? |
13
|
|
|
|
|
|
|
# currently hacked with statements like join(' ', @_) |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
=head1 NAME |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
Zoidberg::Fish::Commands - Zoidberg plugin with builtin commands |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
=head1 SYNOPSIS |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
This module is a Zoidberg plugin, see Zoidberg::Fish for details. |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
=head1 DESCRIPTION |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
This object contains internal/built-in commands |
26
|
|
|
|
|
|
|
for the Zoidberg shell. |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
=head2 EXPORT |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
None by default. |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
=cut |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
sub init { |
35
|
16
|
|
|
16
|
1
|
165
|
$_[0]{dir_hist} = [$ENV{PWD}]; # FIXME try to read log first |
36
|
16
|
|
|
|
|
100
|
$_[0]{_dir_hist_i} = 0; |
37
|
|
|
|
|
|
|
} |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
=head1 COMMANDS |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
=over 4 |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
=item cd [-v|--verbose] [I|-|(+|-)I] |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
=item cd (-l|--list) |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
Changes the current working directory to I. |
48
|
|
|
|
|
|
|
When used with a single dash changes to OLDPWD. |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
This command uses the environment variable 'CDPATH'. It serves as |
51
|
|
|
|
|
|
|
a search path when the directory you want to change to isn't found |
52
|
|
|
|
|
|
|
in the current directory. |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
This command also uses a directory history. |
55
|
|
|
|
|
|
|
The '-number' and '+number' switches are used to change directory |
56
|
|
|
|
|
|
|
to an positive or negative offset in this history. |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
=cut |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
sub cd { # TODO [-L|-P] see man 1 bash |
61
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
62
|
0
|
|
|
|
|
0
|
my ($dir, $done, $verbose); |
63
|
0
|
0
|
0
|
|
|
0
|
if (@_ == 1 and $_[0] eq '-') { # cd - |
64
|
0
|
|
|
|
|
0
|
$dir = $ENV{OLDPWD}; |
65
|
0
|
|
|
|
|
0
|
$verbose++; |
66
|
|
|
|
|
|
|
} |
67
|
|
|
|
|
|
|
else { |
68
|
0
|
|
|
|
|
0
|
my ($opts, $args) = getopt 'list,-l verbose,-v +* -* @', @_; |
69
|
0
|
0
|
|
|
|
0
|
if (@$args) { # 'normal' cd |
70
|
0
|
0
|
|
|
|
0
|
error 'to many arguments' if @$args > 1; |
71
|
0
|
|
|
|
|
0
|
$dir = $$args[0]; |
72
|
|
|
|
|
|
|
} |
73
|
|
|
|
|
|
|
|
74
|
0
|
0
|
|
|
|
0
|
if (%$opts) { |
75
|
0
|
0
|
|
|
|
0
|
$verbose++ if $$opts{verbose}; |
76
|
0
|
0
|
|
|
|
0
|
if (my ($opt) = grep /^[+-][^\d+lv]$/, @{$$opts{_opts}}) { |
|
0
|
0
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
|
77
|
0
|
|
|
|
|
0
|
error "unrecognized option '$opt'"; |
78
|
|
|
|
|
|
|
} |
79
|
0
|
|
|
|
|
0
|
elsif ($$opts{list}) { # list dirhist |
80
|
0
|
0
|
|
|
|
0
|
error 'to many args' if @$args; |
81
|
0
|
|
|
|
|
0
|
return $$self{shell}->builtin(qw/history --type pwd +1 -2/); # last pwd is current |
82
|
|
|
|
|
|
|
} |
83
|
|
|
|
|
|
|
elsif (my ($idx) = grep /^[+-]\d+$/, @{$$opts{_opts}}) { |
84
|
|
|
|
|
|
|
# cd back/forward in history |
85
|
0
|
0
|
|
|
|
0
|
error 'to many args' if @$args; |
86
|
0
|
0
|
|
|
|
0
|
$idx -= 1 if $idx < 1; # last pwd is current |
87
|
0
|
|
|
|
|
0
|
($dir) = $$self{shell}->builtin(qw/history --type pwd/, $idx, $idx); |
88
|
0
|
|
|
|
|
0
|
$verbose++; |
89
|
|
|
|
|
|
|
} |
90
|
|
|
|
|
|
|
} |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
|
93
|
0
|
0
|
|
|
|
0
|
if ($dir) { |
94
|
|
|
|
|
|
|
# due to things like autofs we must *try* every possibility |
95
|
|
|
|
|
|
|
# instead of checking '-d' |
96
|
0
|
|
|
|
|
0
|
$done = chdir path($dir); |
97
|
0
|
0
|
|
|
|
0
|
if ($done) { message $dir if $verbose } |
|
0
|
0
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
|
98
|
|
|
|
|
|
|
elsif ($dir !~ m#^\.{0,2}/#) { |
99
|
0
|
|
|
|
|
0
|
for (@CDPATH) { |
100
|
0
|
0
|
|
|
|
0
|
next unless $done = chdir path("$_/$dir"); |
101
|
0
|
|
|
|
|
0
|
message "$_/$dir"; # verbose |
102
|
0
|
|
|
|
|
0
|
last; |
103
|
|
|
|
|
|
|
} |
104
|
|
|
|
|
|
|
} |
105
|
|
|
|
|
|
|
} |
106
|
|
|
|
|
|
|
else { |
107
|
0
|
0
|
|
|
|
0
|
message $ENV{HOME} if $verbose; |
108
|
0
|
|
|
|
|
0
|
$done = chdir($ENV{HOME}); |
109
|
|
|
|
|
|
|
} |
110
|
|
|
|
|
|
|
|
111
|
0
|
0
|
|
|
|
0
|
unless ($done) { |
112
|
0
|
0
|
|
|
|
0
|
error $dir.': Not a directory' unless -d $dir; |
113
|
0
|
|
|
|
|
0
|
error "Could not change to dir: $dir"; |
114
|
|
|
|
|
|
|
} |
115
|
|
|
|
|
|
|
} |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
#1; |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
#__END__ |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
=item exec I |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
Execute I. This effectively ends the shell session, |
124
|
|
|
|
|
|
|
process flow will B return to the prompt. |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
=cut |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
sub exec { # FIXME not completely stable I'm afraid |
129
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
130
|
0
|
|
|
|
|
0
|
$self->{shell}->{round_up} = 0; |
131
|
0
|
|
|
|
|
0
|
$self->{shell}->shell_string({fork_job => 0}, join(" ", @_)); |
132
|
|
|
|
|
|
|
# the process should not make it to this line |
133
|
0
|
|
|
|
|
0
|
$self->{shell}->{round_up} = 1; |
134
|
0
|
|
|
|
|
0
|
$self->{shell}->exit; |
135
|
|
|
|
|
|
|
} |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
=item eval I |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
Eval I like a shell command. Main use of this is to |
140
|
|
|
|
|
|
|
run code stored in variables. |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
=cut |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
sub eval { |
145
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
146
|
0
|
|
|
|
|
0
|
$$self{shell}->shell(@_); |
147
|
|
|
|
|
|
|
} |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
=item export I=I |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
Set the environment variable I to I. |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
TODO explain how export moved varraibles between the perl namespace and the environment |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
=cut |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
sub export { # TODO if arg == 1 and not hash then export var from zoid::eval to env :D |
158
|
5
|
|
|
5
|
1
|
12
|
my $self = shift; |
159
|
5
|
|
|
|
|
42
|
my ($opt, $args, $vals) = getopt 'unexport,n print,p *', @_; |
160
|
5
|
|
|
|
|
37
|
my $class = $$self{shell}{settings}{perl}{namespace}; |
161
|
17
|
|
|
17
|
|
20218
|
no strict 'refs'; |
|
17
|
|
|
|
|
36
|
|
|
17
|
|
|
|
|
407291
|
|
162
|
5
|
100
|
|
|
|
171
|
if ($$opt{unexport}) { |
|
|
50
|
|
|
|
|
|
163
|
1
|
|
|
|
|
3
|
for (@$args) { |
164
|
1
|
|
|
|
|
8
|
s/^([\$\@]?)//; |
165
|
1
|
50
|
|
|
|
7
|
next unless exists $ENV{$_}; |
166
|
1
|
50
|
|
|
|
4
|
if ($1 eq '@') { @{$class.'::'.$_} = split ':', delete $ENV{$_} } |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
167
|
1
|
|
|
|
|
6
|
else { ${$class.'::'.$_} = delete $ENV{$_} } |
|
1
|
|
|
|
|
17
|
|
168
|
|
|
|
|
|
|
} |
169
|
|
|
|
|
|
|
} |
170
|
|
|
|
|
|
|
elsif ($$opt{print}) { |
171
|
0
|
|
|
|
|
0
|
output [ map { |
172
|
0
|
|
|
|
|
0
|
my $val = $ENV{$_}; |
173
|
0
|
|
|
|
|
0
|
$val =~ s/'/\\'/g; |
174
|
0
|
|
|
|
|
0
|
"export $_='$val'"; |
175
|
|
|
|
|
|
|
} sort keys %ENV ]; |
176
|
|
|
|
|
|
|
} |
177
|
|
|
|
|
|
|
else { # really export |
178
|
4
|
|
|
|
|
10
|
for (@$args) { |
179
|
6
|
|
|
|
|
45
|
s/^([\$\@]?)//; |
180
|
6
|
50
|
|
|
|
27
|
if ($1 eq '@') { # arrays |
181
|
0
|
|
|
|
|
0
|
my @env = defined($$vals{$_}) ? (@{$$vals{$_}}) : |
|
0
|
|
|
|
|
0
|
|
182
|
0
|
0
|
|
|
|
0
|
defined(*{$class.'::'.$_}{ARRAY}) ? (@{$class.'::'.$_}) : () ; |
|
0
|
0
|
|
|
|
0
|
|
183
|
0
|
0
|
|
|
|
0
|
$ENV{$_} = join ':', @env if @env; |
184
|
|
|
|
|
|
|
} |
185
|
|
|
|
|
|
|
else { # scalars |
186
|
5
|
|
|
|
|
54
|
my $env = defined($$vals{$_}) ? $$vals{$_} : |
187
|
6
|
100
|
|
|
|
30
|
defined(${$class.'::'.$_}) ? ${$class.'::'.$_} : undef ; |
|
2
|
100
|
|
|
|
12
|
|
188
|
6
|
100
|
|
|
|
131
|
$ENV{$_} = $env if defined $env; |
189
|
|
|
|
|
|
|
} |
190
|
|
|
|
|
|
|
} |
191
|
|
|
|
|
|
|
} |
192
|
|
|
|
|
|
|
} |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
=item setenv I I |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
Like B, but with a slightly different syntax. |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
=cut |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
sub setenv { |
201
|
0
|
|
|
0
|
1
|
0
|
shift; |
202
|
0
|
|
|
|
|
0
|
my $var = shift; |
203
|
0
|
|
|
|
|
0
|
$ENV{$var} = join ' ', @_; |
204
|
|
|
|
|
|
|
} |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
=item unsetenv I |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
Set I to undefined. |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
=cut |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
sub unsetenv { |
213
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
214
|
0
|
|
|
|
|
0
|
delete $ENV{$_} for @_; |
215
|
|
|
|
|
|
|
} |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
=item set [+-][abCefnmnuvx] |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
=item set [+o|-o] I |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
Set or unset a shell option. Although sometimes confusing |
222
|
|
|
|
|
|
|
a '+' switch unsets the option, while the '-' switch sets it. |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
Short options correspond to the following names: |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
a => allexport * |
227
|
|
|
|
|
|
|
b => notify |
228
|
|
|
|
|
|
|
C => noclobber |
229
|
|
|
|
|
|
|
e => errexit * |
230
|
|
|
|
|
|
|
f => noglob |
231
|
|
|
|
|
|
|
m => monitor * |
232
|
|
|
|
|
|
|
n => noexec * |
233
|
|
|
|
|
|
|
u => nounset * |
234
|
|
|
|
|
|
|
v => verbose |
235
|
|
|
|
|
|
|
x => xtrace * |
236
|
|
|
|
|
|
|
*) Not yet supported by the rest of the shell |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
See L for a description what these and other options do. |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
FIXME takes also hash arguments |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
=cut |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
sub set { |
245
|
13
|
|
|
13
|
1
|
80
|
my $self = shift; |
246
|
13
|
50
|
|
|
|
68
|
unless (@_) { error 'should print out all shell vars, but we don\'t have these' } |
|
0
|
|
|
|
|
0
|
|
247
|
13
|
|
|
|
|
187
|
my ($opts, $keys, $vals) = getopt |
248
|
|
|
|
|
|
|
'allexport,a notify,b noclobber,C errexit,e |
249
|
|
|
|
|
|
|
noglob,f monitor,m noexec,n nounset,u |
250
|
|
|
|
|
|
|
verbose,v xtrace,x -o@ +o@ *', @_; |
251
|
|
|
|
|
|
|
# other posix options: ignoreeof, nolog & vi - bash knows a bit more |
252
|
|
|
|
|
|
|
|
253
|
13
|
|
|
|
|
38
|
my %settings; |
254
|
13
|
100
|
|
|
|
70
|
if (%$opts) { |
255
|
2
|
|
|
|
|
18
|
$settings{$_} = $$opts{$_} |
256
|
2
|
|
|
|
|
7
|
for grep {$_ !~ /^[+-]/} @{$$opts{_opts}}; |
|
2
|
|
|
|
|
8
|
|
257
|
2
|
100
|
|
|
|
6
|
if ($$opts{'-o'}) { $settings{$_} = 1 for @{$$opts{'-o'}} } |
|
1
|
|
|
|
|
11
|
|
|
1
|
|
|
|
|
12
|
|
258
|
2
|
100
|
|
|
|
8
|
if ($$opts{'+o'}) { $settings{$_} = 0 for @{$$opts{'+o'}} } |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
11
|
|
259
|
|
|
|
|
|
|
} |
260
|
|
|
|
|
|
|
|
261
|
13
|
100
|
|
|
|
49
|
for (@$keys) { $settings{$_} = defined($$vals{$_}) ? delete($$vals{$_}) : 1 } |
|
11
|
|
|
|
|
88
|
|
262
|
|
|
|
|
|
|
|
263
|
13
|
|
|
|
|
104
|
for my $opt (keys %settings) { |
264
|
13
|
100
|
|
|
|
100
|
if ($opt =~ m#/#) { |
265
|
9
|
|
|
|
|
121
|
my ($hash, $key, $path) = path2hashref($$self{shell}{settings}, $opt); |
266
|
9
|
50
|
|
|
|
47
|
error "$path: no such hash in settings" unless $hash; |
267
|
9
|
|
|
|
|
176
|
$$hash{$key} = $settings{$opt}; |
268
|
|
|
|
|
|
|
} |
269
|
4
|
|
|
|
|
48
|
else { $$self{shell}{settings}{$opt} = $settings{$opt} } |
270
|
|
|
|
|
|
|
} |
271
|
|
|
|
|
|
|
} |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
=item source I |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
Run the B script I. This script is B the same |
276
|
|
|
|
|
|
|
as the commandline syntax. Try using L in these |
277
|
|
|
|
|
|
|
scripts. |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
=cut |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
sub source { |
282
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
283
|
|
|
|
|
|
|
# FIXME more intelligent behaviour -- see bash man page |
284
|
0
|
|
|
|
|
0
|
$self->{shell}->source(@_); |
285
|
|
|
|
|
|
|
} |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
=item alias |
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
=item alias I |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
=item alias I=I |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
=item alias I I |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
Make I an alias to I. Aliases work like macros |
296
|
|
|
|
|
|
|
in the shell, this means they are substituted before the commnd |
297
|
|
|
|
|
|
|
code is interpreted and can contain complex statements. |
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
Without I shows the alias defined for I if any; |
300
|
|
|
|
|
|
|
without arguments lists all aliases that are currently defined. |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
Aliases are simple substitutions at the start of a command string. |
303
|
|
|
|
|
|
|
If you want something more intelligent like interpolating arguments |
304
|
|
|
|
|
|
|
into a string define a builtin command; see L. |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
=cut |
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
sub alias { |
309
|
2
|
|
|
2
|
1
|
5
|
my $self = shift; |
310
|
2
|
50
|
33
|
|
|
21
|
unless (@_) { # FIXME doesn't handle namespaces / sub hashes |
|
|
50
|
33
|
|
|
|
|
311
|
0
|
|
|
|
|
0
|
my $ref = $$self{shell}{aliases}; |
312
|
0
|
|
|
|
|
0
|
output [ |
313
|
|
|
|
|
|
|
map { |
314
|
0
|
|
|
|
|
0
|
my $al = $$ref{$_}; |
315
|
0
|
0
|
|
|
|
0
|
$al =~ s/(\\)|'/$1 ? '\\\\' : '\\\''/eg; |
|
0
|
|
|
|
|
0
|
|
316
|
0
|
|
|
|
|
0
|
"alias $_='$al'", |
317
|
0
|
|
|
|
|
0
|
} grep {! ref $$ref{$_}} keys %$ref |
318
|
|
|
|
|
|
|
]; |
319
|
0
|
|
|
|
|
0
|
return; |
320
|
|
|
|
|
|
|
} |
321
|
|
|
|
|
|
|
elsif (@_ == 1 and ! ref($_[0]) and $_[0] !~ /^-|=/) { |
322
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
323
|
0
|
|
|
|
|
0
|
my $alias; |
324
|
0
|
0
|
|
|
|
0
|
if ($cmd =~ m#/#) { |
|
|
0
|
|
|
|
|
|
325
|
0
|
|
|
|
|
0
|
my ($hash, $key, $path) = path2hashref($$self{shell}{aliases}, $cmd); |
326
|
0
|
0
|
|
|
|
0
|
error "$path: no such hash in aliases" unless $hash; |
327
|
0
|
|
|
|
|
0
|
$alias = $$hash{$key}; |
328
|
|
|
|
|
|
|
} |
329
|
|
|
|
|
|
|
elsif (exists $$self{shell}{aliases}{$cmd}) { |
330
|
0
|
|
|
|
|
0
|
$alias = $$self{shell}{aliases}{$cmd}; |
331
|
|
|
|
|
|
|
} |
332
|
0
|
|
|
|
|
0
|
else { error $cmd.': no such alias' } |
333
|
0
|
0
|
|
|
|
0
|
$alias =~ s/(\\)|'/$1 ? '\\\\' : '\\\''/eg; |
|
0
|
|
|
|
|
0
|
|
334
|
0
|
|
|
|
|
0
|
output "alias $cmd='$alias'"; |
335
|
0
|
|
|
|
|
0
|
return; |
336
|
|
|
|
|
|
|
} |
337
|
|
|
|
|
|
|
|
338
|
2
|
|
|
|
|
20
|
my (undef, $keys, $val) = getopt '*', @_; |
339
|
2
|
50
|
|
|
|
23
|
return unless @$keys; |
340
|
2
|
|
|
|
|
5
|
my $aliases; |
341
|
2
|
50
|
|
|
|
20
|
if (@$keys == (keys %$val)) { $aliases = $val } # bash style |
|
0
|
50
|
|
|
|
0
|
|
342
|
2
|
|
|
|
|
19
|
elsif (! (keys %$val)) { $aliases = {$$keys[0] => join ' ', splice @$keys, 1} }# tcsh style |
343
|
0
|
|
|
|
|
0
|
else { error 'syntax error' } # mixed style !? |
344
|
|
|
|
|
|
|
|
345
|
2
|
|
|
|
|
9
|
for my $cmd (keys %$aliases) { |
346
|
2
|
100
|
|
|
|
11
|
if ($cmd =~ m#/#) { |
347
|
1
|
|
|
|
|
18
|
my ($hash, $key, $path) = path2hashref($$self{shell}{aliases}, $cmd); |
348
|
1
|
50
|
|
|
|
11
|
error "$path: no such hash in aliases" unless $hash; |
349
|
1
|
|
|
|
|
19
|
$$hash{$key} = $$aliases{$cmd}; |
350
|
|
|
|
|
|
|
} |
351
|
1
|
|
|
|
|
17
|
else { $$self{shell}{aliases}{$cmd} = $$aliases{$cmd} } |
352
|
|
|
|
|
|
|
} |
353
|
|
|
|
|
|
|
} |
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
=item unalias I |
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
Remove an alias definition. |
358
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
=cut |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
sub unalias { |
362
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
363
|
0
|
|
|
|
|
0
|
my ($opts, $args) = getopt 'all,a @', @_; |
364
|
0
|
0
|
|
|
|
0
|
if ($$opts{all}) { %{$self->{shell}{aliases}} = () } |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
365
|
|
|
|
|
|
|
else { |
366
|
0
|
|
|
|
|
0
|
for (@$args) { |
367
|
0
|
0
|
|
|
|
0
|
error "alias: $_: not found" unless exists $self->{shell}{aliases}{$_}; |
368
|
0
|
|
|
|
|
0
|
delete $self->{shell}{aliases}{$_}; |
369
|
|
|
|
|
|
|
} |
370
|
|
|
|
|
|
|
} |
371
|
|
|
|
|
|
|
} |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
=item hash I |
374
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
=item hash -r |
376
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
TODO |
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
Command to manipulate the commands hash and command lookup logic. |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
=item read [-r] I I |
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
Read a line from STDIN, split the line in words |
384
|
|
|
|
|
|
|
and assign the words to the named enironment variables. |
385
|
|
|
|
|
|
|
Remaining words are stored in the last variable. |
386
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
Unless '-r' is specified the backslash is treated as |
388
|
|
|
|
|
|
|
an escape char and is it possible to escape the newline char. |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
=cut |
391
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
sub read { |
393
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
394
|
0
|
|
|
|
|
0
|
my ($opts, $args) = getopt 'raw,r @'; |
395
|
|
|
|
|
|
|
|
396
|
0
|
|
|
|
|
0
|
my $string = ''; |
397
|
0
|
|
|
|
|
0
|
while () { |
398
|
0
|
0
|
|
|
|
0
|
unless ($$opts{raw}) { |
399
|
0
|
|
|
|
|
0
|
my $more = 0; |
400
|
0
|
|
|
|
|
0
|
$_ =~ s/(\\\\)|\\(.)|\\$/ |
401
|
0
|
0
|
|
|
|
0
|
if ($1) { '\\' } |
|
0
|
0
|
|
|
|
0
|
|
402
|
0
|
|
|
|
|
0
|
elsif (length $2) { $2 } |
403
|
0
|
|
|
|
|
0
|
else { $more++; '' } |
|
0
|
|
|
|
|
0
|
|
404
|
|
|
|
|
|
|
/eg; |
405
|
0
|
|
|
|
|
0
|
$string .= $_; |
406
|
0
|
0
|
|
|
|
0
|
last unless $more; |
407
|
|
|
|
|
|
|
} |
408
|
|
|
|
|
|
|
else { |
409
|
0
|
|
|
|
|
0
|
$string = $_; |
410
|
0
|
|
|
|
|
0
|
last; |
411
|
|
|
|
|
|
|
} |
412
|
|
|
|
|
|
|
} |
413
|
0
|
0
|
|
|
|
0
|
return unless @$args; |
414
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
# TODO honour $IFS here instead of word_gram |
416
|
0
|
|
|
|
|
0
|
my @words = $$self{shell}{stringparser}->split('word_gram', $string); |
417
|
0
|
|
|
|
|
0
|
debug "read words: ", \@words; |
418
|
0
|
0
|
|
|
|
0
|
if (@words > @$args) { |
419
|
0
|
|
|
|
|
0
|
@words = @words[0 .. $#$args - 1]; |
420
|
0
|
|
|
|
|
0
|
my $pre = join '\s*', @words; |
421
|
0
|
|
|
|
|
0
|
$string =~ s/^\s*$pre\s*//; |
422
|
0
|
|
|
|
|
0
|
push @words, $string; |
423
|
|
|
|
|
|
|
} |
424
|
|
|
|
|
|
|
|
425
|
0
|
|
0
|
|
|
0
|
$ENV{$_} = shift @words || '' for @$args; |
426
|
|
|
|
|
|
|
} |
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
=item newgrp |
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
TODO |
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
=cut |
433
|
|
|
|
|
|
|
|
434
|
0
|
|
|
0
|
1
|
0
|
sub newgrp { todo } |
435
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
=item umask |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
TODO |
439
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
=cut |
441
|
|
|
|
|
|
|
|
442
|
0
|
|
|
0
|
1
|
0
|
sub umask { todo } |
443
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
=item false |
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
A command that always returns an error without doing anything. |
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
=cut |
449
|
|
|
|
|
|
|
|
450
|
10
|
|
|
10
|
1
|
156
|
sub false { error {silent => 1}, 'the "false" builtin' } |
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
=item true |
453
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
A command that never fails and does absolutely nothing. |
455
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
=cut |
457
|
|
|
|
|
|
|
|
458
|
0
|
|
|
0
|
1
|
|
sub true { 1 } |
459
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
# ######### # |
461
|
|
|
|
|
|
|
# Dir stack # |
462
|
|
|
|
|
|
|
# ######### # |
463
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
=item dirs |
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
Output the current dir stack. |
467
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
TODO some options |
469
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
Note that the dir stack is ont related to the dir history. |
471
|
|
|
|
|
|
|
It was only implemented because historic implementations have it. |
472
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
=cut |
474
|
|
|
|
|
|
|
|
475
|
0
|
0
|
|
0
|
1
|
|
sub dirs { output @DIRSTACK ? [reverse @DIRSTACK] : $ENV{PWD} } |
476
|
|
|
|
|
|
|
# FIXME some options - see man bash |
477
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
=item popd I |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
Pops a directory from the dir stack and Bs to that directory. |
481
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
TODO some options |
483
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
=cut |
485
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
sub popd { # FIXME some options - see man bash |
487
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
488
|
0
|
0
|
|
|
|
|
error 'popd: No other dir on stack' unless $#DIRSTACK; |
489
|
0
|
|
|
|
|
|
pop @DIRSTACK; |
490
|
0
|
0
|
|
|
|
|
my $dir = $#DIRSTACK ? $DIRSTACK[-1] : pop(@DIRSTACK); |
491
|
0
|
|
|
|
|
|
$self->cd($dir); |
492
|
|
|
|
|
|
|
} |
493
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
=item pushd I |
495
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
Push I on the dir stack. |
497
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
TODO some options |
499
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
=cut |
501
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
sub pushd { # FIXME some options - see man bash |
503
|
0
|
|
|
0
|
1
|
|
my ($self, $dir) = (@_); |
504
|
0
|
|
|
|
|
|
my $pwd = $ENV{PWD}; |
505
|
0
|
|
0
|
|
|
|
$dir ||= $ENV{PWD}; |
506
|
0
|
|
|
|
|
|
$self->cd($dir); |
507
|
0
|
0
|
|
|
|
|
@DIRSTACK = ($pwd) unless scalar @DIRSTACK; |
508
|
0
|
|
|
|
|
|
push @DIRSTACK, $dir; |
509
|
|
|
|
|
|
|
} |
510
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
################## |
512
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
=item pwd |
514
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
Prints the current PWD. |
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
=cut |
518
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
sub pwd { |
520
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
521
|
0
|
|
|
|
|
|
output $ENV{PWD}; |
522
|
|
|
|
|
|
|
} |
523
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
=item symbols [-a|--all] [I] |
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
Output a listing of symbols in the specified class. |
527
|
|
|
|
|
|
|
Class defaults to the current perl namespace, by default |
528
|
|
|
|
|
|
|
C. |
529
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
All symbols are prefixed by their sigil ('$', '@', '%', '&' |
531
|
|
|
|
|
|
|
or '*') where '*' is used for filehandles. |
532
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
By default sub classes (hashes containing '::') |
534
|
|
|
|
|
|
|
and special symbols (symbols without letters in their name) |
535
|
|
|
|
|
|
|
are hidden. Use the --all switch to see these. |
536
|
|
|
|
|
|
|
|
537
|
|
|
|
|
|
|
=cut |
538
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
sub symbols { |
540
|
17
|
|
|
17
|
|
146
|
no strict 'refs'; |
|
17
|
|
|
|
|
55
|
|
|
17
|
|
|
|
|
38847
|
|
541
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
542
|
0
|
|
|
|
|
|
my ($opts, $class) = getopt 'all,a @', @_; |
543
|
0
|
0
|
|
|
|
|
error 'to many arguments' if @$class > 1; |
544
|
0
|
|
0
|
|
|
|
$class = shift(@$class) |
545
|
|
|
|
|
|
|
|| $$self{shell}{settings}{perl}{namespace} || 'Zoidberg::Eval'; |
546
|
0
|
|
|
|
|
|
my @sym; |
547
|
0
|
|
|
|
|
|
for (keys %{$class.'::'}) { |
|
0
|
|
|
|
|
|
|
548
|
0
|
0
|
|
|
|
|
unless ($$opts{all}) { |
549
|
0
|
0
|
|
|
|
|
next if /::/; |
550
|
0
|
0
|
|
|
|
|
next unless /[a-z]/i; |
551
|
|
|
|
|
|
|
} |
552
|
0
|
0
|
|
|
|
|
push @sym, '$'.$_ if defined ${$class.'::'.$_}; |
|
0
|
|
|
|
|
|
|
553
|
0
|
0
|
|
|
|
|
push @sym, '@'.$_ if *{$class.'::'.$_}{ARRAY}; |
|
0
|
|
|
|
|
|
|
554
|
0
|
0
|
|
|
|
|
push @sym, '%'.$_ if *{$class.'::'.$_}{HASH}; |
|
0
|
|
|
|
|
|
|
555
|
0
|
0
|
|
|
|
|
push @sym, '&'.$_ if *{$class.'::'.$_}{CODE}; |
|
0
|
|
|
|
|
|
|
556
|
0
|
0
|
|
|
|
|
push @sym, '*'.$_ if *{$class.'::'.$_}{IO}; |
|
0
|
|
|
|
|
|
|
557
|
|
|
|
|
|
|
} |
558
|
0
|
|
|
|
|
|
output [sort @sym]; |
559
|
|
|
|
|
|
|
} |
560
|
|
|
|
|
|
|
|
561
|
|
|
|
|
|
|
=item reload I [I, ..] |
562
|
|
|
|
|
|
|
|
563
|
|
|
|
|
|
|
=item reload I [I, ..] |
564
|
|
|
|
|
|
|
|
565
|
|
|
|
|
|
|
Force (re-)loading of a module file. Typically used for debugging modules, |
566
|
|
|
|
|
|
|
where you reload the module after each modification to test it interactively. |
567
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
TODO: recursive switch that scans for 'use' statements |
569
|
|
|
|
|
|
|
|
570
|
|
|
|
|
|
|
=cut |
571
|
|
|
|
|
|
|
|
572
|
|
|
|
|
|
|
sub reload { |
573
|
0
|
|
|
0
|
1
|
|
shift; # self |
574
|
0
|
|
|
|
|
|
for (@_) { |
575
|
0
|
|
|
|
|
|
my $file = shift; |
576
|
0
|
0
|
|
|
|
|
if ($file =~ m!/!) { $file = path($file) } |
|
0
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
else { |
578
|
0
|
|
|
|
|
|
$file .= '.pm'; |
579
|
0
|
|
|
|
|
|
$file =~ s{::}{/}g; |
580
|
|
|
|
|
|
|
} |
581
|
0
|
|
0
|
|
|
|
$file = $INC{$file} || $file; |
582
|
0
|
|
|
|
|
|
eval "do '$file'"; |
583
|
0
|
0
|
|
|
|
|
error if $@; |
584
|
|
|
|
|
|
|
} |
585
|
|
|
|
|
|
|
} |
586
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
=item help [I|command I] |
588
|
|
|
|
|
|
|
|
589
|
|
|
|
|
|
|
Prints out a help text. |
590
|
|
|
|
|
|
|
|
591
|
|
|
|
|
|
|
=cut |
592
|
|
|
|
|
|
|
|
593
|
|
|
|
|
|
|
sub help { # TODO topics from man1 pod files ?? |
594
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
595
|
0
|
0
|
|
|
|
|
unless (@_) { |
596
|
0
|
|
|
|
|
|
output << 'EOH'; |
597
|
|
|
|
|
|
|
Help topics: |
598
|
|
|
|
|
|
|
about |
599
|
|
|
|
|
|
|
command |
600
|
|
|
|
|
|
|
|
601
|
|
|
|
|
|
|
see also man zoiduser |
602
|
|
|
|
|
|
|
EOH |
603
|
0
|
|
|
|
|
|
return; |
604
|
|
|
|
|
|
|
} |
605
|
|
|
|
|
|
|
|
606
|
0
|
|
|
|
|
|
my $topic = shift; |
607
|
0
|
0
|
|
|
|
|
if ($topic eq 'about') { output "$Zoidberg::LONG_VERSION\n" } |
|
0
|
0
|
|
|
|
|
|
608
|
|
|
|
|
|
|
elsif ($topic eq 'command') { |
609
|
0
|
0
|
|
|
|
|
error usage unless scalar @_; |
610
|
0
|
|
|
|
|
|
$self->help_command(@_) |
611
|
|
|
|
|
|
|
} |
612
|
0
|
|
|
|
|
|
else { $self->help_command($topic, @_) } |
613
|
|
|
|
|
|
|
} |
614
|
|
|
|
|
|
|
|
615
|
|
|
|
|
|
|
sub help_command { |
616
|
0
|
|
|
0
|
0
|
|
my ($self, @cmd) = @_; |
617
|
0
|
|
|
|
|
|
my @info = $self->type_command(@cmd); |
618
|
0
|
0
|
|
|
|
|
if ($info[0] eq 'alias') { output "'$cmd[0]' is an alias\n > $info[1]" } |
|
0
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
619
|
|
|
|
|
|
|
elsif ($info[0] eq 'builtin') { |
620
|
0
|
|
|
|
|
|
output "'$cmd[0]' is a builtin command,"; |
621
|
0
|
0
|
|
|
|
|
if (@info == 1) { |
622
|
0
|
|
|
|
|
|
output "but there is no information available about it."; |
623
|
|
|
|
|
|
|
} |
624
|
|
|
|
|
|
|
else { |
625
|
0
|
|
|
|
|
|
output "it belongs to the $info[1] plugin."; |
626
|
0
|
0
|
|
|
|
|
if (@info == 3) { output "\n", Zoidberg::Utils::help($cmd[0], $info[2]) } |
|
0
|
|
|
|
|
|
|
627
|
0
|
|
|
|
|
|
else { output "\nNo other help available" } |
628
|
|
|
|
|
|
|
} |
629
|
|
|
|
|
|
|
} |
630
|
|
|
|
|
|
|
elsif ($info[0] eq 'system') { |
631
|
0
|
|
|
|
|
|
output "'$cmd[0]' seems to be a system command, try\n > man $cmd[0]"; |
632
|
|
|
|
|
|
|
} |
633
|
|
|
|
|
|
|
elsif ($info[0] eq 'PERL') { |
634
|
0
|
|
|
|
|
|
output "'$cmd[0]' seems to be a perl command, try\n > perldoc -f $cmd[0]"; |
635
|
|
|
|
|
|
|
} |
636
|
0
|
|
|
|
|
|
else { todo "Help functionality for context: $info[1]" } |
637
|
|
|
|
|
|
|
} |
638
|
|
|
|
|
|
|
|
639
|
|
|
|
|
|
|
=item which [-a|--all|-m|--module] ITEM |
640
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
Finds ITEM in PATH or INC if the -m or --module option was used. |
642
|
|
|
|
|
|
|
If the -a or --all option is used all it doesn't stop after the first match. |
643
|
|
|
|
|
|
|
|
644
|
|
|
|
|
|
|
TODO it should identify aliases |
645
|
|
|
|
|
|
|
|
646
|
|
|
|
|
|
|
TODO what should happen with contexts other then CMD ? |
647
|
|
|
|
|
|
|
|
648
|
|
|
|
|
|
|
=cut |
649
|
|
|
|
|
|
|
|
650
|
|
|
|
|
|
|
sub which { |
651
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
652
|
0
|
|
|
|
|
|
my ($opt, $cmd) = getopt 'module,m all,a @', @_; |
653
|
0
|
|
|
|
|
|
my @info = $self->type_command(@$cmd); |
654
|
0
|
|
|
|
|
|
$cmd = shift @$cmd; |
655
|
0
|
|
|
|
|
|
my @dirs; |
656
|
|
|
|
|
|
|
|
657
|
0
|
0
|
|
|
|
|
if ($$opt{module}) { |
658
|
0
|
|
|
|
|
|
$cmd =~ s#::#/#g; |
659
|
0
|
0
|
|
|
|
|
$cmd .= '.pm' unless $cmd =~ /\.\w+$/; |
660
|
0
|
|
|
|
|
|
@dirs = @INC; |
661
|
|
|
|
|
|
|
} |
662
|
|
|
|
|
|
|
else { |
663
|
0
|
0
|
|
|
|
|
error "$cmd is a, or belongs to a $info[0]" |
664
|
|
|
|
|
|
|
unless $info[0] eq 'system'; |
665
|
|
|
|
|
|
|
# TODO aliases |
666
|
0
|
|
|
|
|
|
@dirs = split ':', $ENV{PATH}; |
667
|
|
|
|
|
|
|
} |
668
|
|
|
|
|
|
|
|
669
|
0
|
|
|
|
|
|
my @matches; |
670
|
0
|
|
|
|
|
|
for (@dirs) { |
671
|
0
|
0
|
|
|
|
|
next unless -e "$_/$cmd"; |
672
|
0
|
|
|
|
|
|
push @matches, "$_/$cmd"; |
673
|
0
|
0
|
|
|
|
|
last unless $$opt{all}; |
674
|
|
|
|
|
|
|
} |
675
|
0
|
0
|
|
|
|
|
if (@matches) { output [@matches] } |
|
0
|
|
|
|
|
|
|
676
|
0
|
|
|
|
|
|
else { error "no $cmd in PATH" } |
677
|
0
|
|
|
|
|
|
return; |
678
|
|
|
|
|
|
|
} |
679
|
|
|
|
|
|
|
|
680
|
|
|
|
|
|
|
sub type_command { |
681
|
0
|
|
|
0
|
0
|
|
my ($self, @cmd) = @_; |
682
|
|
|
|
|
|
|
|
683
|
0
|
0
|
0
|
|
|
|
if ( |
684
|
|
|
|
|
|
|
exists $$self{shell}{aliases}{$cmd[0]} |
685
|
|
|
|
|
|
|
and $$self{shell}{aliases}{$cmd[0]} !~ /^$cmd[0]\b/ |
686
|
|
|
|
|
|
|
) { |
687
|
0
|
|
|
|
|
|
my $alias = $$self{shell}{aliases}{$cmd[0]}; |
688
|
0
|
|
|
|
|
|
$alias =~ s/'/\\'/g; |
689
|
0
|
|
|
|
|
|
return 'alias', "alias $cmd[0]='$alias'"; |
690
|
|
|
|
|
|
|
} |
691
|
|
|
|
|
|
|
|
692
|
0
|
|
|
|
|
|
my $block = $$self{shell}->parse_block({pretend => 1}, [@cmd]); |
693
|
0
|
|
|
|
|
|
my $context = uc $$block[0]{context}; |
694
|
0
|
0
|
0
|
|
|
|
if (!$context or $context eq 'CMD') { |
695
|
0
|
0
|
|
|
|
|
return 'system' unless exists $$self{shell}{commands}{$cmd[0]}; |
696
|
0
|
|
|
|
|
|
my $tag = $$self{shell}{commands}->tag($cmd[0]); |
697
|
0
|
0
|
|
|
|
|
return 'builtin' unless $tag; |
698
|
0
|
|
|
|
|
|
my $file = tied( %{$$self{shell}{objects}} )->[1]{$tag}{module}; |
|
0
|
|
|
|
|
|
|
699
|
0
|
|
|
|
|
|
return 'builtin', $tag, $file; |
700
|
|
|
|
|
|
|
} |
701
|
0
|
|
|
|
|
|
else { return $context } |
702
|
|
|
|
|
|
|
} |
703
|
|
|
|
|
|
|
|
704
|
|
|
|
|
|
|
# ############ # |
705
|
|
|
|
|
|
|
# Job routines # |
706
|
|
|
|
|
|
|
# ############ # |
707
|
|
|
|
|
|
|
|
708
|
|
|
|
|
|
|
=item jobs [-l,--list|-p,--pgids] I |
709
|
|
|
|
|
|
|
|
710
|
|
|
|
|
|
|
Lists current jobs. |
711
|
|
|
|
|
|
|
|
712
|
|
|
|
|
|
|
If job specs are given as arguments only lists those jobs. |
713
|
|
|
|
|
|
|
|
714
|
|
|
|
|
|
|
The --pgids option only lists the process group ids for the jobs |
715
|
|
|
|
|
|
|
without additional information. |
716
|
|
|
|
|
|
|
|
717
|
|
|
|
|
|
|
The --list option gives more verbose output, it adds the process group id |
718
|
|
|
|
|
|
|
of the job and also shows the stack of commands pending for this job. |
719
|
|
|
|
|
|
|
|
720
|
|
|
|
|
|
|
This command is not POSIX compliant. It uses '-l' in a more verbose |
721
|
|
|
|
|
|
|
way then specified by POSIX. If you wat to make sure you have POSIX |
722
|
|
|
|
|
|
|
compliant verbose output try: C. |
723
|
|
|
|
|
|
|
|
724
|
|
|
|
|
|
|
=cut |
725
|
|
|
|
|
|
|
|
726
|
|
|
|
|
|
|
sub jobs { |
727
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
728
|
0
|
|
|
|
|
|
my ($opts, $args) = getopt 'list,l pgids,p @', @_; |
729
|
0
|
|
|
|
|
|
$args = @$args |
730
|
0
|
0
|
|
|
|
|
? [ map {$$self{shell}->job_by_spec($_)} @$args ] |
731
|
|
|
|
|
|
|
: $$self{shell}->{jobs} ; |
732
|
0
|
0
|
|
|
|
|
if ($$opts{pgids}) { |
733
|
0
|
|
|
|
|
|
output [ map $$_{pgid}, @$args ]; |
734
|
|
|
|
|
|
|
} |
735
|
|
|
|
|
|
|
else { |
736
|
0
|
|
|
|
|
|
output $_->status_string(undef, $$opts{list}) |
737
|
0
|
|
|
|
|
|
for sort {$$a{id} <=> $$b{id}} @$args; |
738
|
|
|
|
|
|
|
} |
739
|
|
|
|
|
|
|
} |
740
|
|
|
|
|
|
|
|
741
|
|
|
|
|
|
|
=item bg I |
742
|
|
|
|
|
|
|
|
743
|
|
|
|
|
|
|
Run the job corresponding to I as an asynchronous background process. |
744
|
|
|
|
|
|
|
|
745
|
|
|
|
|
|
|
Without argument uses the "current" job. |
746
|
|
|
|
|
|
|
|
747
|
|
|
|
|
|
|
=cut |
748
|
|
|
|
|
|
|
|
749
|
|
|
|
|
|
|
sub bg { |
750
|
0
|
|
|
0
|
1
|
|
my ($self, $id) = @_; |
751
|
0
|
0
|
|
|
|
|
my $j = $$self{shell}->job_by_spec($id) |
|
|
0
|
|
|
|
|
|
752
|
|
|
|
|
|
|
or error 'No such job'.($id ? ": $id" : ''); |
753
|
0
|
|
|
|
|
|
debug "putting bg: $$j{id} == $j"; |
754
|
0
|
|
|
|
|
|
$j->bg; |
755
|
|
|
|
|
|
|
} |
756
|
|
|
|
|
|
|
|
757
|
|
|
|
|
|
|
=item fg I |
758
|
|
|
|
|
|
|
|
759
|
|
|
|
|
|
|
Run the job corresponding to I as a foreground process. |
760
|
|
|
|
|
|
|
|
761
|
|
|
|
|
|
|
Without argument uses the "current" job. |
762
|
|
|
|
|
|
|
|
763
|
|
|
|
|
|
|
=cut |
764
|
|
|
|
|
|
|
|
765
|
|
|
|
|
|
|
sub fg { |
766
|
0
|
|
|
0
|
1
|
|
my ($self, $id) = @_; |
767
|
0
|
0
|
|
|
|
|
my $j = $$self{shell}->job_by_spec($id) |
|
|
0
|
|
|
|
|
|
768
|
|
|
|
|
|
|
or error 'No such job'.($id ? ": $id" : ''); |
769
|
0
|
|
|
|
|
|
debug "putting fg: $$j{id} == $j"; |
770
|
0
|
|
|
|
|
|
$j->fg; |
771
|
|
|
|
|
|
|
} |
772
|
|
|
|
|
|
|
|
773
|
|
|
|
|
|
|
=item wait |
774
|
|
|
|
|
|
|
|
775
|
|
|
|
|
|
|
TODO |
776
|
|
|
|
|
|
|
|
777
|
|
|
|
|
|
|
=cut |
778
|
|
|
|
|
|
|
|
779
|
0
|
|
|
0
|
1
|
|
sub wait { todo } |
780
|
|
|
|
|
|
|
|
781
|
|
|
|
|
|
|
=item kill -l |
782
|
|
|
|
|
|
|
|
783
|
|
|
|
|
|
|
=item kill [-w | -s I|-n I|-I] (I|I) |
784
|
|
|
|
|
|
|
|
785
|
|
|
|
|
|
|
Sends a signal to a process or a process group. |
786
|
|
|
|
|
|
|
By default the "TERM" signal is used. |
787
|
|
|
|
|
|
|
|
788
|
|
|
|
|
|
|
The '-l' option list all possible signals. |
789
|
|
|
|
|
|
|
|
790
|
|
|
|
|
|
|
The -w or --wipe option is zoidberg specific. It not only kills the job, but also |
791
|
|
|
|
|
|
|
wipes the list that would be executed after the job ends. |
792
|
|
|
|
|
|
|
|
793
|
|
|
|
|
|
|
=cut |
794
|
|
|
|
|
|
|
|
795
|
|
|
|
|
|
|
# from bash-2.05/builtins/kill.def: |
796
|
|
|
|
|
|
|
# kill [-s sigspec | -n signum | -sigspec] [pid | job]... or kill -l [sigspec] |
797
|
|
|
|
|
|
|
# Send the processes named by PID (or JOB) the signal SIGSPEC. If |
798
|
|
|
|
|
|
|
# SIGSPEC is not present, then SIGTERM is assumed. An argument of `-l' |
799
|
|
|
|
|
|
|
# lists the signal names; if arguments follow `-l' they are assumed to |
800
|
|
|
|
|
|
|
# be signal numbers for which names should be listed. Kill is a shell |
801
|
|
|
|
|
|
|
# builtin for two reasons: it allows job IDs to be used instead of |
802
|
|
|
|
|
|
|
# process IDs, and, if you have reached the limit on processes that |
803
|
|
|
|
|
|
|
# you can create, you don't have to start a process to kill another one. |
804
|
|
|
|
|
|
|
|
805
|
|
|
|
|
|
|
# Notice that POSIX specifies another list format then the one bash uses |
806
|
|
|
|
|
|
|
|
807
|
|
|
|
|
|
|
sub kill { |
808
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
809
|
0
|
|
|
|
|
|
my ($opts, $args) = getopt 'wipe,-w list,-l sigspec,-s signum,-n -* @', @_; |
810
|
0
|
0
|
|
|
|
|
if ($$opts{list}) { # list sigs |
811
|
0
|
0
|
|
|
|
|
error 'too many options' if @{$$opts{_opts}} > 1; |
|
0
|
|
|
|
|
|
|
812
|
0
|
|
|
|
|
|
my %sh = %{ $$self{shell}{_sighash} }; |
|
0
|
|
|
|
|
|
|
813
|
0
|
0
|
|
|
|
|
my @k = @$args ? (grep exists $sh{$_}, @$args) : (keys %sh); |
814
|
0
|
|
|
|
|
|
output [ map {sprintf '%2i) %s', $_, $sh{$_}} sort {$a <=> $b} @k ]; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
815
|
0
|
|
|
|
|
|
return; |
816
|
|
|
|
|
|
|
} |
817
|
0
|
0
|
|
|
|
|
else { error 'to few arguments' unless @$args } |
818
|
|
|
|
|
|
|
|
819
|
0
|
|
0
|
|
|
|
my $sig = $$opts{signum} || '15'; # sigterm, the default |
820
|
0
|
0
|
|
|
|
|
if ($$opts{_opts}) { |
821
|
0
|
|
|
|
|
|
for ($$opts{signum}, grep s/^-//, @$args) { |
822
|
0
|
0
|
|
|
|
|
next unless $_; |
823
|
0
|
|
|
|
|
|
my $sig = $$self{shell}->sig_by_spec($_); |
824
|
0
|
0
|
|
|
|
|
error $_.': no such signal' unless defined $sig; |
825
|
|
|
|
|
|
|
} |
826
|
|
|
|
|
|
|
} |
827
|
|
|
|
|
|
|
|
828
|
0
|
|
|
|
|
|
for (@$args) { |
829
|
0
|
0
|
|
|
|
|
if (/^\%/) { |
830
|
0
|
0
|
|
|
|
|
my $j = $$self{shell}->job_by_spec($_) |
831
|
|
|
|
|
|
|
or error "$_: no such job"; |
832
|
0
|
|
|
|
|
|
$j->kill($sig, $$opts{wipe}); |
833
|
|
|
|
|
|
|
} |
834
|
0
|
|
|
|
|
|
else { CORE::kill($sig, $_) } |
835
|
|
|
|
|
|
|
} |
836
|
|
|
|
|
|
|
} |
837
|
|
|
|
|
|
|
|
838
|
|
|
|
|
|
|
=item disown |
839
|
|
|
|
|
|
|
|
840
|
|
|
|
|
|
|
TODO |
841
|
|
|
|
|
|
|
|
842
|
|
|
|
|
|
|
=cut |
843
|
|
|
|
|
|
|
|
844
|
|
|
|
|
|
|
sub disown { # dissociate job ... remove from @jobs, nohup |
845
|
0
|
|
|
0
|
1
|
|
todo 'see bash manpage for implementaion details'; |
846
|
|
|
|
|
|
|
|
847
|
|
|
|
|
|
|
# is disowning the same as deamonizing the process ? |
848
|
|
|
|
|
|
|
# if it is, see man perlipc for example code |
849
|
|
|
|
|
|
|
|
850
|
|
|
|
|
|
|
# does this suggest we could also have a 'own' to hijack processes ? |
851
|
|
|
|
|
|
|
# all your pty are belong:0 |
852
|
|
|
|
|
|
|
} |
853
|
|
|
|
|
|
|
|
854
|
|
|
|
|
|
|
=back |
855
|
|
|
|
|
|
|
|
856
|
|
|
|
|
|
|
=head2 Job specs |
857
|
|
|
|
|
|
|
|
858
|
|
|
|
|
|
|
TODO tell bout job specs |
859
|
|
|
|
|
|
|
|
860
|
|
|
|
|
|
|
=head1 AUTHOR |
861
|
|
|
|
|
|
|
|
862
|
|
|
|
|
|
|
Jaap Karssenberg || Pardus [Larus] Epardus@cpan.orgE |
863
|
|
|
|
|
|
|
R.L. Zwart, Erlzwart@cpan.orgE |
864
|
|
|
|
|
|
|
|
865
|
|
|
|
|
|
|
Copyright (c) 2011 Jaap G Karssenberg and Joel Berger. All rights reserved. |
866
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or |
867
|
|
|
|
|
|
|
modify it under the same terms as Perl itself. |
868
|
|
|
|
|
|
|
|
869
|
|
|
|
|
|
|
=head1 SEE ALSO |
870
|
|
|
|
|
|
|
|
871
|
|
|
|
|
|
|
L, L |
872
|
|
|
|
|
|
|
|
873
|
|
|
|
|
|
|
=cut |
874
|
|
|
|
|
|
|
|
875
|
|
|
|
|
|
|
1; |
876
|
|
|
|
|
|
|
|