how to pass operators as arguments to a sub
kurreburre
created: 2006-05-03 02:54:53
Greetings all I have a problem to pass an operator as an argumnt to a sub, like this:
&checkDir('/usr/tmp', '-w');      #or...
&checkDir('/usr/tmp', '-r')

sub checkDir {

  my ($dir, $perm) = @_;

  ### Check if output dir exists
  if (! defined $path) {
    LOG ("The path where to save the output is not defined using temp instead...", 1);
    $path = '/var/tmp/';
   }
  if (! -d $path) {
    die LOG ("$path is not a valid directory...", 0);
  }
  if (! $perm $path) {
    die LOG ("You do not have $perm to $path directory...", 0);
  }
  LOG ("Using $path as output directory...", 2);
}
I have tried to deref the $perm var, but it does not help either. Is it possible at all to pass an operator as an argument?? Cheers and thanks Pär
Re: how to pass operators as arguments to a sub
created: 2006-05-03 03:02:59

The following test code works fine. Alter it to demonstrate your problem:

use warnings;
use strict;

checkDir('/usr/tmp', '-w');      #or...
checkDir('/usr/tmp', '-r');

sub checkDir {

  my ($dir, $perm) = @_;

    print "Dir: $dir, Perm: $perm\n";
}

Prints:

Dir: /usr/tmp, Perm: -w
Dir: /usr/tmp, Perm: -r

Bah, read the code!

Ok, better answer - to the actual question:

use warnings;
use strict;

my $testDir = '.';

checkDir($testDir, '-w');
checkDir($testDir, '-r');

sub checkDir {
    my ($dir, $perm) = @_;
    
    if (eval "$perm '$dir'") {
        print "Can $perm $dir\n";
    } else {
        print "Can $perm $dir\n";
    }
}

Prints:

Can -w .
Can -r .

This may be a bad idea if the call passes in user supplied text is is not from a trusted place!


DWIM is Perl's answer to Gödel
Re^2: how to pass operators as arguments to a sub
created: 2006-05-03 03:58:56
Thanks, Just what I needed. Completely forgot the eval command. And no, it is not unsafe, the argument comes from my own program, I just wanted to make it general. Cheers Pär
Re: how to pass operators as arguments to a sub
created: 2006-05-03 03:03:48

Probably the safest way is to use a hash dispatch table. There are only a small number of possible hyphen-operators anyway. Another alternative would be to use the "quote" version of eval, but that just exposes you to too much potential for an eternity in outer darkness... so stick with a dispatch table.


Dave

Re: how to pass operators as arguments to a sub
created: 2006-05-03 05:45:25
Here is a template without eval.
use warnings;
use strict;

my %h = (
          -r => sub { -r shift },
          -w => sub { -w shift },
);

my $testDir = '/tmp/';

checkDir( $testDir, '-w' );
checkDir( $testDir, '-r' );

sub checkDir {
  my ( $dir, $perm ) = @_;
  die '???' unless exists $h{$perm};
  if ( $h{$perm}($dir) ) {
    print "Can $perm $dir\n";
  }
  else {
    print "Can not $perm $dir\n";
  }
}
Boris
Re: how to pass operators as arguments to a sub
created: 2006-05-03 10:21:17

Rather than passing an operator per se, I'd pass a code reference that uses the operator...

checkDir('/usr/tmp', sub { -w shift }); # need write permission.
checkDir('/usr/tmp', sub { -r shift }); # need read permission.
checkDir('/usr/tmp', sub { my $x = shift; -r $x and -w $x; }); # need both.
checkDir('/usr/tmp', sub { my $x = shift; not $x =~ /\.\./; }); # safety check.

sub checkDir {
  my ($path, $perm) = @_;
  ### Check if output dir exists
  if (! defined $path) {
    LOG ("Output path not defined; using temp instead...", 1);
    $path = '/var/tmp/';
   }
  if (! -d $path) {
    die LOG ("$path is not a valid directory...", 0);
  }
  if (! $perm->($path)) {
    die LOG ("Test failed on $path, not able to use it.", 0);
  }
  LOG ("Using $path as output directory...", 2);
}

Sanity? Oh, yeah, I've got all kinds of sanity. In fact, I've developed whole new kinds of sanity. Why, I've got so much sanity it's driving me crazy.
Re^2: how to pass operators as arguments to a sub
created: 2006-05-03 13:42:31

While I like this idea, a couple of minor comments. First, there's an awful lot of shifting going on. For things like this, the topicaliser, $_, is very useful. So you'd be able to set things up as:

checkdir('/usr/tmp', sub { -w $_ });
checkdir('/usr/tmp', sub { -r $_ });
checkdir('/usr/tmp', sub { -r $_ and -w $_ });
At this point, I want to point out my second minor comment. This is what _ is for.
# now it's:
checkdir('/usr/tmp', sub { -r $_ and -w _ });
checkdir('/usr/tmp', sub { !/\.\./ });
Again, my next minor comment: I'm not sure what this is supposed to be checking. I'm guessing you're looking for a directory named ".." somewhere in there. However, I'm guessing that "/usr/blah..foo" should be permitted? Regexes are powerful, but with power comes great respon... er, complexity ;-) Rather than use a regex, I would actually suggest doing exactly what a human would do: split the path elements up, and then look that none of them are equal to "..". If that's what you really want. (On unix, I claim that this is almost never what you want, due to symlinks.)
use List::MoreUtils qw/none/;
use File::Spec;

checkdir('/usr/tmp', sub {
  none { $_ eq '..' } File::Spec->splitdir($_)
});
With those minor comments, here's the minor change to checkDir:
sub checkDir {
  my ($path, $perm) = @_;
  ### Check if output dir exists
  if (! defined $path) {
    LOG ("Output path not defined; using temp instead...", 1);
    $path = '/var/tmp/';
   }
  if (! -d $path) {
    die LOG ("$path is not a valid directory...", 0);
  }
  if (! do { local $_ = $path; $perm->() }) {
    die LOG ("Test failed on $path, not able to use it.", 0);
  }
  LOG ("Using $path as output directory...", 2);
}
A bit more convoluted internally, but I think a much more perlish exterior.

perlmonks.org content © perlmonks.org and borisz, davido, GrandFather, jonadab, kurreburre, Tanktalus

prlmnks.org © 2006 edmund von der burg (eccles & toad)

v 0.03