| 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
|
|
|
|
|
|
|
|