while (my @parsed = &parseInput) {
&dispatch(@parsed);
}
sub parseInput {
...
my @parsed = split $inp;
return 0 if $parsed[0] eq `q`;
...
}
The expression $parsed[0] eq `q` (backticks) will try to get the system to do qx{q} and then compare $parsed[0] against the value returned from the system; it will probably never be true. The statement should probably be
return 0 if $parsed[0] eq 'q';
or perhaps better yet
return 0 if lc($parsed[0]) eq 'q';
(Update: See also the note about split in Update 2 below.)
But (Update: anonymized user 468275 has since changed the statement in question to return an empty list.)
return 0 if $parsed[0] eq 'q';
still has a problem because it returns a non-empty list if the condition is true, and the while-loop test
while (my @parsed = &parseInput) { ... }
will be true because @parsed is not empty, so &dispatch(@parsed) will be called with a (0) argument list. Probably better to use the statement
return if lc($parsed[0]) eq 'q';
because that will return an empty list and cause the while-loop to terminate.
sub dispatch {
my $cmd = shift;
try { eval( '&' . $cmd . '(' . join ',', @_ . ');' ); }
catch { print STDERR "Unrecognised command $cmd\n"; }
# the above is a non-GUI example which would need adaptation
}
This looks to me like a bad idea because it seems to offer an injection point for user supplied input — code! (Update: See also the note about building the string for eval in Update 3 below.) Better IMHO to use a dispatch table:
c:\@Work\Perl\monks>perl -wMstrict -le
"BEGIN {
;;
my %dispat = (
'foo' => sub { print 'Fooing: ', qq{(@_)}; },
'bar' => \&bar,
);
;;
sub dispatch {
my $cmd = shift;
;;
$cmd = lc $cmd;
if (not exists $dispat{$cmd}) {
print qq{do not know how to '$cmd'};
return;
}
$dispat{$cmd}->(@_);
}
}
;;
while (my @parsed = parseInput()) {
dispatch(@parsed);
}
;;
sub parseInput {
my $inp = <>;
chomp $inp;
my @parsed = split ' ', $inp;
return if lc($parsed[0]) eq 'q';
return @parsed;
}
;;
sub bar { print 'Baring: ', qq{(@_)}; }
"
foo
Fooing: ()
bar
Baring: ()
foo 1 2 3
Fooing: (1 2 3)
bar x xyzzy
Baring: (x xyzzy)
boff
do not know how to 'boff'
Q
Update 1: Several minor wording changes; formatting adjustment to the code example.
Update 2: The statement
my @parsed = split $inp;
in the OPed code | code here is incorrect because it calls split with a split pattern of $inp on the (as-yet uninitialized) string in $_
Update 3: Sorry to seem like piling on, but the statement
eval( '&' . $cmd . '(' . join ',', @_ . ');' );
is also problematic. The scalar concatenation of @_ with another string will evaluate the array in scalar context, i.e., as the number of elements in the array. The problem is easily fixed by a couple more parentheses (if you really want to dance with the Devil by the pale moonlight):
c:\@Work\Perl\monks>perl -wMstrict -le
"dispatch(qw(foo 9 8 7 6));
;;
sub dispatch {
my $cmd = shift;
my $evil_string = '&' . $cmd . '(' . join ',', @_ . ');';
print qq{>>$evil_string<<};
}
"
>>&foo(4);<<
c:\@Work\Perl\monks>perl -wMstrict -le
"dispatch(qw(foo 9 8 7 6));
;;
sub dispatch {
my $cmd = shift;
my $evil_string = '&' . $cmd . '(' . join(',', @_) . ');';
eval $evil_string;
}
;;
sub foo { print qq{'in Foo:' (@_)} }
"
'in Foo:' (9 8 7 6)
Give a man a fish: <%-{-{-{-<
|