Class-Usul

 view release on metacpan or  search on metacpan

lib/Class/Usul/IPC/Cmd.pm  view on Meta::CPAN

# Private functions
my $_child_handler; $_child_handler = sub {
   local $OS_ERROR; # So that waitpid does not step on existing value

   while ((my $child_pid = waitpid -1, WNOHANG) > 0) {
      if (WIFEXITED( $CHILD_ERROR ) and $child_pid > ($CHILD_PID || 0)) {
         $CHILD_PID = $child_pid; $CHILD_ENUM = $CHILD_ERROR;
      }
   }

   $SIG{CHLD} = $_child_handler; # In case of unreliable signals
   return;
};

my $_close_child_io = sub { # In the parent, close the child end of the pipes
   my $pipes = shift;

   close $pipes->[ 0 ]->[ 0 ]; undef $pipes->[ 0 ]->[ 0 ];
   close $pipes->[ 1 ]->[ 1 ]; undef $pipes->[ 1 ]->[ 1 ];
   close $pipes->[ 2 ]->[ 1 ]; undef $pipes->[ 2 ]->[ 1 ];
   close $pipes->[ 3 ]->[ 1 ]; undef $pipes->[ 3 ]->[ 1 ];
   return;
};

my $_drain = sub { # Suck up the output from the child process
   my (%hands, @ready); my $selector = IO::Select->new(); my $i = 0;

   while (defined (my $fh = $_[ $i ])) {
      $selector->add( $fh ); $hands{ fileno $fh } = $_[ $i + 1 ]; $i += 2;
   }

   while (@ready = $selector->can_read) {
      for my $fh (@ready) {
         my $buf; my $bytes_read = sysread $fh, $buf, 64 * 1024;

         if ($bytes_read) { $hands{ fileno $fh }->( "${buf}" ) }
         else { $selector->remove( $fh ); close $fh }
      }
   }

   return;
};

my $_err_handler = sub {
   my ($err, $filtered, $standard) = @_;

   return sub {
      my $buf = shift; defined $buf or return;

      blessed $err     and $err->append( $buf );
      $err eq 'out'    and ${ $filtered } .= $buf;
      $err ne 'null'   and ${ $standard } .= $buf;
      $err eq 'stderr' and emit_to \*STDERR, $buf;
      return;
   }
};

my $_filter_out = sub {
   return join "\n", map    { strip_leader $_ }
                     grep   { not m{ (?: Started | Finished ) }msx }
                     split m{ [\n] }msx, $_[ 0 ];
};

my $_four_nonblocking_pipe_pairs = sub {
   return [ nonblocking_write_pipe_pair, nonblocking_write_pipe_pair,
            nonblocking_write_pipe_pair, nonblocking_write_pipe_pair ];
};

my $_has_shell_meta = sub {
   return (is_arrayref $_[ 0 ] && is_member '|',  $_[ 0 ]) ? TRUE
        : (is_arrayref $_[ 0 ] && is_member '&&', $_[ 0 ]) ? TRUE
        : (                           is_arrayref $_[ 0 ]) ? FALSE
        : (                      $_[ 0 ] =~ m{ [|]    }mx) ? TRUE
        : (                      $_[ 0 ] =~ m{ [&][&] }mx) ? TRUE
                                                           : FALSE;
};

my $_make_socket_pipe = sub {
   socketpair( $_[ 0 ], $_[ 1 ], AF_UNIX, SOCK_STREAM, PF_UNSPEC )
      or throw $EXTENDED_OS_ERROR;
   shutdown  ( $_[ 0 ], 1 );  # No more writing for reader
   shutdown  ( $_[ 1 ], 0 );  # No more reading for writer
   return;
};

my $_out_handler = sub {
   my ($out, $filtered, $standard) = @_;

   return sub {
      my $buf = shift; defined $buf or return;

      blessed $out     and $out->append( $buf );
      $out ne 'null'   and ${ $filtered } .= $buf;
      $out ne 'null'   and ${ $standard } .= $buf;
      $out eq 'stdout' and emit_to \*STDOUT, $buf;
      return;
   }
};

my $_partition_command = sub {
   my $cmd = shift; my $aref = []; my @command = ();

   for my $item (grep { defined && length } @{ $cmd }) {
      if ($item !~ m{ [^\\][\<\>\|\&] }mx) { push @{ $aref }, $item }
      else { push @command, $aref, $item; $aref = [] }
   }

   if ($aref->[ 0 ]) {
      if ($command[ 0 ]) { push @command, $aref }
      else { @command = @{ $aref } }
   }

   return \@command;
};

my $_pipe_handler; $_pipe_handler = sub {
   local $OS_ERROR; # So that wait does not step on existing value

   $CHILD_PID = wait; $CHILD_ENUM = (255 << 8) + 13;
   $SIG{PIPE} = $_pipe_handler;
   return;



( run in 0.975 second using v1.01-cache-2.11-cpan-71847e10f99 )