summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorRafael Garcia-Suarez <rgarciasuarez@gmail.com>2005-03-22 15:30:18 +0000
committerRafael Garcia-Suarez <rgarciasuarez@gmail.com>2005-03-22 15:30:18 +0000
commit74969006268f55e199dd9a95a052217885269405 (patch)
tree2ca74c7bcb9e90059d01d18c0401b2bd0e8b6b8c /lib
parent3ffe3ee448ef9bbf97ab82f8e68a3a749541b012 (diff)
downloadperl-74969006268f55e199dd9a95a052217885269405.tar.gz
Remove IPC::Run for 5.9.2
p4raw-id: //depot/perl@24071
Diffstat (limited to 'lib')
-rw-r--r--lib/IPC/Run.pm4476
-rw-r--r--lib/IPC/Run/Debug.pm311
-rw-r--r--lib/IPC/Run/IO.pm554
-rw-r--r--lib/IPC/Run/Timer.pm688
-rw-r--r--lib/IPC/Run/Win32Helper.pm481
-rw-r--r--lib/IPC/Run/Win32IO.pm556
-rw-r--r--lib/IPC/Run/Win32Pump.pm162
-rw-r--r--lib/IPC/Run/t/adopt.t120
-rw-r--r--lib/IPC/Run/t/binmode.t102
-rw-r--r--lib/IPC/Run/t/bogus.t69
-rw-r--r--lib/IPC/Run/t/filter.t120
-rw-r--r--lib/IPC/Run/t/harness.t149
-rw-r--r--lib/IPC/Run/t/io.t133
-rw-r--r--lib/IPC/Run/t/kill_kill.t59
-rw-r--r--lib/IPC/Run/t/parallel.t110
-rw-r--r--lib/IPC/Run/t/pty.t275
-rw-r--r--lib/IPC/Run/t/pump.t119
-rw-r--r--lib/IPC/Run/t/run.t1080
-rw-r--r--lib/IPC/Run/t/signal.t90
-rw-r--r--lib/IPC/Run/t/timeout.t117
-rw-r--r--lib/IPC/Run/t/timer.t150
-rw-r--r--lib/IPC/Run/t/win32_compile.t92
22 files changed, 0 insertions, 10013 deletions
diff --git a/lib/IPC/Run.pm b/lib/IPC/Run.pm
deleted file mode 100644
index fd21836c97..0000000000
--- a/lib/IPC/Run.pm
+++ /dev/null
@@ -1,4476 +0,0 @@
-package IPC::Run ;
-#
-# Copyright (c) 1999 by Barrie Slaymaker, barries@slaysys.com
-#
-# You may distribute under the terms of either the GNU General Public
-# License or the Artistic License, as specified in the README file.
-#
-
-$VERSION = 0.80;
-
-=head1 NAME
-
-IPC::Run - system() and background procs w/ piping, redirs, ptys (Unix, Win32)
-
-=head1 SYNOPSIS
-
- ## First,a command to run:
- my @cat = qw( cat ) ;
-
- ## Using run() instead of system():
- use IPC::Run qw( run timeout ) ;
-
- run \@cmd, \$in, \$out, \$err, timeout( 10 ) or die "cat: $?"
-
- # Can do I/O to sub refs and filenames, too:
- run \@cmd, '<', "in.txt", \&out, \&err or die "cat: $?"
- run \@cat, '<', "in.txt", '>>', "out.txt", '2>>', "err.txt" ;
-
-
- # Redirecting using psuedo-terminals instad of pipes.
- run \@cat, '<pty<', \$in, '>pty>', \$out_and_err ;
-
- ## Scripting subprocesses (like Expect):
-
- use IPC::Run qw( start pump finish timeout ) ;
-
- # Incrementally read from / write to scalars.
- # $in is drained as it is fed to cat's stdin,
- # $out accumulates cat's stdout
- # $err accumulates cat's stderr
- # $h is for "harness".
- my $h = start \@cat, \$in, \$out, \$err, timeout( 10 ) ;
-
- $in .= "some input\n" ;
- pump $h until $out =~ /input\n/g ;
-
- $in .= "some more input\n" ;
- pump $h until $out =~ /\G.*more input\n/ ;
-
- $in .= "some final input\n" ;
- finish $h or die "cat returned $?" ;
-
- warn $err if $err ;
- print $out ; ## All of cat's output
-
- # Piping between children
- run \@cat, '|', \@gzip ;
-
- # Multiple children simultaneously (run() blocks until all
- # children exit, use start() for background execution):
- run \@foo1, '&', \@foo2 ;
-
- # Calling \&set_up_child in the child before it executes the
- # command (only works on systems with true fork() & exec())
- # exceptions thrown in set_up_child() will be propagated back
- # to the parent and thrown from run().
- run \@cat, \$in, \$out,
- init => \&set_up_child ;
-
- # Read from / write to file handles you open and close
- open IN, '<in.txt' or die $! ;
- open OUT, '>out.txt' or die $! ;
- print OUT "preamble\n" ;
- run \@cat, \*IN, \*OUT or die "cat returned $?" ;
- print OUT "postamble\n" ;
- close IN ;
- close OUT ;
-
- # Create pipes for you to read / write (like IPC::Open2 & 3).
- $h = start
- \@cat,
- '<pipe', \*IN,
- '>pipe', \*OUT,
- '2>pipe', \*ERR
- or die "cat returned $?" ;
- print IN "some input\n" ;
- close IN ;
- print <OUT>, <ERR> ;
- finish $h ;
-
- # Mixing input and output modes
- run \@cat, 'in.txt', \&catch_some_out, \*ERR_LOG ) ;
-
- # Other redirection constructs
- run \@cat, '>&', \$out_and_err ;
- run \@cat, '2>&1' ;
- run \@cat, '0<&3' ;
- run \@cat, '<&-' ;
- run \@cat, '3<', \$in3 ;
- run \@cat, '4>', \$out4 ;
- # etc.
-
- # Passing options:
- run \@cat, 'in.txt', debug => 1 ;
-
- # Call this system's shell, returns TRUE on 0 exit code
- # THIS IS THE OPPOSITE SENSE OF system()'s RETURN VALUE
- run "cat a b c" or die "cat returned $?" ;
-
- # Launch a sub process directly, no shell. Can't do redirection
- # with this form, it's here to behave like system() with an
- # inverted result.
- $r = run "cat a b c" ;
-
- # Read from a file in to a scalar
- run io( "filename", 'r', \$recv ) ;
- run io( \*HANDLE, 'r', \$recv ) ;
-
-=head1 DESCRIPTION
-
-IPC::Run allows you run and interact with child processes using files, pipes,
-and pseudo-ttys. Both system()-style and scripted usages are supported and
-may be mixed. Likewise, functional and OO API styles are both supported and
-may be mixed.
-
-Various redirection operators reminiscent of those seen on common Unix and DOS
-command lines are provided.
-
-Before digging in to the details a few LIMITATIONS are important enough
-to be mentioned right up front:
-
-=over
-
-=item Win32 Support
-
-Win32 support is working but B<EXPERIMENTAL>, but does pass all relevant tests
-on NT 4.0. See L</Win32 LIMITATIONS>.
-
-=item pty Support
-
-If you need pty support, IPC::Run should work well enough most of the
-time, but IO::Pty is being improved, and IPC::Run will be improved to
-use IO::Pty's new features when it is release.
-
-The basic problem is that the pty needs to initialize itself before the
-parent writes to the master pty, or the data written gets lost. So
-IPC::Run does a sleep(1) in the parent after forking to (hopefully) give
-the child a chance to run. This is a kludge that works well on non
-heavily loaded systems :(.
-
-ptys are not supported yet under Win32, but will be emulated...
-
-=item Debugging Tip
-
-You may use the environment variable C<IPCRUNDEBUG> to see what's going on
-under the hood:
-
- $ IPCRUNDEBUG=basic myscript # prints minimal debugging
- $ IPCRUNDEBUG=data myscript # prints all data reads/writes
- $ IPCRUNDEBUG=details myscript # prints lots of low-level details
- $ IPCRUNDEBUG=gory myscript # (Win32 only) prints data moving through
- # the helper processes.
-
-=back
-
-We now return you to your regularly scheduled documentation.
-
-=head2 Harnesses
-
-Child processes and I/O handles are gathered in to a harness, then
-started and run until the processing is finished or aborted.
-
-=head2 run() vs. start(); pump(); finish();
-
-There are two modes you can run harnesses in: run() functions as an
-enhanced system(), and start()/pump()/finish() allow for background
-processes and scripted interactions with them.
-
-When using run(), all data to be sent to the harness is set up in
-advance (though one can feed subprocesses input from subroutine refs to
-get around this limitation). The harness is run and all output is
-collected from it, then any child processes are waited for:
-
- run \@cmd, \<<IN, \$out ;
- blah
- IN
-
- ## To precompile harnesses and run them later:
- my $h = harness \@cmd, \<<IN, \$out ;
- blah
- IN
-
- run $h ;
-
-The background and scripting API is provided by start(), pump(), and
-finish(): start() creates a harness if need be (by calling harness())
-and launches any subprocesses, pump() allows you to poll them for
-activity, and finish() then monitors the harnessed activities until they
-complete.
-
- ## Build the harness, open all pipes, and launch the subprocesses
- my $h = start \@cat, \$in, \$out ;
- $in = "first input\n" ;
-
- ## Now do I/O. start() does no I/O.
- pump $h while length $in ; ## Wait for all input to go
-
- ## Now do some more I/O.
- $in = "second input\n" ;
- pump $h until $out =~ /second input/ ;
-
- ## Clean up
- finish $h or die "cat returned $?" ;
-
-You can optionally compile the harness with harness() prior to
-start()ing or run()ing, and you may omit start() between harness() and
-pump(). You might want to do these things if you compile your harnesses
-ahead of time.
-
-=head2 Using regexps to match output
-
-As shown in most of the scripting examples, the read-to-scalar facility
-for gathering subcommand's output is often used with regular expressions
-to detect stopping points. This is because subcommand output often
-arrives in dribbles and drabs, often only a character or line at a time.
-This output is input for the main program and piles up in variables like
-the C<$out> and C<$err> in our examples.
-
-Regular expressions can be used to wait for appropriate output in
-several ways. The C<cat> example in the previous section demonstrates
-how to pump() until some string appears in the output. Here's an
-example that uses C<smb> to fetch files from a remote server:
-
- $h = harness \@smbclient, \$in, \$out ;
-
- $in = "cd /src\n" ;
- $h->pump until $out =~ /^smb.*> \Z/m ;
- die "error cding to /src:\n$out" if $out =~ "ERR" ;
- $out = '' ;
-
- $in = "mget *\n" ;
- $h->pump until $out =~ /^smb.*> \Z/m ;
- die "error retrieving files:\n$out" if $out =~ "ERR" ;
-
- $in = "quit\n" ;
- $h->finish ;
-
-Notice that we carefully clear $out after the first command/response
-cycle? That's because IPC::Run does not delete $out when we continue,
-and we don't want to trip over the old output in the second
-command/response cycle.
-
-Say you want to accumulate all the output in $out and analyze it
-afterwards. Perl offers incremental regular expression matching using
-the C<m//gc> and pattern matching idiom and the C<\G> assertion.
-IPC::Run is careful not to disturb the current C<pos()> value for
-scalars it appends data to, so we could modify the above so as not to
-destroy $out by adding a couple of C</gc> modifiers. The C</g> keeps us
-from tripping over the previous prompt and the C</c> keeps us from
-resetting the prior match position if the expected prompt doesn't
-materialize immediately:
-
- $h = harness \@smbclient, \$in, \$out ;
-
- $in = "cd /src\n" ;
- $h->pump until $out =~ /^smb.*> \Z/mgc ;
- die "error cding to /src:\n$out" if $out =~ "ERR" ;
-
- $in = "mget *\n" ;
- $h->pump until $out =~ /^smb.*> \Z/mgc ;
- die "error retrieving files:\n$out" if $out =~ "ERR" ;
-
- $in = "quit\n" ;
- $h->finish ;
-
- analyze( $out ) ;
-
-When using this technique, you may want to preallocate $out to have
-plenty of memory or you may find that the act of growing $out each time
-new input arrives causes an C<O(length($out)^2)> slowdown as $out grows.
-Say we expect no more than 10,000 characters of input at the most. To
-preallocate memory to $out, do something like:
-
- my $out = "x" x 10_000 ;
- $out = "" ;
-
-C<perl> will allocate at least 10,000 characters' worth of space, then
-mark the $out as having 0 length without freeing all that yummy RAM.
-
-=head2 Timeouts and Timers
-
-More than likely, you don't want your subprocesses to run forever, and
-sometimes it's nice to know that they're going a little slowly.
-Timeouts throw exceptions after a some time has elapsed, timers merely
-cause pump() to return after some time has elapsed. Neither is
-reset/restarted automatically.
-
-Timeout objects are created by calling timeout( $interval ) and passing
-the result to run(), start() or harness(). The timeout period starts
-ticking just after all the child processes have been fork()ed or
-spawn()ed, and are polled for expiration in run(), pump() and finish().
-If/when they expire, an exception is thrown. This is typically useful
-to keep a subprocess from taking too long.
-
-If a timeout occurs in run(), all child processes will be terminated and
-all file/pipe/ptty descriptors opened by run() will be closed. File
-descriptors opened by the parent process and passed in to run() are not
-closed in this event.
-
-If a timeout occurs in pump(), pump_nb(), or finish(), it's up to you to
-decide whether to kill_kill() all the children or to implement some more
-graceful fallback. No I/O will be closed in pump(), pump_nb() or
-finish() by such an exception (though I/O is often closed down in those
-routines during the natural course of events).
-
-Often an exception is too harsh. timer( $interval ) creates timer
-objects that merely prevent pump() from blocking forever. This can be
-useful for detecting stalled I/O or printing a soothing message or "."
-to pacify an anxious user.
-
-Timeouts and timers can both be restarted at any time using the timer's
-start() method (this is not the start() that launches subprocesses). To
-restart a timer, you need to keep a reference to the timer:
-
- ## Start with a nice long timeout to let smbclient connect. If
- ## pump or finish take too long, an exception will be thrown.
-
- my $h ;
- eval {
- $h = harness \@smbclient, \$in, \$out, \$err, ( my $t = timeout 30 ) ;
- sleep 11 ; # No effect: timer not running yet
-
- start $h ;
- $in = "cd /src\n" ;
- pump $h until ! length $in ;
-
- $in = "ls\n" ;
- ## Now use a short timeout, since this should be faster
- $t->start( 5 ) ;
- pump $h until ! length $in ;
-
- $t->start( 10 ) ; ## Give smbclient a little while to shut down.
- $h->finish ;
- } ;
- if ( $@ ) {
- my $x = $@ ; ## Preserve $@ in case another exception occurs
- $h->kill_kill ; ## kill it gently, then brutally if need be, or just
- ## brutally on Win32.
- die $x ;
- }
-
-Timeouts and timers are I<not> checked once the subprocesses are shut
-down; they will not expire in the interval between the last valid
-process and when IPC::Run scoops up the processes' result codes, for
-instance.
-
-=head2 Spawning synchronization, child exception propagation
-
-start() pauses the parent until the child executes the command or CODE
-reference and propagates any exceptions thrown (including exec()
-failure) back to the parent. This has several pleasant effects: any
-exceptions thrown in the child, including exec() failure, come flying
-out of start() or run() as though they had ocurred in the parent.
-
-This includes exceptions your code thrown from init subs. In this
-example:
-
- eval {
- run \@cmd, init => sub { die "blast it! foiled again!" } ;
- } ;
- print $@ ;
-
-the exception "blast it! foiled again" will be thrown from the child
-process (preventing the exec()) and printed by the parent.
-
-In situations like
-
- run \@cmd1, "|", \@cmd2, "|", \@cmd3 ;
-
-@cmd1 will be initted and exec()ed before @cmd2, and @cmd2 before @cmd3.
-This can save time and prevent oddball errors emitted by later commands
-when earlier commands fail to execute. Note that IPC::Run doesn't start
-any commands unless it can find the executables referenced by all
-commands. These executables must pass both the C<-f> and C<-x> tests
-described in L<perlfunc>.
-
-Another nice effect is that init() subs can take their time doing things
-and there will be no problems caused by a parent continuing to execute
-before a child's init() routine is complete. Say the init() routine
-needs to open a socket or a temp file that the parent wants to connect
-to; without this synchronization, the parent will need to implement a
-retry loop to wait for the child to run, since often, the parent gets a
-lot of things done before the child's first timeslice is allocated.
-
-This is also quite necessary for pseudo-tty initialization, which needs
-to take place before the parent writes to the child via pty. Writes
-that occur before the pty is set up can get lost.
-
-A final, minor, nicety is that debugging output from the child will be
-emitted before the parent continues on, making for much clearer debugging
-output in complex situations.
-
-The only drawback I can conceive of is that the parent can't continue to
-operate while the child is being initted. If this ever becomes a
-problem in the field, we can implement an option to avoid this behavior,
-but I don't expect it to.
-
-B<Win32>: executing CODE references isn't supported on Win32, see
-L</Win32 LIMITATIONS> for details.
-
-=head2 Syntax
-
-run(), start(), and harness() can all take a harness specification
-as input. A harness specification is either a single string to be passed
-to the systems' shell:
-
- run "echo 'hi there'" ;
-
-or a list of commands, io operations, and/or timers/timeouts to execute.
-Consecutive commands must be separated by a pipe operator '|' or an '&'.
-External commands are passed in as array references, and, on systems
-supporting fork(), Perl code may be passed in as subs:
-
- run \@cmd ;
- run \@cmd1, '|', \@cmd2 ;
- run \@cmd1, '&', \@cmd2 ;
- run \&sub1 ;
- run \&sub1, '|', \&sub2 ;
- run \&sub1, '&', \&sub2 ;
-
-'|' pipes the stdout of \@cmd1 the stdin of \@cmd2, just like a
-shell pipe. '&' does not. Child processes to the right of a '&'
-will have their stdin closed unless it's redirected-to.
-
-L<IPC::Run::IO> objects may be passed in as well, whether or not
-child processes are also specified:
-
- run io( "infile", ">", \$in ), io( "outfile", "<", \$in ) ;
-
-as can L<IPC::Run::Timer> objects:
-
- run \@cmd, io( "outfile", "<", \$in ), timeout( 10 ) ;
-
-Commands may be followed by scalar, sub, or i/o handle references for
-redirecting
-child process input & output:
-
- run \@cmd, \undef, \$out ;
- run \@cmd, \$in, \$out ;
- run \@cmd1, \&in, '|', \@cmd2, \*OUT ;
- run \@cmd1, \*IN, '|', \@cmd2, \&out ;
-
-This is known as succinct redirection syntax, since run(), start()
-and harness(), figure out which file descriptor to redirect and how.
-File descriptor 0 is presumed to be an input for
-the child process, all others are outputs. The assumed file
-descriptor always starts at 0, unless the command is being piped to,
-in which case it starts at 1.
-
-To be explicit about your redirects, or if you need to do more complex
-things, there's also a redirection operator syntax:
-
- run \@cmd, '<', \undef, '>', \$out ;
- run \@cmd, '<', \undef, '>&', \$out_and_err ;
- run(
- \@cmd1,
- '<', \$in,
- '|', \@cmd2,
- \$out
- ) ;
-
-Operator syntax is required if you need to do something other than simple
-redirection to/from scalars or subs, like duping or closing file descriptors
-or redirecting to/from a named file. The operators are covered in detail
-below.
-
-After each \@cmd (or \&foo), parsing begins in succinct mode and toggles to
-operator syntax mode when an operator (ie plain scalar, not a ref) is seen.
-Once in
-operator syntax mode, parsing only reverts to succinct mode when a '|' or
-'&' is seen.
-
-In succinct mode, each parameter after the \@cmd specifies what to
-do with the next highest file descriptor. These File descriptor start
-with 0 (stdin) unless stdin is being piped to (C<'|', \@cmd>), in which
-case they start with 1 (stdout). Currently, being on the left of
-a pipe (C<\@cmd, \$out, \$err, '|'>) does I<not> cause stdout to be
-skipped, though this may change since it's not as DWIMerly as it
-could be. Only stdin is assumed to be an
-input in succinct mode, all others are assumed to be outputs.
-
-If no piping or redirection is specified for a child, it will inherit
-the parent's open file handles as dictated by your system's
-close-on-exec behavior and the $^F flag, except that processes after a
-'&' will not inherit the parent's stdin. Also note that $^F does not
-affect file desciptors obtained via POSIX, since it only applies to
-full-fledged Perl file handles. Such processes will have their stdin
-closed unless it has been redirected-to.
-
-If you want to close a child processes stdin, you may do any of:
-
- run \@cmd, \undef ;
- run \@cmd, \"" ;
- run \@cmd, '<&-' ;
- run \@cmd, '0<&-' ;
-
-Redirection is done by placing redirection specifications immediately
-after a command or child subroutine:
-
- run \@cmd1, \$in, '|', \@cmd2, \$out ;
- run \@cmd1, '<', \$in, '|', \@cmd2, '>', \$out ;
-
-If you omit the redirection operators, descriptors are counted
-starting at 0. Descriptor 0 is assumed to be input, all others
-are outputs. A leading '|' consumes descriptor 0, so this
-works as expected.
-
- run \@cmd1, \$in, '|', \@cmd2, \$out ;
-
-The parameter following a redirection operator can be a scalar ref,
-a subroutine ref, a file name, an open filehandle, or a closed
-filehandle.
-
-If it's a scalar ref, the child reads input from or sends output to
-that variable:
-
- $in = "Hello World.\n" ;
- run \@cat, \$in, \$out ;
- print $out ;
-
-Scalars used in incremental (start()/pump()/finish()) applications are treated
-as queues: input is removed from input scalers, resulting in them dwindling
-to '', and output is appended to output scalars. This is not true of
-harnesses run() in batch mode.
-
-It's usually wise to append new input to be sent to the child to the input
-queue, and you'll often want to zap output queues to '' before pumping.
-
- $h = start \@cat, \$in ;
- $in = "line 1\n" ;
- pump $h ;
- $in .= "line 2\n" ;
- pump $h ;
- $in .= "line 3\n" ;
- finish $h ;
-
-The final call to finish() must be there: it allows the child process(es)
-to run to completion and waits for their exit values.
-
-=head1 OBSTINATE CHILDREN
-
-Interactive applications are usually optimized for human use. This
-can help or hinder trying to interact with them through modules like
-IPC::Run. Frequently, programs alter their behavior when they detect
-that stdin, stdout, or stderr are not connected to a tty, assuming that
-they are being run in batch mode. Whether this helps or hurts depends
-on which optimizations change. And there's often no way of telling
-what a program does in these areas other than trial and error and,
-occasionally, reading the source. This includes different versions
-and implementations of the same program.
-
-All hope is not lost, however. Most programs behave in reasonably
-tractable manners, once you figure out what it's trying to do.
-
-Here are some of the issues you might need to be aware of.
-
-=over
-
-=item *
-
-fflush()ing stdout and stderr
-
-This lets the user see stdout and stderr immediately. Many programs
-undo this optimization if stdout is not a tty, making them harder to
-manage by things like IPC::Run.
-
-Many programs decline to fflush stdout or stderr if they do not
-detect a tty there. Some ftp commands do this, for instance.
-
-If this happens to you, look for a way to force interactive behavior,
-like a command line switch or command. If you can't, you will
-need to use a pseudo terminal ('<pty<' and '>pty>').
-
-=item *
-
-false prompts
-
-Interactive programs generally do not guarantee that output from user
-commands won't contain a prompt string. For example, your shell prompt
-might be a '$', and a file named '$' might be the only file in a directory
-listing.
-
-This can make it hard to guarantee that your output parser won't be fooled
-into early termination of results.
-
-To help work around this, you can see if the program can alter it's
-prompt, and use something you feel is never going to occur in actual
-practice.
-
-You should also look for your prompt to be the only thing on a line:
-
- pump $h until $out =~ /^<SILLYPROMPT>\s?\z/m ;
-
-(use C<(?!\n)\Z> in place of C<\z> on older perls).
-
-You can also take the approach that IPC::ChildSafe takes and emit a
-command with known output after each 'real' command you issue, then
-look for this known output. See new_appender() and new_chunker() for
-filters that can help with this task.
-
-If it's not convenient or possibly to alter a prompt or use a known
-command/response pair, you might need to autodetect the prompt in case
-the local version of the child program is different then the one
-you tested with, or if the user has control over the look & feel of
-the prompt.
-
-=item *
-
-Refusing to accept input unless stdin is a tty.
-
-Some programs, for security reasons, will only accept certain types
-of input from a tty. su, notable, will not prompt for a password unless
-it's connected to a tty.
-
-If this is your situation, use a pseudo terminal ('<pty<' and '>pty>').
-
-=item *
-
-Not prompting unless connected to a tty.
-
-Some programs don't prompt unless stdin or stdout is a tty. See if you can
-turn prompting back on. If not, see if you can come up with a command that
-you can issue after every real command and look for it's output, as
-IPC::ChildSafe does. There are two filters included with IPC::Run that
-can help with doing this: appender and chunker (see new_appender() and
-new_chunker()).
-
-=item *
-
-Different output format when not connected to a tty.
-
-Some commands alter their formats to ease machine parsability when they
-aren't connected to a pipe. This is actually good, but can be surprising.
-
-=back
-
-=head1 PSEUDO TERMINALS
-
-On systems providing pseudo terminals under /dev, IPC::Run can use IO::Pty
-(available on CPAN) to provide a terminal environment to subprocesses.
-This is necessary when the subprocess really wants to think it's connected
-to a real terminal.
-
-=head2 CAVEATS
-
-Psuedo-terminals are not pipes, though they are similar. Here are some
-differences to watch out for.
-
-=over
-
-=item Echoing
-
-Sending to stdin will cause an echo on stdout, which occurs before each
-line is passed to the child program. There is currently no way to
-disable this, although the child process can and should disable it for
-things like passwords.
-
-=item Shutdown
-
-IPC::Run cannot close a pty until all output has been collected. This
-means that it is not possible to send an EOF to stdin by half-closing
-the pty, as we can when using a pipe to stdin.
-
-This means that you need to send the child process an exit command or
-signal, or run() / finish() will time out. Be careful not to expect a
-prompt after sending the exit command.
-
-=item Command line editing
-
-Some subprocesses, notable shells that depend on the user's prompt
-settings, will reissue the prompt plus the command line input so far
-once for each character.
-
-=item '>pty>' means '&>pty>', not '1>pty>'
-
-The pseudo terminal redirects both stdout and stderr unless you specify
-a file descriptor. If you want to grab stderr separately, do this:
-
- start \@cmd, '<pty<', \$in, '>pty>', \$out, '2>', \$err ;
-
-=item stdin, stdout, and stderr not inherited
-
-Child processes harnessed to a pseudo terminal have their stdin, stdout,
-and stderr completely closed before any redirection operators take
-effect. This casts of the bonds of the controlling terminal. This is
-not done when using pipes.
-
-Right now, this affects all children in a harness that has a pty in use,
-even if that pty would not affect a particular child. That's a bug and
-will be fixed. Until it is, it's best not to mix-and-match children.
-
-=back
-
-=head2 Redirection Operators
-
- Operator SHNP Description
- ======== ==== ===========
- <, N< SHN Redirects input to a child's fd N (0 assumed)
-
- >, N> SHN Redirects output from a child's fd N (1 assumed)
- >>, N>> SHN Like '>', but appends to scalars or named files
- >&, &> SHN Redirects stdout & stderr from a child process
-
- <pty, N<pty S Like '<', but uses a pseudo-tty instead of a pipe
- >pty, N>pty S Like '>', but uses a pseudo-tty instead of a pipe
-
- N<&M Dups input fd N to input fd M
- M>&N Dups output fd N to input fd M
- N<&- Closes fd N
-
- <pipe, N<pipe P Pipe opens H for caller to read, write, close.
- >pipe, N>pipe P Pipe opens H for caller to read, write, close.
-
-'N' and 'M' are placeholders for integer file descriptor numbers. The
-terms 'input' and 'output' are from the child process's perspective.
-
-The SHNP field indicates what parameters an operator can take:
-
- S: \$scalar or \&function references. Filters may be used with
- these operators (and only these).
- H: \*HANDLE or IO::Handle for caller to open, and close
- N: "file name".
- P: \*HANDLE opened by IPC::Run as the parent end of a pipe, but read
- and written to and closed by the caller (like IPC::Open3).
-
-=over
-
-=item Redirecting input: [n]<, [n]<pipe
-
-You can input the child reads on file descriptor number n to come from a
-scalar variable, subroutine, file handle, or a named file. If stdin
-is not redirected, the parent's stdin is inherited.
-
- run \@cat, \undef ## Closes child's stdin immediately
- or die "cat returned $?" ;
-
- run \@cat, \$in ;
-
- run \@cat, \<<TOHERE ;
- blah
- TOHERE
-
- run \@cat, \&input ; ## Calls &input, feeding data returned
- ## to child's. Closes child's stdin
- ## when undef is returned.
-
-Redirecting from named files requires you to use the input
-redirection operator:
-
- run \@cat, '<.profile' ;
- run \@cat, '<', '.profile' ;
-
- open IN, "<foo" ;
- run \@cat, \*IN ;
- run \@cat, *IN{IO} ;
-
-The form used second example here is the safest,
-since filenames like "0" and "&more\n" won't confuse &run:
-
-You can't do either of
-
- run \@a, *IN ; ## INVALID
- run \@a, '<', *IN ; ## BUGGY: Reads file named like "*main::A"
-
-because perl passes a scalar containing a string that
-looks like "*main::A" to &run, and &run can't tell the difference
-between that and a redirection operator or a file name. &run guarantees
-that any scalar you pass after a redirection operator is a file name.
-
-If your child process will take input from file descriptors other
-than 0 (stdin), you can use a redirection operator with any of the
-valid input forms (scalar ref, sub ref, etc.):
-
- run \@cat, '3<', \$in3 ;
-
-When redirecting input from a scalar ref, the scalar ref is
-used as a queue. This allows you to use &harness and pump() to
-feed incremental bits of input to a coprocess. See L</Coprocesses>
-below for more information.
-
-The <pipe operator opens the write half of a pipe on the filehandle
-glob reference it takes as an argument:
-
- $h = start \@cat, '<pipe', \*IN ;
- print IN "hello world\n" ;
- pump $h ;
- close IN ;
- finish $h ;
-
-Unlike the other '<' operators, IPC::Run does nothing further with
-it: you are responsible for it. The previous example is functionally
-equivalent to:
-
- pipe( \*R, \*IN ) or die $! ;
- $h = start \@cat, '<', \*IN ;
- print IN "hello world\n" ;
- pump $h ;
- close IN ;
- finish $h ;
-
-This is like the behavior of IPC::Open2 and IPC::Open3.
-
-B<Win32>: The handle returned is actually a socket handle, so you can
-use select() on it.
-
-=item Redirecting output: [n]>, [n]>>, [n]>&[m], [n]>pipe
-
-You can redirect any output the child emits
-to a scalar variable, subroutine, file handle, or file name. You
-can have &run truncate or append to named files or scalars. If
-you are redirecting stdin as well, or if the command is on the
-receiving end of a pipeline ('|'), you can omit the redirection
-operator:
-
- @ls = ( 'ls' ) ;
- run \@ls, \undef, \$out
- or die "ls returned $?" ;
-
- run \@ls, \undef, \&out ; ## Calls &out each time some output
- ## is received from the child's
- ## when undef is returned.
-
- run \@ls, \undef, '2>ls.err' ;
- run \@ls, '2>', 'ls.err' ;
-
-The two parameter form guarantees that the filename
-will not be interpreted as a redirection operator:
-
- run \@ls, '>', "&more" ;
- run \@ls, '2>', ">foo\n" ;
-
-You can pass file handles you've opened for writing:
-
- open( *OUT, ">out.txt" ) ;
- open( *ERR, ">err.txt" ) ;
- run \@cat, \*OUT, \*ERR ;
-
-Passing a scalar reference and a code reference requires a little
-more work, but allows you to capture all of the output in a scalar
-or each piece of output by a callback:
-
-These two do the same things:
-
- run( [ 'ls' ], '2>', sub { $err_out .= $_[0] } ) ;
-
-does the same basic thing as:
-
- run( [ 'ls' ], '2>', \$err_out ) ;
-
-The subroutine will be called each time some data is read from the child.
-
-The >pipe operator is different in concept than the other '>' operators,
-although it's syntax is similar:
-
- $h = start \@cat, $in, '>pipe', \*OUT, '2>pipe', \*ERR ;
- $in = "hello world\n" ;
- finish $h ;
- print <OUT> ;
- print <ERR> ;
- close OUT ;
- close ERR ;
-
-causes two pipe to be created, with one end attached to cat's stdout
-and stderr, respectively, and the other left open on OUT and ERR, so
-that the script can manually
-read(), select(), etc. on them. This is like
-the behavior of IPC::Open2 and IPC::Open3.
-
-B<Win32>: The handle returned is actually a socket handle, so you can
-use select() on it.
-
-=item Duplicating output descriptors: >&m, n>&m
-
-This duplicates output descriptor number n (default is 1 if n is omitted)
-from descriptor number m.
-
-=item Duplicating input descriptors: <&m, n<&m
-
-This duplicates input descriptor number n (default is 0 if n is omitted)
-from descriptor number m
-
-=item Closing descriptors: <&-, 3<&-
-
-This closes descriptor number n (default is 0 if n is omitted). The
-following commands are equivalent:
-
- run \@cmd, \undef ;
- run \@cmd, '<&-' ;
- run \@cmd, '<in.txt', '<&-' ;
-
-Doing
-
- run \@cmd, \$in, '<&-' ; ## SIGPIPE recipe.
-
-is dangerous: the parent will get a SIGPIPE if $in is not empty.
-
-=item Redirecting both stdout and stderr: &>, >&, &>pipe, >pipe&
-
-The following pairs of commands are equivalent:
-
- run \@cmd, '>&', \$out ; run \@cmd, '>', \$out, '2>&1' ;
- run \@cmd, '>&', 'out.txt' ; run \@cmd, '>', 'out.txt', '2>&1' ;
-
-etc.
-
-File descriptor numbers are not permitted to the left or the right of
-these operators, and the '&' may occur on either end of the operator.
-
-The '&>pipe' and '>pipe&' variants behave like the '>pipe' operator, except
-that both stdout and stderr write to the created pipe.
-
-=item Redirection Filters
-
-Both input redirections and output redirections that use scalars or
-subs as endpoints may have an arbitrary number of filter subs placed
-between them and the child process. This is useful if you want to
-receive output in chunks, or if you want to massage each chunk of
-data sent to the child. To use this feature, you must use operator
-syntax:
-
- run(
- \@cmd
- '<', \&in_filter_2, \&in_filter_1, $in,
- '>', \&out_filter_1, \&in_filter_2, $out,
- ) ;
-
-This capability is not provided for IO handles or named files.
-
-Two filters are provided by IPC::Run: appender and chunker. Because
-these may take an argument, you need to use the constructor functions
-new_appender() and new_chunker() rather than using \& syntax:
-
- run(
- \@cmd
- '<', new_appender( "\n" ), $in,
- '>', new_chunker, $out,
- ) ;
-
-=back
-
-=head2 Just doing I/O
-
-If you just want to do I/O to a handle or file you open yourself, you
-may specify a filehandle or filename instead of a command in the harness
-specification:
-
- run io( "filename", '>', \$recv ) ;
-
- $h = start io( $io, '>', \$recv ) ;
-
- $h = harness \@cmd, '&', io( "file", '<', \$send ) ;
-
-=head2 Options
-
-Options are passed in as name/value pairs:
-
- run \@cat, \$in, debug => 1 ;
-
-If you pass the debug option, you may want to pass it in first, so you
-can see what parsing is going on:
-
- run debug => 1, \@cat, \$in ;
-
-=over
-
-=item debug
-
-Enables debugging output in parent and child. Debugging info is emitted
-to the STDERR that was present when IPC::Run was first C<use()>ed (it's
-C<dup()>ed out of the way so that it can be redirected in children without
-having debugging output emitted on it).
-
-=back
-
-=head1 RETURN VALUES
-
-harness() and start() return a reference to an IPC::Run harness. This is
-blessed in to the IPC::Run package, so you may make later calls to
-functions as members if you like:
-
- $h = harness( ... ) ;
- $h->start ;
- $h->pump ;
- $h->finish ;
-
- $h = start( .... ) ;
- $h->pump ;
- ...
-
-Of course, using method call syntax lets you deal with any IPC::Run
-subclasses that might crop up, but don't hold your breath waiting for
-any.
-
-run() and finish() return TRUE when all subcommands exit with a 0 result
-code. B<This is the opposite of perl's system() command>.
-
-All routines raise exceptions (via die()) when error conditions are
-recognized. A non-zero command result is not treated as an error
-condition, since some commands are tests whose results are reported
-in their exit codes.
-
-=head1 ROUTINES
-
-=over
-
-=cut
-
-@ISA = qw( Exporter ) ;
-
-## We use @EXPORT for the end user's convenience: there's only one function
-## exported, it's homonymous with the module, it's an unusual name, and
-## it can be suppressed by "use IPC::Run () ;".
-
-my @FILTER_IMP = qw( input_avail get_more_input ) ;
-my @FILTERS = qw(
- new_appender
- new_chunker
- new_string_source
- new_string_sink
-) ;
-my @API = qw(
- run
- harness start pump pumpable finish
- signal kill_kill reap_nb
- io timer timeout
- close_terminal
- binary
-) ;
-
-@EXPORT_OK = ( @API, @FILTER_IMP, @FILTERS, qw( filter_tests Win32_MODE ) ) ;
-%EXPORT_TAGS = (
- 'filter_imp' => \@FILTER_IMP,
- 'all' => \@EXPORT_OK,
- 'filters' => \@FILTERS,
- 'api' => \@API,
-) ;
-
-use strict ;
-
-use IPC::Run::Debug;
-use Exporter ;
-use Fcntl ;
-use POSIX () ;
-use Symbol ;
-use Carp ;
-use File::Spec ;
-use IO::Handle ;
-require IPC::Run::IO ;
-require IPC::Run::Timer ;
-use UNIVERSAL qw( isa ) ;
-
-use constant Win32_MODE => $^O =~ /os2|Win32/i ;
-
-BEGIN {
- if ( Win32_MODE ) {
- eval "use IPC::Run::Win32Helper; 1;"
- or ( $@ && die ) or die "$!" ;
- }
- else {
- eval "use File::Basename; 1;" or die $! ;
- }
-}
-
-
-sub input_avail() ;
-sub get_more_input() ;
-
-###############################################################################
-
-##
-## State machine states, set in $self->{STATE}
-##
-## These must be in ascending order numerically
-##
-sub _newed() {0}
-sub _harnessed(){1}
-sub _finished() {2} ## _finished behave almost exactly like _harnessed
-sub _started() {3}
-
-##
-## Which fds have been opened in the parent. This may have extra fds, since
-## we aren't all that rigorous about closing these off, but that's ok. This
-## is used on Unixish OSs to close all fds in the child that aren't needed
-## by that particular child.
-my %fds ;
-
-## There's a bit of hackery going on here.
-##
-## We want to have any code anywhere be able to emit
-## debugging statements without knowing what harness the code is
-## being called in/from, since we'd need to pass a harness around to
-## everything.
-##
-## Thus, $cur_self was born.
-
-use vars qw( $cur_self ) ;
-
-sub _debug_fd {
- return fileno STDERR unless defined $cur_self ;
-
- if ( _debugging && ! defined $cur_self->{DEBUG_FD} ) {
- my $fd = select STDERR ; $| = 1 ; select $fd ;
- $cur_self->{DEBUG_FD} = POSIX::dup fileno STDERR ;
- _debug( "debugging fd is $cur_self->{DEBUG_FD}\n" )
- if _debugging_details ;
- }
-
- return fileno STDERR unless defined $cur_self->{DEBUG_FD} ;
-
- return $cur_self->{DEBUG_FD}
-}
-
-sub DESTROY {
- ## We absolutely do not want to do anything else here. We are likely
- ## to be in a child process and we don't want to do things like kill_kill
- ## ourself or cause other destruction.
- my IPC::Run $self = shift ;
- POSIX::close $self->{DEBUG_FD} if defined $self->{DEBUG_FD} ;
- $self->{DEBUG_FD} = undef ;
-}
-
-##
-## Support routines (NOT METHODS)
-##
-my %cmd_cache ;
-
-sub _search_path {
- my ( $cmd_name ) = @_ ;
- if ( File::Spec->file_name_is_absolute( $cmd_name ) && -x $cmd_name) {
- _debug "'", $cmd_name, "' is absolute"
- if _debugging_details ;
- return $cmd_name ;
- }
-
- my $dirsep =
- ( Win32_MODE
- ? '[/\\\\]'
- : $^O =~ /MacOS/
- ? ':'
- : $^O =~ /VMS/
- ? '[\[\]]'
- : '/'
- ) ;
-
- if ( Win32_MODE
- && ( $cmd_name =~ /$dirsep/ )
- && ( $cmd_name !~ /\..+$/ ) ## Only run if cmd_name has no extension?
- ) {
- for ( split /;/, $ENV{PATHEXT} || ".COM;.BAT;.EXE" ) {
- my $name = "$cmd_name$_";
- $cmd_name = $name, last if -f $name && -x _;
- }
- }
-
- if ( $cmd_name =~ /($dirsep)/ ) {
- _debug "'$cmd_name' contains '$1'" if _debugging;
- croak "file not found: $cmd_name" unless -e $cmd_name ;
- croak "not a file: $cmd_name" unless -f $cmd_name ;
- croak "permission denied: $cmd_name" unless -x $cmd_name ;
- return $cmd_name ;
- }
-
- if ( exists $cmd_cache{$cmd_name} ) {
- _debug "'$cmd_name' found in cache: '$cmd_cache{$cmd_name}'"
- if _debugging;
- return $cmd_cache{$cmd_name} if -x $cmd_cache{$cmd_name} ;
- _debug "'$cmd_cache{$cmd_name}' no longer executable, searching..."
- if _debugging;
- delete $cmd_cache{$cmd_name} ;
- }
-
- my @searched_in ;
-
- ## This next bit is Unix/Win32 specific, unfortunately.
- ## There's been some conversation about extending File::Spec to provide
- ## a universal interface to PATH, but I haven't seen it yet.
- my $re = Win32_MODE ? qr/;/ : qr/:/ ;
-
-LOOP:
- for ( split( $re, $ENV{PATH}, -1 ) ) {
- $_ = "." unless length $_ ;
- push @searched_in, $_ ;
-
- my $prospect = File::Spec->catfile( $_, $cmd_name ) ;
- my @prospects ;
-
- @prospects =
- ( Win32_MODE && ! ( -f $prospect && -x _ ) )
- ? map "$prospect$_", split /;/, $ENV{PATHEXT} || ".COM;.BAT;.EXE"
- : ( $prospect ) ;
-
- for my $found ( @prospects ) {
- if ( -f $found && -x _ ) {
- $cmd_cache{$cmd_name} = $found ;
- last LOOP ;
- }
- }
- }
-
- if ( exists $cmd_cache{$cmd_name} ) {
- _debug "'", $cmd_name, "' added to cache: '", $cmd_cache{$cmd_name}, "'"
- if _debugging_details ;
- return $cmd_cache{$cmd_name} ;
- }
-
- croak "Command '$cmd_name' not found in " . join( ", ", @searched_in ) ;
-}
-
-
-sub _empty($) { ! ( defined $_[0] && length $_[0] ) }
-
-## 'safe' versions of otherwise fun things to do. See also IPC::Run::Win32Helper.
-sub _close {
- confess 'undef' unless defined $_[0] ;
- no strict 'refs' ;
- my $fd = $_[0] =~ /^\d+$/ ? $_[0] : fileno $_[0] ;
- my $r = POSIX::close $fd ;
- $r = $r ? '' : " ERROR $!" ;
- delete $fds{$fd} ;
- _debug "close( $fd ) = " . ( $r || 0 ) if _debugging_details ;
-}
-
-sub _dup {
- confess 'undef' unless defined $_[0] ;
- my $r = POSIX::dup( $_[0] ) ;
- croak "$!: dup( $_[0] )" unless defined $r ;
- $r = 0 if $r eq '0 but true' ;
- _debug "dup( $_[0] ) = $r" if _debugging_details ;
- $fds{$r} = 1 ;
- return $r ;
-}
-
-
-sub _dup2_rudely {
- confess 'undef' unless defined $_[0] && defined $_[1] ;
- my $r = POSIX::dup2( $_[0], $_[1] ) ;
- croak "$!: dup2( $_[0], $_[1] )" unless defined $r ;
- $r = 0 if $r eq '0 but true' ;
- _debug "dup2( $_[0], $_[1] ) = $r" if _debugging_details ;
- $fds{$r} = 1 ;
- return $r ;
-}
-
-sub _exec {
- confess 'undef passed' if grep !defined, @_ ;
-# exec @_ or croak "$!: exec( " . join( ', ', @_ ) . " )" ;
- _debug 'exec()ing ', join " ", map "'$_'", @_ if _debugging_details ;
-
-# {
-## Commented out since we don't call this on Win32.
-# # This works around the bug where 5.6.1 complains
-# # "Can't exec ...: No error" after an exec on NT, where
-# # exec() is simulated and actually returns in Perl's C
-# # code, though Perl's &exec does not...
-# no warnings "exec" ;
-#
-# # Just in case the no warnings workaround
-# # stops beign a workaround, we don't want
-# # old values of $! causing spurious strerr()
-# # messages to appear in the "Can't exec" message
-# undef $! ;
- exec @_ ;
-# }
-# croak "$!: exec( " . join( ', ', map "'$_'", @_ ) . " )" ;
- ## Fall through so $! can be reported to parent.
-}
-
-
-sub _sysopen {
- confess 'undef' unless defined $_[0] && defined $_[1] ;
-_debug sprintf( "O_RDONLY=0x%02x ", O_RDONLY ),
-sprintf( "O_WRONLY=0x%02x ", O_WRONLY ),
-sprintf( "O_RDWR=0x%02x ", O_RDWR ),
-sprintf( "O_TRUNC=0x%02x ", O_TRUNC),
-sprintf( "O_CREAT=0x%02x ", O_CREAT),
-sprintf( "O_APPEND=0x%02x ", O_APPEND),
-if _debugging_details ;
- my $r = POSIX::open( $_[0], $_[1], 0644 ) ;
- croak "$!: open( $_[0], ", sprintf( "0x%03x", $_[1] ), " )" unless defined $r ;
- _debug "open( $_[0], ", sprintf( "0x%03x", $_[1] ), " ) = $r"
- if _debugging_data ;
- $fds{$r} = 1 ;
- return $r ;
-}
-
-sub _pipe {
- ## Normal, blocking write for pipes that we read and the child writes,
- ## since most children expect writes to stdout to block rather than
- ## do a partial write.
- my ( $r, $w ) = POSIX::pipe ;
- croak "$!: pipe()" unless defined $r ;
- _debug "pipe() = ( $r, $w ) " if _debugging_details ;
- $fds{$r} = $fds{$w} = 1 ;
- return ( $r, $w ) ;
-}
-
-sub _pipe_nb {
- ## For pipes that we write, unblock the write side, so we can fill a buffer
- ## and continue to select().
- ## Contributed by Borislav Deianov <borislav@ensim.com>, with minor
- ## bugfix on fcntl result by me.
- local ( *R, *W ) ;
- my $f = pipe( R, W ) ;
- croak "$!: pipe()" unless defined $f ;
- my ( $r, $w ) = ( fileno R, fileno W ) ;
- _debug "pipe_nb pipe() = ( $r, $w )" if _debugging_details ;
- unless ( Win32_MODE ) {
- ## POSIX::fcntl doesn't take fd numbers, so gotta use Perl's and
- ## then _dup the originals (which get closed on leaving this block)
- my $fres = fcntl( W, &F_SETFL, O_WRONLY | O_NONBLOCK );
- croak "$!: fcntl( $w, F_SETFL, O_NONBLOCK )" unless $fres ;
- _debug "fcntl( $w, F_SETFL, O_NONBLOCK )" if _debugging_details ;
- }
- ( $r, $w ) = ( _dup( $r ), _dup( $w ) ) ;
- _debug "pipe_nb() = ( $r, $w )" if _debugging_details ;
- return ( $r, $w ) ;
-}
-
-sub _pty {
- require IO::Pty ;
- my $pty = IO::Pty->new() ;
- croak "$!: pty ()" unless $pty ;
- $pty->autoflush() ;
- $pty->blocking( 0 ) or croak "$!: pty->blocking ( 0 )" ;
- _debug "pty() = ( ", $pty->fileno, ", ", $pty->slave->fileno, " )"
- if _debugging_details ;
- $fds{$pty->fileno} = $fds{$pty->slave->fileno} = 1 ;
- return $pty ;
-}
-
-
-sub _read {
- confess 'undef' unless defined $_[0] ;
- my $s = '' ;
- my $r = POSIX::read( $_[0], $s, 10_000 ) ;
- croak "$!: read( $_[0] )" if not($r) and $! != POSIX::EINTR;
- $r ||= 0;
- _debug "read( $_[0] ) = $r chars '$s'" if _debugging_data ;
- return $s ;
-}
-
-
-## A METHOD, not a function.
-sub _spawn {
- my IPC::Run $self = shift ;
- my ( $kid ) = @_ ;
-
- _debug "opening sync pipe ", $kid->{PID} if _debugging_details ;
- my $sync_reader_fd ;
- ( $sync_reader_fd, $self->{SYNC_WRITER_FD} ) = _pipe ;
- $kid->{PID} = fork() ;
- croak "$! during fork" unless defined $kid->{PID} ;
-
- unless ( $kid->{PID} ) {
- ## _do_kid_and_exit closes sync_reader_fd since it closes all unwanted and
- ## unloved fds.
- $self->_do_kid_and_exit( $kid ) ;
- }
- _debug "fork() = ", $kid->{PID} if _debugging_details ;
-
- ## Wait for kid to get to it's exec() and see if it fails.
- _close $self->{SYNC_WRITER_FD} ;
- my $sync_pulse = _read $sync_reader_fd ;
- _close $sync_reader_fd ;
-
- if ( ! defined $sync_pulse || length $sync_pulse ) {
- if ( waitpid( $kid->{PID}, 0 ) >= 0 ) {
- $kid->{RESULT} = $? ;
- }
- else {
- $kid->{RESULT} = -1 ;
- }
- $sync_pulse =
- "error reading synchronization pipe for $kid->{NUM}, pid $kid->{PID}"
- unless length $sync_pulse ;
- croak $sync_pulse ;
- }
- return $kid->{PID} ;
-
-## Wait for pty to get set up. This is a hack until we get synchronous
-## selects.
-if ( keys %{$self->{PTYS}} && $IO::Pty::VERSION < 0.9 ) {
-_debug "sleeping to give pty a chance to init, will fix when newer IO::Pty arrives." ;
-sleep 1 ;
-}
-}
-
-
-sub _write {
- confess 'undef' unless defined $_[0] && defined $_[1] ;
- my $r = POSIX::write( $_[0], $_[1], length $_[1] ) ;
- croak "$!: write( $_[0], '$_[1]' )" unless $r ;
- _debug "write( $_[0], '$_[1]' ) = $r" if _debugging_data ;
- return $r ;
-}
-
-
-=item run
-
-Run takes a harness or harness specification and runs it, pumping
-all input to the child(ren), closing the input pipes when no more
-input is available, collecting all output that arrives, until the
-pipes delivering output are closed, then waiting for the children to
-exit and reaping their result codes.
-
-You may think of C<run( ... )> as being like
-
- start( ... )->finish() ;
-
-, though there is one subtle difference: run() does not
-set \$input_scalars to '' like finish() does. If an exception is thrown
-from run(), all children will be killed off "gently", and then "annihilated"
-if they do not go gently (in to that dark night. sorry).
-
-If any exceptions are thrown, this does a L</kill_kill> before propogating
-them.
-
-=cut
-
-use vars qw( $in_run ); ## No, not Enron ;)
-
-sub run {
- local $in_run = 1; ## Allow run()-only optimizations.
- my IPC::Run $self = start( @_ );
- my $r = eval {
- $self->{clear_ins} = 0 ;
- $self->finish ;
- } ;
- if ( $@ ) {
- my $x = $@ ;
- $self->kill_kill ;
- die $x ;
- }
- return $r ;
-}
-
-
-=item signal
-
- ## To send it a specific signal by name ("USR1"):
- signal $h, "USR1" ;
- $h->signal ( "USR1" ) ;
-
-If $signal is provided and defined, sends a signal to all child processes. Try
-not to send numeric signals, use C<"KILL"> instead of C<9>, for instance.
-Numeric signals aren't portable.
-
-Throws an exception if $signal is undef.
-
-This will I<not> clean up the harness, C<finish> it if you kill it.
-
-Normally TERM kills a process gracefully (this is what the command line utility
-C<kill> does by default), INT is sent by one of the keys C<^C>, C<Backspace> or
-C<E<lt>DelE<gt>>, and C<QUIT> is used to kill a process and make it coredump.
-
-The C<HUP> signal is often used to get a process to "restart", rereading
-config files, and C<USR1> and C<USR2> for really application-specific things.
-
-Often, running C<kill -l> (that's a lower case "L") on the command line will
-list the signals present on your operating system.
-
-B<WARNING>: The signal subsystem is not at all portable. We *may* offer
-to simulate C<TERM> and C<KILL> on some operating systems, submit code
-to me if you want this.
-
-B<WARNING 2>: Up to and including perl v5.6.1, doing almost anything in a
-signal handler could be dangerous. The most safe code avoids all
-mallocs and system calls, usually by preallocating a flag before
-entering the signal handler, altering the flag's value in the
-handler, and responding to the changed value in the main system:
-
- my $got_usr1 = 0 ;
- sub usr1_handler { ++$got_signal }
-
- $SIG{USR1} = \&usr1_handler ;
- while () { sleep 1 ; print "GOT IT" while $got_usr1-- ; }
-
-Even this approach is perilous if ++ and -- aren't atomic on your system
-(I've never heard of this on any modern CPU large enough to run perl).
-
-=cut
-
-sub signal {
- my IPC::Run $self = shift ;
-
- local $cur_self = $self ;
-
- $self->_kill_kill_kill_pussycat_kill unless @_ ;
-
- Carp::cluck "Ignoring extra parameters passed to kill()" if @_ > 1 ;
-
- my ( $signal ) = @_ ;
- croak "Undefined signal passed to signal" unless defined $signal ;
- for ( grep $_->{PID} && ! defined $_->{RESULT}, @{$self->{KIDS}} ) {
- _debug "sending $signal to $_->{PID}"
- if _debugging;
- kill $signal, $_->{PID}
- or _debugging && _debug "$! sending $signal to $_->{PID}" ;
- }
-
- return ;
-}
-
-
-=item kill_kill
-
- ## To kill off a process:
- $h->kill_kill ;
- kill_kill $h ;
-
- ## To specify the grace period other than 30 seconds:
- kill_kill $h, grace => 5 ;
-
- ## To send QUIT instead of KILL if a process refuses to die:
- kill_kill $h, coup_d_grace => "QUIT" ;
-
-Sends a C<TERM>, waits for all children to exit for up to 30 seconds, then
-sends a C<KILL> to any that survived the C<TERM>.
-
-Will wait for up to 30 more seconds for the OS to sucessfully C<KILL> the
-processes.
-
-The 30 seconds may be overriden by setting the C<grace> option, this
-overrides both timers.
-
-The harness is then cleaned up.
-
-The doubled name indicates that this function may kill again and avoids
-colliding with the core Perl C<kill> function.
-
-Returns a 1 if the C<TERM> was sufficient, or a 0 if C<KILL> was
-required. Throws an exception if C<KILL> did not permit the children
-to be reaped.
-
-B<NOTE>: The grace period is actually up to 1 second longer than that
-given. This is because the granularity of C<time> is 1 second. Let me
-know if you need finer granularity, we can leverage Time::HiRes here.
-
-B<Win32>: Win32 does not know how to send real signals, so C<TERM> is
-a full-force kill on Win32. Thus all talk of grace periods, etc. do
-not apply to Win32.
-
-=cut
-
-sub kill_kill {
- my IPC::Run $self = shift ;
-
- my %options = @_ ;
- my $grace = $options{grace} ;
- $grace = 30 unless defined $grace ;
- ++$grace ; ## Make grace time a _minimum_
-
- my $coup_d_grace = $options{coup_d_grace} ;
- $coup_d_grace = "KILL" unless defined $coup_d_grace ;
-
- delete $options{$_} for qw( grace coup_d_grace ) ;
- Carp::cluck "Ignoring unknown options for kill_kill: ",
- join " ",keys %options
- if keys %options ;
-
- $self->signal( "TERM" ) ;
-
- my $quitting_time = time + $grace ;
- my $delay = 0.01 ;
- my $accum_delay ;
-
- my $have_killed_before ;
-
- while () {
- ## delay first to yeild to other processes
- select undef, undef, undef, $delay ;
- $accum_delay += $delay ;
-
- $self->reap_nb ;
- last unless $self->_running_kids ;
-
- if ( $accum_delay >= $grace*0.8 ) {
- ## No point in checking until delay has grown some.
- if ( time >= $quitting_time ) {
- if ( ! $have_killed_before ) {
- $self->signal( $coup_d_grace ) ;
- $have_killed_before = 1 ;
- $quitting_time += $grace ;
- $delay = 0.01 ;
- $accum_delay = 0 ;
- next ;
- }
- croak "Unable to reap all children, even after KILLing them"
- }
- }
-
- $delay *= 2 ;
- $delay = 0.5 if $delay >= 0.5 ;
- }
-
- $self->_cleanup ;
- return $have_killed_before ;
-}
-
-
-=item harness
-
-Takes a harness specification and returns a harness. This harness is
-blessed in to IPC::Run, allowing you to use method call syntax for
-run(), start(), et al if you like.
-
-harness() is provided so that you can pre-build harnesses if you
-would like to, but it's not required..
-
-You may proceed to run(), start() or pump() after calling harness() (pump()
-calls start() if need be). Alternatively, you may pass your
-harness specification to run() or start() and let them harness() for
-you. You can't pass harness specifications to pump(), though.
-
-=cut
-
-##
-## Notes: I've avoided handling a scalar that doesn't look like an
-## opcode as a here document or as a filename, though I could DWIM
-## those. I'm not sure that the advantages outweight the danger when
-## the DWIMer guesses wrong.
-##
-## TODO: allow user to spec default shell. Hmm, globally, in the
-## lexical scope hash, or per instance? 'Course they can do that
-## now by using a [...] to hold the command.
-##
-my $harness_id = 0 ;
-sub harness {
- my $options ;
- if ( @_ && ref $_[-1] eq 'HASH' ) {
- $options = pop ;
- require Data::Dumper ;
- carp "Passing in options as a hash is deprecated:\n", Data::Dumper::Dumper( $options ) ;
- }
-
-# local $IPC::Run::debug = $options->{debug}
-# if $options && defined $options->{debug} ;
-
- my @args ;
-
- if ( @_ == 1 && ! ref $_[0] ) {
- if ( Win32_MODE ) {
- @args = ( [ qw( command /c ), win32_parse_cmd_line $_[0] ] ) ;
- }
- else {
- @args = ( [ qw( sh -c ), @_ ] ) ;
- }
- }
- elsif ( @_ > 1 && ! grep ref $_, @_ ) {
- @args = ( [ @_ ] ) ;
- }
- else {
- @args = @_ ;
- }
-
- my @errs ; # Accum errors, emit them when done.
-
- my $succinct ; # set if no redir ops are required yet. Cleared
- # if an op is seen.
-
- my $cur_kid ; # references kid or handle being parsed
-
- my $assumed_fd = 0 ; # fd to assume in succinct mode (no redir ops)
- my $handle_num = 0 ; # 1... is which handle we're parsing
-
- my IPC::Run $self = bless {}, __PACKAGE__;
-
- local $cur_self = $self ;
-
- $self->{ID} = ++$harness_id ;
- $self->{IOS} = [] ;
- $self->{KIDS} = [] ;
- $self->{PIPES} = [] ;
- $self->{PTYS} = {} ;
- $self->{STATE} = _newed ;
-
- if ( $options ) {
- $self->{$_} = $options->{$_}
- for keys %$options ;
- }
-
- _debug "****** harnessing *****" if _debugging;
-
- my $first_parse ;
- local $_ ;
- my $arg_count = @args ;
- while ( @args ) { for ( shift @args ) {
- eval {
- $first_parse = 1 ;
- _debug(
- "parsing ",
- defined $_
- ? ref $_ eq 'ARRAY'
- ? ( '[ ', join( ', ', map "'$_'", @$_ ), ' ]' )
- : ( ref $_
- || ( length $_ < 50
- ? "'$_'"
- : join( '', "'", substr( $_, 0, 10 ), "...'" )
- )
- )
- : '<undef>'
- ) if _debugging;
-
- REPARSE:
- if ( ref eq 'ARRAY' || ( ! $cur_kid && ref eq 'CODE' ) ) {
- croak "Process control symbol ('|', '&') missing" if $cur_kid ;
- croak "Can't spawn a subroutine on Win32"
- if Win32_MODE && ref eq "CODE" ;
- $cur_kid = {
- TYPE => 'cmd',
- VAL => $_,
- NUM => @{$self->{KIDS}} + 1,
- OPS => [],
- PID => '',
- RESULT => undef,
- } ;
- push @{$self->{KIDS}}, $cur_kid ;
- $succinct = 1 ;
- }
-
- elsif ( isa( $_, 'IPC::Run::IO' ) ) {
- push @{$self->{IOS}}, $_ ;
- $cur_kid = undef ;
- $succinct = 1 ;
- }
-
- elsif ( isa( $_, 'IPC::Run::Timer' ) ) {
- push @{$self->{TIMERS}}, $_ ;
- $cur_kid = undef ;
- $succinct = 1 ;
- }
-
- elsif ( /^(\d*)>&(\d+)$/ ) {
- croak "No command before '$_'" unless $cur_kid ;
- push @{$cur_kid->{OPS}}, {
- TYPE => 'dup',
- KFD1 => $2,
- KFD2 => length $1 ? $1 : 1,
- } ;
- _debug "redirect operators now required" if _debugging_details ;
- $succinct = ! $first_parse ;
- }
-
- elsif ( /^(\d*)<&(\d+)$/ ) {
- croak "No command before '$_'" unless $cur_kid ;
- push @{$cur_kid->{OPS}}, {
- TYPE => 'dup',
- KFD1 => $2,
- KFD2 => length $1 ? $1 : 0,
- } ;
- $succinct = ! $first_parse ;
- }
-
- elsif ( /^(\d*)<&-$/ ) {
- croak "No command before '$_'" unless $cur_kid ;
- push @{$cur_kid->{OPS}}, {
- TYPE => 'close',
- KFD => length $1 ? $1 : 0,
- } ;
- $succinct = ! $first_parse ;
- }
-
- elsif (
- /^(\d*) (<pipe)() () () $/x
- || /^(\d*) (<pty) ((?:\s+\S+)?) (<) () $/x
- || /^(\d*) (<) () () (.*)$/x
- ) {
- croak "No command before '$_'" unless $cur_kid ;
-
- $succinct = ! $first_parse ;
-
- my $type = $2 . $4 ;
-
- my $kfd = length $1 ? $1 : 0 ;
-
- my $pty_id ;
- if ( $type eq '<pty<' ) {
- $pty_id = length $3 ? $3 : '0' ;
- ## do the require here to cause early error reporting
- require IO::Pty ;
- ## Just flag the pyt's existence for now. It'll be
- ## converted to a real IO::Pty by _open_pipes.
- $self->{PTYS}->{$pty_id} = undef ;
- }
-
- my $source = $5 ;
-
- my @filters ;
- my $binmode ;
-
- unless ( length $source ) {
- if ( ! $succinct ) {
- while ( @args > 1
- && (
- ( ref $args[1] && ! isa $args[1], "IPC::Run::Timer" )
- || isa $args[0], "IPC::Run::binmode_pseudo_filter"
- )
- ) {
- if ( isa $args[0], "IPC::Run::binmode_pseudo_filter" ) {
- $binmode = shift( @args )->() ;
- }
- else {
- push @filters, shift @args
- }
- }
- }
- $source = shift @args ;
- croak "'$_' missing a source" if _empty $source ;
-
- _debug(
- 'Kid ', $cur_kid->{NUM}, "'s input fd ", $kfd,
- ' has ', scalar( @filters ), ' filters.'
- ) if _debugging_details && @filters ;
- } ;
-
- my IPC::Run::IO $pipe = IPC::Run::IO->_new_internal(
- $type, $kfd, $pty_id, $source, $binmode, @filters
- ) ;
-
- if ( ( ref $source eq 'GLOB' || isa $source, 'IO::Handle' )
- && $type !~ /^<p(ty<|ipe)$/
- ) {
- _debug "setting DONT_CLOSE" if _debugging_details ;
- $pipe->{DONT_CLOSE} = 1 ; ## this FD is not closed by us.
- _dont_inherit( $source ) if Win32_MODE ;
- }
-
- push @{$cur_kid->{OPS}}, $pipe ;
- }
-
- elsif ( /^() (>>?) (&) () (.*)$/x
- || /^() (&) (>pipe) () () $/x
- || /^() (>pipe)(&) () () $/x
- || /^(\d*)() (>pipe) () () $/x
- || /^() (&) (>pty) ( \w*)> () $/x
-## TODO: || /^() (>pty) (\d*)> (&) () $/x
- || /^(\d*)() (>pty) ( \w*)> () $/x
- || /^() (&) (>>?) () (.*)$/x
- || /^(\d*)() (>>?) () (.*)$/x
- ) {
- croak "No command before '$_'" unless $cur_kid ;
-
- $succinct = ! $first_parse ;
-
- my $type = (
- $2 eq '>pipe' || $3 eq '>pipe'
- ? '>pipe'
- : $2 eq '>pty' || $3 eq '>pty'
- ? '>pty>'
- : '>'
- ) ;
- my $kfd = length $1 ? $1 : 1 ;
- my $trunc = ! ( $2 eq '>>' || $3 eq '>>' ) ;
- my $pty_id = (
- $2 eq '>pty' || $3 eq '>pty'
- ? length $4 ? $4 : 0
- : undef
- ) ;
-
- my $stderr_too =
- $2 eq '&'
- || $3 eq '&'
- || ( ! length $1 && substr( $type, 0, 4 ) eq '>pty' ) ;
-
- my $dest = $5 ;
- my @filters ;
- my $binmode = 0 ;
- unless ( length $dest ) {
- if ( ! $succinct ) {
- ## unshift...shift: '>' filters source...sink left...right
- while ( @args > 1
- && (
- ( ref $args[1] && ! isa $args[1], "IPC::Run::Timer" )
- || isa $args[0], "IPC::Run::binmode_pseudo_filter"
- )
- ) {
- if ( isa $args[0], "IPC::Run::binmode_pseudo_filter" ) {
- $binmode = shift( @args )->() ;
- }
- else {
- unshift @filters, shift @args ;
- }
- }
- }
-
- $dest = shift @args ;
-
- _debug(
- 'Kid ', $cur_kid->{NUM}, "'s output fd ", $kfd,
- ' has ', scalar( @filters ), ' filters.'
- ) if _debugging_details && @filters ;
-
- if ( $type eq '>pty>' ) {
- ## do the require here to cause early error reporting
- require IO::Pty ;
- ## Just flag the pyt's existence for now. _open_pipes()
- ## will new an IO::Pty for each key.
- $self->{PTYS}->{$pty_id} = undef ;
- }
- }
-
- croak "'$_' missing a destination" if _empty $dest ;
- my $pipe = IPC::Run::IO->_new_internal(
- $type, $kfd, $pty_id, $dest, $binmode, @filters
- ) ;
- $pipe->{TRUNC} = $trunc ;
-
- if ( ( isa( $dest, 'GLOB' ) || isa( $dest, 'IO::Handle' ) )
- && $type !~ /^>(pty>|pipe)$/
- ) {
- _debug "setting DONT_CLOSE" if _debugging_details ;
- $pipe->{DONT_CLOSE} = 1 ; ## this FD is not closed by us.
- }
- push @{$cur_kid->{OPS}}, $pipe ;
- push @{$cur_kid->{OPS}}, {
- TYPE => 'dup',
- KFD1 => 1,
- KFD2 => 2,
- } if $stderr_too ;
- }
-
- elsif ( $_ eq "|" ) {
- croak "No command before '$_'" unless $cur_kid ;
- unshift @{$cur_kid->{OPS}}, {
- TYPE => '|',
- KFD => 1,
- } ;
- $succinct = 1 ;
- $assumed_fd = 1 ;
- $cur_kid = undef ;
- }
-
- elsif ( $_ eq "&" ) {
- croak "No command before '$_'" unless $cur_kid ;
- unshift @{$cur_kid->{OPS}}, {
- TYPE => 'close',
- KFD => 0,
- } ;
- $succinct = 1 ;
- $assumed_fd = 0 ;
- $cur_kid = undef ;
- }
-
- elsif ( $_ eq 'init' ) {
- croak "No command before '$_'" unless $cur_kid ;
- push @{$cur_kid->{OPS}}, {
- TYPE => 'init',
- SUB => shift @args,
- } ;
- }
-
- elsif ( ! ref $_ ) {
- $self->{$_} = shift @args;
- }
-
- elsif ( $_ eq 'init' ) {
- croak "No command before '$_'" unless $cur_kid ;
- push @{$cur_kid->{OPS}}, {
- TYPE => 'init',
- SUB => shift @args,
- } ;
- }
-
- elsif ( $succinct && $first_parse ) {
- ## It's not an opcode, and no explicit opcodes have been
- ## seen yet, so assume it's a file name.
- unshift @args, $_ ;
- if ( ! $assumed_fd ) {
- $_ = "$assumed_fd<",
- }
- else {
- $_ = "$assumed_fd>",
- }
- _debug "assuming '", $_, "'" if _debugging_details ;
- ++$assumed_fd ;
- $first_parse = 0 ;
- goto REPARSE ;
- }
-
- else {
- croak join(
- '',
- 'Unexpected ',
- ( ref() ? $_ : 'scalar' ),
- ' in harness() parameter ',
- $arg_count - @args
- ) ;
- }
- } ;
- if ( $@ ) {
- push @errs, $@ ;
- _debug 'caught ', $@ if _debugging;
- }
- } }
-
- die join( '', @errs ) if @errs ;
-
-
- $self->{STATE} = _harnessed ;
-# $self->timeout( $options->{timeout} ) if exists $options->{timeout} ;
- return $self ;
-}
-
-
-sub _open_pipes {
- my IPC::Run $self = shift ;
-
- my @errs ;
-
- my @close_on_fail ;
-
- ## When a pipe character is seen, a pipe is created. $pipe_read_fd holds
- ## the dangling read end of the pipe until we get to the next process.
- my $pipe_read_fd ;
-
- ## Output descriptors for the last command are shared by all children.
- ## @output_fds_accum accumulates the current set of output fds.
- my @output_fds_accum ;
-
- for ( sort keys %{$self->{PTYS}} ) {
- _debug "opening pty '", $_, "'" if _debugging_details ;
- my $pty = _pty ;
- $self->{PTYS}->{$_} = $pty ;
- }
-
- for ( @{$self->{IOS}} ) {
- eval { $_->init ; } ;
- if ( $@ ) {
- push @errs, $@ ;
- _debug 'caught ', $@ if _debugging;
- }
- else {
- push @close_on_fail, $_ ;
- }
- }
-
- ## Loop through the kids and their OPS, interpreting any that require
- ## parent-side actions.
- for my $kid ( @{$self->{KIDS}} ) {
- unless ( ref $kid->{VAL} eq 'CODE' ) {
- $kid->{PATH} = _search_path $kid->{VAL}->[0] ;
- }
- if ( defined $pipe_read_fd ) {
- _debug "placing write end of pipe on kid $kid->{NUM}'s stdin"
- if _debugging_details ;
- unshift @{$kid->{OPS}}, {
- TYPE => 'PIPE', ## Prevent next loop from triggering on this
- KFD => 0,
- TFD => $pipe_read_fd,
- } ;
- $pipe_read_fd = undef ;
- }
- @output_fds_accum = () ;
- for my $op ( @{$kid->{OPS}} ) {
-# next if $op->{IS_DEBUG} ;
- my $ok = eval {
- if ( $op->{TYPE} eq '<' ) {
- my $source = $op->{SOURCE};
- if ( ! ref $source ) {
- _debug(
- "kid ", $kid->{NUM}, " to read ", $op->{KFD},
- " from '" . $source, "' (read only)"
- ) if _debugging_details ;
- croak "simulated open failure"
- if $self->{_simulate_open_failure} ;
- $op->{TFD} = _sysopen( $source, O_RDONLY ) ;
- push @close_on_fail, $op->{TFD} ;
- }
- elsif ( isa( $source, 'GLOB' )
- || isa( $source, 'IO::Handle' )
- ) {
- croak
- "Unopened filehandle in input redirect for $op->{KFD}"
- unless defined fileno $source ;
- $op->{TFD} = fileno $source ;
- _debug(
- "kid ", $kid->{NUM}, " to read ", $op->{KFD},
- " from fd ", $op->{TFD}
- ) if _debugging_details ;
- }
- elsif ( isa( $source, 'SCALAR' ) ) {
- _debug(
- "kid ", $kid->{NUM}, " to read ", $op->{KFD},
- " from SCALAR"
- ) if _debugging_details ;
-
- $op->open_pipe( $self->_debug_fd ) ;
- push @close_on_fail, $op->{KFD}, $op->{FD} ;
-
- my $s = '' ;
- $op->{KIN_REF} = \$s ;
- }
- elsif ( isa( $source, 'CODE' ) ) {
- _debug(
- 'kid ', $kid->{NUM}, ' to read ', $op->{KFD}, ' from CODE'
- ) if _debugging_details ;
-
- $op->open_pipe( $self->_debug_fd ) ;
- push @close_on_fail, $op->{KFD}, $op->{FD} ;
-
- my $s = '' ;
- $op->{KIN_REF} = \$s ;
- }
- else {
- croak(
- "'"
- . ref( $source )
- . "' not allowed as a source for input redirection"
- ) ;
- }
- $op->_init_filters ;
- }
- elsif ( $op->{TYPE} eq '<pipe' ) {
- _debug(
- 'kid to read ', $op->{KFD},
- ' from a pipe IPC::Run opens and returns',
- ) if _debugging_details ;
-
- my ( $r, $w ) = $op->open_pipe( $self->_debug_fd, $op->{SOURCE} ) ;
- _debug "caller will write to ", fileno $op->{SOURCE}
- if _debugging_details;
-
- $op->{TFD} = $r ;
- $op->{FD} = undef ; # we don't manage this fd
- $op->_init_filters ;
- }
- elsif ( $op->{TYPE} eq '<pty<' ) {
- _debug(
- 'kid to read ', $op->{KFD}, " from pty '", $op->{PTY_ID}, "'",
- ) if _debugging_details ;
-
- for my $source ( $op->{SOURCE} ) {
- if ( isa( $source, 'SCALAR' ) ) {
- _debug(
- "kid ", $kid->{NUM}, " to read ", $op->{KFD},
- " from SCALAR via pty '", $op->{PTY_ID}, "'"
- ) if _debugging_details ;
-
- my $s = '' ;
- $op->{KIN_REF} = \$s ;
- }
- elsif ( isa( $source, 'CODE' ) ) {
- _debug(
- "kid ", $kid->{NUM}, " to read ", $op->{KFD},
- " from CODE via pty '", $op->{PTY_ID}, "'"
- ) if _debugging_details ;
- my $s = '' ;
- $op->{KIN_REF} = \$s ;
- }
- else {
- croak(
- "'"
- . ref( $source )
- . "' not allowed as a source for '<pty<' redirection"
- ) ;
- }
- }
- $op->{FD} = $self->{PTYS}->{$op->{PTY_ID}}->fileno ;
- $op->{TFD} = undef ; # The fd isn't known until after fork().
- $op->_init_filters ;
- }
- elsif ( $op->{TYPE} eq '>' ) {
- ## N> output redirection.
- my $dest = $op->{DEST} ;
- if ( ! ref $dest ) {
- _debug(
- "kid ", $kid->{NUM}, " to write ", $op->{KFD},
- " to '", $dest, "' (write only, create, ",
- ( $op->{TRUNC} ? 'truncate' : 'append' ),
- ")"
- ) if _debugging_details ;
- croak "simulated open failure"
- if $self->{_simulate_open_failure} ;
- $op->{TFD} = _sysopen(
- $dest,
- ( O_WRONLY
- | O_CREAT
- | ( $op->{TRUNC} ? O_TRUNC : O_APPEND )
- )
- ) ;
- if ( Win32_MODE ) {
- ## I have no idea why this is needed to make the current
- ## file position survive the gyrations TFD must go
- ## through...
- POSIX::lseek( $op->{TFD}, 0, POSIX::SEEK_END() ) ;
- }
- push @close_on_fail, $op->{TFD} ;
- }
- elsif ( isa( $dest, 'GLOB' ) ) {
- croak(
- "Unopened filehandle in output redirect, command $kid->{NUM}"
- ) unless defined fileno $dest ;
- ## Turn on autoflush, mostly just to flush out
- ## existing output.
- my $old_fh = select( $dest ) ; $| = 1 ; select( $old_fh ) ;
- $op->{TFD} = fileno $dest ;
- _debug(
- 'kid to write ', $op->{KFD}, ' to handle ', $op->{TFD}
- ) if _debugging_details ;
- }
- elsif ( isa( $dest, 'SCALAR' ) ) {
- _debug(
- "kid ", $kid->{NUM}, " to write $op->{KFD} to SCALAR"
- ) if _debugging_details ;
-
- $op->open_pipe( $self->_debug_fd ) ;
- push @close_on_fail, $op->{FD}, $op->{TFD} ;
- $$dest = '' if $op->{TRUNC} ;
- }
- elsif ( isa( $dest, 'CODE' ) ) {
- _debug(
- "kid $kid->{NUM} to write $op->{KFD} to CODE"
- ) if _debugging_details ;
-
- $op->open_pipe( $self->_debug_fd ) ;
- push @close_on_fail, $op->{FD}, $op->{TFD} ;
- }
- else {
- croak(
- "'"
- . ref( $dest )
- . "' not allowed as a sink for output redirection"
- ) ;
- }
- $output_fds_accum[$op->{KFD}] = $op ;
- $op->_init_filters ;
- }
-
- elsif ( $op->{TYPE} eq '>pipe' ) {
- ## N> output redirection to a pipe we open, but don't select()
- ## on.
- _debug(
- "kid ", $kid->{NUM}, " to write ", $op->{KFD},
- ' to a pipe IPC::Run opens and returns'
- ) if _debugging_details ;
-
- my ( $r, $w ) = $op->open_pipe( $self->_debug_fd, $op->{DEST} ) ;
- _debug "caller will read from ", fileno $op->{DEST}
- if _debugging_details ;
-
- $op->{TFD} = $w ;
- $op->{FD} = undef ; # we don't manage this fd
- $op->_init_filters ;
-
- $output_fds_accum[$op->{KFD}] = $op ;
- }
- elsif ( $op->{TYPE} eq '>pty>' ) {
- my $dest = $op->{DEST} ;
- if ( isa( $dest, 'SCALAR' ) ) {
- _debug(
- "kid ", $kid->{NUM}, " to write ", $op->{KFD},
- " to SCALAR via pty '", $op->{PTY_ID}, "'"
- ) if _debugging_details ;
-
- $$dest = '' if $op->{TRUNC} ;
- }
- elsif ( isa( $dest, 'CODE' ) ) {
- _debug(
- "kid ", $kid->{NUM}, " to write ", $op->{KFD},
- " to CODE via pty '", $op->{PTY_ID}, "'"
- ) if _debugging_details ;
- }
- else {
- croak(
- "'"
- . ref( $dest )
- . "' not allowed as a sink for output redirection"
- ) ;
- }
-
- $op->{FD} = $self->{PTYS}->{$op->{PTY_ID}}->fileno ;
- $op->{TFD} = undef ; # The fd isn't known until after fork().
- $output_fds_accum[$op->{KFD}] = $op ;
- $op->_init_filters ;
- }
- elsif ( $op->{TYPE} eq '|' ) {
- _debug(
- "pipelining $kid->{NUM} and "
- . ( $kid->{NUM} + 1 )
- ) if _debugging_details ;
- ( $pipe_read_fd, $op->{TFD} ) = _pipe ;
- if ( Win32_MODE ) {
- _dont_inherit( $pipe_read_fd ) ;
- _dont_inherit( $op->{TFD} ) ;
- }
- @output_fds_accum = () ;
- }
- elsif ( $op->{TYPE} eq '&' ) {
- @output_fds_accum = () ;
- } # end if $op->{TYPE} tree
- 1;
- } ; # end eval
- unless ( $ok ) {
- push @errs, $@ ;
- _debug 'caught ', $@ if _debugging;
- }
- } # end for ( OPS }
- }
-
- if ( @errs ) {
- for ( @close_on_fail ) {
- _close( $_ ) ;
- $_ = undef ;
- }
- for ( keys %{$self->{PTYS}} ) {
- next unless $self->{PTYS}->{$_} ;
- close $self->{PTYS}->{$_} ;
- $self->{PTYS}->{$_} = undef ;
- }
- die join( '', @errs )
- }
-
- ## give all but the last child all of the output file descriptors
- ## These will be reopened (and thus rendered useless) if the child
- ## dup2s on to these descriptors, since we unshift these. This way
- ## each process emits output to the same file descriptors that the
- ## last child will write to. This is probably not quite correct,
- ## since each child should write to the file descriptors inherited
- ## from the parent.
- ## TODO: fix the inheritance of output file descriptors.
- ## NOTE: This sharing of OPS among kids means that we can't easily put
- ## a kid number in each OPS structure to ping the kid when all ops
- ## have closed (when $self->{PIPES} has emptied). This means that we
- ## need to scan the KIDS whenever @{$self->{PIPES}} is empty to see
- ## if there any of them are still alive.
- for ( my $num = 0 ; $num < $#{$self->{KIDS}} ; ++$num ) {
- for ( reverse @output_fds_accum ) {
- next unless defined $_ ;
- _debug(
- 'kid ', $self->{KIDS}->[$num]->{NUM}, ' also to write ', $_->{KFD},
- ' to ', ref $_->{DEST}
- ) if _debugging_details ;
- unshift @{$self->{KIDS}->[$num]->{OPS}}, $_ ;
- }
- }
-
- ## Open the debug pipe if we need it
- ## Create the list of PIPES we need to scan and the bit vectors needed by
- ## select(). Do this first so that _cleanup can _clobber() them if an
- ## exception occurs.
- @{$self->{PIPES}} = () ;
- $self->{RIN} = '' ;
- $self->{WIN} = '' ;
- $self->{EIN} = '' ;
- ## PIN is a vec()tor that indicates who's paused.
- $self->{PIN} = '' ;
- for my $kid ( @{$self->{KIDS}} ) {
- for ( @{$kid->{OPS}} ) {
- if ( defined $_->{FD} ) {
- _debug(
- 'kid ', $kid->{NUM}, '[', $kid->{PID}, "]'s ", $_->{KFD},
- ' is my ', $_->{FD}
- ) if _debugging_details ;
- vec( $self->{ $_->{TYPE} =~ /^</ ? 'WIN' : 'RIN' }, $_->{FD}, 1 ) = 1 ;
-# vec( $self->{EIN}, $_->{FD}, 1 ) = 1 ;
- push @{$self->{PIPES}}, $_ ;
- }
- }
- }
-
- for my $io ( @{$self->{IOS}} ) {
- my $fd = $io->fileno ;
- vec( $self->{RIN}, $fd, 1 ) = 1 if $io->mode =~ /r/ ;
- vec( $self->{WIN}, $fd, 1 ) = 1 if $io->mode =~ /w/ ;
-# vec( $self->{EIN}, $fd, 1 ) = 1 ;
- push @{$self->{PIPES}}, $io ;
- }
-
- ## Put filters on the end of the filter chains to read & write the pipes.
- ## Clear pipe states
- for my $pipe ( @{$self->{PIPES}} ) {
- $pipe->{SOURCE_EMPTY} = 0 ;
- $pipe->{PAUSED} = 0 ;
- if ( $pipe->{TYPE} =~ /^>/ ) {
- my $pipe_reader = sub {
- my ( undef, $out_ref ) = @_ ;
-
- return undef unless defined $pipe->{FD} ;
- return 0 unless vec( $self->{ROUT}, $pipe->{FD}, 1 ) ;
-
- vec( $self->{ROUT}, $pipe->{FD}, 1 ) = 0 ;
-
- _debug_desc_fd( 'reading from', $pipe ) if _debugging_details ;
- my $in = eval { _read( $pipe->{FD} ) } ;
- if ( $@ ) {
- $in = '' ;
- ## IO::Pty throws the Input/output error if the kid dies.
- ## read() throws the bad file descriptor message if the
- ## kid dies on Win32.
- die $@ unless
- $@ =~ /^Input\/output error: read/ ||
- ($@ =~ /input or output/ && $^O =~ /aix/)
- || ( Win32_MODE && $@ =~ /Bad file descriptor/ ) ;
- }
-
- unless ( length $in ) {
- $self->_clobber( $pipe ) ;
- return undef ;
- }
-
- ## Protect the position so /.../g matches may be used.
- my $pos = pos $$out_ref ;
- $$out_ref .= $in ;
- pos( $$out_ref ) = $pos ;
- return 1 ;
- } ;
- ## Input filters are the last filters
- push @{$pipe->{FILTERS}}, $pipe_reader ;
- push @{$self->{TEMP_FILTERS}}, $pipe_reader ;
- }
- else {
- my $pipe_writer = sub {
- my ( $in_ref, $out_ref ) = @_ ;
- return undef unless defined $pipe->{FD} ;
- return 0
- unless vec( $self->{WOUT}, $pipe->{FD}, 1 )
- || $pipe->{PAUSED} ;
-
- vec( $self->{WOUT}, $pipe->{FD}, 1 ) = 0 ;
-
- if ( ! length $$in_ref ) {
- if ( ! defined get_more_input ) {
- $self->_clobber( $pipe ) ;
- return undef ;
- }
- }
-
- unless ( length $$in_ref ) {
- unless ( $pipe->{PAUSED} ) {
- _debug_desc_fd( 'pausing', $pipe ) if _debugging_details ;
- vec( $self->{WIN}, $pipe->{FD}, 1 ) = 0 ;
-# vec( $self->{EIN}, $pipe->{FD}, 1 ) = 0 ;
- vec( $self->{PIN}, $pipe->{FD}, 1 ) = 1 ;
- $pipe->{PAUSED} = 1 ;
- }
- return 0 ;
- }
- _debug_desc_fd( 'writing to', $pipe ) if _debugging_details ;
-
- my $c = _write( $pipe->{FD}, $$in_ref ) ;
- substr( $$in_ref, 0, $c, '' ) ;
- return 1 ;
- } ;
- ## Output filters are the first filters
- unshift @{$pipe->{FILTERS}}, $pipe_writer ;
- push @{$self->{TEMP_FILTERS}}, $pipe_writer ;
- }
- }
-}
-
-
-sub _dup2_gently {
- ## A METHOD, NOT A FUNCTION, NEEDS $self!
- my IPC::Run $self = shift ;
- my ( $files, $fd1, $fd2 ) = @_ ;
- ## Moves TFDs that are using the destination fd out of the
- ## way before calling _dup2
- for ( @$files ) {
- next unless defined $_->{TFD} ;
- $_->{TFD} = _dup( $_->{TFD} ) if $_->{TFD} == $fd2 ;
- }
- $self->{DEBUG_FD} = _dup $self->{DEBUG_FD}
- if defined $self->{DEBUG_FD} && $self->{DEBUG_FD} == $fd2 ;
-
- _dup2_rudely( $fd1, $fd2 ) ;
-}
-
-=item close_terminal
-
-This is used as (or in) an init sub to cast off the bonds of a controlling
-terminal. It must precede all other redirection ops that affect
-STDIN, STDOUT, or STDERR to be guaranteed effective.
-
-=cut
-
-
-sub close_terminal {
- ## Cast of the bonds of a controlling terminal
-
- POSIX::setsid() || croak "POSIX::setsid() failed" ;
- _debug "closing stdin, out, err"
- if _debugging_details ;
- close STDIN ;
- close STDERR ;
- close STDOUT ;
-}
-
-
-sub _do_kid_and_exit {
- my IPC::Run $self = shift ;
- my ( $kid ) = @_ ;
-
- ## For unknown reasons, placing these two statements in the eval{}
- ## causes the eval {} to not catch errors after they are executed in
- ## perl 5.6.0, godforsaken version that it is...not sure about 5.6.1.
- ## Part of this could be that these symbols get destructed when
- ## exiting the eval, and that destruction might be what's (wrongly)
- ## confusing the eval{}, allowing the exception to probpogate.
- my $s1 = gensym ;
- my $s2 = gensym ;
-
- eval {
- local $cur_self = $self ;
-
- _set_child_debug_name( ref $kid->{VAL} eq "CODE"
- ? "CODE"
- : basename( $kid->{VAL}->[0] )
- );
-
- ## close parent FD's first so they're out of the way.
- ## Don't close STDIN, STDOUT, STDERR: they should be inherited or
- ## overwritten below.
- my @needed = $self->{noinherit} ? () : ( 1, 1, 1 ) ;
- $needed[ $self->{SYNC_WRITER_FD} ] = 1 ;
- $needed[ $self->{DEBUG_FD} ] = 1 if defined $self->{DEBUG_FD} ;
-
- for ( @{$kid->{OPS}} ) {
- $needed[ $_->{TFD} ] = 1 if defined $_->{TFD} ;
- }
-
- ## TODO: use the forthcoming IO::Pty to close the terminal and
- ## make the first pty for this child the controlling terminal.
- ## This will also make it so that pty-laden kids don't cause
- ## other kids to lose stdin/stdout/stderr.
- my @closed ;
- if ( %{$self->{PTYS}} ) {
- ## Clean up the parent's fds.
- for ( keys %{$self->{PTYS}} ) {
- _debug "Cleaning up parent's ptty '$_'" if _debugging_details ;
- my $slave = $self->{PTYS}->{$_}->slave ;
- $closed[ $self->{PTYS}->{$_}->fileno ] = 1 ;
- close $self->{PTYS}->{$_} ;
- $self->{PTYS}->{$_} = $slave ;
- }
-
- close_terminal ;
- $closed[ $_ ] = 1 for ( 0..2 ) ;
- }
-
- for my $sibling ( @{$self->{KIDS}} ) {
- for ( @{$sibling->{OPS}} ) {
- if ( $_->{TYPE} =~ /^.pty.$/ ) {
- $_->{TFD} = $self->{PTYS}->{$_->{PTY_ID}}->fileno ;
- $needed[$_->{TFD}] = 1 ;
- }
-
-# for ( $_->{FD}, ( $sibling != $kid ? $_->{TFD} : () ) ) {
-# if ( defined $_ && ! $closed[$_] && ! $needed[$_] ) {
-# _close( $_ ) ;
-# $closed[$_] = 1 ;
-# $_ = undef ;
-# }
-# }
- }
- }
-
- ## This is crude: we have no way of keeping track of browsing all open
- ## fds, so we scan to a fairly high fd.
- _debug "open fds: ", join " ", keys %fds if _debugging_details ;
- for (keys %fds) {
- if ( ! $closed[$_] && ! $needed[$_] ) {
- _close( $_ ) ;
- $closed[$_] = 1 ;
- }
- }
-
- ## Lazy closing is so the same fd (ie the same TFD value) can be dup2'ed on
- ## several times.
- my @lazy_close ;
- for ( @{$kid->{OPS}} ) {
- if ( defined $_->{TFD} ) {
- unless ( $_->{TFD} == $_->{KFD} ) {
- $self->_dup2_gently( $kid->{OPS}, $_->{TFD}, $_->{KFD} ) ;
- push @lazy_close, $_->{TFD} ;
- }
- }
- elsif ( $_->{TYPE} eq 'dup' ) {
- $self->_dup2_gently( $kid->{OPS}, $_->{KFD1}, $_->{KFD2} )
- unless $_->{KFD1} == $_->{KFD2} ;
- }
- elsif ( $_->{TYPE} eq 'close' ) {
- for ( $_->{KFD} ) {
- if ( ! $closed[$_] ) {
- _close( $_ ) ;
- $closed[$_] = 1 ;
- $_ = undef ;
- }
- }
- }
- elsif ( $_->{TYPE} eq 'init' ) {
- $_->{SUB}->() ;
- }
- }
-
- for ( @lazy_close ) {
- unless ( $closed[$_] ) {
- _close( $_ ) ;
- $closed[$_] = 1 ;
- }
- }
-
- if ( ref $kid->{VAL} ne 'CODE' ) {
- open $s1, ">&=$self->{SYNC_WRITER_FD}"
- or croak "$! setting filehandle to fd SYNC_WRITER_FD" ;
- fcntl $s1, F_SETFD, 1 ;
-
- if ( defined $self->{DEBUG_FD} ) {
- open $s2, ">&=$self->{DEBUG_FD}"
- or croak "$! setting filehandle to fd DEBUG_FD" ;
- fcntl $s2, F_SETFD, 1 ;
- }
-
- my @cmd = ( $kid->{PATH}, @{$kid->{VAL}}[1..$#{$kid->{VAL}}] ) ;
- _debug 'execing ', join " ", map { /[\s"]/ ? "'$_'" : $_ } @cmd
- if _debugging ;
-
- die "exec failed: simulating exec() failure"
- if $self->{_simulate_exec_failure} ;
-
- _exec $kid->{PATH}, @{$kid->{VAL}}[1..$#{$kid->{VAL}}] ;
-
- croak "exec failed: $!" ;
- }
- } ;
- if ( $@ ) {
- _write $self->{SYNC_WRITER_FD}, $@ ;
- ## Avoid DESTROY.
- POSIX::exit 1 ;
- }
-
- ## We must be executing code in the child, otherwise exec() would have
- ## prevented us from being here.
- _close $self->{SYNC_WRITER_FD} ;
- _debug 'calling fork()ed CODE ref' if _debugging;
- POSIX::close $self->{DEBUG_FD} if defined $self->{DEBUG_FD} ;
- ## TODO: Overload CORE::GLOBAL::exit...
- $kid->{VAL}->() ;
-
- ## There are bugs in perl closures up to and including 5.6.1
- ## that may keep this next line from having any effect, and it
- ## won't have any effect if our caller has kept a copy of it, but
- ## this may cause the closure to be cleaned up. Maybe.
- $kid->{VAL} = undef ;
-
- ## Use POSIX::exit to avoid global destruction, since this might
- ## cause DESTROY() to be called on objects created in the parent
- ## and thus cause double cleanup. For instance, if DESTROY() unlinks
- ## a file in the child, we don't want the parent to suddenly miss
- ## it.
- POSIX::exit 0 ;
-}
-
-
-=item start
-
- $h = start(
- \@cmd, \$in, \$out, ...,
- timeout( 30, name => "process timeout" ),
- $stall_timeout = timeout( 10, name => "stall timeout" ),
- ) ;
-
- $h = start \@cmd, '<', \$in, '|', \@cmd2, ... ;
-
-start() accepts a harness or harness specification and returns a harness
-after building all of the pipes and launching (via fork()/exec(), or, maybe
-someday, spawn()) all the child processes. It does not send or receive any
-data on the pipes, see pump() and finish() for that.
-
-You may call harness() and then pass it's result to start() if you like,
-but you only need to if it helps you structure or tune your application.
-If you do call harness(), you may skip start() and proceed directly to
-pump.
-
-start() also starts all timers in the harness. See L<IPC::Run::Timer>
-for more information.
-
-start() flushes STDOUT and STDERR to help you avoid duplicate output.
-It has no way of asking Perl to flush all your open filehandles, so
-you are going to need to flush any others you have open. Sorry.
-
-Here's how if you don't want to alter the state of $| for your
-filehandle:
-
- $ofh = select HANDLE ; $of = $| ; $| = 1 ; $| = $of ; select $ofh;
-
-If you don't mind leaving output unbuffered on HANDLE, you can do
-the slightly shorter
-
- $ofh = select HANDLE ; $| = 1 ; select $ofh;
-
-Or, you can use IO::Handle's flush() method:
-
- use IO::Handle ;
- flush HANDLE ;
-
-Perl needs the equivalent of C's fflush( (FILE *)NULL ).
-
-=cut
-
-sub start {
-# $SIG{__DIE__} = sub { my $s = shift ; Carp::cluck $s ; die $s } ;
- my $options ;
- if ( @_ && ref $_[-1] eq 'HASH' ) {
- $options = pop ;
- require Data::Dumper ;
- carp "Passing in options as a hash is deprecated:\n", Data::Dumper::Dumper( $options ) ;
- }
-
- my IPC::Run $self ;
- if ( @_ == 1 && isa( $_[0], __PACKAGE__ ) ) {
- $self = shift ;
- $self->{$_} = $options->{$_} for keys %$options ;
- }
- else {
- $self = harness( @_, $options ? $options : () ) ;
- }
-
- local $cur_self = $self ;
-
- $self->kill_kill if $self->{STATE} == _started ;
-
- _debug "** starting" if _debugging;
-
- $_->{RESULT} = undef for @{$self->{KIDS}} ;
-
- ## Assume we're not being called from &run. It will correct our
- ## assumption if need be. This affects whether &_select_loop clears
- ## input queues to '' when they're empty.
- $self->{clear_ins} = 1 ;
-
- IPC::Run::Win32Helper::optimize $self
- if Win32_MODE && $in_run;
-
- my @errs ;
-
- for ( @{$self->{TIMERS}} ) {
- eval { $_->start } ;
- if ( $@ ) {
- push @errs, $@ ;
- _debug 'caught ', $@ if _debugging;
- }
- }
-
- eval { $self->_open_pipes } ;
- if ( $@ ) {
- push @errs, $@ ;
- _debug 'caught ', $@ if _debugging;
- }
-
- if ( ! @errs ) {
- ## This is a bit of a hack, we should do it for all open filehandles.
- ## Since there's no way I know of to enumerate open filehandles, we
- ## autoflush STDOUT and STDERR. This is done so that the children don't
- ## inherit output buffers chock full o' redundant data. It's really
- ## confusing to track that down.
- { my $ofh = select STDOUT ; local $| = 1 ; select $ofh; }
- { my $ofh = select STDERR ; local $| = 1 ; select $ofh; }
- for my $kid ( @{$self->{KIDS}} ) {
- $kid->{RESULT} = undef ;
- _debug "child: ",
- ref( $kid->{VAL} ) eq "CODE"
- ? "CODE ref"
- : (
- "`",
- join( " ", map /[^\w.-]/ ? "'$_'" : $_, @{$kid->{VAL}} ),
- "`"
- ) if _debugging_details ;
- eval {
- croak "simulated failure of fork"
- if $self->{_simulate_fork_failure} ;
- unless ( Win32_MODE ) {
- $self->_spawn( $kid ) ;
- }
- else {
-## TODO: Test and debug spawing code. Someday.
- _debug(
- 'spawning ',
- join(
- ' ',
- map(
- "'$_'",
- ( $kid->{PATH}, @{$kid->{VAL}}[1..$#{$kid->{VAL}}] )
- )
- )
- ) if _debugging;
- ## The external kid wouldn't know what to do with it anyway.
- ## This is only used by the "helper" pump processes on Win32.
- _dont_inherit( $self->{DEBUG_FD} ) ;
- ( $kid->{PID}, $kid->{PROCESS} ) =
- IPC::Run::Win32Helper::win32_spawn(
- [ $kid->{PATH}, @{$kid->{VAL}}[1..$#{$kid->{VAL}}] ],
- $kid->{OPS},
- ) ;
- _debug "spawn() = ", $kid->{PID} if _debugging;
- }
- } ;
- if ( $@ ) {
- push @errs, $@ ;
- _debug 'caught ', $@ if _debugging;
- }
- }
- }
-
- ## Close all those temporary filehandles that the kids needed.
- for my $pty ( values %{$self->{PTYS}} ) {
- close $pty->slave ;
- }
-
- my @closed ;
- for my $kid ( @{$self->{KIDS}} ) {
- for ( @{$kid->{OPS}} ) {
- my $close_it = eval {
- defined $_->{TFD}
- && ! $_->{DONT_CLOSE}
- && ! $closed[$_->{TFD}]
- && ( ! Win32_MODE || ! $_->{RECV_THROUGH_TEMP_FILE} ) ## Win32 hack
- } ;
- if ( $@ ) {
- push @errs, $@ ;
- _debug 'caught ', $@ if _debugging;
- }
- if ( $close_it || $@ ) {
- eval {
- _close( $_->{TFD} ) ;
- $closed[$_->{TFD}] = 1 ;
- $_->{TFD} = undef ;
- } ;
- if ( $@ ) {
- push @errs, $@ ;
- _debug 'caught ', $@ if _debugging;
- }
- }
- }
- }
-confess "gak!" unless defined $self->{PIPES} ;
-
- if ( @errs ) {
- eval { $self->_cleanup } ;
- warn $@ if $@ ;
- die join( '', @errs ) ;
- }
-
- $self->{STATE} = _started ;
- return $self ;
-}
-
-
-sub adopt {
- ## NOT FUNCTIONAL YET, NEED TO CLOSE FDS BETTER IN CHILDREN. SEE
- ## t/adopt.t for a test suite.
- my IPC::Run $self = shift ;
-
- for my $adoptee ( @_ ) {
- push @{$self->{IOS}}, @{$adoptee->{IOS}} ;
- ## NEED TO RENUMBER THE KIDS!!
- push @{$self->{KIDS}}, @{$adoptee->{KIDS}} ;
- push @{$self->{PIPES}}, @{$adoptee->{PIPES}} ;
- $self->{PTYS}->{$_} = $adoptee->{PTYS}->{$_}
- for keys %{$adoptee->{PYTS}} ;
- push @{$self->{TIMERS}}, @{$adoptee->{TIMERS}} ;
- $adoptee->{STATE} = _finished ;
- }
-}
-
-
-sub _clobber {
- my IPC::Run $self = shift ;
- my ( $file ) = @_ ;
- _debug_desc_fd( "closing", $file ) if _debugging_details ;
- my $doomed = $file->{FD} ;
- my $dir = $file->{TYPE} =~ /^</ ? 'WIN' : 'RIN' ;
- vec( $self->{$dir}, $doomed, 1 ) = 0 ;
-# vec( $self->{EIN}, $doomed, 1 ) = 0 ;
- vec( $self->{PIN}, $doomed, 1 ) = 0 ;
- if ( $file->{TYPE} =~ /^(.)pty.$/ ) {
- if ( $1 eq '>' ) {
- ## Only close output ptys. This is so that ptys as inputs are
- ## never autoclosed, which would risk losing data that was
- ## in the slave->parent queue.
- _debug_desc_fd "closing pty", $file if _debugging_details ;
- close $self->{PTYS}->{$file->{PTY_ID}}
- if defined $self->{PTYS}->{$file->{PTY_ID}} ;
- $self->{PTYS}->{$file->{PTY_ID}} = undef ;
- }
- }
- elsif ( isa( $file, 'IPC::Run::IO' ) ) {
- $file->close unless $file->{DONT_CLOSE} ;
- }
- else {
- _close( $doomed ) ;
- }
-
- @{$self->{PIPES}} = grep
- defined $_->{FD} && ( $_->{TYPE} ne $file->{TYPE} || $_->{FD} ne $doomed),
- @{$self->{PIPES}} ;
-
- $file->{FD} = undef ;
-}
-
-sub _select_loop {
- my IPC::Run $self = shift ;
-
- my $io_occurred ;
-
- my $not_forever = 0.01 ;
-
-SELECT:
- while ( $self->pumpable ) {
- if ( $io_occurred && $self->{break_on_io} ) {
- _debug "exiting _select(): io occured and break_on_io set"
- if _debugging_details ;
- last ;
- }
-
- my $timeout = $self->{non_blocking} ? 0 : undef ;
-
- if ( @{$self->{TIMERS}} ) {
- my $now = time ;
- my $time_left ;
- for ( @{$self->{TIMERS}} ) {
- next unless $_->is_running ;
- $time_left = $_->check( $now ) ;
- ## Return when a timer expires
- return if defined $time_left && ! $time_left ;
- $timeout = $time_left
- if ! defined $timeout || $time_left < $timeout ;
- }
- }
-
- ##
- ## See if we can unpause any input channels
- ##
- my $paused = 0 ;
-
- for my $file ( @{$self->{PIPES}} ) {
- next unless $file->{PAUSED} && $file->{TYPE} =~ /^</ ;
-
- _debug_desc_fd( "checking for more input", $file ) if _debugging_details ;
- my $did ;
- 1 while $did = $file->_do_filters( $self ) ;
- if ( defined $file->{FD} && ! defined( $did ) || $did ) {
- _debug_desc_fd( "unpausing", $file ) if _debugging_details ;
- $file->{PAUSED} = 0 ;
- vec( $self->{WIN}, $file->{FD}, 1 ) = 1 ;
-# vec( $self->{EIN}, $file->{FD}, 1 ) = 1 ;
- vec( $self->{PIN}, $file->{FD}, 1 ) = 0 ;
- }
- else {
- ## This gets incremented occasionally when the IO channel
- ## was actually closed. That's a bug, but it seems mostly
- ## harmless: it causes us to exit if break_on_io, or to set
- ## the timeout to not be forever. I need to fix it, though.
- ++$paused ;
- }
- }
-
- if ( _debugging_details ) {
- my $map = join(
- '',
- map {
- my $out ;
- $out = 'r' if vec( $self->{RIN}, $_, 1 ) ;
- $out = $out ? 'b' : 'w' if vec( $self->{WIN}, $_, 1 ) ;
- $out = 'p' if ! $out && vec( $self->{PIN}, $_, 1 ) ;
- $out = $out ? uc( $out ) : 'x' if vec( $self->{EIN}, $_, 1 ) ;
- $out = '-' unless $out ;
- $out ;
- } (0..1024)
- ) ;
- $map =~ s/((?:[a-zA-Z-]|\([^\)]*\)){12,}?)-*$/$1/ ;
- _debug 'fds for select: ', $map if _debugging_details ;
- }
-
- ## _do_filters may have closed our last fd, and we need to see if
- ## we have I/O, or are just waiting for children to exit.
- my $p = $self->pumpable;
- last unless $p;
- if ( $p > 0 && ( ! defined $timeout || $timeout > 0.1 ) ) {
- ## No I/O will wake the select loop up, but we have children
- ## lingering, so we need to poll them with a short timeout.
- ## Otherwise, assume more input will be coming.
- $timeout = $not_forever ;
- $not_forever *= 2 ;
- $not_forever = 0.5 if $not_forever >= 0.5 ;
- }
-
- ## Make sure we don't block forever in select() because inputs are
- ## paused.
- if ( ! defined $timeout && ! ( @{$self->{PIPES}} - $paused ) ) {
- ## Need to return if we're in pump and all input is paused, or
- ## we'll loop until all inputs are unpaused, which is darn near
- ## forever. And a day.
- if ( $self->{break_on_io} ) {
- _debug "exiting _select(): no I/O to do and timeout=forever"
- if _debugging;
- last ;
- }
-
- ## Otherwise, assume more input will be coming.
- $timeout = $not_forever ;
- $not_forever *= 2 ;
- $not_forever = 0.5 if $not_forever >= 0.5 ;
- }
-
- _debug 'timeout=', defined $timeout ? $timeout : 'forever'
- if _debugging_details ;
-
- my $nfound ;
- unless ( Win32_MODE ) {
- $nfound = select(
- $self->{ROUT} = $self->{RIN},
- $self->{WOUT} = $self->{WIN},
- $self->{EOUT} = $self->{EIN},
- $timeout
- ) ;
- }
- else {
- my @in = map $self->{$_}, qw( RIN WIN EIN ) ;
- ## Win32's select() on Win32 seems to die if passed vectors of
- ## all 0's. Need to report this when I get back online.
- for ( @in ) {
- $_ = undef unless index( ( unpack "b*", $_ ), 1 ) >= 0 ;
- }
-
- $nfound = select(
- $self->{ROUT} = $in[0],
- $self->{WOUT} = $in[1],
- $self->{EOUT} = $in[2],
- $timeout
- ) ;
-
- for ( $self->{ROUT}, $self->{WOUT}, $self->{EOUT} ) {
- $_ = "" unless defined $_ ;
- }
- }
- last if ! $nfound && $self->{non_blocking} ;
-
- croak "$! in select" if $nfound < 0 and $! != POSIX::EINTR;
- ## TODO: Analyze the EINTR failure mode and see if this patch
- ## is adequate and optimal.
- ## TODO: Add an EINTR test to the test suite.
-
- if ( _debugging_details ) {
- my $map = join(
- '',
- map {
- my $out ;
- $out = 'r' if vec( $self->{ROUT}, $_, 1 ) ;
- $out = $out ? 'b' : 'w' if vec( $self->{WOUT}, $_, 1 ) ;
- $out = $out ? uc( $out ) : 'x' if vec( $self->{EOUT}, $_, 1 ) ;
- $out = '-' unless $out ;
- $out ;
- } (0..128)
- ) ;
- $map =~ s/((?:[a-zA-Z-]|\([^\)]*\)){12,}?)-*$/$1/ ;
- _debug "selected ", $map ;
- }
-
- ## Need to copy since _clobber alters @{$self->{PIPES}}.
- ## TODO: Rethink _clobber(). Rethink $file->{PAUSED}, too.
- my @pipes = @{$self->{PIPES}} ;
- $io_occurred = $_->poll( $self ) ? 1 : $io_occurred for @pipes;
-# FILE:
-# for my $pipe ( @pipes ) {
-# ## Pipes can be shared among kids. If another kid closes the
-# ## pipe, then it's {FD} will be undef. Also, on Win32, pipes can
-# ## be optimized to be files, in which case the FD is left undef
-# ## so we don't try to select() on it.
-# if ( $pipe->{TYPE} =~ /^>/
-# && defined $pipe->{FD}
-# && vec( $self->{ROUT}, $pipe->{FD}, 1 )
-# ) {
-# _debug_desc_fd( "filtering data from", $pipe ) if _debugging_details ;
-#confess "phooey" unless isa( $pipe, "IPC::Run::IO" ) ;
-# $io_occurred = 1 if $pipe->_do_filters( $self ) ;
-#
-# next FILE unless defined $pipe->{FD} ;
-# }
-#
-# ## On Win32, pipes to the child can be optimized to be files
-# ## and FD left undefined so we won't select on it.
-# if ( $pipe->{TYPE} =~ /^</
-# && defined $pipe->{FD}
-# && vec( $self->{WOUT}, $pipe->{FD}, 1 )
-# ) {
-# _debug_desc_fd( "filtering data to", $pipe ) if _debugging_details ;
-# $io_occurred = 1 if $pipe->_do_filters( $self ) ;
-#
-# next FILE unless defined $pipe->{FD} ;
-# }
-#
-# if ( defined $pipe->{FD} && vec( $self->{EOUT}, $pipe->{FD}, 1 ) ) {
-# ## BSD seems to sometimes raise the exceptional condition flag
-# ## when a pipe is closed before we read it's last data. This
-# ## causes spurious warnings and generally renders the exception
-# ## mechanism useless for our purposes. The exception
-# ## flag semantics are too variable (they're device driver
-# ## specific) for me to easily map to any automatic action like
-# ## warning or croaking (try running v0.42 if you don't beleive me
-# ## :-).
-# warn "Exception on descriptor $pipe->{FD}" ;
-# }
-# }
- }
-
- return ;
-}
-
-
-sub _cleanup {
- my IPC::Run $self = shift ;
- _debug "cleaning up" if _debugging_details ;
-
- for ( values %{$self->{PTYS}} ) {
- next unless ref $_ ;
- eval {
- _debug "closing slave fd ", fileno $_->slave if _debugging_data;
- close $_->slave ;
- } ;
- carp $@ . " while closing ptys" if $@ ;
- eval {
- _debug "closing master fd ", fileno $_ if _debugging_data;
- close $_ ;
- } ;
- carp $@ . " closing ptys" if $@ ;
- }
-
- _debug "cleaning up pipes" if _debugging_details ;
- ## _clobber modifies PIPES
- $self->_clobber( $self->{PIPES}->[0] ) while @{$self->{PIPES}} ;
-
- for my $kid ( @{$self->{KIDS}} ) {
- _debug "cleaning up kid ", $kid->{NUM} if _debugging_details ;
- if ( ! length $kid->{PID} ) {
- _debug 'never ran child ', $kid->{NUM}, ", can't reap"
- if _debugging;
- for my $op ( @{$kid->{OPS}} ) {
- _close( $op->{TFD} )
- if defined $op->{TFD} && ! defined $op->{TEMP_FILE_HANDLE};
- }
- }
- elsif ( ! defined $kid->{RESULT} ) {
- _debug 'reaping child ', $kid->{NUM}, ' (pid ', $kid->{PID}, ')'
- if _debugging;
- my $pid = waitpid $kid->{PID}, 0 ;
- $kid->{RESULT} = $? ;
- _debug 'reaped ', $pid, ', $?=', $kid->{RESULT}
- if _debugging;
- }
-
-# if ( defined $kid->{DEBUG_FD} ) {
-# die;
-# @{$kid->{OPS}} = grep
-# ! defined $_->{KFD} || $_->{KFD} != $kid->{DEBUG_FD},
-# @{$kid->{OPS}} ;
-# $kid->{DEBUG_FD} = undef ;
-# }
-
- _debug "cleaning up filters" if _debugging_details ;
- for my $op ( @{$kid->{OPS}} ) {
- @{$op->{FILTERS}} = grep {
- my $filter = $_ ;
- ! grep $filter == $_, @{$self->{TEMP_FILTERS}} ;
- } @{$op->{FILTERS}} ;
- }
-
- for my $op ( @{$kid->{OPS}} ) {
- $op->_cleanup( $self ) if UNIVERSAL::isa( $op, "IPC::Run::IO" );
- }
- }
- $self->{STATE} = _finished ;
- @{$self->{TEMP_FILTERS}} = () ;
- _debug "done cleaning up" if _debugging_details ;
-
- POSIX::close $self->{DEBUG_FD} if defined $self->{DEBUG_FD} ;
- $self->{DEBUG_FD} = undef ;
-}
-
-
-=item pump
-
- pump $h ;
- $h->pump ;
-
-Pump accepts a single parameter harness. It blocks until it delivers some
-input or recieves some output. It returns TRUE if there is still input or
-output to be done, FALSE otherwise.
-
-pump() will automatically call start() if need be, so you may call harness()
-then proceed to pump() if that helps you structure your application.
-
-If pump() is called after all harnessed activities have completed, a "process
-ended prematurely" exception to be thrown. This allows for simple scripting
-of external applications without having to add lots of error handling code at
-each step of the script:
-
- $h = harness \@smbclient, \$in, \$out, $err ;
-
- $in = "cd /foo\n" ;
- $h->pump until $out =~ /^smb.*> \Z/m ;
- die "error cding to /foo:\n$out" if $out =~ "ERR" ;
- $out = '' ;
-
- $in = "mget *\n" ;
- $h->pump until $out =~ /^smb.*> \Z/m ;
- die "error retrieving files:\n$out" if $out =~ "ERR" ;
-
- $h->finish ;
-
- warn $err if $err ;
-
-=cut
-
-
-sub pump {
- die "pump() takes only a a single harness as a parameter"
- unless @_ == 1 && isa( $_[0], __PACKAGE__ ) ;
-
- my IPC::Run $self = shift ;
-
- local $cur_self = $self ;
-
- _debug "** pumping"
- if _debugging;
-
-# my $r = eval {
- $self->start if $self->{STATE} < _started ;
- croak "process ended prematurely" unless $self->pumpable ;
-
- $self->{auto_close_ins} = 0 ;
- $self->{break_on_io} = 1 ;
- $self->_select_loop ;
- return $self->pumpable ;
-# } ;
-# if ( $@ ) {
-# my $x = $@ ;
-# _debug $x if _debugging && $x ;
-# eval { $self->_cleanup } ;
-# warn $@ if $@ ;
-# die $x ;
-# }
-# return $r ;
-}
-
-
-=item pump_nb
-
- pump_nb $h ;
- $h->pump_nb ;
-
-"pump() non-blocking", pumps if anything's ready to be pumped, returns
-immediately otherwise. This is useful if you're doing some long-running
-task in the foreground, but don't want to starve any child processes.
-
-=cut
-
-sub pump_nb {
- my IPC::Run $self = shift ;
-
- $self->{non_blocking} = 1 ;
- my $r = eval { $self->pump } ;
- $self->{non_blocking} = 0 ;
- die $@ if $@ ;
- return $r ;
-}
-
-=item pumpable
-
-Returns TRUE if calling pump() won't throw an immediate "process ended
-prematurely" exception. This means that there are open I/O channels or
-active processes. May yield the parent processes' time slice for 0.01
-second if all pipes are to the child and all are paused. In this case
-we can't tell if the child is dead, so we yield the processor and
-then attempt to reap the child in a nonblocking way.
-
-=cut
-
-## Undocumented feature (don't depend on it outside this module):
-## returns -1 if we have I/O channels open, or >0 if no I/O channels
-## open, but we have kids running. This allows the select loop
-## to poll for child exit.
-sub pumpable {
- my IPC::Run $self = shift ;
-
- ## There's a catch-22 we can get in to if there is only one pipe left
- ## open to the child and it's paused (ie the SCALAR it's tied to
- ## is ''). It's paused, so we're not select()ing on it, so we don't
- ## check it to see if the child attached to it is alive and it stays
- ## in @{$self->{PIPES}} forever. So, if all pipes are paused, see if
- ## we can reap the child.
- return -1 if grep !$_->{PAUSED}, @{$self->{PIPES}};
-
- ## See if the child is dead.
- $self->reap_nb;
- return 0 unless $self->_running_kids;
-
- ## If we reap_nb and it's not dead yet, yield to it to see if it
- ## exits.
- ##
- ## A better solution would be to unpause all the pipes, but I tried that
- ## and it never errored on linux. Sigh.
- select undef, undef, undef, 0.0001;
-
- ## try again
- $self->reap_nb ;
- return 0 unless $self->_running_kids;
-
- return -1; ## There are pipes waiting
-}
-
-
-sub _running_kids {
- my IPC::Run $self = shift ;
- return grep
- defined $_->{PID} && ! defined $_->{RESULT},
- @{$self->{KIDS}} ;
-}
-
-
-=item reap_nb
-
-Attempts to reap child processes, but does not block.
-
-Does not currently take any parameters, one day it will allow specific
-children to be reaped.
-
-Only call this from a signal handler if your C<perl> is recent enough
-to have safe signal handling (5.6.1 did not, IIRC, but it was beign discussed
-on perl5-porters). Calling this (or doing any significant work) in a signal
-handler on older C<perl>s is asking for seg faults.
-
-=cut
-
-my $still_runnings ;
-
-sub reap_nb {
- my IPC::Run $self = shift ;
-
- local $cur_self = $self ;
-
- ## No more pipes, look to see if all the kids yet live, reaping those
- ## that haven't. I'd use $SIG{CHLD}/$SIG{CLD}, but that's broken
- ## on older (SYSV) platforms and perhaps less portable than waitpid().
- ## This could be slow with a lot of kids, but that's rare and, well,
- ## a lot of kids is slow in the first place.
- ## Oh, and this keeps us from reaping other children the process
- ## may have spawned.
- for my $kid ( @{$self->{KIDS}} ) {
- if ( Win32_MODE ) {
- next if ! defined $kid->{PROCESS} || defined $kid->{RESULT} ;
- unless ( $kid->{PROCESS}->Wait( 0 ) ) {
- _debug "kid $kid->{NUM} ($kid->{PID}) still running"
- if _debugging_details;
- next ;
- }
-
- _debug "kid $kid->{NUM} ($kid->{PID}) exited"
- if _debugging;
-
- $kid->{PROCESS}->GetExitCode( $kid->{RESULT} )
- or croak "$! while GetExitCode()ing for Win32 process" ;
-
- unless ( defined $kid->{RESULT} ) {
- $kid->{RESULT} = "0 but true" ;
- $? = $kid->{RESULT} = 0x0F ;
- }
- else {
- $? = $kid->{RESULT} << 8 ;
- }
- }
- else {
- next if ! defined $kid->{PID} || defined $kid->{RESULT} ;
- my $pid = waitpid $kid->{PID}, POSIX::WNOHANG() ;
- unless ( $pid ) {
- _debug "$kid->{NUM} ($kid->{PID}) still running"
- if _debugging_details;
- next ;
- }
-
- if ( $pid < 0 ) {
- _debug "No such process: $kid->{PID}\n" if _debugging ;
- $kid->{RESULT} = "unknown result, unknown PID" ;
- }
- else {
- _debug "kid $kid->{NUM} ($kid->{PID}) exited"
- if _debugging;
-
- confess "waitpid returned the wrong PID: $pid instead of $kid->{PID}"
- unless $pid = $kid->{PID} ;
- _debug "$kid->{PID} returned $?\n" if _debugging ;
- $kid->{RESULT} = $? ;
- }
- }
- }
-}
-
-
-=item finish
-
-This must be called after the last start() or pump() call for a harness,
-or your system will accumulate defunct processes and you may "leak"
-file descriptors.
-
-finish() returns TRUE if all children returned 0 (and were not signaled and did
-not coredump, ie ! $?), and FALSE otherwise (this is like run(), and the
-opposite of system()).
-
-Once a harness has been finished, it may be run() or start()ed again,
-including by pump()s auto-start.
-
-If this throws an exception rather than a normal exit, the harness may
-be left in an unstable state, it's best to kill the harness to get rid
-of all the child processes, etc.
-
-Specifically, if a timeout expires in finish(), finish() will not
-kill all the children. Call C<<$h->kill_kill>> in this case if you care.
-This differs from the behavior of L</run>.
-
-=cut
-
-
-sub finish {
- my IPC::Run $self = shift ;
- my $options = @_ && ref $_[-1] eq 'HASH' ? pop : {} ;
-
- local $cur_self = $self ;
-
- _debug "** finishing" if _debugging;
-
- $self->{non_blocking} = 0 ;
- $self->{auto_close_ins} = 1 ;
- $self->{break_on_io} = 0 ;
- # We don't alter $self->{clear_ins}, start() and run() control it.
-
- while ( $self->pumpable ) {
- $self->_select_loop( $options ) ;
- }
- $self->_cleanup ;
-
- return ! $self->full_result ;
-}
-
-
-=item result
-
- $h->result ;
-
-Returns the first non-zero result code (ie $? >> 8). See L</full_result> to
-get the $? value for a child process.
-
-To get the result of a particular child, do:
-
- $h->result( 0 ) ; # first child's $? >> 8
- $h->result( 1 ) ; # second child
-
-or
-
- ($h->results)[0]
- ($h->results)[1]
-
-Returns undef if no child processes were spawned and no child number was
-specified. Throws an exception if an out-of-range child number is passed.
-
-=cut
-
-sub _assert_finished {
- my IPC::Run $self = $_[0] ;
-
- croak "Harness not run" unless $self->{STATE} >= _finished ;
- croak "Harness not finished running" unless $self->{STATE} == _finished ;
-}
-
-
-sub result {
- &_assert_finished ;
- my IPC::Run $self = shift ;
-
- if ( @_ ) {
- my ( $which ) = @_ ;
- croak(
- "Only ",
- scalar( @{$self->{KIDS}} ),
- " child processes, no process $which"
- )
- unless $which >= 0 && $which <= $#{$self->{KIDS}} ;
- return $self->{KIDS}->[$which]->{RESULT} >> 8 ;
- }
- else {
- return undef unless @{$self->{KIDS}} ;
- for ( @{$self->{KIDS}} ) {
- return $_->{RESULT} >> 8 if $_->{RESULT} >> 8 ;
- }
- }
-}
-
-
-=item results
-
-Returns a list of child exit values. See L</full_results> if you want to
-know if a signal killed the child.
-
-Throws an exception if the harness is not in a finished state.
-
-=cut
-
-sub results {
- &_assert_finished ;
- my IPC::Run $self = shift ;
-
- # we add 0 here to stop warnings associated with "unknown result, unknown PID"
- return map { (0+$_->{RESULT}) >> 8 } @{$self->{KIDS}} ;
-}
-
-
-=item full_result
-
- $h->full_result ;
-
-Returns the first non-zero $?. See L</result> to get the first $? >> 8
-value for a child process.
-
-To get the result of a particular child, do:
-
- $h->full_result( 0 ) ; # first child's $? >> 8
- $h->full_result( 1 ) ; # second child
-
-or
-
- ($h->full_results)[0]
- ($h->full_results)[1]
-
-Returns undef if no child processes were spawned and no child number was
-specified. Throws an exception if an out-of-range child number is passed.
-
-=cut
-
-sub full_result {
- goto &result if @_ > 1 ;
- &_assert_finished ;
-
- my IPC::Run $self = shift ;
-
- return undef unless @{$self->{KIDS}} ;
- for ( @{$self->{KIDS}} ) {
- return $_->{RESULT} if $_->{RESULT} ;
- }
-}
-
-
-=item full_results
-
-Returns a list of child exit values as returned by C<wait>. See L</results>
-if you don't care about coredumps or signals.
-
-Throws an exception if the harness is not in a finished state.
-
-=cut
-
-sub full_results {
- &_assert_finished ;
- my IPC::Run $self = shift ;
-
- croak "Harness not run" unless $self->{STATE} >= _finished ;
- croak "Harness not finished running" unless $self->{STATE} == _finished ;
-
- return map $_->{RESULT}, @{$self->{KIDS}} ;
-}
-
-
-##
-## Filter Scaffolding
-##
-use vars (
- '$filter_op', ## The op running a filter chain right now
- '$filter_num', ## Which filter is being run right now.
-) ;
-
-##
-## A few filters and filter constructors
-##
-
-=back
-
-=head1 FILTERS
-
-These filters are used to modify input our output between a child
-process and a scalar or subroutine endpoint.
-
-=over
-
-=item binary
-
- run \@cmd, ">", binary, \$out ;
- run \@cmd, ">", binary, \$out ; ## Any TRUE value to enable
- run \@cmd, ">", binary 0, \$out ; ## Any FALSE value to disable
-
-This is a constructor for a "binmode" "filter" that tells IPC::Run to keep
-the carriage returns that would ordinarily be edited out for you (binmode
-is usually off). This is not a real filter, but an option masquerading as
-a filter.
-
-It's not named "binmode" because you're likely to want to call Perl's binmode
-in programs that are piping binary data around.
-
-=cut
-
-sub binary(;$) {
- my $enable = @_ ? shift : 1 ;
- return bless sub { $enable }, "IPC::Run::binmode_pseudo_filter" ;
-}
-
-=item new_chunker
-
-This breaks a stream of data in to chunks, based on an optional
-scalar or regular expression parameter. The default is the Perl
-input record separator in $/, which is a newline be default.
-
- run \@cmd, '>', new_chunker, \&lines_handler ;
- run \@cmd, '>', new_chunker( "\r\n" ), \&lines_handler ;
-
-Because this uses $/ by default, you should always pass in a parameter
-if you are worried about other code (modules, etc) modifying $/.
-
-If this filter is last in a filter chain that dumps in to a scalar,
-the scalar must be set to '' before a new chunk will be written to it.
-
-As an example of how a filter like this can be written, here's a
-chunker that splits on newlines:
-
- sub line_splitter {
- my ( $in_ref, $out_ref ) = @_ ;
-
- return 0 if length $$out_ref ;
-
- return input_avail && do {
- while (1) {
- if ( $$in_ref =~ s/\A(.*?\n)// ) {
- $$out_ref .= $1 ;
- return 1 ;
- }
- my $hmm = get_more_input ;
- unless ( defined $hmm ) {
- $$out_ref = $$in_ref ;
- $$in_ref = '' ;
- return length $$out_ref ? 1 : 0 ;
- }
- return 0 if $hmm eq 0 ;
- }
- }
- } ;
-
-=cut
-
-sub new_chunker(;$) {
- my ( $re ) = @_ ;
- $re = $/ if _empty $re ;
- $re = quotemeta( $re ) unless ref $re eq 'Regexp' ;
- $re = qr/\A(.*?$re)/s ;
-
- return sub {
- my ( $in_ref, $out_ref ) = @_ ;
-
- return 0 if length $$out_ref ;
-
- return input_avail && do {
- while (1) {
- if ( $$in_ref =~ s/$re// ) {
- $$out_ref .= $1 ;
- return 1 ;
- }
- my $hmm = get_more_input ;
- unless ( defined $hmm ) {
- $$out_ref = $$in_ref ;
- $$in_ref = '' ;
- return length $$out_ref ? 1 : 0 ;
- }
- return 0 if $hmm eq 0 ;
- }
- }
- } ;
-}
-
-
-=item new_appender
-
-This appends a fixed string to each chunk of data read from the source
-scalar or sub. This might be useful if you're writing commands to a
-child process that always must end in a fixed string, like "\n":
-
- run( \@cmd,
- '<', new_appender( "\n" ), \&commands,
- ) ;
-
-Here's a typical filter sub that might be created by new_appender():
-
- sub newline_appender {
- my ( $in_ref, $out_ref ) = @_ ;
-
- return input_avail && do {
- $$out_ref = join( '', $$out_ref, $$in_ref, "\n" ) ;
- $$in_ref = '' ;
- 1 ;
- }
- } ;
-
-=cut
-
-sub new_appender($) {
- my ( $suffix ) = @_ ;
- croak "\$suffix undefined" unless defined $suffix ;
-
- return sub {
- my ( $in_ref, $out_ref ) = @_ ;
-
- return input_avail && do {
- $$out_ref = join( '', $$out_ref, $$in_ref, $suffix ) ;
- $$in_ref = '' ;
- 1 ;
- }
- } ;
-}
-
-
-sub new_string_source {
- my $ref ;
- if ( @_ > 1 ) {
- $ref = [ @_ ],
- }
- else {
- $ref = shift ;
- }
-
- return ref $ref eq 'SCALAR'
- ? sub {
- my ( $in_ref, $out_ref ) = @_ ;
-
- return defined $$ref
- ? do {
- $$out_ref .= $$ref ;
- my $r = length $$ref ? 1 : 0 ;
- $$ref = undef ;
- $r ;
- }
- : undef
- }
- : sub {
- my ( $in_ref, $out_ref ) = @_ ;
-
- return @$ref
- ? do {
- my $s = shift @$ref ;
- $$out_ref .= $s ;
- length $s ? 1 : 0 ;
- }
- : undef ;
- }
-}
-
-
-sub new_string_sink {
- my ( $string_ref ) = @_ ;
-
- return sub {
- my ( $in_ref, $out_ref ) = @_ ;
-
- return input_avail && do {
- $$string_ref .= $$in_ref ;
- $$in_ref = '' ;
- 1 ;
- }
- } ;
-}
-
-
-#=item timeout
-#
-#This function defines a time interval, starting from when start() is
-#called, or when timeout() is called. If all processes have not finished
-#by the end of the timeout period, then a "process timed out" exception
-#is thrown.
-#
-#The time interval may be passed in seconds, or as an end time in
-#"HH:MM:SS" format (any non-digit other than '.' may be used as
-#spacing and puctuation). This is probably best shown by example:
-#
-# $h->timeout( $val ) ;
-#
-# $val Effect
-# ======================== =====================================
-# undef Timeout timer disabled
-# '' Almost immediate timeout
-# 0 Almost immediate timeout
-# 0.000001 timeout > 0.0000001 seconds
-# 30 timeout > 30 seconds
-# 30.0000001 timeout > 30 seconds
-# 10:30 timeout > 10 minutes, 30 seconds
-#
-#Timeouts are currently evaluated with a 1 second resolution, though
-#this may change in the future. This means that setting
-#timeout($h,1) will cause a pokey child to be aborted sometime after
-#one second has elapsed and typically before two seconds have elapsed.
-#
-#This sub does not check whether or not the timeout has expired already.
-#
-#Returns the number of seconds set as the timeout (this does not change
-#as time passes, unless you call timeout( val ) again).
-#
-#The timeout does not include the time needed to fork() or spawn()
-#the child processes, though some setup time for the child processes can
-#included. It also does not include the length of time it takes for
-#the children to exit after they've closed all their pipes to the
-#parent process.
-#
-#=cut
-#
-#sub timeout {
-# my IPC::Run $self = shift ;
-#
-# if ( @_ ) {
-# ( $self->{TIMEOUT} ) = @_ ;
-# $self->{TIMEOUT_END} = undef ;
-# if ( defined $self->{TIMEOUT} ) {
-# if ( $self->{TIMEOUT} =~ /[^\d.]/ ) {
-# my @f = split( /[^\d\.]+/i, $self->{TIMEOUT} ) ;
-# unshift @f, 0 while @f < 3 ;
-# $self->{TIMEOUT} = (($f[0]*60)+$f[1])*60+$f[2] ;
-# }
-# elsif ( $self->{TIMEOUT} =~ /^(\d*)(?:\.(\d*))/ ) {
-# $self->{TIMEOUT} = $1 + 1 ;
-# }
-# $self->_calc_timeout_end if $self->{STATE} >= _started ;
-# }
-# }
-# return $self->{TIMEOUT} ;
-#}
-#
-#
-#sub _calc_timeout_end {
-# my IPC::Run $self = shift ;
-#
-# $self->{TIMEOUT_END} = defined $self->{TIMEOUT}
-# ? time + $self->{TIMEOUT}
-# : undef ;
-#
-# ## We add a second because we might be at the very end of the current
-# ## second, and we want to guarantee that we don't have a timeout even
-# ## one second less then the timeout period.
-# ++$self->{TIMEOUT_END} if $self->{TIMEOUT} ;
-#}
-
-=item io
-
-Takes a filename or filehandle, a redirection operator, optional filters,
-and a source or destination (depends on the redirection operator). Returns
-an IPC::Run::IO object suitable for harness()ing (including via start()
-or run()).
-
-This is shorthand for
-
-
- require IPC::Run::IO ;
-
- ... IPC::Run::IO->new(...) ...
-
-=cut
-
-sub io {
- require IPC::Run::IO ;
- IPC::Run::IO->new( @_ ) ;
-}
-
-=item timer
-
- $h = start( \@cmd, \$in, \$out, $t = timer( 5 ) ) ;
-
- pump $h until $out =~ /expected stuff/ || $t->is_expired ;
-
-Instantiates a non-fatal timer. pump() returns once each time a timer
-expires. Has no direct effect on run(), but you can pass a subroutine
-to fire when the timer expires.
-
-See L</timeout> for building timers that throw exceptions on
-expiration.
-
-See L<IPC::Run::Timer/timer> for details.
-
-=cut
-
-# Doing the prototype suppresses 'only used once' on older perls.
-sub timer ;
-*timer = \&IPC::Run::Timer::timer ;
-
-
-=item timeout
-
- $h = start( \@cmd, \$in, \$out, $t = timeout( 5 ) ) ;
-
- pump $h until $out =~ /expected stuff/ ;
-
-Instantiates a timer that throws an exception when it expires.
-If you don't provide an exception, a default exception that matches
-/^IPC::Run: .*timed out/ is thrown by default. You can pass in your own
-exception scalar or reference:
-
- $h = start(
- \@cmd, \$in, \$out,
- $t = timeout( 5, exception => 'slowpoke' ),
- ) ;
-
-or set the name used in debugging message and in the default exception
-string:
-
- $h = start(
- \@cmd, \$in, \$out,
- timeout( 50, name => 'process timer' ),
- $stall_timer = timeout( 5, name => 'stall timer' ),
- ) ;
-
- pump $h until $out =~ /started/ ;
-
- $in = 'command 1' ;
- $stall_timer->start ;
- pump $h until $out =~ /command 1 finished/ ;
-
- $in = 'command 2' ;
- $stall_timer->start ;
- pump $h until $out =~ /command 2 finished/ ;
-
- $in = 'very slow command 3' ;
- $stall_timer->start( 10 ) ;
- pump $h until $out =~ /command 3 finished/ ;
-
- $stall_timer->start( 5 ) ;
- $in = 'command 4' ;
- pump $h until $out =~ /command 4 finished/ ;
-
- $stall_timer->reset; # Prevent restarting or expirng
- finish $h ;
-
-See L</timer> for building non-fatal timers.
-
-See L<IPC::Run::Timer/timer> for details.
-
-=cut
-
-# Doing the prototype suppresses 'only used once' on older perls.
-sub timeout ;
-*timeout = \&IPC::Run::Timer::timeout ;
-
-
-=back
-
-=head1 FILTER IMPLEMENTATION FUNCTIONS
-
-These functions are for use from within filters.
-
-=over
-
-=item input_avail
-
-Returns TRUE if input is available. If none is available, then
-&get_more_input is called and its result is returned.
-
-This is usually used in preference to &get_more_input so that the
-calling filter removes all data from the $in_ref before more data
-gets read in to $in_ref.
-
-C<input_avail> is usually used as part of a return expression:
-
- return input_avail && do {
- ## process the input just gotten
- 1 ;
- } ;
-
-This technique allows input_avail to return the undef or 0 that a
-filter normally returns when there's no input to process. If a filter
-stores intermediate values, however, it will need to react to an
-undef:
-
- my $got = input_avail ;
- if ( ! defined $got ) {
- ## No more input ever, flush internal buffers to $out_ref
- }
- return $got unless $got ;
- ## Got some input, move as much as need be
- return 1 if $added_to_out_ref ;
-
-=cut
-
-sub input_avail() {
- confess "Undefined FBUF ref for $filter_num+1"
- unless defined $filter_op->{FBUFS}->[$filter_num+1] ;
- length ${$filter_op->{FBUFS}->[$filter_num+1]} || get_more_input ;
-}
-
-
-=item get_more_input
-
-This is used to fetch more input in to the input variable. It returns
-undef if there will never be any more input, 0 if there is none now,
-but there might be in the future, and TRUE if more input was gotten.
-
-C<get_more_input> is usually used as part of a return expression,
-see L</input_avail> for more information.
-
-=cut
-
-##
-## Filter implementation interface
-##
-sub get_more_input() {
- ++$filter_num ;
- my $r = eval {
- confess "get_more_input() called and no more filters in chain"
- unless defined $filter_op->{FILTERS}->[$filter_num] ;
- $filter_op->{FILTERS}->[$filter_num]->(
- $filter_op->{FBUFS}->[$filter_num+1],
- $filter_op->{FBUFS}->[$filter_num],
- ) ; # if defined ${$filter_op->{FBUFS}->[$filter_num+1]} ;
- } ;
- --$filter_num ;
- die $@ if $@ ;
- return $r ;
-}
-
-
-## This is not needed by most users. Should really move to IPC::Run::TestUtils
-#=item filter_tests
-#
-# my @tests = filter_tests( "foo", "in", "out", \&filter ) ;
-# $_->() for ( @tests ) ;
-#
-#This creates a list of test subs that can be used to test most filters
-#for basic functionality. The first parameter is the name of the
-#filter to be tested, the second is sample input, the third is the
-#test(s) to apply to the output(s), and the rest of the parameters are
-#the filters to be linked and tested.
-#
-#If the filter chain is to be fed multiple inputs in sequence, the second
-#parameter should be a reference to an array of thos inputs:
-#
-# my @tests = filter_tests( "foo", [qw(1 2 3)], "123", \&filter ) ;
-#
-#If the filter chain should produce a sequence of outputs, then the
-#thrid parameter should be a reference to an array of those outputs:
-#
-# my @tests = filter_tests(
-# "foo",
-# "1\n\2\n",
-# [ qr/^1$/, qr/^2$/ ],
-# new_chunker
-# ) ;
-#
-#See t/run.t and t/filter.t for an example of this in practice.
-#
-#=cut
-
-##
-## Filter testing routines
-##
-sub filter_tests($;@) {
- my ( $name, $in, $exp, @filters ) = @_ ;
-
- my @in = ref $in eq 'ARRAY' ? @$in : ( $in ) ;
- my @exp = ref $exp eq 'ARRAY' ? @$exp : ( $exp ) ;
-
- require Test ;
- *ok = \&Test::ok ;
-
- my IPC::Run::IO $op ;
- my $output ;
- my @input ;
- my $in_count = 0 ;
-
- my @out ;
-
- my $h ;
-
- return (
- sub {
- $h = harness() ;
- $op = IPC::Run::IO->_new_internal( '<', 0, 0, 0, undef,
- new_string_sink( \$output ),
- @filters,
- new_string_source( \@input ),
- ) ;
- $op->_init_filters ;
- @input = () ;
- $output = '' ;
- ok(
- ! defined $op->_do_filters( $h ),
- 1,
- "$name didn't pass undef (EOF) through"
- ) ;
- },
-
- ## See if correctly does nothing on 0, (please try again)
- sub {
- $op->_init_filters ;
- $output = '' ;
- @input = ( '' ) ;
- ok(
- $op->_do_filters( $h ),
- 0,
- "$name didn't return 0 (please try again) when given a 0"
- ) ;
- },
-
- sub {
- @input = ( '' ) ;
- ok(
- $op->_do_filters( $h ),
- 0,
- "$name didn't return 0 (please try again) when given a second 0"
- ) ;
- },
-
- sub {
- for (1..100) {
- last unless defined $op->_do_filters( $h ) ;
- }
- ok(
- ! defined $op->_do_filters( $h ),
- 1,
- "$name didn't return undef (EOF) after two 0s and an undef"
- ) ;
- },
-
- ## See if it can take @in and make @out
- sub {
- $op->_init_filters ;
- $output = '' ;
- @input = @in ;
- while ( defined $op->_do_filters( $h ) && @input ) {
- if ( length $output ) {
- push @out, $output ;
- $output = '' ;
- }
- }
- if ( length $output ) {
- push @out, $output ;
- $output = '' ;
- }
- ok(
- scalar @input,
- 0,
- "$name didn't consume it's input"
- ) ;
- },
-
- sub {
- for (1..100) {
- last unless defined $op->_do_filters( $h ) ;
- if ( length $output ) {
- push @out, $output ;
- $output = '' ;
- }
- }
- ok(
- ! defined $op->_do_filters( $h ),
- 1,
- "$name didn't return undef (EOF), tried 100 times"
- ) ;
- },
-
- sub {
- ok(
- join( ', ', map "'$_'", @out ),
- join( ', ', map "'$_'", @exp ),
- $name
- )
- },
-
- sub {
- ## Force the harness to be cleaned up.
- $h = undef ;
- ok( 1 ) ;
- }
- ) ;
-}
-
-
-=back
-
-=head1 TODO
-
-These will be addressed as needed and as time allows.
-
-Stall timeout.
-
-Expose a list of child process objects. When I do this,
-each child process is likely to be blessed into IPC::Run::Proc.
-
-$kid->abort(), $kid->kill(), $kid->signal( $num_or_name ).
-
-Write tests for /(full_)?results?/ subs.
-
-Currently, pump() and run() only work on systems where select() works on the
-filehandles returned by pipe(). This does *not* include ActiveState on Win32,
-although it does work on cygwin under Win32 (thought the tests whine a bit).
-I'd like to rectify that, suggestions and patches welcome.
-
-Likewise start() only fully works on fork()/exec() machines (well, just
-fork() if you only ever pass perl subs as subprocesses). There's
-some scaffolding for calling Open3::spawn_with_handles(), but that's
-untested, and not that useful with limited select().
-
-Support for C<\@sub_cmd> as an argument to a command which
-gets replaced with /dev/fd or the name of a temporary file containing foo's
-output. This is like <(sub_cmd ...) found in bash and csh (IIRC).
-
-Allow multiple harnesses to be combined as independant sets of processes
-in to one 'meta-harness'.
-
-Allow a harness to be passed in place of an \@cmd. This would allow
-multiple harnesses to be aggregated.
-
-Ability to add external file descriptors w/ filter chains and endpoints.
-
-Ability to add timeouts and timing generators (i.e. repeating timeouts).
-
-High resolution timeouts.
-
-=head1 Win32 LIMITATIONS
-
-=over
-
-=item Fails on Win9X
-
-If you want Win9X support, you'll have to debug it or fund me because I
-don't use that system any more. The Win32 subsysem has been extended to
-use temporary files in simple run() invocations and these may actually
-work on Win9X too, but I don't have time to work on it.
-
-=item May deadlock on Win2K (but not WinNT4 or WinXPPro)
-
-Spawning more than one subprocess on Win2K causes a deadlock I haven't
-figured out yet, but simple uses of run() often work. Passes all tests
-on WinXPPro and WinNT.
-
-=item no support yet for <pty< and >pty>
-
-These are likely to be implemented as "<" and ">" with binmode on, not
-sure.
-
-=item no support for file descriptors higher than 2 (stderr)
-
-Win32 only allows passing explicit fds 0, 1, and 2. If you really, really need to pass file handles, us Win32API:: GetOsFHandle() or ::FdGetOsFHandle() to
-get the integer handle and pass it to the child process using the command
-line, environment, stdin, intermediary file, or other IPC mechnism. Then
-use that handle in the child (Win32API.pm provides ways to reconstitute
-Perl file handles from Win32 file handles).
-
-=item no support for subroutine subprocesses (CODE refs)
-
-Can't fork(), so the subroutines would have no context, and closures certainly
-have no meaning
-
-Perhaps with Win32 fork() emulation, this can be supported in a limited
-fashion, but there are other very serious problems with that: all parent
-fds get dup()ed in to the thread emulating the forked process, and that
-keeps the parent from being able to close all of the appropriate fds.
-
-=item no support for init => sub {} routines.
-
-Win32 processes are created from scratch, there is no way to do an init
-routine that will affect the running child. Some limited support might
-be implemented one day, do chdir() and %ENV changes can be made.
-
-=item signals
-
-Win32 does not fully support signals. signal() is likely to cause errors
-unless sending a signal that Perl emulates, and C<kill_kill()> is immediately
-fatal (there is no grace period).
-
-=item helper processes
-
-IPC::Run uses helper processes, one per redirected file, to adapt between the
-anonymous pipe connected to the child and the TCP socket connected to the
-parent. This is a waste of resources and will change in the future to either
-use threads (instead of helper processes) or a WaitForMultipleObjects call
-(instead of select). Please contact me if you can help with the
-WaitForMultipleObjects() approach; I haven't figured out how to get at it
-without C code.
-
-=item shutdown pause
-
-There seems to be a pause of up to 1 second between when a child program exits
-and the corresponding sockets indicate that they are closed in the parent.
-Not sure why.
-
-=item binmode
-
-binmode is not supported yet. The underpinnings are implemented, just ask
-if you need it.
-
-=item IPC::Run::IO
-
-IPC::Run::IO objects can be used on Unix to read or write arbitrary files. On
-Win32, they will need to use the same helper processes to adapt from
-non-select()able filehandles to select()able ones (or perhaps
-WaitForMultipleObjects() will work with them, not sure).
-
-=item startup race conditions
-
-There seems to be an occasional race condition between child process startup
-and pipe closings. It seems like if the child is not fully created by the time
-CreateProcess returns and we close the TCP socket being handed to it, the
-parent socket can also get closed. This is seen with the Win32 pumper
-applications, not the "real" child process being spawned.
-
-I assume this is because the kernel hasn't gotten around to incrementing the
-reference count on the child's end (since the child was slow in starting), so
-the parent's closing of the child end causes the socket to be closed, thus
-closing the parent socket.
-
-Being a race condition, it's hard to reproduce, but I encountered it while
-testing this code on a drive share to a samba box. In this case, it takes
-t/run.t a long time to spawn it's chile processes (the parent hangs in the
-first select for several seconds until the child emits any debugging output).
-
-I have not seen it on local drives, and can't reproduce it at will,
-unfortunately. The symptom is a "bad file descriptor in select()" error, and,
-by turning on debugging, it's possible to see that select() is being called on
-a no longer open file descriptor that was returned from the _socket() routine
-in Win32Helper. There's a new confess() that checks for this ("PARENT_HANDLE
-no longer open"), but I haven't been able to reproduce it (typically).
-
-=back
-
-=head1 LIMITATIONS
-
-On Unix, requires a system that supports C<waitpid( $pid, WNOHANG )> so
-it can tell if a child process is still running.
-
-PTYs don't seem to be non-blocking on some versions of Solaris. Here's a
-test script contributed by Borislav Deianov <borislav@ensim.com> to see
-if you have the problem. If it dies, you have the problem.
-
- #!/usr/bin/perl
-
- use IPC::Run qw(run);
- use Fcntl;
- use IO::Pty;
-
- sub makecmd {
- return ['perl', '-e',
- '<STDIN>, print "\n" x '.$_[0].'; while(<STDIN>){last if /end/}'];
- }
-
- #pipe R, W;
- #fcntl(W, F_SETFL, O_NONBLOCK);
- #while (syswrite(W, "\n", 1)) { $pipebuf++ };
- #print "pipe buffer size is $pipebuf\n";
- my $pipebuf=4096;
- my $in = "\n" x ($pipebuf * 2) . "end\n";
- my $out;
-
- $SIG{ALRM} = sub { die "Never completed!\n" } ;
-
- print "reading from scalar via pipe...";
- alarm( 2 ) ;
- run(makecmd($pipebuf * 2), '<', \$in, '>', \$out);
- alarm( 0 );
- print "done\n";
-
- print "reading from code via pipe... ";
- alarm( 2 ) ;
- run(makecmd($pipebuf * 3), '<', sub { $t = $in; undef $in; $t}, '>', \$out);
- alarm( 0 ) ;
- print "done\n";
-
- $pty = IO::Pty->new();
- $pty->blocking(0);
- $slave = $pty->slave();
- while ($pty->syswrite("\n", 1)) { $ptybuf++ };
- print "pty buffer size is $ptybuf\n";
- $in = "\n" x ($ptybuf * 3) . "end\n";
-
- print "reading via pty... ";
- alarm( 2 ) ;
- run(makecmd($ptybuf * 3), '<pty<', \$in, '>', \$out);
- alarm(0);
- print "done\n";
-
-No support for ';', '&&', '||', '{ ... }', etc: use perl's, since run()
-returns TRUE when the command exits with a 0 result code.
-
-Does not provide shell-like string interpolation.
-
-No support for C<cd>, C<setenv>, or C<export>: do these in an init() sub
-
- run(
- \cmd,
- ...
- init => sub {
- chdir $dir or die $! ;
- $ENV{FOO}='BAR'
- }
- ) ;
-
-Timeout calculation does not allow absolute times, or specification of
-days, months, etc.
-
-B<WARNING:> Function coprocesses (C<run \&foo, ...>) suffer from two
-limitations. The first is that it is difficult to close all filehandles the
-child inherits from the parent, since there is no way to scan all open
-FILEHANDLEs in Perl and it both painful and a bit dangerous to close all open
-file descriptors with C<POSIX::close()>. Painful because we can't tell which
-fds are open at the POSIX level, either, so we'd have to scan all possible fds
-and close any that we don't want open (normally C<exec()> closes any
-non-inheritable but we don't C<exec()> for &sub processes.
-
-The second problem is that Perl's DESTROY subs and other on-exit cleanup gets
-run in the child process. If objects are instantiated in the parent before the
-child is forked, the the DESTROY will get run once in the parent and once in
-the child. When coprocess subs exit, POSIX::exit is called to work around this,
-but it means that objects that are still referred to at that time are not
-cleaned up. So setting package vars or closure vars to point to objects that
-rely on DESTROY to affect things outside the process (files, etc), will
-lead to bugs.
-
-I goofed on the syntax: "<pipe" vs. "<pty<" and ">filename" are both
-oddities.
-
-=head1 TODO
-
-=over
-
-=item Allow one harness to "adopt" another:
-
- $new_h = harness \@cmd2 ;
- $h->adopt( $new_h ) ;
-
-=item Close all filehandles not explicitly marked to stay open.
-
-The problem with this one is that there's no good way to scan all open
-FILEHANDLEs in Perl, yet you don't want child processes inheriting handles
-willy-nilly.
-
-=back
-
-=head1 INSPIRATION
-
-Well, select() and waitpid() badly needed wrapping, and open3() isn't
-open-minded enough for me.
-
-The shell-like API inspired by a message Russ Allbery sent to perl5-porters,
-which included:
-
- I've thought for some time that it would be
- nice to have a module that could handle full Bourne shell pipe syntax
- internally, with fork and exec, without ever invoking a shell. Something
- that you could give things like:
-
- pipeopen (PIPE, [ qw/cat file/ ], '|', [ 'analyze', @args ], '>&3');
-
-Message ylln51p2b6.fsf@windlord.stanford.edu, on 2000/02/04.
-
-=head1 AUTHOR
-
-Barrie Slaymaker <barries@slaysys.com>, with numerous suggestions by p5p.
-
-=cut
-
-1 ;
diff --git a/lib/IPC/Run/Debug.pm b/lib/IPC/Run/Debug.pm
deleted file mode 100644
index 9b2c452801..0000000000
--- a/lib/IPC/Run/Debug.pm
+++ /dev/null
@@ -1,311 +0,0 @@
-package IPC::Run::Debug;
-
-=head1 NAME
-
-IPC::Run::Debug - debugging routines for IPC::Run
-
-=head1 SYNOPSIS
-
- ##
- ## Environment variable usage
- ##
- ## To force debugging off and shave a bit of CPU and memory
- ## by compile-time optimizing away all debugging code in IPC::Run
- ## (debug => ...) options to IPC::Run will be ignored.
- export IPCRUNDEBUG=none
-
- ## To force debugging on (levels are from 0..10)
- export IPCRUNDEBUG=basic
-
- ## Leave unset or set to "" to compile in debugging support and
- ## allow runtime control of it using the debug option.
-
-=head1 DESCRIPTION
-
-Controls IPC::Run debugging. Debugging levels are now set by using words,
-but the numbers shown are still supported for backwards compatability:
-
- 0 none disabled (special, see below)
- 1 basic what's running
- 2 data what's being sent/recieved
- 3 details what's going on in more detail
- 4 gory way too much detail for most uses
- 10 all use this when submitting bug reports
- noopts optimizations forbidden due to inherited STDIN
-
-The C<none> level is special when the environment variable IPCRUNDEBUG
-is set to this the first time IPC::Run::Debug is loaded: it prevents
-the debugging code from being compiled in to the remaining IPC::Run modules,
-saving a bit of cpu.
-
-To do this in a script, here's a way that allows it to be overridden:
-
- BEGIN {
- unless ( defined $ENV{IPCRUNDEBUG} ) {
- eval 'local $ENV{IPCRUNDEBUG} = "none"; require IPC::Run::Debug"'
- or die $@;
- }
- }
-
-This should force IPC::Run to not be debuggable unless somebody sets
-the IPCRUNDEBUG flag; modify this formula to grep @ARGV if need be:
-
- BEGIN {
- unless ( grep /^--debug/, @ARGV ) {
- eval 'local $ENV{IPCRUNDEBUG} = "none"; require IPC::Run::Debug"'
- or die $@;
- }
-
-Both of those are untested.
-
-=cut
-
-@ISA = qw( Exporter ) ;
-
-## We use @EXPORT for the end user's convenience: there's only one function
-## exported, it's homonymous with the module, it's an unusual name, and
-## it can be suppressed by "use IPC::Run () ;".
-
-@EXPORT = qw(
- _debug
- _debug_desc_fd
- _debugging
- _debugging_data
- _debugging_details
- _debugging_gory_details
- _debugging_not_optimized
- _set_child_debug_name
-);
-
-
-@EXPORT_OK = qw(
- _debug_init
- _debugging_level
- _map_fds
-);
-
-%EXPORT_TAGS = (
- default => \@EXPORT,
- all => [ @EXPORT, @EXPORT_OK ],
-);
-
-use strict ;
-use Exporter ;
-
-my $disable_debugging =
- defined $ENV{IPCRUNDEBUG}
- && (
- ! $ENV{IPCRUNDEBUG}
- || lc $ENV{IPCRUNDEBUG} eq "none"
- );
-
-eval( $disable_debugging ? <<'STUBS' : <<'SUBS' ) or die $@;
-sub _map_fds() { "" }
-sub _debug {}
-sub _debug_desc_fd {}
-sub _debug_init {}
-sub _set_child_debug_name {}
-sub _debugging() { 0 }
-sub _debugging_level() { 0 }
-sub _debugging_data() { 0 }
-sub _debugging_details() { 0 }
-sub _debugging_gory_details() { 0 }
-sub _debugging_not_optimized() { 0 }
-
-1;
-STUBS
-
-use POSIX;
-use UNIVERSAL qw( isa );
-
-sub _map_fds {
- my $map = '' ;
- my $digit = 0 ;
- my $in_use ;
- my $dummy ;
- for my $fd (0..63) {
- ## I'd like a quicker way (less user, cpu & expecially sys and kernal
- ## calls) to detect open file descriptors. Let me know...
- ## Hmmm, could do a 0 length read and check for bad file descriptor...
- ## but that segfaults on Win32
- my $test_fd = POSIX::dup( $fd ) ;
- $in_use = defined $test_fd ;
- POSIX::close $test_fd if $in_use ;
- $map .= $in_use ? $digit : '-';
- $digit = 0 if ++$digit > 9 ;
- }
- warn "No fds open???" unless $map =~ /\d/ ;
- $map =~ s/(.{1,12})-*$/$1/ ;
- return $map ;
-}
-
-use vars qw( $parent_pid ) ;
-
-$parent_pid = $$ ;
-
-## TODO: move debugging to it's own module and make it compile-time
-## optimizable.
-
-## Give kid process debugging nice names
-my $debug_name ;
-
-sub _set_child_debug_name {
- $debug_name = shift;
-}
-
-## There's a bit of hackery going on here.
-##
-## We want to have any code anywhere be able to emit
-## debugging statements without knowing what harness the code is
-## being called in/from, since we'd need to pass a harness around to
-## everything.
-##
-## Thus, $cur_self was born.
-#
-my %debug_levels = (
- none => 0,
- basic => 1,
- data => 2,
- details => 3,
- gore => 4,
- gory_details => 4,
- "gory details" => 4,
- gory => 4,
- gorydetails => 4,
- all => 10,
- notopt => 0,
-);
-
-my $warned;
-
-sub _debugging_level() {
- my $level = 0 ;
-
- $level = $IPC::Run::cur_self->{debug} || 0
- if $IPC::Run::cur_self
- && ( $IPC::Run::cur_self->{debug} || 0 ) >= $level ;
-
- if ( defined $ENV{IPCRUNDEBUG} ) {
- my $v = $ENV{IPCRUNDEBUG};
- $v = $debug_levels{lc $v} if $v =~ /[a-zA-Z]/;
- unless ( defined $v ) {
- $warned ||= warn "Unknown debug level $ENV{IPCRUNDEBUG}, assuming 'basic' (1)\n";
- $v = 1;
- }
- $level = $v if $v > $level ;
- }
- return $level ;
-}
-
-sub _debugging_atleast($) {
- my $min_level = shift || 1 ;
-
- my $level = _debugging_level ;
-
- return $level >= $min_level ? $level : 0 ;
-}
-
-sub _debugging() { _debugging_atleast 1 }
-sub _debugging_data() { _debugging_atleast 2 }
-sub _debugging_details() { _debugging_atleast 3 }
-sub _debugging_gory_details() { _debugging_atleast 4 }
-sub _debugging_not_optimized() { ( $ENV{IPCRUNDEBUG} || "" ) eq "notopt" }
-
-sub _debug_init {
- ## This routine is called only in spawned children to fake out the
- ## debug routines so they'll emit debugging info.
- $IPC::Run::cur_self = {} ;
- ( $parent_pid,
- $^T,
- $IPC::Run::cur_self->{debug},
- $IPC::Run::cur_self->{DEBUG_FD},
- $debug_name
- ) = @_ ;
-}
-
-
-sub _debug {
-# return unless _debugging || _debugging_not_optimized ;
-
- my $fd = defined &IPC::Run::_debug_fd
- ? IPC::Run::_debug_fd()
- : fileno STDERR;
-
- my $s ;
- my $debug_id ;
- $debug_id = join(
- " ",
- join(
- "",
- defined $IPC::Run::cur_self ? "#$IPC::Run::cur_self->{ID}" : (),
- "($$)",
- ),
- defined $debug_name && length $debug_name ? $debug_name : (),
- ) ;
- my $prefix = join(
- "",
- "IPC::Run",
- sprintf( " %04d", time - $^T ),
- ( _debugging_details ? ( " ", _map_fds ) : () ),
- length $debug_id ? ( " [", $debug_id, "]" ) : (),
- ": ",
- ) ;
-
- my $msg = join( '', map defined $_ ? $_ : "<undef>", @_ ) ;
- chomp $msg ;
- $msg =~ s{^}{$prefix}gm ;
- $msg .= "\n" ;
- POSIX::write( $fd, $msg, length $msg ) ;
-}
-
-
-my @fd_descs = ( 'stdin', 'stdout', 'stderr' ) ;
-
-sub _debug_desc_fd {
- return unless _debugging ;
- my $text = shift ;
- my $op = pop ;
- my $kid = $_[0] ;
-
-Carp::carp join " ", caller(0), $text, $op if defined $op && isa( $op, "IO::Pty" ) ;
-
- _debug(
- $text,
- ' ',
- ( defined $op->{FD}
- ? $op->{FD} < 3
- ? ( $fd_descs[$op->{FD}] )
- : ( 'fd ', $op->{FD} )
- : $op->{FD}
- ),
- ( defined $op->{KFD}
- ? (
- ' (kid',
- ( defined $kid ? ( ' ', $kid->{NUM}, ) : () ),
- "'s ",
- ( $op->{KFD} < 3
- ? $fd_descs[$op->{KFD}]
- : defined $kid
- && defined $kid->{DEBUG_FD}
- && $op->{KFD} == $kid->{DEBUG_FD}
- ? ( 'debug (', $op->{KFD}, ')' )
- : ( 'fd ', $op->{KFD} )
- ),
- ')',
- )
- : ()
- ),
- ) ;
-}
-
-1;
-
-SUBS
-
-=head1 AUTHOR
-
-Barrie Slaymaker <barries@slaysys.com>, with numerous suggestions by p5p.
-
-=cut
-
-1 ;
diff --git a/lib/IPC/Run/IO.pm b/lib/IPC/Run/IO.pm
deleted file mode 100644
index a6ce1d990a..0000000000
--- a/lib/IPC/Run/IO.pm
+++ /dev/null
@@ -1,554 +0,0 @@
-package IPC::Run::IO ;
-
-=head1 NAME
-
- IPC::Run::IO -- I/O channels for IPC::Run.
-
-=head1 SYNOPSIS
-
-B<NOT IMPLEMENTED YET ON Win32! Win32 does not allow select() on
-normal file descriptors; IPC::RUN::IO needs to use IPC::Run::Win32Helper
-to do this.>
-
- use IPC::Run qw( io ) ;
-
- ## The sense of '>' and '<' is opposite of perl's open(),
- ## but agrees with IPC::Run.
- $io = io( "filename", '>', \$recv ) ;
- $io = io( "filename", 'r', \$recv ) ;
-
- ## Append to $recv:
- $io = io( "filename", '>>', \$recv ) ;
- $io = io( "filename", 'ra', \$recv ) ;
-
- $io = io( "filename", '<', \$send ) ;
- $io = io( "filename", 'w', \$send ) ;
-
- $io = io( "filename", '<<', \$send ) ;
- $io = io( "filename", 'wa', \$send ) ;
-
- ## Handles / IO objects that the caller opens:
- $io = io( \*HANDLE, '<', \$send ) ;
-
- $f = IO::Handle->new( ... ) ; # Any subclass of IO::Handle
- $io = io( $f, '<', \$send ) ;
-
- require IPC::Run::IO ;
- $io = IPC::Run::IO->new( ... ) ;
-
- ## Then run(), harness(), or start():
- run $io, ... ;
-
- ## You can, of course, use io() or IPC::Run::IO->new() as an
- ## argument to run(), harness, or start():
- run io( ... ) ;
-
-
-=head1 DESCRIPTION
-
-This class and module allows filehandles and filenames to be harnessed for
-I/O when used IPC::Run, independant of anything else IPC::Run is doing
-(except that errors & exceptions can affect all things that IPC::Run is
-doing).
-
-=head1 SUBCLASSING
-
-INCOMPATIBLE CHANGE: due to the awkwardness introduced in ripping pseudohashes
-out of Perl, this class I<no longer> uses the fields pragma.
-
-=head1 TODO
-
-Implement bidirectionality.
-
-=head1 AUTHOR
-
-Barrie Slaymaker <barries@slaysys.com>
-
-=cut ;
-
-## This class is also used internally by IPC::Run in a very initimate way,
-## since this is a partial factoring of code from IPC::Run plus some code
-## needed to do standalone channels. This factoring process will continue
-## at some point. Don't know how far how fast.
-
-use strict ;
-use Carp ;
-use Fcntl ;
-use Symbol ;
-use UNIVERSAL qw( isa ) ;
-
-use IPC::Run::Debug;
-use IPC::Run qw( Win32_MODE );
-
-BEGIN {
- if ( Win32_MODE ) {
- eval "use IPC::Run::Win32Helper; require IPC::Run::Win32IO; 1"
- or ( $@ && die ) or die "$!" ;
- }
-}
-
-sub _empty($) ;
-
-*_empty = \&IPC::Run::_empty ;
-
-
-sub new {
- my $class = shift ;
- $class = ref $class || $class ;
-
- my ( $external, $type, $internal ) = ( shift, shift, pop ) ;
-
- croak "$class: '$_' is not a valid I/O operator"
- unless $type =~ /^(?:<<?|>>?)$/ ;
-
- my IPC::Run::IO $self = $class->_new_internal(
- $type, undef, undef, $internal, undef, @_
- ) ;
-
- if ( ! ref $external ) {
- $self->{FILENAME} = $external ;
- }
- elsif ( ref eq 'GLOB' || isa( $external, 'IO::Handle' ) ) {
- $self->{HANDLE} = $external ;
- $self->{DONT_CLOSE} = 1 ;
- }
- else {
- croak "$class: cannot accept " . ref( $external ) . " to do I/O with" ;
- }
-
- return $self ;
-}
-
-
-## IPC::Run uses this ctor, since it preparses things and needs more
-## smarts.
-sub _new_internal {
- my $class = shift ;
- $class = ref $class || $class ;
-
- $class = "IPC::Run::Win32IO"
- if Win32_MODE && $class eq "IPC::Run::IO";
-
- my IPC::Run::IO $self ;
- $self = bless {}, $class ;
-
- my ( $type, $kfd, $pty_id, $internal, $binmode, @filters ) = @_ ;
-
- # Older perls (<=5.00503, at least) don't do list assign to
- # psuedo-hashes well.
- $self->{TYPE} = $type ;
- $self->{KFD} = $kfd ;
- $self->{PTY_ID} = $pty_id ;
- $self->binmode( $binmode ) ;
- $self->{FILTERS} = [ @filters ] ;
-
- ## Add an adapter to the end of the filter chain (which is usually just the
- ## read/writer sub pushed by IPC::Run) to the DEST or SOURCE, if need be.
- if ( $self->op =~ />/ ) {
- croak "'$_' missing a destination" if _empty $internal ;
- $self->{DEST} = $internal ;
- if ( isa( $self->{DEST}, 'CODE' ) ) {
- ## Put a filter on the end of the filter chain to pass the
- ## output on to the CODE ref. For SCALAR refs, the last
- ## filter in the chain writes directly to the scalar itself. See
- ## _init_filters(). For CODE refs, however, we need to adapt from
- ## the SCALAR to calling the CODE.
- unshift(
- @{$self->{FILTERS}},
- sub {
- my ( $in_ref ) = @_ ;
-
- return IPC::Run::input_avail() && do {
- $self->{DEST}->( $$in_ref ) ;
- $$in_ref = '' ;
- 1 ;
- }
- }
- ) ;
- }
- }
- else {
- croak "'$_' missing a source" if _empty $internal ;
- $self->{SOURCE} = $internal ;
- if ( isa( $internal, 'CODE' ) ) {
- push(
- @{$self->{FILTERS}},
- sub {
- my ( $in_ref, $out_ref ) = @_ ;
- return 0 if length $$out_ref ;
-
- return undef
- if $self->{SOURCE_EMPTY} ;
-
- my $in = $internal->() ;
- unless ( defined $in ) {
- $self->{SOURCE_EMPTY} = 1 ;
- return undef
- }
- return 0 unless length $in ;
- $$out_ref = $in ;
-
- return 1 ;
- }
- ) ;
- }
- elsif ( isa( $internal, 'SCALAR' ) ) {
- push(
- @{$self->{FILTERS}},
- sub {
- my ( $in_ref, $out_ref ) = @_ ;
- return 0 if length $$out_ref ;
-
- ## pump() clears auto_close_ins, finish() sets it.
- return $self->{HARNESS}->{auto_close_ins} ? undef : 0
- if IPC::Run::_empty ${$self->{SOURCE}}
- || $self->{SOURCE_EMPTY} ;
-
- $$out_ref = $$internal ;
- eval { $$internal = '' }
- if $self->{HARNESS}->{clear_ins} ;
-
- $self->{SOURCE_EMPTY} = $self->{HARNESS}->{auto_close_ins} ;
-
- return 1 ;
- }
- ) ;
- }
- }
-
- return $self ;
-}
-
-
-=item filename
-
-Gets/sets the filename. Returns the value after the name change, if
-any.
-
-=cut
-
-sub filename {
- my IPC::Run::IO $self = shift ;
- $self->{FILENAME} = shift if @_ ;
- return $self->{FILENAME} ;
-}
-
-
-=item init
-
-Does initialization required before this can be run. This includes open()ing
-the file, if necessary, and clearing the destination scalar if necessary.
-
-=cut
-
-sub init {
- my IPC::Run::IO $self = shift ;
-
- $self->{SOURCE_EMPTY} = 0 ;
- ${$self->{DEST}} = ''
- if $self->mode =~ /r/ && ref $self->{DEST} eq 'SCALAR' ;
-
- $self->open if defined $self->filename ;
- $self->{FD} = $self->fileno ;
-
- if ( ! $self->{FILTERS} ) {
- $self->{FBUFS} = undef ;
- }
- else {
- @{$self->{FBUFS}} = map {
- my $s = "" ;
- \$s ;
- } ( @{$self->{FILTERS}}, '' ) ;
-
- $self->{FBUFS}->[0] = $self->{DEST}
- if $self->{DEST} && ref $self->{DEST} eq 'SCALAR' ;
- push @{$self->{FBUFS}}, $self->{SOURCE} ;
- }
-
- return undef ;
-}
-
-
-=item open
-
-If a filename was passed in, opens it. Determines if the handle is open
-via fileno(). Throws an exception on error.
-
-=cut
-
-my %open_flags = (
- '>' => O_RDONLY,
- '>>' => O_RDONLY,
- '<' => O_WRONLY | O_CREAT | O_TRUNC,
- '<<' => O_WRONLY | O_CREAT | O_APPEND,
-) ;
-
-sub open {
- my IPC::Run::IO $self = shift ;
-
- croak "IPC::Run::IO: Can't open() a file with no name"
- unless defined $self->{FILENAME} ;
- $self->{HANDLE} = gensym unless $self->{HANDLE} ;
-
- _debug
- "opening '", $self->filename, "' mode '", $self->mode, "'"
- if _debugging_data ;
- sysopen(
- $self->{HANDLE},
- $self->filename,
- $open_flags{$self->op},
- ) or croak
- "IPC::Run::IO: $! opening '$self->{FILENAME}', mode '" . $self->mode . "'" ;
-
- return undef ;
-}
-
-
-=item open_pipe
-
-If this is a redirection IO object, this opens the pipe in a platform
-independant manner.
-
-=cut
-
-sub _do_open {
- my $self = shift;
- my ( $child_debug_fd, $parent_handle ) = @_ ;
-
-
- if ( $self->dir eq "<" ) {
- ( $self->{TFD}, $self->{FD} ) = IPC::Run::_pipe_nb ;
- if ( $parent_handle ) {
- CORE::open $parent_handle, ">&=$self->{FD}"
- or croak "$! duping write end of pipe for caller" ;
- }
- }
- else {
- ( $self->{FD}, $self->{TFD} ) = IPC::Run::_pipe ;
- if ( $parent_handle ) {
- CORE::open $parent_handle, "<&=$self->{FD}"
- or croak "$! duping read end of pipe for caller" ;
- }
- }
-}
-
-sub open_pipe {
- my IPC::Run::IO $self = shift ;
-
- ## Hmmm, Maybe allow named pipes one day. But until then...
- croak "IPC::Run::IO: Can't pipe() when a file name has been set"
- if defined $self->{FILENAME} ;
-
- $self->_do_open( @_ );
-
- ## return ( child_fd, parent_fd )
- return $self->dir eq "<"
- ? ( $self->{TFD}, $self->{FD} )
- : ( $self->{FD}, $self->{TFD} ) ;
-}
-
-
-sub _cleanup { ## Called from Run.pm's _cleanup
- my $self = shift;
- undef $self->{FAKE_PIPE};
-}
-
-
-=item close
-
-Closes the handle. Throws an exception on failure.
-
-
-=cut
-
-sub close {
- my IPC::Run::IO $self = shift ;
-
- if ( defined $self->{HANDLE} ) {
- close $self->{HANDLE}
- or croak( "IPC::Run::IO: $! closing "
- . ( defined $self->{FILENAME}
- ? "'$self->{FILENAME}'"
- : "handle"
- )
- ) ;
- }
- else {
- IPC::Run::_close( $self->{FD} ) ;
- }
-
- $self->{FD} = undef ;
-
- return undef ;
-}
-
-=item fileno
-
-Returns the fileno of the handle. Throws an exception on failure.
-
-
-=cut
-
-sub fileno {
- my IPC::Run::IO $self = shift ;
-
- my $fd = fileno $self->{HANDLE} ;
- croak( "IPC::Run::IO: $! "
- . ( defined $self->{FILENAME}
- ? "'$self->{FILENAME}'"
- : "handle"
- )
- ) unless defined $fd ;
-
- return $fd ;
-}
-
-=item mode
-
-Returns the operator in terms of 'r', 'w', and 'a'. There is a state
-'ra', unlike Perl's open(), which indicates that data read from the
-handle or file will be appended to the output if the output is a scalar.
-This is only meaningful if the output is a scalar, it has no effect if
-the output is a subroutine.
-
-The redirection operators can be a little confusing, so here's a reference
-table:
-
- > r Read from handle in to process
- < w Write from process out to handle
- >> ra Read from handle in to process, appending it to existing
- data if the destination is a scalar.
- << wa Write from process out to handle, appending to existing
- data if IPC::Run::IO opened a named file.
-
-=cut
-
-sub mode {
- my IPC::Run::IO $self = shift ;
-
- croak "IPC::Run::IO: unexpected arguments for mode(): @_" if @_ ;
-
- ## TODO: Optimize this
- return ( $self->{TYPE} =~ /</ ? 'w' : 'r' ) .
- ( $self->{TYPE} =~ /<<|>>/ ? 'a' : '' ) ;
-}
-
-
-=item op
-
-Returns the operation: '<', '>', '<<', '>>'. See L</mode> if you want
-to spell these 'r', 'w', etc.
-
-=cut
-
-sub op {
- my IPC::Run::IO $self = shift ;
-
- croak "IPC::Run::IO: unexpected arguments for op(): @_" if @_ ;
-
- return $self->{TYPE} ;
-}
-
-=item binmode
-
-Sets/gets whether this pipe is in binmode or not. No effect off of Win32
-OSs, of course, and on Win32, no effect after the harness is start()ed.
-
-=cut
-
-sub binmode {
- my IPC::Run::IO $self = shift ;
-
- $self->{BINMODE} = shift if @_ ;
-
- return $self->{BINMODE} ;
-}
-
-
-=item dir
-
-Returns the first character of $self->op. This is either "<" or ">".
-
-=cut
-
-sub dir {
- my IPC::Run::IO $self = shift ;
-
- croak "IPC::Run::IO: unexpected arguments for dir(): @_" if @_ ;
-
- return substr $self->{TYPE}, 0, 1 ;
-}
-
-
-##
-## Filter Scaffolding
-##
-#my $filter_op ; ## The op running a filter chain right now
-#my $filter_num ; ## Which filter is being run right now.
-
-use vars (
-'$filter_op', ## The op running a filter chain right now
-'$filter_num' ## Which filter is being run right now.
-) ;
-
-sub _init_filters {
- my IPC::Run::IO $self = shift ;
-
-confess "\$self not an IPC::Run::IO" unless isa( $self, "IPC::Run::IO" ) ;
- $self->{FBUFS} = [] ;
-
- $self->{FBUFS}->[0] = $self->{DEST}
- if $self->{DEST} && ref $self->{DEST} eq 'SCALAR' ;
-
- return unless $self->{FILTERS} && @{$self->{FILTERS}} ;
-
- push @{$self->{FBUFS}}, map {
- my $s = "" ;
- \$s ;
- } ( @{$self->{FILTERS}}, '' ) ;
-
- push @{$self->{FBUFS}}, $self->{SOURCE} ;
-}
-
-
-sub poll {
- my IPC::Run::IO $self = shift;
- my ( $harness ) = @_;
-
- if ( defined $self->{FD} ) {
- my $d = $self->dir;
- if ( $d eq "<" ) {
- if ( vec $harness->{WOUT}, $self->{FD}, 1 ) {
- _debug_desc_fd( "filtering data to", $self )
- if _debugging_details ;
- return $self->_do_filters( $harness );
- }
- }
- elsif ( $d eq ">" ) {
- if ( vec $harness->{ROUT}, $self->{FD}, 1 ) {
- _debug_desc_fd( "filtering data from", $self )
- if _debugging_details ;
- return $self->_do_filters( $harness );
- }
- }
- }
- return 0;
-}
-
-
-sub _do_filters {
- my IPC::Run::IO $self = shift ;
-
- ( $self->{HARNESS} ) = @_ ;
-
- my ( $saved_op, $saved_num ) =($IPC::Run::filter_op,$IPC::Run::filter_num) ;
- $IPC::Run::filter_op = $self ;
- $IPC::Run::filter_num = -1 ;
- my $r = eval { IPC::Run::get_more_input() ; } ;
- ( $IPC::Run::filter_op, $IPC::Run::filter_num ) = ( $saved_op, $saved_num ) ;
- $self->{HARNESS} = undef ;
- die $@ if $@ ;
- return $r ;
-}
-
-1 ;
diff --git a/lib/IPC/Run/Timer.pm b/lib/IPC/Run/Timer.pm
deleted file mode 100644
index 82d418997d..0000000000
--- a/lib/IPC/Run/Timer.pm
+++ /dev/null
@@ -1,688 +0,0 @@
-package IPC::Run::Timer ;
-
-=head1 NAME
-
- IPC::Run::Timer -- Timer channels for IPC::Run.
-
-=head1 SYNOPSIS
-
- use IPC::Run qw( run timer timeout ) ;
- ## or IPC::Run::Timer ( timer timeout ) ;
- ## or IPC::Run::Timer ( :all ) ;
-
- ## A non-fatal timer:
- $t = timer( 5 ) ; # or...
- $t = IO::Run::Timer->new( 5 ) ;
- run $t, ... ;
-
- ## A timeout (which is a timer that dies on expiry):
- $t = timeout( 5 ) ; # or...
- $t = IO::Run::Timer->new( 5, exception => "harness timed out" ) ;
-
-=head1 DESCRIPTION
-
-This class and module allows timers and timeouts to be created for use
-by IPC::Run. A timer simply expires when it's time is up. A timeout
-is a timer that throws an exception when it expires.
-
-Timeouts are usually a bit simpler to use than timers: they throw an
-exception on expiration so you don't need to check them:
-
- ## Give @cmd 10 seconds to get started, then 5 seconds to respond
- my $t = timeout( 10 ) ;
- $h = start(
- \@cmd, \$in, \$out,
- $t,
- ) ;
- pump $h until $out =~ /prompt/ ;
-
- $in = "some stimulus" ;
- $out = '' ;
- $t->time( 5 )
- pump $h until $out =~ /expected response/ ;
-
-You do need to check timers:
-
- ## Give @cmd 10 seconds to get started, then 5 seconds to respond
- my $t = timer( 10 ) ;
- $h = start(
- \@cmd, \$in, \$out,
- $t,
- ) ;
- pump $h until $t->is_expired || $out =~ /prompt/ ;
-
- $in = "some stimulus" ;
- $out = '' ;
- $t->time( 5 )
- pump $h until $out =~ /expected response/ || $t->is_expired ;
-
-Timers and timeouts that are reset get started by start() and
-pump(). Timers change state only in pump(). Since run() and
-finish() both call pump(), they act like pump() with repect to
-timers.
-
-Timers and timeouts have three states: reset, running, and expired.
-Setting the timeout value resets the timer, as does calling
-the reset() method. The start() method starts (or restarts) a
-timer with the most recently set time value, no matter what state
-it's in.
-
-=head2 Time values
-
-All time values are in seconds. Times may be specified as integer or
-floating point seconds, optionally preceded by puncuation-separated
-days, hours, and minutes.\
-
-Examples:
-
- 1 1 second
- 1.1 1.1 seconds
- 60 60 seconds
- 1:0 1 minute
- 1:1 1 minute, 1 second
- 1:90 2 minutes, 30 seconds
- 1:2:3:4.5 1 day, 2 hours, 3 minutes, 4.5 seconds
-
-Absolute date/time strings are *not* accepted: year, month and
-day-of-month parsing is not available (patches welcome :-).
-
-=head2 Interval fudging
-
-When calculating an end time from a start time and an interval, IPC::Run::Timer
-instances add a little fudge factor. This is to ensure that no time will
-expire before the interval is up.
-
-First a little background. Time is sampled in discrete increments. We'll
-call the
-exact moment that the reported time increments from one interval to the
-next a tick, and the interval between ticks as the time period. Here's
-a diagram of three ticks and the periods between them:
-
-
- -0-0-0-0-0-0-0-0-0-0-1-1-1-1-1-1-1-1-1-1-2-...
- ^ ^ ^
- |<--- period 0 ---->|<--- period 1 ---->|
- | | |
- tick 0 tick 1 tick 2
-
-To see why the fudge factor is necessary, consider what would happen
-when a timer with an interval of 1 second is started right at the end of
-period 0:
-
-
- -0-0-0-0-0-0-0-0-0-0-1-1-1-1-1-1-1-1-1-1-2-...
- ^ ^ ^ ^
- | | | |
- | | | |
- tick 0 |tick 1 tick 2
- |
- start $t
-
-Assuming that check() is called many times per period, then the timer
-is likely to expire just after tick 1, since the time reported will have
-lept from the value '0' to the value '1':
-
- -0-0-0-0-0-0-0-0-0-0-1-1-1-1-1-1-1-1-1-1-2-...
- ^ ^ ^ ^ ^
- | | | | |
- | | | | |
- tick 0 |tick 1| tick 2
- | |
- start $t |
- |
- check $t
-
-Adding a fudge of '1' in this example means that the timer is guaranteed
-not to expire before tick 2.
-
-The fudge is not added to an interval of '0'.
-
-This means that intervals guarantee a minimum interval. Given that
-the process running perl may be suspended for some period of time, or that
-it gets busy doing something time-consuming, there are no other guarantees on
-how long it will take a timer to expire.
-
-=head1 SUBCLASSING
-
-INCOMPATIBLE CHANGE: Due to the awkwardness introduced by ripping
-pseudohashes out of Perl, this class I<no longer> uses the fields
-pragma.
-
-=head1 FUNCTIONS & METHODS
-
-=over
-
-=cut ;
-
-use strict ;
-use Carp ;
-use Fcntl ;
-use Symbol ;
-use UNIVERSAL qw( isa ) ;
-use Exporter ;
-use vars qw( @EXPORT_OK %EXPORT_TAGS @ISA ) ;
-
-@EXPORT_OK = qw(
- check
- end_time
- exception
- expire
- interval
- is_expired
- is_reset
- is_running
- name
- reset
- start
-
- timeout
- timer
-) ;
-
-%EXPORT_TAGS = ( 'all' => \@EXPORT_OK ) ;
-
-@ISA = qw( Exporter ) ;
-
-require IPC::Run ;
-use IPC::Run::Debug ;
-
-##
-## Some helpers
-##
-my $resolution = 1 ;
-
-sub _parse_time {
- for ( $_[0] ) {
- return $_ unless defined $_ ;
- return $_ if /^\d*(?:\.\d*)?$/ ;
-
- my @f = reverse split( /[^\d\.]+/i ) ;
- croak "IPC::Run: invalid time string '$_'" unless @f <= 4 ;
- my ( $s, $m, $h, $d ) = @f ;
- return
- ( (
- ( $d || 0 ) * 24
- + ( $h || 0 ) ) * 60
- + ( $m || 0 ) ) * 60
- + ( $s || 0 ) ;
- }
-}
-
-
-sub _calc_end_time {
- my IPC::Run::Timer $self = shift ;
-
- my $interval = $self->interval ;
- $interval += $resolution if $interval ;
-
- $self->end_time( $self->start_time + $interval ) ;
-}
-
-
-=item timer
-
-A constructor function (not method) of IPC::Run::Timer instances:
-
- $t = timer( 5 ) ;
- $t = timer( 5, name => 'stall timer', debug => 1 ) ;
-
- $t = timer ;
- $t->interval( 5 ) ;
-
- run ..., $t ;
- run ..., $t = timer( 5 ) ;
-
-This convenience function is a shortened spelling of
-
- IPC::Run::Timer->new( ... ) ;
-
-. It returns a timer in the reset state with a given interval.
-
-If an exception is provided, it will be thrown when the timer notices that
-it has expired (in check()). The name is for debugging usage, if you plan on
-having multiple timers around. If no name is provided, a name like "timer #1"
-will be provided.
-
-=cut
-
-sub timer {
- return IPC::Run::Timer->new( @_ ) ;
-}
-
-
-=item timeout
-
-A constructor function (not method) of IPC::Run::Timer instances:
-
- $t = timeout( 5 ) ;
- $t = timeout( 5, exception => "kablooey" ) ;
- $t = timeout( 5, name => "stall", exception => "kablooey" ) ;
-
- $t = timeout ;
- $t->interval( 5 ) ;
-
- run ..., $t ;
- run ..., $t = timeout( 5 ) ;
-
-A This convenience function is a shortened spelling of
-
- IPC::Run::Timer->new( exception => "IPC::Run: timeout ...", ... ) ;
-
-. It returns a timer in the reset state that will throw an
-exception when it expires.
-
-Takes the same parameters as L</timer>, any exception passed in overrides
-the default exception.
-
-=cut
-
-sub timeout {
- my $t = IPC::Run::Timer->new( @_ ) ;
- $t->exception( "IPC::Run: timeout on " . $t->name )
- unless defined $t->exception ;
- return $t ;
-}
-
-
-=item new
-
- IPC::Run::Timer->new() ;
- IPC::Run::Timer->new( 5 ) ;
- IPC::Run::Timer->new( 5, exception => 'kablooey' ) ;
-
-Constructor. See L</timer> for details.
-
-=cut
-
-my $timer_counter ;
-
-
-sub new {
- my $class = shift ;
- $class = ref $class || $class ;
-
- my IPC::Run::Timer $self = bless {}, $class;
-
- $self->{STATE} = 0 ;
- $self->{DEBUG} = 0 ;
- $self->{NAME} = "timer #" . ++$timer_counter ;
-
- while ( @_ ) {
- my $arg = shift ;
- if ( $arg =~ /^(?:\d+[^\a\d]){0,3}\d*(?:\.\d*)?$/ ) {
- $self->interval( $arg ) ;
- }
- elsif ( $arg eq 'exception' ) {
- $self->exception( shift ) ;
- }
- elsif ( $arg eq 'name' ) {
- $self->name( shift ) ;
- }
- elsif ( $arg eq 'debug' ) {
- $self->debug( shift ) ;
- }
- else {
- croak "IPC::Run: unexpected parameter '$arg'" ;
- }
- }
-
- _debug $self->name . ' constructed'
- if $self->{DEBUG} || _debugging_details ;
-
- return $self ;
-}
-
-=item check
-
- check $t ;
- check $t, $now ;
- $t->check ;
-
-Checks to see if a timer has expired since the last check. Has no effect
-on non-running timers. This will throw an exception if one is defined.
-
-IPC::Run::pump() calls this routine for any timers in the harness.
-
-You may pass in a version of now, which is useful in case you have
-it lying around or you want to check several timers with a consistent
-concept of the current time.
-
-Returns the time left before end_time or 0 if end_time is no longer
-in the future or the timer is not running
-(unless, of course, check() expire()s the timer and this
-results in an exception being thrown).
-
-Returns undef if the timer is not running on entry, 0 if check() expires it,
-and the time left if it's left running.
-
-=cut
-
-sub check {
- my IPC::Run::Timer $self = shift ;
- return undef if ! $self->is_running ;
- return 0 if $self->is_expired ;
-
- my ( $now ) = @_ ;
- $now = _parse_time( $now ) ;
- $now = time unless defined $now ;
-
- _debug(
- "checking ", $self->name, " (end time ", $self->end_time, ") at ", $now
- ) if $self->{DEBUG} || _debugging_details ;
-
- my $left = $self->end_time - $now ;
- return $left if $left > 0 ;
-
- $self->expire ;
- return 0 ;
-}
-
-
-=item debug
-
-Sets/gets the current setting of the debugging flag for this timer. This
-has no effect if debugging is not enabled for the current harness.
-
-=cut
-
-
-sub debug {
- my IPC::Run::Timer $self = shift ;
- $self->{DEBUG} = shift if @_ ;
- return $self->{DEBUG} ;
-}
-
-
-=item end_time
-
- $et = $t->end_time ;
- $et = end_time $t ;
-
- $t->end_time( time + 10 ) ;
-
-Returns the time when this timer will or did expire. Even if this time is
-in the past, the timer may not be expired, since check() may not have been
-called yet.
-
-Note that this end_time is not start_time($t) + interval($t), since some
-small extra amount of time is added to make sure that the timer does not
-expire before interval() elapses. If this were not so, then
-
-Changing end_time() while a timer is running will set the expiration time.
-Changing it while it is expired has no affect, since reset()ing a timer always
-clears the end_time().
-
-=cut
-
-
-sub end_time {
- my IPC::Run::Timer $self = shift ;
- if ( @_ ) {
- $self->{END_TIME} = shift ;
- _debug $self->name, ' end_time set to ', $self->{END_TIME}
- if $self->{DEBUG} > 2 || _debugging_details ;
- }
- return $self->{END_TIME} ;
-}
-
-
-=item exception
-
- $x = $t->exception ;
- $t->exception( $x ) ;
- $t->exception( undef ) ;
-
-Sets/gets the exception to throw, if any. 'undef' means that no
-exception will be thrown. Exception does not need to be a scalar: you
-may ask that references be thrown.
-
-=cut
-
-
-sub exception {
- my IPC::Run::Timer $self = shift ;
- if ( @_ ) {
- $self->{EXCEPTION} = shift ;
- _debug $self->name, ' exception set to ', $self->{EXCEPTION}
- if $self->{DEBUG} || _debugging_details ;
- }
- return $self->{EXCEPTION} ;
-}
-
-
-=item interval
-
- $i = interval $t ;
- $i = $t->interval ;
- $t->interval( $i ) ;
-
-Sets the interval. Sets the end time based on the start_time() and the
-interval (and a little fudge) if the timer is running.
-
-=cut
-
-sub interval {
- my IPC::Run::Timer $self = shift ;
- if ( @_ ) {
- $self->{INTERVAL} = _parse_time( shift ) ;
- _debug $self->name, ' interval set to ', $self->{INTERVAL}
- if $self->{DEBUG} > 2 || _debugging_details ;
-
- $self->_calc_end_time if $self->state ;
- }
- return $self->{INTERVAL} ;
-}
-
-
-=item expire
-
- expire $t ;
- $t->expire ;
-
-Sets the state to expired (undef).
-Will throw an exception if one
-is defined and the timer was not already expired. You can expire a
-reset timer without starting it.
-
-=cut
-
-
-sub expire {
- my IPC::Run::Timer $self = shift ;
- if ( defined $self->state ) {
- _debug $self->name . ' expired'
- if $self->{DEBUG} || _debugging ;
-
- $self->state( undef ) ;
- croak $self->exception if $self->exception ;
- }
- return undef ;
-}
-
-
-=item is_running
-
-=cut
-
-
-sub is_running {
- my IPC::Run::Timer $self = shift ;
- return $self->state ? 1 : 0 ;
-}
-
-
-=item is_reset
-
-=cut
-
-sub is_reset {
- my IPC::Run::Timer $self = shift ;
- return defined $self->state && $self->state == 0 ;
-}
-
-
-=item is_expired
-
-=cut
-
-sub is_expired {
- my IPC::Run::Timer $self = shift ;
- return ! defined $self->state ;
-}
-
-=item name
-
-Sets/gets this timer's name. The name is only used for debugging
-purposes so you can tell which freakin' timer is doing what.
-
-=cut
-
-sub name {
- my IPC::Run::Timer $self = shift ;
-
- $self->{NAME} = shift if @_ ;
- return defined $self->{NAME}
- ? $self->{NAME}
- : defined $self->{EXCEPTION}
- ? 'timeout'
- : 'timer' ;
-}
-
-
-=item reset
-
- reset $t ;
- $t->reset ;
-
-Resets the timer to the non-running, non-expired state and clears
-the end_time().
-
-=cut
-
-sub reset {
- my IPC::Run::Timer $self = shift ;
- $self->state( 0 ) ;
- $self->end_time( undef ) ;
- _debug $self->name . ' reset'
- if $self->{DEBUG} || _debugging ;
-
- return undef ;
-}
-
-
-=item start
-
- start $t ;
- $t->start ;
- start $t, $interval ;
- start $t, $interval, $now ;
-
-Starts or restarts a timer. This always sets the start_time. It sets the
-end_time based on the interval if the timer is running or if no end time
-has been set.
-
-You may pass an optional interval or current time value.
-
-Not passing a defined interval causes the previous interval setting to be
-re-used unless the timer is reset and an end_time has been set
-(an exception is thrown if no interval has been set).
-
-Not passing a defined current time value causes the current time to be used.
-
-Passing a current time value is useful if you happen to have a time value
-lying around or if you want to make sure that several timers are started
-with the same concept of start time. You might even need to lie to an
-IPC::Run::Timer, occasionally.
-
-=cut
-
-sub start {
- my IPC::Run::Timer $self = shift ;
-
- my ( $interval, $now ) = map { _parse_time( $_ ) } @_ ;
- $now = _parse_time( $now ) ;
- $now = time unless defined $now ;
-
- $self->interval( $interval ) if defined $interval ;
-
- ## start()ing a running or expired timer clears the end_time, so that the
- ## interval is used. So does specifying an interval.
- $self->end_time( undef ) if ! $self->is_reset || $interval ;
-
- croak "IPC::Run: no timer interval or end_time defined for " . $self->name
- unless defined $self->interval || defined $self->end_time ;
-
- $self->state( 1 ) ;
- $self->start_time( $now ) ;
- ## The "+ 1" is in case the START_TIME was sampled at the end of a
- ## tick (which are one second long in this module).
- $self->_calc_end_time
- unless defined $self->end_time ;
-
- _debug(
- $self->name, " started at ", $self->start_time,
- ", with interval ", $self->interval, ", end_time ", $self->end_time
- ) if $self->{DEBUG} || _debugging ;
- return undef ;
-}
-
-
-=item start_time
-
-Sets/gets the start time, in seconds since the epoch. Setting this manually
-is a bad idea, it's better to call L</start>() at the correct time.
-
-=cut
-
-
-sub start_time {
- my IPC::Run::Timer $self = shift ;
- if ( @_ ) {
- $self->{START_TIME} = _parse_time( shift ) ;
- _debug $self->name, ' start_time set to ', $self->{START_TIME}
- if $self->{DEBUG} > 2 || _debugging ;
- }
-
- return $self->{START_TIME} ;
-}
-
-
-=item state
-
- $s = state $t ;
- $t->state( $s ) ;
-
-Get/Set the current state. Only use this if you really need to transfer the
-state to/from some variable.
-Use L</expire>, L</start>, L</reset>, L</is_expired>, L</is_running>,
-L</is_reset>.
-
-Note: Setting the state to 'undef' to expire a timer will not throw an
-exception.
-
-=cut
-
-sub state {
- my IPC::Run::Timer $self = shift ;
- if ( @_ ) {
- $self->{STATE} = shift ;
- _debug $self->name, ' state set to ', $self->{STATE}
- if $self->{DEBUG} > 2 || _debugging ;
- }
- return $self->{STATE} ;
-}
-
-
-=head1 TODO
-
-use Time::HiRes ; if it's present.
-
-Add detection and parsing of [[[HH:]MM:]SS formatted times and intervals.
-
-=head1 AUTHOR
-
-Barrie Slaymaker <barries@slaysys.com>
-
-=cut
-
-1 ;
diff --git a/lib/IPC/Run/Win32Helper.pm b/lib/IPC/Run/Win32Helper.pm
deleted file mode 100644
index 178dd58fe5..0000000000
--- a/lib/IPC/Run/Win32Helper.pm
+++ /dev/null
@@ -1,481 +0,0 @@
-package IPC::Run::Win32Helper ;
-
-=head1 NAME
-
-IPC::Run::Win32Helper - helper routines for IPC::Run on Win32 platforms.
-
-=head1 SYNOPSIS
-
-use IPC::Run::Win32Helper ; # Exports all by default
-
-=head1 DESCRIPTION
-
-IPC::Run needs to use sockets to redirect subprocess I/O so that the select() loop
-will work on Win32. This seems to only work on WinNT and Win2K at this time, not
-sure if it will ever work on Win95 or Win98. If you have experience in this area, please
-contact me at barries@slaysys.com, thanks!.
-
-=cut
-
-@ISA = qw( Exporter ) ;
-
-@EXPORT = qw(
- win32_spawn
- win32_parse_cmd_line
- _dont_inherit
- _inherit
-) ;
-
-use strict ;
-use Carp ;
-use IO::Handle ;
-#use IPC::Open3 ();
-require POSIX ;
-
-use Text::ParseWords ;
-use Win32::Process ;
-use IPC::Run::Debug;
-## REMOVE OSFHandleOpen
-use Win32API::File qw(
- FdGetOsFHandle
- SetHandleInformation
- HANDLE_FLAG_INHERIT
- INVALID_HANDLE_VALUE
-) ;
-
-## Takes an fd or a GLOB ref, never never never a Win32 handle.
-sub _dont_inherit {
- for ( @_ ) {
- next unless defined $_ ;
- my $fd = $_ ;
- $fd = fileno $fd if ref $fd ;
- _debug "disabling inheritance of ", $fd if _debugging_details ;
- my $osfh = FdGetOsFHandle $fd ;
- croak $^E if ! defined $osfh || $osfh == INVALID_HANDLE_VALUE ;
-
- SetHandleInformation( $osfh, HANDLE_FLAG_INHERIT, 0 ) ;
- }
-}
-
-sub _inherit { #### REMOVE
- for ( @_ ) { #### REMOVE
- next unless defined $_ ; #### REMOVE
- my $fd = $_ ; #### REMOVE
- $fd = fileno $fd if ref $fd ; #### REMOVE
- _debug "enabling inheritance of ", $fd if _debugging_details ; #### REMOVE
- my $osfh = FdGetOsFHandle $fd ; #### REMOVE
- croak $^E if ! defined $osfh || $osfh == INVALID_HANDLE_VALUE ; #### REMOVE
- #### REMOVE
- SetHandleInformation( $osfh, HANDLE_FLAG_INHERIT, 1 ) ; #### REMOVE
- } #### REMOVE
-} #### REMOVE
- #### REMOVE
-#sub _inherit {
-# for ( @_ ) {
-# next unless defined $_ ;
-# my $osfh = GetOsFHandle $_ ;
-# croak $^E if ! defined $osfh || $osfh == INVALID_HANDLE_VALUE ;
-# SetHandleInformation( $osfh, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT ) ;
-# }
-#}
-
-=head1 FUNCTIONS
-
-=over
-
-=cut
-
-=item optimize()
-
-Most common incantations of C<run()> (I<not> C<harness()>, C<start()>,
-or C<finish()>) now use temporary files to redirect input and output
-instead of pumper processes.
-
-Temporary files are used when sending to child processes if input is
-taken from a scalar with no filter subroutines. This is the only time
-we can assume that the parent is not interacting with the child's
-redirected input as it runs.
-
-Temporary files are used when receiving from children when output is
-to a scalar or subroutine with or without filters, but only if
-the child in question closes its inputs or takes input from
-unfiltered SCALARs or named files. Normally, a child inherits its STDIN
-from its parent; to close it, use "0<&-" or the C<noinherit => 1> option.
-If data is sent to the child from CODE refs, filehandles or from
-scalars through filters than the child's outputs will not be optimized
-because C<optimize()> assumes the parent is interacting with the child.
-It is ok if the output is filtered or handled by a subroutine, however.
-
-This assumes that all named files are real files (as opposed to named
-pipes) and won't change; and that a process is not communicating with
-the child indirectly (through means not visible to IPC::Run).
-These can be an invalid assumptions, but are the 99% case.
-Write me if you need an option to enable or disable optimizations; I
-suspect it will work like the C<binary()> modifier.
-
-To detect cases that you might want to optimize by closing inputs, try
-setting the C<IPCRUNDEBUG> environment variable to the special C<notopt>
-value:
-
- C:> set IPCRUNDEBUG=notopt
- C:> my_app_that_uses_IPC_Run.pl
-
-=item optimizer() rationalizations
-
-Only for that limited case can we be sure that it's ok to batch all the
-input in to a temporary file. If STDIN is from a SCALAR or from a named
-file or filehandle (again, only in C<run()>), then outputs to CODE refs
-are also assumed to be safe enough to batch through a temp file,
-otherwise only outputs to SCALAR refs are batched. This can cause a bit
-of grief if the parent process benefits from or relies on a bit of
-"early returns" coming in before the child program exits. As long as
-the output is redirected to a SCALAR ref, this will not be visible.
-When output is redirected to a subroutine or (deprecated) filters, the
-subroutine will not get any data until after the child process exits,
-and it is likely to get bigger chunks of data at once.
-
-The reason for the optimization is that, without it, "pumper" processes
-are used to overcome the inconsistancies of the Win32 API. We need to
-use anonymous pipes to connect to the child processes' stdin, stdout,
-and stderr, yet select() does not work on these. select() only works on
-sockets on Win32. So for each redirected child handle, there is
-normally a "pumper" process that connects to the parent using a
-socket--so the parent can select() on that fd--and to the child on an
-anonymous pipe--so the child can read/write a pipe.
-
-Using a socket to connect directly to the child (as at least one MSDN
-article suggests) seems to cause the trailing output from most children
-to be lost. I think this is because child processes rarely close their
-stdout and stderr explicitly, and the winsock dll does not seem to flush
-output when a process that uses it exits without explicitly closing
-them.
-
-Because of these pumpers and the inherent slowness of Win32
-CreateProcess(), child processes with redirects are quite slow to
-launch; so this routine looks for the very common case of
-reading/writing to/from scalar references in a run() routine and
-converts such reads and writes in to temporary file reads and writes.
-
-Such files are marked as FILE_ATTRIBUTE_TEMPORARY to increase speed and
-as FILE_FLAG_DELETE_ON_CLOSE so it will be cleaned up when the child
-process exits (for input files). The user's default permissions are
-used for both the temporary files and the directory that contains them,
-hope your Win32 permissions are secure enough for you. Files are
-created with the Win32API::File defaults of
-FILE_SHARE_READ|FILE_SHARE_WRITE.
-
-Setting the debug level to "details" or "gory" will give detailed
-information about the optimization process; setting it to "basic" or
-higher will tell whether or not a given call is optimized. Setting
-it to "notopt" will highligh those calls that aren't optimized.
-
-=cut
-
-sub optimize {
- my ( $h ) = @_;
-
- my @kids = @{$h->{KIDS}};
-
- my $saw_pipe;
-
- my ( $ok_to_optimize_outputs, $veto_output_optimization );
-
- for my $kid ( @kids ) {
- ( $ok_to_optimize_outputs, $veto_output_optimization ) = ()
- unless $saw_pipe;
-
- _debug
- "Win32 optimizer: (kid $kid->{NUM}) STDIN piped, carrying over ok of non-SCALAR output optimization"
- if _debugging_details && $ok_to_optimize_outputs;
- _debug
- "Win32 optimizer: (kid $kid->{NUM}) STDIN piped, carrying over veto of non-SCALAR output optimization"
- if _debugging_details && $veto_output_optimization;
-
- if ( $h->{noinherit} && ! $ok_to_optimize_outputs ) {
- _debug
- "Win32 optimizer: (kid $kid->{NUM}) STDIN not inherited from parent oking non-SCALAR output optimization"
- if _debugging_details && $ok_to_optimize_outputs;
- $ok_to_optimize_outputs = 1;
- }
-
- for ( @{$kid->{OPS}} ) {
- if ( substr( $_->{TYPE}, 0, 1 ) eq "<" ) {
- if ( $_->{TYPE} eq "<" ) {
- if ( @{$_->{FILTERS}} > 1 ) {
- ## Can't assume that the filters are idempotent.
- }
- elsif ( ref $_->{SOURCE} eq "SCALAR"
- || ref $_->{SOURCE} eq "GLOB"
- || UNIVERSAL::isa( $_, "IO::Handle" )
- ) {
- if ( $_->{KFD} == 0 ) {
- _debug
- "Win32 optimizer: (kid $kid->{NUM}) 0$_->{TYPE}",
- ref $_->{SOURCE},
- ", ok to optimize outputs"
- if _debugging_details;
- $ok_to_optimize_outputs = 1;
- }
- $_->{SEND_THROUGH_TEMP_FILE} = 1;
- next;
- }
- elsif ( ! ref $_->{SOURCE} && defined $_->{SOURCE} ) {
- if ( $_->{KFD} == 0 ) {
- _debug
- "Win32 optimizer: (kid $kid->{NUM}) 0<$_->{SOURCE}, ok to optimize outputs",
- if _debugging_details;
- $ok_to_optimize_outputs = 1;
- }
- next;
- }
- }
- _debug
- "Win32 optimizer: (kid $kid->{NUM}) ",
- $_->{KFD},
- $_->{TYPE},
- defined $_->{SOURCE}
- ? ref $_->{SOURCE} ? ref $_->{SOURCE}
- : $_->{SOURCE}
- : defined $_->{FILENAME}
- ? $_->{FILENAME}
- : "",
- @{$_->{FILTERS}} > 1 ? " with filters" : (),
- ", VETOING output opt."
- if _debugging_details || _debugging_not_optimized;
- $veto_output_optimization = 1;
- }
- elsif ( $_->{TYPE} eq "close" && $_->{KFD} == 0 ) {
- $ok_to_optimize_outputs = 1;
- _debug "Win32 optimizer: (kid $kid->{NUM}) saw 0<&-, ok to optimize outputs"
- if _debugging_details;
- }
- elsif ( $_->{TYPE} eq "dup" && $_->{KFD2} == 0 ) {
- $veto_output_optimization = 1;
- _debug "Win32 optimizer: (kid $kid->{NUM}) saw 0<&$_->{KFD2}, VETOING output opt."
- if _debugging_details || _debugging_not_optimized;
- }
- elsif ( $_->{TYPE} eq "|" ) {
- $saw_pipe = 1;
- }
- }
-
- if ( ! $ok_to_optimize_outputs && ! $veto_output_optimization ) {
- _debug
- "Win32 optimizer: (kid $kid->{NUM}) child STDIN not redirected, VETOING non-SCALAR output opt."
- if _debugging_details || _debugging_not_optimized;
- $veto_output_optimization = 1;
- }
-
- if ( $ok_to_optimize_outputs && $veto_output_optimization ) {
- $ok_to_optimize_outputs = 0;
- _debug "Win32 optimizer: (kid $kid->{NUM}) non-SCALAR output optimizations VETOed"
- if _debugging_details || _debugging_not_optimized;
- }
-
- ## SOURCE/DEST ARRAY means it's a filter.
- ## TODO: think about checking to see if the final input/output of
- ## a filter chain (an ARRAY SOURCE or DEST) is a scalar...but
- ## we may be deprecating filters.
-
- for ( @{$kid->{OPS}} ) {
- if ( $_->{TYPE} eq ">" ) {
- if ( ref $_->{DEST} eq "SCALAR"
- || (
- ( @{$_->{FILTERS}} > 1
- || ref $_->{DEST} eq "CODE"
- || ref $_->{DEST} eq "ARRAY" ## Filters?
- )
- && ( $ok_to_optimize_outputs && ! $veto_output_optimization )
- )
- ) {
- $_->{RECV_THROUGH_TEMP_FILE} = 1;
- next;
- }
- _debug
- "Win32 optimizer: NOT optimizing (kid $kid->{NUM}) ",
- $_->{KFD},
- $_->{TYPE},
- defined $_->{DEST}
- ? ref $_->{DEST} ? ref $_->{DEST}
- : $_->{SOURCE}
- : defined $_->{FILENAME}
- ? $_->{FILENAME}
- : "",
- @{$_->{FILTERS}} ? " with filters" : (),
- if _debugging_details;
- }
- }
- }
-
-}
-
-=item win32_parse_cmd_line
-
- @words = win32_parse_cmd_line( q{foo bar 'baz baz' "bat bat"} ) ;
-
-returns 4 words. This parses like the bourne shell (see
-the bit about shellwords() in L<Text::ParseWords>), assuming we're
-trying to be a little cross-platform here. The only difference is
-that "\" is *not* treated as an escape except when it precedes
-punctuation, since it's used all over the place in DOS path specs.
-
-TODO: globbing? probably not (it's unDOSish).
-
-TODO: shebang emulation? Probably, but perhaps that should be part
-of Run.pm so all spawned processes get the benefit.
-
-LIMITATIONS: shellwords dies silently on malformed input like
-
- a\"
-
-=cut
-
-sub win32_parse_cmd_line {
- my $line = shift ;
- $line =~ s{(\\[\w\s])}{\\$1}g ;
- return shellwords $line ;
-}
-
-
-=item win32_spawn
-
-Spawns a child process, possibly with STDIN, STDOUT, and STDERR (file descriptors 0, 1, and 2, respectively) redirected.
-
-B<LIMITATIONS>.
-
-Cannot redirect higher file descriptors due to lack of support for this in the
-Win32 environment.
-
-This can be worked around by marking a handle as inheritable in the
-parent (or leaving it marked; this is the default in perl), obtaining it's
-Win32 handle with C<Win32API::GetOSFHandle(FH)> or
-C<Win32API::FdGetOsFHandle($fd)> and passing it to the child using the command
-line, the environment, or any other IPC mechanism (it's a plain old integer).
-The child can then use C<OsFHandleOpen()> or C<OsFHandleOpenFd()> and possibly
-C<<open FOO ">&BAR">> or C<<open FOO ">&$fd>> as need be. Ach, the pain!
-
-Remember to check the Win32 handle against INVALID_HANDLE_VALUE.
-
-=cut
-
-sub _save {
- my ( $saved, $saved_as, $fd ) = @_ ;
-
- ## We can only save aside the original fds once.
- return if exists $saved->{$fd} ;
-
- my $saved_fd = IPC::Run::_dup( $fd ) ;
- _dont_inherit $saved_fd ;
-
- $saved->{$fd} = $saved_fd ;
- $saved_as->{$saved_fd} = $fd ;
-
- _dont_inherit $saved->{$fd} ;
-}
-
-sub _dup2_gently {
- my ( $saved, $saved_as, $fd1, $fd2 ) = @_ ;
- _save $saved, $saved_as, $fd2 ;
-
- if ( exists $saved_as->{$fd2} ) {
- ## The target fd is colliding with a saved-as fd, gotta bump
- ## the saved-as fd to another fd.
- my $orig_fd = delete $saved_as->{$fd2} ;
- my $saved_fd = IPC::Run::_dup( $fd2 ) ;
- _dont_inherit $saved_fd ;
-
- $saved->{$orig_fd} = $saved_fd ;
- $saved_as->{$saved_fd} = $orig_fd ;
- }
- _debug "moving $fd1 to kid's $fd2" if _debugging_details ;
- IPC::Run::_dup2_rudely( $fd1, $fd2 ) ;
-}
-
-sub win32_spawn {
- my ( $cmd, $ops) = @_ ;
-
- ## NOTE: The debug pipe write handle is passed to pump processes as STDOUT.
- ## and is not to the "real" child process, since they would not know
- ## what to do with it...unlike Unix, we have no code executing in the
- ## child before the "real" child is exec()ed.
-
- my %saved ; ## Map of parent's orig fd -> saved fd
- my %saved_as ; ## Map of parent's saved fd -> orig fd, used to
- ## detect collisions between a KFD and the fd a
- ## parent's fd happened to be saved to.
-
- for my $op ( @$ops ) {
- _dont_inherit $op->{FD} if defined $op->{FD} ;
-
- if ( defined $op->{KFD} && $op->{KFD} > 2 ) {
- ## TODO: Detect this in harness()
- ## TODO: enable temporary redirections if ever necessary, not
- ## sure why they would be...
- ## 4>&1 1>/dev/null 1>&4 4>&-
- croak "Can't redirect fd #", $op->{KFD}, " on Win32" ;
- }
-
- ## This is very similar logic to IPC::Run::_do_kid_and_exit().
- if ( defined $op->{TFD} ) {
- unless ( $op->{TFD} == $op->{KFD} ) {
- _dup2_gently \%saved, \%saved_as, $op->{TFD}, $op->{KFD} ;
- _dont_inherit $op->{TFD} ;
- }
- }
- elsif ( $op->{TYPE} eq "dup" ) {
- _dup2_gently \%saved, \%saved_as, $op->{KFD1}, $op->{KFD2}
- unless $op->{KFD1} == $op->{KFD2} ;
- }
- elsif ( $op->{TYPE} eq "close" ) {
- _save \%saved, \%saved_as, $op->{KFD} ;
- IPC::Run::_close( $op->{KFD} ) ;
- }
- elsif ( $op->{TYPE} eq "init" ) {
- ## TODO: detect this in harness()
- croak "init subs not allowed on Win32" ;
- }
- }
-
- my $process ;
- my $cmd_line = join " ", map {
- ( my $s = $_ ) =~ s/"/"""/g;
- $s = qq{"$s"} if /["\s]/;
- $s ;
- } @$cmd ;
-
- _debug "cmd line: ", $cmd_line
- if _debugging;
-
- Win32::Process::Create(
- $process,
- $cmd->[0],
- $cmd_line,
- 1, ## Inherit handles
- NORMAL_PRIORITY_CLASS,
- ".",
- ) or croak "$!: Win32::Process::Create()" ;
-
- for my $orig_fd ( keys %saved ) {
- IPC::Run::_dup2_rudely( $saved{$orig_fd}, $orig_fd ) ;
- IPC::Run::_close( $saved{$orig_fd} ) ;
- }
-
- return ( $process->GetProcessID(), $process ) ;
-}
-
-
-=back
-
-=head1 AUTHOR
-
-Barries Slaymaker <barries@slaysys.com>. Funded by Perforce Software, Inc.
-
-=head1 COPYRIGHT
-
-Copyright 2001, Barrie Slaymaker, All Rights Reserved.
-
-You may use this under the terms of either the GPL 2.0 ir the Artistic License.
-
-=cut
-
-1 ;
diff --git a/lib/IPC/Run/Win32IO.pm b/lib/IPC/Run/Win32IO.pm
deleted file mode 100644
index 56d55cf621..0000000000
--- a/lib/IPC/Run/Win32IO.pm
+++ /dev/null
@@ -1,556 +0,0 @@
-package IPC::Run::Win32IO;
-
-=head1 NAME
-
-IPC::Run::Win32IO - helper routines for IPC::Run on Win32 platforms.
-
-=head1 SYNOPSIS
-
-use IPC::Run::Win32IO; # Exports all by default
-
-=head1 DESCRIPTION
-
-IPC::Run needs to use sockets to redirect subprocess I/O so that the select()
-loop will work on Win32. This seems to only work on WinNT and Win2K at this
-time, not sure if it will ever work on Win95 or Win98. If you have experience
-in this area, please contact me at barries@slaysys.com, thanks!.
-
-=cut
-
-=head1 DESCRIPTION
-
-A specialized IO class used on Win32.
-
-=cut
-
-use strict ;
-use Carp ;
-use IO::Handle ;
-use Socket ;
-require POSIX ;
-
-use Socket qw( IPPROTO_TCP TCP_NODELAY ) ;
-use Symbol ;
-use Text::ParseWords ;
-use Win32::Process ;
-use IPC::Run::Debug qw( :default _debugging_level );
-use IPC::Run::Win32Helper qw( _inherit _dont_inherit );
-use Fcntl qw( O_TEXT O_RDONLY );
-
-use base qw( IPC::Run::IO );
-my @cleanup_fields;
-BEGIN {
- ## These fields will be set to undef in _cleanup to close
- ## the handles.
- @cleanup_fields = (
- 'SEND_THROUGH_TEMP_FILE', ## Set by WinHelper::optimize()
- 'RECV_THROUGH_TEMP_FILE', ## Set by WinHelper::optimize()
- 'TEMP_FILE_NAME', ## The name of the temp file, needed for
- ## error reporting / debugging only.
-
- 'PARENT_HANDLE', ## The handle of the socket for the parent
- 'PUMP_SOCKET_HANDLE', ## The socket handle for the pump
- 'PUMP_PIPE_HANDLE', ## The anon pipe handle for the pump
- 'CHILD_HANDLE', ## The anon pipe handle for the child
-
- 'TEMP_FILE_HANDLE', ## The Win32 filehandle for the temp file
- );
-}
-
-## REMOVE OSFHandleOpen
-use Win32API::File qw(
- GetOsFHandle
- OsFHandleOpenFd
- OsFHandleOpen
- FdGetOsFHandle
- SetHandleInformation
- SetFilePointer
- HANDLE_FLAG_INHERIT
- INVALID_HANDLE_VALUE
-
- createFile
- WriteFile
- ReadFile
- CloseHandle
-
- FILE_ATTRIBUTE_TEMPORARY
- FILE_FLAG_DELETE_ON_CLOSE
- FILE_FLAG_WRITE_THROUGH
-
- FILE_BEGIN
-) ;
-
-# FILE_ATTRIBUTE_HIDDEN
-# FILE_ATTRIBUTE_SYSTEM
-
-
-BEGIN {
- ## Force AUTOLOADED constants to be, well, constant by getting them
- ## to AUTOLOAD before compilation continues. Sigh.
- () = (
- SOL_SOCKET,
- SO_REUSEADDR,
- IPPROTO_TCP,
- TCP_NODELAY,
- HANDLE_FLAG_INHERIT,
- INVALID_HANDLE_VALUE,
- );
-}
-
-
-use constant temp_file_flags => (
- FILE_ATTRIBUTE_TEMPORARY() |
- FILE_FLAG_DELETE_ON_CLOSE() |
- FILE_FLAG_WRITE_THROUGH()
-);
-
-# FILE_ATTRIBUTE_HIDDEN() |
-# FILE_ATTRIBUTE_SYSTEM() |
-my $tmp_file_counter;
-my $tmp_dir;
-
-sub _cleanup {
- my IPC::Run::Win32IO $self = shift;
- my ( $harness ) = @_;
-
- $self->_recv_through_temp_file( $harness )
- if $self->{RECV_THROUGH_TEMP_FILE};
-
- CloseHandle( $self->{TEMP_FILE_HANDLE} )
- if defined $self->{TEMP_FILE_HANDLE};
-
- $self->{$_} = undef for @cleanup_fields;
-}
-
-
-sub _create_temp_file {
- my IPC::Run::Win32IO $self = shift;
-
- ## Create a hidden temp file that Win32 will delete when we close
- ## it.
- unless ( defined $tmp_dir ) {
- $tmp_dir = File::Spec->catdir(
- File::Spec->tmpdir, "IPC-Run.tmp"
- );
-
- ## Trust in the user's umask.
- ## This could possibly be a security hole, perhaps
- ## we should offer an option. Hmmmm, really, people coding
- ## security conscious apps should audit this code and
- ## tell me how to make it better. Nice cop-out :).
- unless ( -d $tmp_dir ) {
- mkdir $tmp_dir or croak "$!: $tmp_dir";
- }
- }
-
- $self->{TEMP_FILE_NAME} = File::Spec->catfile(
- ## File name is designed for easy sorting and not conflicting
- ## with other processes. This should allow us to use "t"runcate
- ## access in CreateFile in case something left some droppings
- ## around (which should never happen because we specify
- ## FLAG_DELETE_ON_CLOSE.
- ## heh, belt and suspenders are better than bug reports; God forbid
- ## that NT should ever crash before a temp file gets deleted!
- $tmp_dir, sprintf "Win32io-%06d-%08d", $$, $tmp_file_counter++
- );
-
- $self->{TEMP_FILE_HANDLE} = createFile(
- $self->{TEMP_FILE_NAME},
- "trw", ## new, truncate, read, write
- {
- Flags => temp_file_flags,
- },
- ) or croak "Can't create temporary file, $self->{TEMP_FILE_NAME}: $^E";
-
- $self->{TFD} = OsFHandleOpenFd $self->{TEMP_FILE_HANDLE}, 0;
- $self->{FD} = undef;
-
- _debug
- "Win32 Optimizer: temp file (",
- $self->{KFD},
- $self->{TYPE},
- $self->{TFD},
- ", fh ",
- $self->{TEMP_FILE_HANDLE},
- "): ",
- $self->{TEMP_FILE_NAME}
- if _debugging_details;
-}
-
-
-sub _reset_temp_file_pointer {
- my $self = shift;
- SetFilePointer( $self->{TEMP_FILE_HANDLE}, 0, 0, FILE_BEGIN )
- or confess "$^E seeking on (fd $self->{TFD}) $self->{TEMP_FILE_NAME} for kid's fd $self->{KFD}";
-}
-
-
-sub _send_through_temp_file {
- my IPC::Run::Win32IO $self = shift;
-
- _debug
- "Win32 optimizer: optimizing "
- . " $self->{KFD} $self->{TYPE} temp file instead of ",
- ref $self->{SOURCE} || $self->{SOURCE}
- if _debugging_details;
-
- $self->_create_temp_file;
-
- if ( defined ${$self->{SOURCE}} ) {
- my $bytes_written = 0;
- my $data_ref;
- if ( $self->binmode ) {
- $data_ref = $self->{SOURCE};
- }
- else {
- my $data = ${$self->{SOURCE}}; # Ugh, a copy.
- $data =~ s/(?<!\r)\n/\r\n/g;
- $data_ref = \$data;
- }
-
- WriteFile(
- $self->{TEMP_FILE_HANDLE},
- $$data_ref,
- 0, ## Write entire buffer
- $bytes_written,
- [], ## Not overlapped.
- ) or croak
- "$^E writing $self->{TEMP_FILE_NAME} for kid to read on fd $self->{KFD}";
- _debug
- "Win32 optimizer: wrote $bytes_written to temp file $self->{TEMP_FILE_NAME}"
- if _debugging_data;
-
- $self->_reset_temp_file_pointer;
-
- }
-
-
- _debug "Win32 optimizer: kid to read $self->{KFD} from temp file on $self->{TFD}"
- if _debugging_details;
-}
-
-
-sub _init_recv_through_temp_file {
- my IPC::Run::Win32IO $self = shift;
-
- $self->_create_temp_file;
-}
-
-
-## TODO: USe the Win32 API in the select loop to see if the file has grown
-## and read it incrementally if it has.
-sub _recv_through_temp_file {
- my IPC::Run::Win32IO $self = shift;
-
- ## This next line kicks in if the run() never got to initting things
- ## and needs to clean up.
- return undef unless defined $self->{TEMP_FILE_HANDLE};
-
- push @{$self->{FILTERS}}, sub {
- my ( undef, $out_ref ) = @_;
-
- return undef unless defined $self->{TEMP_FILE_HANDLE};
-
- my $r;
- my $s;
- ReadFile(
- $self->{TEMP_FILE_HANDLE},
- $s,
- 999_999, ## Hmmm, should read the size.
- $r,
- []
- ) or croak "$^E reading from $self->{TEMP_FILE_NAME}";
-
- _debug "ReadFile( $self->{TFD} ) = $r chars '$s'" if _debugging_data ;
-
- return undef unless $r;
-
- $s =~ s/\r\n/\n/g unless $self->binmode;
-
- my $pos = pos $$out_ref;
- $$out_ref .= $s;
- pos( $out_ref ) = $pos;
- return 1;
- };
-
- my ( $harness ) = @_;
-
- $self->_reset_temp_file_pointer;
-
- 1 while $self->_do_filters( $harness );
-
- pop @{$self->{FILTERS}};
-
- IPC::Run::_close( $self->{TFD} );
-}
-
-
-sub poll {
- my IPC::Run::Win32IO $self = shift;
-
- return if $self->{SEND_THROUGH_TEMP_FILE} || $self->{RECV_THROUGH_TEMP_FILE};
-
- return $self->SUPER::poll( @_ );
-}
-
-
-## When threaded Perls get good enough, we should use threads here.
-## The problem with threaded perls is that they dup() all sorts of
-## filehandles and fds and don't allow sufficient control over
-## closing off the ones we don't want.
-
-sub _spawn_pumper {
- my ( $stdin, $stdout, $debug_fd, $binmode, $child_label, @opts ) = @_ ;
- my ( $stdin_fd, $stdout_fd ) = ( fileno $stdin, fileno $stdout ) ;
-
- _debug "pumper stdin = ", $stdin_fd if _debugging_details;
- _debug "pumper stdout = ", $stdout_fd if _debugging_details;
- _inherit $stdin_fd, $stdout_fd, $debug_fd ;
- my @I_options = map qq{"-I$_"}, @INC;
-
- my $cmd_line = join( " ",
- qq{"$^X"},
- @I_options,
- qw(-MIPC::Run::Win32Pump -e 1 ),
-## I'm using this clunky way of passing filehandles to the child process
-## in order to avoid some kind of premature closure of filehandles
-## problem I was having with VCP's test suite when passing them
-## via CreateProcess. All of the ## REMOVE code is stuff I'd like
-## to be rid of and the ## ADD code is what I'd like to use.
- FdGetOsFHandle( $stdin_fd ), ## REMOVE
- FdGetOsFHandle( $stdout_fd ), ## REMOVE
- FdGetOsFHandle( $debug_fd ), ## REMOVE
- $binmode ? 1 : 0,
- $$, $^T, _debugging_level, qq{"$child_label"},
- @opts
- ) ;
-
-# open SAVEIN, "<&STDIN" or croak "$! saving STDIN" ; #### ADD
-# open SAVEOUT, ">&STDOUT" or croak "$! saving STDOUT" ; #### ADD
-# open SAVEERR, ">&STDERR" or croak "$! saving STDERR" ; #### ADD
-# _dont_inherit \*SAVEIN ; #### ADD
-# _dont_inherit \*SAVEOUT ; #### ADD
-# _dont_inherit \*SAVEERR ; #### ADD
-# open STDIN, "<&$stdin_fd" or croak "$! dup2()ing $stdin_fd (pumper's STDIN)" ; #### ADD
-# open STDOUT, ">&$stdout_fd" or croak "$! dup2()ing $stdout_fd (pumper's STDOUT)" ; #### ADD
-# open STDERR, ">&$debug_fd" or croak "$! dup2()ing $debug_fd (pumper's STDERR/debug_fd)" ; #### ADD
-
- _debug "pump cmd line: ", $cmd_line if _debugging_details;
-
- my $process ;
- Win32::Process::Create(
- $process,
- $^X,
- $cmd_line,
- 1, ## Inherit handles
- NORMAL_PRIORITY_CLASS,
- ".",
- ) or croak "$!: Win32::Process::Create()" ;
-
-# open STDIN, "<&SAVEIN" or croak "$! restoring STDIN" ; #### ADD
-# open STDOUT, ">&SAVEOUT" or croak "$! restoring STDOUT" ; #### ADD
-# open STDERR, ">&SAVEERR" or croak "$! restoring STDERR" ; #### ADD
-# close SAVEIN or croak "$! closing SAVEIN" ; #### ADD
-# close SAVEOUT or croak "$! closing SAVEOUT" ; #### ADD
-# close SAVEERR or croak "$! closing SAVEERR" ; #### ADD
-
- close $stdin or croak "$! closing pumper's stdin in parent" ;
- close $stdout or croak "$! closing pumper's stdout in parent" ;
- # Don't close $debug_fd, we need it, as do other pumpers.
-
- # Pause a moment to allow the child to get up and running and emit
- # debug messages. This does not always work.
- # select undef, undef, undef, 1 if _debugging_details ;
-
- _debug "_spawn_pumper pid = ", $process->GetProcessID
- if _debugging_data;
-}
-
-
-my $next_port = 2048 ;
-my $loopback = inet_aton "127.0.0.1" ;
-my $tcp_proto = getprotobyname('tcp');
-croak "$!: getprotobyname('tcp')" unless defined $tcp_proto ;
-
-sub _socket {
- my ( $server ) = @_ ;
- $server ||= gensym ;
- my $client = gensym ;
-
- my $listener = gensym ;
- socket $listener, PF_INET, SOCK_STREAM, $tcp_proto
- or croak "$!: socket()";
- setsockopt $listener, SOL_SOCKET, SO_REUSEADDR, pack("l", 0)
- or croak "$!: setsockopt()";
-
- my $port ;
- my @errors ;
-PORT_FINDER_LOOP:
- {
- $port = $next_port ;
- $next_port = 2048 if ++$next_port > 65_535 ;
- unless ( bind $listener, sockaddr_in( $port, INADDR_ANY ) ) {
- push @errors, "$! on port $port" ;
- croak join "\n", @errors if @errors > 10 ;
- goto PORT_FINDER_LOOP;
- }
- }
-
- _debug "win32 port = $port" if _debugging_details;
-
- listen $listener, my $queue_size = 1
- or croak "$!: listen()" ;
-
- {
- socket $client, PF_INET, SOCK_STREAM, $tcp_proto
- or croak "$!: socket()";
-
- my $paddr = sockaddr_in($port, $loopback );
-
- connect $client, $paddr
- or croak "$!: connect()" ;
-
- croak "$!: accept" unless defined $paddr ;
-
- ## The windows "default" is SO_DONTLINGER, which should make
- ## sure all socket data goes through. I have my doubts based
- ## on experimentation, but nothing prompts me to set SO_LINGER
- ## at this time...
- setsockopt $client, IPPROTO_TCP, TCP_NODELAY, pack("l", 0)
- or croak "$!: setsockopt()";
- }
-
- {
- _debug "accept()ing on port $port" if _debugging_details;
- my $paddr = accept( $server, $listener ) ;
- croak "$!: accept()" unless defined $paddr ;
- }
-
- _debug
- "win32 _socket = ( ", fileno $server, ", ", fileno $client, " ) on port $port"
- if _debugging_details;
- return ( $server, $client ) ;
-}
-
-
-sub _open_socket_pipe {
- my IPC::Run::Win32IO $self = shift;
- my ( $debug_fd, $parent_handle ) = @_ ;
-
- my $is_send_to_child = $self->dir eq "<";
-
- $self->{CHILD_HANDLE} = gensym;
- $self->{PUMP_PIPE_HANDLE} = gensym;
-
- (
- $self->{PARENT_HANDLE},
- $self->{PUMP_SOCKET_HANDLE}
- ) = _socket $parent_handle ;
-
- ## These binmodes seem to have no effect on Win2K, but just to be safe
- ## I do them.
- binmode $self->{PARENT_HANDLE} or die $!;
- binmode $self->{PUMP_SOCKET_HANDLE} or die $!;
-
-_debug "PUMP_SOCKET_HANDLE = ", fileno $self->{PUMP_SOCKET_HANDLE}
- if _debugging_details;
-##my $buf ;
-##$buf = "write on child end of " . fileno( $self->{WRITE_HANDLE} ) . "\n\n\n\n\n" ;
-##POSIX::write(fileno $self->{WRITE_HANDLE}, $buf, length $buf) or warn "$! in syswrite" ;
-##$buf = "write on parent end of " . fileno( $self->{CHILD_HANDLE} ) . "\r\n" ;
-##POSIX::write(fileno $self->{CHILD_HANDLE},$buf, length $buf) or warn "$! in syswrite" ;
-## $self->{CHILD_HANDLE}->autoflush( 1 ) ;
-## $self->{WRITE_HANDLE}->autoflush( 1 ) ;
-
- ## Now fork off a data pump and arrange to return the correct fds.
- if ( $is_send_to_child ) {
- pipe $self->{CHILD_HANDLE}, $self->{PUMP_PIPE_HANDLE}
- or croak "$! opening child pipe" ;
-_debug "CHILD_HANDLE = ", fileno $self->{CHILD_HANDLE}
- if _debugging_details;
-_debug "PUMP_PIPE_HANDLE = ", fileno $self->{PUMP_PIPE_HANDLE}
- if _debugging_details;
- }
- else {
- pipe $self->{PUMP_PIPE_HANDLE}, $self->{CHILD_HANDLE}
- or croak "$! opening child pipe" ;
-_debug "CHILD_HANDLE = ", fileno $self->{CHILD_HANDLE}
- if _debugging_details;
-_debug "PUMP_PIPE_HANDLE = ", fileno $self->{PUMP_PIPE_HANDLE}
- if _debugging_details;
- }
-
- ## These binmodes seem to have no effect on Win2K, but just to be safe
- ## I do them.
- binmode $self->{CHILD_HANDLE};
- binmode $self->{PUMP_PIPE_HANDLE};
-
- ## No child should ever see this.
- _dont_inherit $self->{PARENT_HANDLE} ;
-
- ## We clear the inherit flag so these file descriptors are not inherited.
- ## It'll be dup()ed on to STDIN/STDOUT/STDERR before CreateProcess is
- ## called and *that* fd will be inheritable.
- _dont_inherit $self->{PUMP_SOCKET_HANDLE} ;
- _dont_inherit $self->{PUMP_PIPE_HANDLE} ;
- _dont_inherit $self->{CHILD_HANDLE} ;
-
- ## Need to return $self so the HANDLEs don't get freed.
- ## Return $self, $parent_fd, $child_fd
- my ( $parent_fd, $child_fd ) = (
- fileno $self->{PARENT_HANDLE},
- fileno $self->{CHILD_HANDLE}
- ) ;
-
- ## Both PUMP_..._HANDLEs will be closed, no need to worry about
- ## inheritance.
- _debug "binmode on" if _debugging_data && $self->binmode;
- _spawn_pumper(
- $is_send_to_child
- ? ( $self->{PUMP_SOCKET_HANDLE}, $self->{PUMP_PIPE_HANDLE} )
- : ( $self->{PUMP_PIPE_HANDLE}, $self->{PUMP_SOCKET_HANDLE} ),
- $debug_fd,
- $self->binmode,
- $child_fd . $self->dir . "pump" . $self->dir . $parent_fd,
- ) ;
-
-{
-my $foo ;
-confess "PARENT_HANDLE no longer open"
- unless POSIX::read( $parent_fd, $foo, 0 ) ;
-}
-
- _debug "win32_fake_pipe = ( $parent_fd, $child_fd )"
- if _debugging_details;
-
- $self->{FD} = $parent_fd;
- $self->{TFD} = $child_fd;
-}
-
-sub _do_open {
- my IPC::Run::Win32IO $self = shift;
-
- if ( $self->{SEND_THROUGH_TEMP_FILE} ) {
- return $self->_send_through_temp_file( @_ );
- }
- elsif ( $self->{RECV_THROUGH_TEMP_FILE} ) {
- return $self->_init_recv_through_temp_file( @_ );
- }
- else {
- return $self->_open_socket_pipe( @_ );
- }
-}
-
-=head1 AUTHOR
-
-Barries Slaymaker <barries@slaysys.com>. Funded by Perforce Software, Inc.
-
-=head1 COPYRIGHT
-
-Copyright 2001, Barrie Slaymaker, All Rights Reserved.
-
-You may use this under the terms of either the GPL 2.0 ir the Artistic License.
-
-=cut
-
-1;
diff --git a/lib/IPC/Run/Win32Pump.pm b/lib/IPC/Run/Win32Pump.pm
deleted file mode 100644
index 22adc2e8cc..0000000000
--- a/lib/IPC/Run/Win32Pump.pm
+++ /dev/null
@@ -1,162 +0,0 @@
-package IPC::Run::Win32Pump;
-
-=head1 NAME
-
-IPC::Run::Win32Pumper - helper processes to shovel data to/from parent, child
-
-=head1 SYNOPSIS
-
-Internal use only; see IPC::Run::Win32IO and best of luck to you.
-
-=head1 DESCRIPTION
-
-See L<IPC::Run::Win32Helper|IPC::Run::Win32Helper> for details. This
-module is used in subprocesses that are spawned to shovel data to/from
-parent processes from/to their child processes. Where possible, pumps
-are optimized away.
-
-NOTE: This is not a real module: it's a script in module form, designed
-to be run like
-
- $^X -MIPC::Run::Win32Pumper -e 1 ...
-
-It parses a bunch of command line parameters from IPC::Run::Win32IO.
-
-=cut
-
-use strict ;
-
-use Win32API::File qw(
- OsFHandleOpen
-) ;
-
-
-my ( $stdin_fh, $stdout_fh, $debug_fh, $binmode, $parent_pid, $parent_start_time, $debug, $child_label );
-BEGIN {
- ( $stdin_fh, $stdout_fh, $debug_fh, $binmode, $parent_pid, $parent_start_time, $debug, $child_label ) = @ARGV ;
- ## Rather than letting IPC::Run::Debug export all-0 constants
- ## when not debugging, we do it manually in order to not even
- ## load IPC::Run::Debug.
- if ( $debug ) {
- eval "use IPC::Run::Debug qw( :default _debug_init ); 1;"
- or die $@;
- }
- else {
- eval <<STUBS_END or die $@;
- sub _debug {}
- sub _debug_init {}
- sub _debugging() { 0 }
- sub _debugging_data() { 0 }
- sub _debugging_details() { 0 }
- sub _debugging_gory_details() { 0 }
- 1;
-STUBS_END
- }
-}
-
-## For some reason these get created with binmode on. AAargh, gotta #### REMOVE
-## do it by hand below. #### REMOVE
-if ( $debug ) { #### REMOVE
-close STDERR; #### REMOVE
-OsFHandleOpen( \*STDERR, $debug_fh, "w" ) #### REMOVE
- or print "$! opening STDERR as Win32 handle $debug_fh in pumper $$" ; #### REMOVE
-} #### REMOVE
-close STDIN; #### REMOVE
-OsFHandleOpen( \*STDIN, $stdin_fh, "r" ) #### REMOVE
-or die "$! opening STDIN as Win32 handle $stdin_fh in pumper $$" ; #### REMOVE
-close STDOUT; #### REMOVE
-OsFHandleOpen( \*STDOUT, $stdout_fh, "w" ) #### REMOVE
-or die "$! opening STDOUT as Win32 handle $stdout_fh in pumper $$" ; #### REMOVE
-
-binmode STDIN;
-binmode STDOUT;
-$| = 1 ;
-select STDERR ; $| = 1 ; select STDOUT ;
-
-$child_label ||= "pump" ;
-_debug_init(
-$parent_pid,
-$parent_start_time,
-$debug,
-fileno STDERR,
-$child_label,
-) ;
-
-_debug "Entered" if _debugging_details ;
-
-# No need to close all fds; win32 doesn't seem to pass any on to us.
-$| = 1 ;
-my $buf ;
-my $total_count = 0 ;
-while (1) {
-my $count = sysread STDIN, $buf, 10_000 ;
-last unless $count ;
-if ( _debugging_gory_details ) {
- my $msg = "'$buf'" ;
- substr( $msg, 100, -1 ) = '...' if length $msg > 100 ;
- $msg =~ s/\n/\\n/g ;
- $msg =~ s/\r/\\r/g ;
- $msg =~ s/\t/\\t/g ;
- $msg =~ s/([\000-\037\177-\277])/sprintf "\0x%02x", ord $1/eg ;
- _debug sprintf( "%5d chars revc: ", $count ), $msg ;
-}
-$total_count += $count ;
-$buf =~ s/\r//g unless $binmode;
-if ( _debugging_gory_details ) {
- my $msg = "'$buf'" ;
- substr( $msg, 100, -1 ) = '...' if length $msg > 100 ;
- $msg =~ s/\n/\\n/g ;
- $msg =~ s/\r/\\r/g ;
- $msg =~ s/\t/\\t/g ;
- $msg =~ s/([\000-\037\177-\277])/sprintf "\0x%02x", ord $1/eg ;
- _debug sprintf( "%5d chars sent: ", $count ), $msg ;
-}
-print $buf ;
-}
-
-_debug "Exiting, transferred $total_count chars" if _debugging_details ;
-
-## Perform a graceful socket shutdown. Windows defaults to SO_DONTLINGER,
-## which should cause a "graceful shutdown in the background" on sockets.
-## but that's only true if the process closes the socket manually, it
-## seems; if the process exits and lets the OS clean up, the OS is not
-## so kind. STDOUT is not always a socket, of course, but it won't hurt
-## to close a pipe and may even help. With a closed source OS, who
-## can tell?
-##
-## In any case, this close() is one of the main reasons we have helper
-## processes; if the OS closed socket fds gracefully when an app exits,
-## we'd just redirect the client directly to what is now the pump end
-## of the socket. As it is, however, we need to let the client play with
-## pipes, which don't have the abort-on-app-exit behavior, and then
-## adapt to the sockets in the helper processes to allow the parent to
-## select.
-##
-## Possible alternatives / improvements:
-##
-## 1) use helper threads instead of processes. I don't trust perl's threads
-## as of 5.005 or 5.6 enough (which may be myopic of me).
-##
-## 2) figure out if/how to get at WaitForMultipleObjects() with pipe
-## handles. May be able to take the Win32 handle and pass it to
-## Win32::Event::wait_any, dunno.
-##
-## 3) Use Inline::C or a hand-tooled XS module to do helper threads.
-## This would be faster than #1, but would require a ppm distro.
-##
-close STDOUT ;
-close STDERR ;
-
-=head1 AUTHOR
-
-Barries Slaymaker <barries@slaysys.com>. Funded by Perforce Software, Inc.
-
-=head1 COPYRIGHT
-
-Copyright 2001, Barrie Slaymaker, All Rights Reserved.
-
-You may use this under the terms of either the GPL 2.0 ir the Artistic License.
-
-=cut
-
-1 ;
diff --git a/lib/IPC/Run/t/adopt.t b/lib/IPC/Run/t/adopt.t
deleted file mode 100644
index 7458758581..0000000000
--- a/lib/IPC/Run/t/adopt.t
+++ /dev/null
@@ -1,120 +0,0 @@
-#!/usr/bin/perl -w
-
-=head1 NAME
-
-adopt.t - Test suite for IPC::Run::adopt
-
-=cut
-
-BEGIN {
- if( $ENV{PERL_CORE} ) {
- use Cwd;
- $^X = Cwd::abs_path($^X);
- $^X = qq("$^X") if $^X =~ /\s/;
- chdir '../lib/IPC/Run' if -d '../lib/IPC/Run';
- unshift @INC, 'lib', '../..';
- }
-}
-
-
-use strict ;
-
-use Test ;
-
-use IPC::Run qw( start pump finish ) ;
-use UNIVERSAL qw( isa ) ;
-
-##
-## $^X is the path to the perl binary. This is used run all the subprocesses.
-##
-my @echoer = ( $^X, '-pe', 'BEGIN { $| = 1 }' ) ;
-
-my $h ;
-my $in ;
-my $out ;
-my $fd_map ;
-
-my $h1 ;
-my $in1 ;
-my $out1 ;
-my $fd_map1 ;
-
-sub map_fds() { &IPC::Run::_map_fds }
-
-my @tests = (
-##
-## harness, pump, run
-##
-sub {
- $in = 'SHOULD BE UNCHANGED' ;
- $out = 'REPLACE ME' ;
- $? = 99 ;
- $fd_map = map_fds ;
- $h = start( \@echoer, \$in, \$out ) ;
- ok( isa( $h, 'IPC::Run' ) ) ;
-},
-sub { ok( $?, 99 ) },
-
-sub { ok( $in, 'SHOULD BE UNCHANGED' ) },
-sub { ok( $out, '' ) },
-sub { ok( $h->pumpable ) },
-
-sub {
- $in = '' ;
- $? = 0 ;
- pump_nb $h for ( 1..100 ) ;
- ok( 1 ) ;
-},
-sub { ok( $in, '' ) },
-sub { ok( $out, '' ) },
-sub { ok( $h->pumpable ) },
-
-sub {
- $in1 = 'SHOULD BE UNCHANGED' ;
- $out1 = 'REPLACE ME' ;
- $? = 99 ;
- $fd_map1 = map_fds ;
- $h1 = start( \@echoer, \$in1, \$out1 ) ;
- ok( isa( $h1, 'IPC::Run' ) ) ;
-},
-sub { ok( $?, 99 ) },
-sub { ok( $in1, 'SHOULD BE UNCHANGED' ) },
-sub { ok( $out1, '' ) },
-sub { ok( $h1->pumpable ) },
-
-
-sub {
- $in = "hello\n" ;
- $? = 0 ;
- pump $h until $out =~ /hello/ ;
- ok( 1 ) ;
-},
-sub { ok( ! $? ) },
-sub { ok( $in, '' ) },
-sub { ok( $out, "hello\n" ) },
-sub { ok( $h->pumpable ) },
-
-sub {
- $in = "world\n" ;
- $? = 0 ;
- pump $h until $out =~ /world/ ;
- ok( 1 ) ;
-},
-sub { ok( ! $? ) },
-sub { ok( $in, '' ) },
-sub { ok( $out, "hello\nworld\n" ) },
-sub { ok( $h->pumpable ) },
-
-sub { warn "hi" ;ok( $h->finish ) },
-sub { ok( ! $? ) },
-sub { ok( map_fds, $fd_map ) },
-sub { ok( $out, "hello\nworld\n" ) },
-sub { ok( ! $h->pumpable ) },
-) ;
-
-plan tests => scalar @tests ;
-
-skip "adopt not done yet", 1 for ( @tests ) ;
-exit 0 ;
-
-$_->() for ( @tests ) ;
diff --git a/lib/IPC/Run/t/binmode.t b/lib/IPC/Run/t/binmode.t
deleted file mode 100644
index cbfca0fb4d..0000000000
--- a/lib/IPC/Run/t/binmode.t
+++ /dev/null
@@ -1,102 +0,0 @@
-#!/usr/bin/perl -w
-
-=head1 NAME
-
-binary.t - Test suite for IPC::Run binary functionality
-
-=cut
-
-BEGIN {
- if( $ENV{PERL_CORE} ) {
- use Cwd;
- $^X = Cwd::abs_path($^X);
- $^X = qq("$^X") if $^X =~ /\s/;
- chdir '../lib/IPC/Run' if -d '../lib/IPC/Run';
- unshift @INC, 'lib', '../..';
- }
-}
-
-## Handy to have when our output is intermingled with debugging output sent
-## to the debugging fd.
-$| = 1 ;
-select STDERR ; $| = 1 ; select STDOUT ;
-
-use strict ;
-
-use Test ;
-
-use IPC::Run qw( harness run binary ) ;
-
-sub Win32_MODE() ;
-*Win32_MODE = \&IPC::Run::Win32_MODE ;
-
-my $crlf_text = "Hello World\r\n" ;
-
-my $text = $crlf_text ;
-$text =~ s/\r//g if Win32_MODE ;
-
-my $nl_text = $crlf_text ;
-$nl_text =~ s/\r//g ;
-
-my @perl = ( $^X ) ;
-
-my $emitter_script = q{ binmode STDOUT ; print "Hello World\r\n" } ;
-my @emitter = ( @perl, '-e', $emitter_script ) ;
-
-my $reporter_script =
- q{ binmode STDIN ; $_ = join "", <>; s/([\000-\037])/sprintf "\\\\0x%02x", ord $1/ge; print } ;
-my @reporter = ( @perl, '-e', $reporter_script ) ;
-
-my $in ;
-my $out ;
-my $err ;
-
-sub f($) {
- my $s = shift ;
- $s =~ s/([\000-\027])/sprintf "\\0x%02x", ord $1/ge ;
- $s
-}
-
-my @tests = (
-## Parsing tests
-sub { ok eval { harness [], '>', binary, \$out } ? 1 : $@, 1 } ,
-sub { ok eval { harness [], '>', binary, "foo" } ? 1 : $@, 1 },
-sub { ok eval { harness [], '<', binary, \$in } ? 1 : $@, 1 },
-sub { ok eval { harness [], '<', binary, "foo" } ? 1 : $@, 1 },
-
-## Testing from-kid now so we can use it to test stdin later
-sub { ok run \@emitter, ">", \$out },
-sub { ok f $out, f $text, "no binary" },
-
-sub { ok run \@emitter, ">", binary, \$out },
-sub { ok f $out, f $crlf_text, "out binary" },
-
-sub { ok run \@emitter, ">", binary( 0 ), \$out },
-sub { ok f $out, f $text, "out binary 0" },
-
-sub { ok run \@emitter, ">", binary( 1 ), \$out },
-sub { ok f $out, f $crlf_text, "out binary 1" },
-
-## Test to-kid
-sub { ok run \@reporter, "<", \$nl_text, ">", \$out },
-sub { ok $out, "Hello World" . ( Win32_MODE ? "\\0x0d" : "" ) . "\\0x0a", "reporter < \\n" },
-
-sub { ok run \@reporter, "<", binary, \$nl_text, ">", \$out },
-sub { ok $out, "Hello World\\0x0a", "reporter < binary \\n" },
-
-sub { ok run \@reporter, "<", binary, \$crlf_text, ">", \$out },
-sub { ok $out, "Hello World\\0x0d\\0x0a", "reporter < binary \\r\\n" },
-
-sub { ok run \@reporter, "<", binary( 0 ), \$nl_text, ">", \$out },
-sub { ok $out, "Hello World" . ( Win32_MODE ? "\\0x0d" : "" ) . "\\0x0a", "reporter < binary(0) \\n" },
-
-sub { ok run \@reporter, "<", binary( 1 ), \$nl_text, ">", \$out },
-sub { ok $out, "Hello World\\0x0a", "reporter < binary(1) \\n" },
-
-sub { ok run \@reporter, "<", binary( 1 ), \$crlf_text, ">", \$out },
-sub { ok $out, "Hello World\\0x0d\\0x0a", "reporter < binary(1) \\r\\n" },
-) ;
-
-plan tests => scalar @tests ;
-
-$_->() for ( @tests ) ;
diff --git a/lib/IPC/Run/t/bogus.t b/lib/IPC/Run/t/bogus.t
deleted file mode 100644
index 176315b35a..0000000000
--- a/lib/IPC/Run/t/bogus.t
+++ /dev/null
@@ -1,69 +0,0 @@
-#!/usr/bin/perl -w
-
-=head1 NAME
-
-bogus.t - test bogus file cases.
-
-=cut
-
-BEGIN {
- if( $ENV{PERL_CORE} ) {
- use Cwd;
- $^X = Cwd::abs_path($^X);
- $^X = qq("$^X") if $^X =~ /\s/;
- chdir '../lib/IPC/Run' if -d '../lib/IPC/Run';
- unshift @INC, 'lib', '../..';
- }
-}
-
-
-use strict ;
-
-use Test ;
-
-use IPC::Run qw( start ) ;
-use UNIVERSAL qw( isa ) ;
-
-my $r ;
-
-sub Win32_MODE() ;
-*Win32_MODE = \&IPC::Run::Win32_MODE ;
-
-## Win32 does not support a lot of things that Unix does. These
-## skip_unless subs help that.
-##
-## TODO: There are also a few things that Win32 supports (passing Win32 OS
-## handles) that we should test for, conversely.
-sub skip_unless_exec(&) {
- if ( Win32_MODE ) {
- return sub {
- skip "Can't really exec() $^O", 0 ;
- } ;
- }
- shift ;
-}
-
-my @tests = (
-sub {
- ## Older Test.pm's don't grok qr// in $expected.
- my $expected = 'file not found' ;
- eval { start ["./bogus_really_bogus"] } ;
- my $got = $@ =~ $expected ? $expected : $@ || "" ;
- ok $got, $expected, "starting ./bogus_really_bogus" ;
-},
-
-skip_unless_exec {
- ## Older Test.pm's don't grok qr// in $expected.
- my $expected = 'exec failed' ;
- my $h = eval {
- start [$^X, "-e", 1], _simulate_exec_failure => 1 ;
- } ;
- my $got = $@ =~ $expected ? $expected : $@ || "" ;
- ok $got, $expected, "starting $^X with simulated_exec_failure => 1" ;
-},
-
-) ;
-
-plan tests => scalar @tests ;
-
-$_->() for ( @tests ) ;
diff --git a/lib/IPC/Run/t/filter.t b/lib/IPC/Run/t/filter.t
deleted file mode 100644
index 90126df611..0000000000
--- a/lib/IPC/Run/t/filter.t
+++ /dev/null
@@ -1,120 +0,0 @@
-#!/usr/bin/perl -w
-
-=head1 NAME
-
-filter.t - Test suite for IPC::Run filter scaffolding
-
-=cut
-
-BEGIN {
- if( $ENV{PERL_CORE} ) {
- use Cwd;
- $^X = Cwd::abs_path($^X);
- $^X = qq("$^X") if $^X =~ /\s/;
- chdir '../lib/IPC/Run' if -d '../lib/IPC/Run';
- unshift @INC, 'lib', '../..';
- }
-}
-
-use strict ;
-
-use Test ;
-
-use IPC::Run qw( :filters :filter_imp filter_tests ) ;
-
-sub uc_filter {
- my ( $in_ref, $out_ref ) = @_ ;
-
- return input_avail && do {
- $$out_ref .= uc( $$in_ref ) ;
- $$in_ref = '' ;
- 1 ;
- }
-}
-
-
-my $string ;
-
-sub string_source {
- my ( $in_ref, $out_ref ) = @_ ;
- return undef unless defined $string ;
- $$out_ref .= $string ;
- $string = undef ;
- return 1 ;
-}
-
-
-my $accum ;
-
-sub accum {
- my ( $in_ref, $out_ref ) = @_ ;
- return input_avail && do {
- $accum .= $$in_ref ;
- $$in_ref = '' ;
- 1 ;
- } ;
-}
-
-
-my $op ;
-
-## "import" the things we're testing.
-*_init_filters = \&IPC::Run::_init_filters ;
-*_do_filters = \&IPC::Run::_do_filters ;
-
-
-my @tests = (
-
-filter_tests( "filter_tests", "hello world", "hello world" ),
-filter_tests( "filter_tests []", [qq(hello world)], [qq(hello world)] ),
-filter_tests( "filter_tests [] 2", [qw(hello world)], [qw(hello world)] ),
-
-filter_tests( "uc_filter", "hello world", "HELLO WORLD", \&uc_filter ),
-
-filter_tests(
- "chunking_filter by lines 1",
- "hello 1\nhello 2\nhello 3",
- ["hello 1\n", "hello 2\n", "hello 3"],
- new_chunker
-),
-
-filter_tests(
- "chunking_filter by lines 2",
- "hello 1\nhello 2\nhello 3",
- ["hello 1\n", "hello 2\n", "hello 3"],
- new_chunker
-),
-
-filter_tests(
- "chunking_filter by lines 2",
- [split( /(\s|\n)/, "hello 1\nhello 2\nhello 3" )],
- ["hello 1\n", "hello 2\n", "hello 3"],
- new_chunker
-),
-
-filter_tests(
- "chunking_filter by an odd separator",
- "hello world",
- "hello world",
- new_chunker( 'odd separator' )
-),
-
-filter_tests(
- "chunking_filter 2",
- "hello world",
- ['hello world' =~ m/(.)/g],
- new_chunker( qr/./ )
-),
-
-filter_tests(
- "appending_filter",
- [qw( 1 2 3 )],
- [qw( 1a 2a 3a )],
- new_appender("a")
-),
-) ;
-
-plan tests => scalar @tests ;
-
-$_->() for ( @tests ) ;
-
diff --git a/lib/IPC/Run/t/harness.t b/lib/IPC/Run/t/harness.t
deleted file mode 100644
index e42ee18ba6..0000000000
--- a/lib/IPC/Run/t/harness.t
+++ /dev/null
@@ -1,149 +0,0 @@
-#!/usr/bin/perl -w
-
-=head1 NAME
-
-harness.t - Test suite for IPC::Run::harness
-
-=cut
-
-BEGIN {
- if( $ENV{PERL_CORE} ) {
- use Cwd;
- $^X = Cwd::abs_path($^X);
- $^X = qq("$^X") if $^X =~ /\s/;
- chdir '../lib/IPC/Run' if -d '../lib/IPC/Run';
- unshift @INC, 'lib', '../..';
- }
-}
-
-use strict ;
-
-use Test ;
-
-use IPC::Run qw( harness ) ;
-
-my $f ;
-
-sub expand_test {
- my ( $args, $expected ) = @_ ;
-
- my $h ;
- my @out ;
- my $i = 0 ;
- return (
- sub {
- $h = IPC::Run::harness( @$args ) ;
- @out = @{$h->{KIDS}->[0]->{OPS}} ;
- ok(
- scalar( @out ),
- scalar( @$expected ),
- join( ' ', @$args )
- )
- },
- map {
- my $j = $i++ ;
- my $h = $_ ;
- map {
- my ( $key, $value ) = ( $_, $h->{$_} ) ;
- sub {
- my $got = $out[$j]->{$key} ;
- $got = @$got if ref $got eq 'ARRAY' ;
- $got = '<undef>' unless defined $got ;
- ok( $got, $value, join( ' ', @$args ) . ": $j, $key" )
- } ;
- } sort keys %$h ;
- } @$expected
- ) ;
-}
-
-
-
-my @tests = (
-
- expand_test(
- [ ['a'], qw( <b < c 0<d 0< e 1<f 1< g) ],
- [
- { TYPE => '<', SOURCE => 'b', KFD => 0, },
- { TYPE => '<', SOURCE => 'c', KFD => 0, },
- { TYPE => '<', SOURCE => 'd', KFD => 0, },
- { TYPE => '<', SOURCE => 'e', KFD => 0, },
- { TYPE => '<', SOURCE => 'f', KFD => 1, },
- { TYPE => '<', SOURCE => 'g', KFD => 1, },
- ]
- ),
-
- expand_test(
- [ ['a'], qw( >b > c 2>d 2> e >>f >> g 2>>h 2>> i) ],
- [
- { TYPE => '>', DEST => 'b', KFD => 1, TRUNC => 1, },
- { TYPE => '>', DEST => 'c', KFD => 1, TRUNC => 1, },
- { TYPE => '>', DEST => 'd', KFD => 2, TRUNC => 1, },
- { TYPE => '>', DEST => 'e', KFD => 2, TRUNC => 1, },
- { TYPE => '>', DEST => 'f', KFD => 1, TRUNC => '', },
- { TYPE => '>', DEST => 'g', KFD => 1, TRUNC => '', },
- { TYPE => '>', DEST => 'h', KFD => 2, TRUNC => '', },
- { TYPE => '>', DEST => 'i', KFD => 2, TRUNC => '', },
- ]
- ),
-
- expand_test(
- [ ['a'], qw( >&b >& c &>d &> e ) ],
- [
- { TYPE => '>', DEST => 'b', KFD => 1, TRUNC => 1, },
- { TYPE => 'dup', KFD1 => 1, KFD2 => 2 },
- { TYPE => '>', DEST => 'c', KFD => 1, TRUNC => 1, },
- { TYPE => 'dup', KFD1 => 1, KFD2 => 2 },
- { TYPE => '>', DEST => 'd', KFD => 1, TRUNC => 1, },
- { TYPE => 'dup', KFD1 => 1, KFD2 => 2 },
- { TYPE => '>', DEST => 'e', KFD => 1, TRUNC => 1, },
- { TYPE => 'dup', KFD1 => 1, KFD2 => 2 },
- ]
- ),
-
- expand_test(
- [ ['a'],
- '>&', sub{}, sub{}, \$f,
- '>', sub{}, sub{}, \$f,
- '<', sub{}, sub{}, \$f,
- ],
- [
- { TYPE => '>', DEST => \$f, KFD => 1, TRUNC => 1,
- FILTERS => 2 },
- { TYPE => 'dup', KFD1 => 1, KFD2 => 2 },
- { TYPE => '>', DEST => \$f, KFD => 1, TRUNC => 1,
- FILTERS => 2 },
- { TYPE => '<', SOURCE => \$f, KFD => 0,
- FILTERS => 3 },
- ]
- ),
-
- expand_test(
- [ ['a'], '<', \$f, '>', \$f ],
- [
- { TYPE => '<', SOURCE => \$f, KFD => 0, },
- { TYPE => '>', DEST => \$f, KFD => 1, },
- ]
- ),
-
- expand_test(
- [ ['a'], '<pipe', \$f, '>pipe', \$f ],
- [
- { TYPE => '<pipe', SOURCE => \$f, KFD => 0, },
- { TYPE => '>pipe', DEST => \$f, KFD => 1, },
- ]
- ),
-
- expand_test(
- [ ['a'], '<pipe', \$f, '>', \$f ],
- [
- { TYPE => '<pipe', SOURCE => \$f, KFD => 0, },
- { TYPE => '>', DEST => \$f, KFD => 1, },
- ]
- ),
-
-) ;
-
-plan tests => scalar @tests ;
-
-$_->() for ( @tests ) ;
-
diff --git a/lib/IPC/Run/t/io.t b/lib/IPC/Run/t/io.t
deleted file mode 100644
index 56a5c6fbd9..0000000000
--- a/lib/IPC/Run/t/io.t
+++ /dev/null
@@ -1,133 +0,0 @@
-#!/usr/bin/perl -w
-
-=head1 NAME
-
-io.t - Test suite excercising IPC::Run::IO with IPC::Run::run.
-
-=cut
-
-BEGIN {
- if( $ENV{PERL_CORE} ) {
- use Cwd;
- $^X = Cwd::abs_path($^X);
- $^X = qq("$^X") if $^X =~ /\s/;
- chdir '../lib/IPC/Run' if -d '../lib/IPC/Run';
- unshift @INC, 'lib', '../..';
- }
-}
-
-use strict ;
-
-use Test ;
-
-use IPC::Run qw( :filters run io ) ;
-use IPC::Run::Debug qw( _map_fds );
-use UNIVERSAL qw( isa ) ;
-
-sub skip_unless_select (&) {
- if ( IPC::Run::Win32_MODE() ) {
- return sub {
- skip "$^O does not allow select() on non-sockets", 0 ;
- } ;
- }
- shift ;
-}
-
-my $text = "Hello World\n" ;
-
-my $emitter_script = qq{print '$text' ; print STDERR uc( '$text' )} ;
-##
-## $^X is the path to the perl binary. This is used run all the subprocesses.
-##
-my @perl = ( $^X ) ;
-my @emitter = ( @perl, '-e', $emitter_script ) ;
-
-my $recv ;
-my $send ;
-
-my $in_file = 'io.t.in' ;
-my $out_file = 'io.t.out' ;
-my $err_file = 'io.t.err' ;
-
-my $io ;
-my $r ;
-
-my $fd_map ;
-
-## TODO: Test filters, etc.
-
-sub slurp($) {
- my ( $f ) = @_ ;
- open( S, "<$f" ) or return "$! '$f'" ;
- my $r = join( '', <S> ) ;
- close S or warn "$! closing '$f'";
- return $r ;
-}
-
-
-sub spit($$) {
- my ( $f, $s ) = @_ ;
- open( S, ">$f" ) or die "$! '$f'" ;
- print S $s or die "$! '$f'" ;
- close S or die "$! '$f'" ;
-}
-
-sub wipe($) {
- my ( $f ) = @_ ;
- unlink $f or warn "$! unlinking '$f'" if -f $f ;
-}
-
-
-
-my @tests = (
-##
-## Parsing
-##
-sub {
- $io = io( 'foo', '<', \$send ) ;
- ok isa $io, 'IPC::Run::IO' ;
-},
-
-sub { ok( io( 'foo', '<', \$send )->mode, 'w' ) },
-sub { ok( io( 'foo', '<<', \$send )->mode, 'wa' ) },
-sub { ok( io( 'foo', '>', \$recv )->mode, 'r' ) },
-sub { ok( io( 'foo', '>>', \$recv )->mode, 'ra' ) },
-
-##
-## Input from a file
-##
-skip_unless_select {
- spit $in_file, $text ;
- $recv = 'REPLACE ME' ;
- $fd_map = _map_fds ;
- $r = run io( $in_file, '>', \$recv ) ;
- wipe $in_file ;
- ok( $r ) ;
-},
-skip_unless_select { ok( ! $? ) },
-skip_unless_select { ok( _map_fds, $fd_map ) },
-
-skip_unless_select { ok( $recv, $text ) },
-
-##
-## Output to a file
-##
-skip_unless_select {
- wipe $out_file ;
- $send = $text ;
- $fd_map = _map_fds ;
- $r = run io( $out_file, '<', \$send ) ;
- $recv = slurp $out_file ;
- wipe $out_file ;
- ok( $r ) ;
-},
-skip_unless_select { ok( ! $? ) },
-skip_unless_select { ok( _map_fds, $fd_map ) },
-
-skip_unless_select { ok( $send, $text ) },
-skip_unless_select { ok( $recv, $text ) },
-) ;
-
-plan tests => scalar @tests ;
-
-$_->() for ( @tests ) ;
diff --git a/lib/IPC/Run/t/kill_kill.t b/lib/IPC/Run/t/kill_kill.t
deleted file mode 100644
index cec0f6a675..0000000000
--- a/lib/IPC/Run/t/kill_kill.t
+++ /dev/null
@@ -1,59 +0,0 @@
-#!/usr/bin/perl -w
-
-=head1 NAME
-
-kill_kill.t - Test suite IPC::Run->kill_kill
-
-=cut
-
-BEGIN {
- if( $ENV{PERL_CORE} ) {
- use Cwd;
- $^X = Cwd::abs_path($^X);
- $^X = qq("$^X") if $^X =~ /\s/;
- chdir '../lib/IPC/Run' if -d '../lib/IPC/Run';
- unshift @INC, 'lib', '../..';
- }
-}
-
-
-use strict ;
-
-use Test ;
-
-use IPC::Run qw( start ) ;
-
-sub skip_unless_ignore_term(&) {
- if ( IPC::Run::Win32_MODE() ) {
- return sub {
- skip "$^O does not support ignoring the TERM signal", 0 ;
- } ;
- }
- shift ;
-}
-
-my @quiter = ( $^X, '-e', 'sleep while 1' ) ;
-my @zombie00 = ( $^X, '-e', '$SIG{TERM}=sub{};$|=1;print "running\n";sleep while 1');
-
-my @tests = (
-sub {
- my $h = start \@quiter ;
- my $needed_kill = $h->kill_kill ; # grace => 2 ) ;
- ok ! $needed_kill ;
-},
-
-skip_unless_ignore_term {
- my $out ;
- my $h = start \@zombie00, \undef, \$out ;
- pump $h until $out =~ /running/ ;
- my $needed_kill = $h->kill_kill( grace => 1 ) ;
- ok $needed_kill ;
-},
-
-## not testing coredumps; some systems don't provide them. #'
-
-) ;
-
-plan tests => scalar @tests ;
-
-$_->() for ( @tests ) ;
diff --git a/lib/IPC/Run/t/parallel.t b/lib/IPC/Run/t/parallel.t
deleted file mode 100644
index d178247a84..0000000000
--- a/lib/IPC/Run/t/parallel.t
+++ /dev/null
@@ -1,110 +0,0 @@
-#!/usr/bin/perl -w
-
-=head1 NAME
-
-parallel.t - Test suite for running multiple processes in parallel.
-
-=cut
-
-BEGIN {
- if( $ENV{PERL_CORE} ) {
- use Cwd;
- $^X = Cwd::abs_path($^X);
- $^X = qq("$^X") if $^X =~ /\s/;
- chdir '../lib/IPC/Run' if -d '../lib/IPC/Run';
- unshift @INC, 'lib', '../..';
- }
-}
-
-## Handy to have when our output is intermingled with debugging output sent
-## to the debugging fd.
-$| = 1 ;
-select STDERR ; $| = 1 ; select STDOUT ;
-
-use strict ;
-
-use Test ;
-
-use IPC::Run qw( start pump finish ) ;
-use UNIVERSAL qw( isa ) ;
-
-sub Win32_MODE() ;
-*Win32_MODE = \&IPC::Run::Win32_MODE ;
-
-## Win32 does not support a lot of things that Unix does. These
-## skip_unless subs help that.
-##
-## TODO: There are also a few things that Win32 supports (passing Win32 OS
-## handles) that we should test for, conversely.
-sub skip_unless_subs(&) {
- if ( Win32_MODE ) {
- return sub {
- skip "Can't spawn subroutines on $^O", 0 ;
- } ;
- }
- shift ;
-}
-
-my $text1 = "Hello world 1\n" ;
-my $text2 = "Hello world 2\n" ;
-
-my @perl = ( $^X ) ;
-
-my @catter = ( @perl, '-pe1' ) ;
-
-sub slurp($) {
- my ( $f ) = @_ ;
- open( S, "<$f" ) or return "$! $f" ;
- my $r = join( '', <S> ) ;
- close S ;
- return $r ;
-}
-
-
-sub spit($$) {
- my ( $f, $s ) = @_ ;
- open( S, ">$f" ) or die "$! $f" ;
- print S $s or die "$! $f" ;
- close S or die "$! $f" ;
-}
-
-my ( $h1, $h2 ) ;
-my ( $out1, $out2 ) ;
-
-my @tests = (
-
-sub {
- $h1 = start \@catter, "<", \$text1, ">", \$out1 ;
- ok $h1 ;
-},
-
-sub {
- $h2 = start \@catter, "<", \$text2, ">", \$out2 ;
- ok $h2 ;
-},
-
-sub {
- pump $h1 ;
- ok 1 ;
-},
-
-sub {
- pump $h2 ;
- ok 1 ;
-},
-
-sub {
- finish $h1 ;
- ok 1 ;
-},
-
-sub {
- finish $h2 ;
- ok 1 ;
-},
-
-) ;
-
-plan tests => scalar @tests ;
-
-$_->() for ( @tests ) ;
diff --git a/lib/IPC/Run/t/pty.t b/lib/IPC/Run/t/pty.t
deleted file mode 100644
index 83ad5beca6..0000000000
--- a/lib/IPC/Run/t/pty.t
+++ /dev/null
@@ -1,275 +0,0 @@
-#!/usr/bin/perl -w
-
-=head1 NAME
-
-pty.t - Test suite for IPC::Run's pty (psuedo-terminal) support
-
-=head1 DESCRIPTION
-
-This test suite starts off with a test that seems to cause a deadlock
-on freebsd: \@cmd, '<pty<', ... '>', ..., '2>'...
-
-This seems to cause the child process entry in the process table to
-hang around after the child exits. Both output pipes are closed, but
-the PID is still valid so IPC::Run::finish() thinks it's still alive and
-the whole shebang deadlocks waiting for the child to exit.
-
-This is a very rare corner condition, so I'm not patching in a fix yet.
-One fix might be to hack IPC::Run to close the master pty when all outputs
-from the child are closed. That's a hack, not sure what to do about it.
-
-This problem needs to be reproduced in a standalone script and investigated
-further, but I have not the time.
-
-=cut
-
-BEGIN {
- if( $ENV{PERL_CORE} ) {
- use Cwd;
- $^X = Cwd::abs_path($^X);
- $^X = qq("$^X") if $^X =~ /\s/;
- chdir '../lib/IPC/Run' if -d '../lib/IPC/Run';
- unshift @INC, 'lib', '../..';
- }
-}
-
-
-use strict ;
-
-use Test ;
-
-use IPC::Run::Debug qw( _map_fds );
-use IPC::Run qw( start pump finish ) ;
-use UNIVERSAL qw( isa ) ;
-
-select STDERR ; $| = 1 ; select STDOUT ;
-
-sub pty_warn {
- warn "\nWARNING: $_[0].\nWARNING: '<pty<', '>pty>' $_[1] not work.\n\n";
-}
-
-if ( $^O !~ /Win32/ ) {
-# my $min = 0.9 ;
- for ( eval { require IO::Pty ; IO::Pty->VERSION } ) {
- s/_//g if defined ;
- if ( ! defined ) {
- pty_warn "IO::Pty not found", "will" ;
- }
- elsif ( $_ == 0.02 ) {
- pty_warn "IO::Pty v$_ has spurious warnings, try 0.9 or later", "may"
- }
- elsif ( $_ < 1.00 ) {
- pty_warn "IO::Pty 1.00 is strongly recommended", "may" ;
- }
- }
-}
-
-
-my $echoer_script = <<TOHERE ;
-\$| = 1 ;
-\$s = select STDERR ; \$| = 1 ; select \$s ;
-while (<>) {
- print STDERR uc \$_ ;
- print ;
- last if /quit/ ;
-}
-TOHERE
-
-##
-## $^X is the path to the perl binary. This is used run all the subprocesses.
-##
-my @echoer = ( $^X, '-e', $echoer_script ) ;
-
-my $in ;
-my $out ;
-my $err;
-
-my $h ;
-my $r ;
-
-my $fd_map ;
-
-my $text = "hello world\n" ;
-
-## TODO: test lots of mixtures of pty's and pipes & files. Use run().
-
-## Older Perls can't ok( a, qr// ), so I manually do that here.
-my $exp ;
-
-my $platform_skip = $^O =~ /(?:aix|freebsd|openbsd)/ ? "$^O deadlocks on this test" : "" ;
-
-my @tests = (
-##
-## stdin only
-##
-sub {
- return skip $platform_skip, 1 if $platform_skip;
- $out = 'REPLACE ME' ;
- $? = 99 ;
- $fd_map = _map_fds ;
- $h = start \@echoer, '<pty<', \$in, '>', \$out, '2>', \$err ;
-
- $in = "hello\n" ;
- $? = 0 ;
- pump $h until $out =~ /hello/ && $err =~ /HELLO/ ;
- ok( $out, "hello\n" ) ;
-},
-sub {
- return skip $platform_skip, 1 if $platform_skip;
- $exp = qr/^HELLO\n(?!\n)$/ ;
- $err =~ $exp ? ok( 1 ) : ok( $err, $exp ) ;
-},
-sub {
- return skip $platform_skip, 1 if $platform_skip;
- ok( $in, '' )
-},
-
-sub {
- return skip $platform_skip, 1 if $platform_skip;
- $in = "world\n" ;
- $? = 0 ;
- pump $h until $out =~ /world/ && $err =~ /WORLD/ ;
- ok( $out, "hello\nworld\n" ) ;
-},
-sub {
- return skip $platform_skip, 1 if $platform_skip;
- $exp = qr/^HELLO\nWORLD\n(?!\n)$/ ;
- $err =~ $exp ? ok( 1 ) : ok( $err, $exp ) ;
-},
-sub {
- return skip $platform_skip, 1 if $platform_skip;
- ok( $in, '' )
-},
-
-sub {
- return skip $platform_skip, 1 if $platform_skip;
- $in = "quit\n" ;
- ok( $h->finish ) ;
-},
-sub {
- return skip $platform_skip, 1 if $platform_skip;
- ok( ! $? )
-},
-sub {
- return skip $platform_skip, 1 if $platform_skip;
- ok( _map_fds, $fd_map )
-},
-
-##
-## stdout, stderr
-##
-sub {
- $out = 'REPLACE ME' ;
- $? = 99 ;
- $fd_map = _map_fds ;
- $h = start \@echoer, \$in, '>pty>', \$out ;
- $in = "hello\n" ;
- $? = 0 ;
- pump $h until $out =~ /hello/ ;
- ## We assume that the slave's write()s are atomic
- $exp = qr/^(?:hello\r?\n){2}(?!\n)$/i ;
- $out =~ $exp ? ok( 1 ) : ok( $out, $exp ) ;
-},
-sub { ok( $in, '' ) },
-
-sub {
- $in = "world\n" ;
- $? = 0 ;
- pump $h until $out =~ /world/ ;
- $exp = qr/^(?:hello\r?\n){2}(?:world\r?\n){2}(?!\n)$/i ;
- $out =~ $exp ? ok( 1 ) : ok( $out, $exp ) ;
-},
-sub { ok( $in, '' ) },
-
-sub {
- $in = "quit\n" ;
- ok( $h->finish ) ;
-},
-sub { ok( ! $? ) },
-sub { ok( _map_fds, $fd_map ) },
-
-##
-## stdout only
-##
-sub {
- $out = 'REPLACE ME' ;
- $? = 99 ;
- $fd_map = _map_fds ;
- $h = start \@echoer, \$in, '>pty>', \$out, '2>', \$err ;
- $in = "hello\n" ;
- $? = 0 ;
- pump $h until $out =~ /hello/ && $err =~ /HELLO/ ;
- $exp = qr/^hello\r?\n(?!\n)$/ ;
- $out =~ $exp ? ok( 1 ) : ok( $out, $exp ) ;
-},
-sub {
- $exp = qr/^HELLO\n(?!\n)$/ ;
- $err =~ $exp ? ok( 1 ) : ok( $err, $exp ) ;
-},
-sub { ok( $in, '' ) },
-
-sub {
- $in = "world\n" ;
- $? = 0 ;
- pump $h until $out =~ /world/ && $err =~ /WORLD/ ;
- $exp = qr/^hello\r?\nworld\r?\n(?!\n)$/ ;
- $out =~ $exp ? ok( 1 ) : ok( $out, $exp ) ;
-},
-sub {
- $exp = qr/^HELLO\nWORLD\n(?!\n)$/ ,
- $err =~ $exp ? ok( 1 ) : ok( $err, $exp ) ;
-},
-sub { ok( $in, '' ) },
-
-sub {
- $in = "quit\n" ;
- ok( $h->finish ) ;
-},
-sub { ok( ! $? ) },
-sub { ok( _map_fds, $fd_map ) },
-
-##
-## stdin, stdout, stderr
-##
-sub {
- $out = 'REPLACE ME' ;
- $? = 99 ;
- $fd_map = _map_fds ;
- $h = start \@echoer, '<pty<', \$in, '>pty>', \$out ;
- $in = "hello\n" ;
- $? = 0 ;
- pump $h until $out =~ /hello.*hello.*hello/is ;
- ## We assume that the slave's write()s are atomic
- $exp = qr/^(?:hello\r?\n){3}(?!\n)$/i ;
- $out =~ $exp ? ok( 1 ) : ok( $out, $exp ) ;
-},
-sub { ok( $in, '' ) },
-
-sub {
- $in = "world\n" ;
- $? = 0 ;
- pump $h until $out =~ /world.*world.*world/is ;
- $exp = qr/^(?:hello\r?\n){3}(?:world\r?\n){3}(?!\n)$/i ;
- $out =~ $exp ? ok( 1 ) : ok( $out, $exp ) ;
-},
-sub { ok( $in, '' ) },
-
-sub {
- $in = "quit\n" ;
- ok( $h->finish ) ;
-},
-sub { ok( ! $? ) },
-sub { ok( _map_fds, $fd_map ) },
-) ;
-
-plan tests => scalar @tests ;
-
-unless ( eval { require IO::Pty ; } ) {
- skip( "skip: IO::Pty not found", 0 ) for @tests ;
- exit ;
-}
-
-print "# Using IO::Tty $IO::Tty::VERSION\n";
-print "# Using IO::Pty $IO::Pty::VERSION\n";
-
-$_->() for ( @tests ) ;
diff --git a/lib/IPC/Run/t/pump.t b/lib/IPC/Run/t/pump.t
deleted file mode 100644
index 7878d786b2..0000000000
--- a/lib/IPC/Run/t/pump.t
+++ /dev/null
@@ -1,119 +0,0 @@
-#!/usr/bin/perl -w
-
-=head1 NAME
-
-pump.t - Test suite for IPC::Run::run, etc.
-
-=cut
-
-BEGIN {
- if( $ENV{PERL_CORE} ) {
- use Cwd;
- $^X = Cwd::abs_path($^X);
- $^X = qq("$^X") if $^X =~ /\s/;
- chdir '../lib/IPC/Run' if -d '../lib/IPC/Run';
- unshift @INC, 'lib', '../..';
- }
-}
-
-use strict ;
-
-use Test ;
-
-use IPC::Run::Debug qw( _map_fds );
-use IPC::Run qw( start pump finish timeout ) ;
-use UNIVERSAL qw( isa ) ;
-
-##
-## $^X is the path to the perl binary. This is used run all the subprocesses.
-##
-my @echoer = ( $^X, '-pe', 'BEGIN { $| = 1 }' ) ;
-
-my $in ;
-my $out ;
-
-my $h ;
-
-my $fd_map ;
-
-my @tests = (
-##
-## harness, pump, run
-##
-sub {
- $in = 'SHOULD BE UNCHANGED' ;
- $out = 'REPLACE ME' ;
- $? = 99 ;
- $fd_map = _map_fds ;
- $h = start( \@echoer, \$in, \$out, timeout 5 ) ;
- ok( isa( $h, 'IPC::Run' ) ) ;
-},
-sub { ok( $?, 99 ) },
-
-sub { ok( $in, 'SHOULD BE UNCHANGED' ) },
-sub { ok( $out, '' ) },
-sub { ok( $h->pumpable ) },
-
-sub {
- $in = '' ;
- $? = 0 ;
- pump_nb $h for ( 1..100 ) ;
- ok( 1 ) ;
-},
-sub { ok( $in, '' ) },
-sub { ok( $out, '' ) },
-sub { ok( $h->pumpable ) },
-
-sub {
- $in = "hello\n" ;
- $? = 0 ;
- pump $h until $out =~ /hello/ ;
- ok( 1 ) ;
-},
-sub { ok( ! $? ) },
-sub { ok( $in, '' ) },
-sub { ok( $out, "hello\n" ) },
-sub { ok( $h->pumpable ) },
-
-sub {
- $in = "world\n" ;
- $? = 0 ;
- pump $h until $out =~ /world/ ;
- ok( 1 ) ;
-},
-sub { ok( ! $? ) },
-sub { ok( $in, '' ) },
-sub { ok( $out, "hello\nworld\n" ) },
-sub { ok( $h->pumpable ) },
-
-## Test \G pos() restoral
-sub {
- $in = "hello\n" ;
- $out = "" ;
- $? = 0 ;
- pump $h until $out =~ /hello\n/g ;
- ok( 1 ) ;
-},
-
-sub {
- ok pos( $out ), 6, "pos\$out" ;
-},
-
-sub {
- $in = "world\n" ;
- $? = 0 ;
- pump $h until $out =~ /\Gworld/gc ;
- ok( 1 ) ;
-},
-
-
-sub { ok( $h->finish ) },
-sub { ok( ! $? ) },
-sub { ok( _map_fds, $fd_map ) },
-sub { ok( $out, "hello\nworld\n" ) },
-sub { ok( ! $h->pumpable ) },
-) ;
-
-plan tests => scalar @tests ;
-
-$_->() for ( @tests ) ;
diff --git a/lib/IPC/Run/t/run.t b/lib/IPC/Run/t/run.t
deleted file mode 100644
index 4f0206f487..0000000000
--- a/lib/IPC/Run/t/run.t
+++ /dev/null
@@ -1,1080 +0,0 @@
-#!/usr/bin/perl -w
-
-=head1 NAME
-
-run.t - Test suite for IPC::Run::run, etc.
-
-=cut
-
-BEGIN {
- if( $ENV{PERL_CORE} ) {
- use Cwd;
- $^X = Cwd::abs_path($^X);
- $^X = qq("$^X") if $^X =~ /\s/;
- chdir '../lib/IPC/Run' if -d '../lib/IPC/Run';
- unshift @INC, 'lib', '../..';
- }
-}
-
-
-## Handy to have when our output is intermingled with debugging output sent
-## to the debugging fd.
-$| = 1 ;
-select STDERR ; $| = 1 ; select STDOUT ;
-
-use strict ;
-
-use Test ;
-
-use IPC::Run::Debug qw( _map_fds );
-use IPC::Run qw( :filters :filter_imp start filter_tests Win32_MODE ) ;
-
-sub run { IPC::Run::run( ref $_[0] ? ( noinherit => 1 ) : (), @_ ) }
-
-use UNIVERSAL qw( isa ) ;
-
-## Test at least some of the win32 PATHEXT logic
-my $perl = $^X;
-$perl =~ s/\.\w+\z// if Win32_MODE;
-
-## Win32 does not support a lot of things that Unix does. These
-## skip_unless subs help that.
-##
-## TODO: There are also a few things that Win32 supports (passing Win32 OS
-## handles) that we should test for, conversely.
-sub skip_unless_subs(&) {
- if ( Win32_MODE ) {
- return sub {
- skip "Can't spawn subroutines on $^O", 0 ;
- } ;
- }
- shift ;
-}
-
-sub skip_unless_shell(&) {
- if ( Win32_MODE ) {
- return sub {
- skip "$^O's shell returns 0 even if last command doesn't", 0 ;
- } ;
- }
- shift ;
-}
-
-sub skip_unless_high_fds(&) {
- if ( Win32_MODE ) {
- return sub {
- skip "$^O does not allow redirection of file descriptors > 2", 0 ;
- } ;
- }
- shift ;
-}
-
-
-sub _unlink {
- my ( $f ) = @_;
- my $tries;
- while () {
- return if unlink $f;
- if ( $^O =~ /Win32/ && ++$tries <= 10 ) {
- print STDOUT "# Waiting for Win32 to allow $f to be unlinked ($!)\n";
- select undef, undef, undef, 0.1;
- next;
- }
- die "$! unlinking $f at ", join( ", line ", (caller)[1,2] ), "\n";
- }
-}
-
-
-my $text = "Hello World\n" ;
-
-my @perl = ( $perl ) ;
-# When utf8 is turned on via environment variables, then uc will attempt to
-# use utf8; as part of the swash initialisation. The tests here run a child
-# perl and get it to uc() strings. So that child needs to know where utf8.pm
-# is.
-push @perl, q(-I../..) if $ENV{PERL_CORE};
-
-my $emitter_script =
- qq{print '$text' ; print STDERR uc( '$text' ) unless \@ARGV } ;
-my @emitter = ( @perl, '-e', $emitter_script ) ;
-
-my $in ;
-my $out ;
-my $err ;
-
-my $in_file = 'run.t.in' ;
-my $out_file = 'run.t.out' ;
-my $err_file = 'run.t.err' ;
-
-my $h ;
-
-# initialized during the first test
-my $fd_map;
-
-sub slurp($) {
- my ( $f ) = @_ ;
- open( S, "<$f" ) or return "$! $f" ;
- my $r = join( '', <S> ) ;
- close S or warn "$!: $f";
- select 0.1 if $^O =~ /Win32/;
- return $r ;
-}
-
-
-sub spit($$) {
- my ( $f, $s ) = @_ ;
- open( S, ">$f" ) or die "$! $f" ;
- print S $s or die "$! $f" ;
- close S or die "$! $f" ;
-}
-
-##
-## A grossly inefficient filter to test filter
-## chains. It's inefficient because we want to make sure that the
-## filter chain flushing logic works. The inefficiency is that it
-## doesn't process as much input as it could each call, so lots of calls
-## are required.
-##
-sub alt_casing_filter {
- my ( $in_ref, $out_ref ) = @_ ;
- return input_avail && do {
- $$out_ref .= lc( substr( $$in_ref, 0, 1, '' ) ) ;
- 1 ;
- } && (
- ! input_avail || do {
- $$out_ref .= uc( substr( $$in_ref, 0, 1, '' ) ) ;
- 1 ;
- }
- ) ;
-}
-
-
-sub case_inverting_filter {
- my ( $in_ref, $out_ref ) = @_ ;
- return input_avail && do {
- $$in_ref =~ tr/a-zA-Z/A-Za-z/ ;
- $$out_ref .= $$in_ref ;
- $$in_ref = '' ;
- 1 ;
- } ;
-}
-
-
-sub eok {
- my ( $got, $exp ) = ( shift, shift );
- $got =~ s/([\000-\037])/sprintf "\\0x%02x", ord $1/ge if defined $exp;
- $exp =~ s/([\000-\037])/sprintf "\\0x%02x", ord $1/ge if defined $exp;
- @_ = ( $got, $exp, @_ );
- goto &ok;
-}
-
-
-my $r ;
-
-
-my @tests = (
-
-sub { ok( _map_fds, $fd_map ) ; $fd_map = _map_fds },
-
-##
-## Internal testing
-##
-filter_tests(
- "alt_casing_filter",
- "Hello World",
- ["hElLo wOrLd" =~ m/(..?)/g],
- \&alt_casing_filter
-),
-
-sub { ok( _map_fds, $fd_map ) ; $fd_map = _map_fds },
-
-filter_tests(
- "case_inverting_filter",
- "Hello World",
- "hELLO wORLD",
- \&case_inverting_filter
-),
-
-sub { ok( _map_fds, $fd_map ) ; $fd_map = _map_fds },
-
-##
-## Calling the local system shell
-##
-sub { ok run qq{$perl -e exit} },
-sub { ok $?, 0 },
-
-sub { ok( _map_fds, $fd_map ) ; $fd_map = _map_fds },
-
-skip_unless_shell { ok ! run qq{$perl -e 'exit(42)'} },
-skip_unless_shell { ok $? },
-skip_unless_shell { ok $? >> 8, 42 },
-
-sub { ok( _map_fds, $fd_map ) ; $fd_map = _map_fds },
-
-##
-## Simple commands, not executed via shell
-##
-sub { ok( run $perl, qw{-e exit} ) },
-sub { ok( $?, 0 ) },
-
-sub { ok( _map_fds, $fd_map ) ; $fd_map = _map_fds },
-
-sub { ok( ! run $perl, qw{-e exit(42)} ) },
-sub { ok( $? ) },
-sub { ok $? >> 8, 42 },
-
-sub { ok( _map_fds, $fd_map ) ; $fd_map = _map_fds },
-
-##
-## A function
-##
-skip_unless_subs { ok run sub{} },
-skip_unless_subs { ok $?, 0 },
-skip_unless_subs { ok !run sub{ exit 42 } },
-skip_unless_subs { ok $? },
-skip_unless_subs { ok $? >> 8, 42 },
-
-sub { ok( _map_fds, $fd_map ) ; $fd_map = _map_fds },
-
-##
-## A function, and an init function
-##
-skip_unless_subs {
- my $e = 0 ;
- ok(
- ! run(
- sub{ exit($e) },
- init => sub { $e = 42 }
- )
- ) ;
-},
-skip_unless_subs { ok( $? ) },
-
-sub { ok( _map_fds, $fd_map ) ; $fd_map = _map_fds },
-
-##
-## scalar ref I & O redirection using op tokens
-##
-sub {
- $out = 'REPLACE ME' ;
- $fd_map = _map_fds ;
- $r = run [ @emitter, "nostderr" ], '>', \$out ;
- ok( $r ) ;
-},
-sub { ok( ! $? ) },
-sub { ok( _map_fds, $fd_map ) },
-sub { eok( $out, $text ) },
-
-sub {
- $out = 'REPLACE ME' ;
- $fd_map = _map_fds ;
- $r = run [ @emitter, "nostderr" ], '<', \undef, '>', \$out ;
- ok( $r ) ;
-},
-sub { ok( ! $? ) },
-sub { ok( _map_fds, $fd_map ) },
-sub { eok( $out, $text ) },
-sub {
- $in = $emitter_script ;
- $out = 'REPLACE ME' ;
- $err = 'REPLACE ME' ;
- $fd_map = _map_fds ;
- $r = run \@perl, '<', \$in, '>', \$out, '2>', \$err, ;
- ok( $r ) ;
-},
-sub { ok( ! $? ) },
-sub { ok( _map_fds, $fd_map ) },
-
-sub { eok( $in, $emitter_script ) },
-sub { eok( $out, $text ) },
-sub { eok( $err, uc( $text ) ) },
-##
-## scalar ref I & O redirection, succinct mode.
-##
-sub {
- $in = $emitter_script ;
- $out = 'REPLACE ME' ;
- $err = 'REPLACE ME' ;
- $fd_map = _map_fds ;
- $r = run \@perl, \$in, \$out, \$err ;
- ok( $r ) ;
-},
-sub { ok( ! $? ) },
-sub { ok( _map_fds, $fd_map ) },
-
-sub { eok( $in, $emitter_script ) },
-sub { eok( $out, $text ) },
-sub { eok( $err, uc( $text ) ) },
-
-##
-## Long output, to test for blocking read.
-##
-## Assume pipe buffer length <= 10000, need to double that to assure enough
-## chars to fill a buffer so. This test adapted from a test submitted by
-## Borislav Deianov <borislav@ensim.com>.
-sub {
- $in = "-" x 20000 . "end\n" ;
- $out = 'REPLACE ME' ;
- $fd_map = _map_fds ;
- $r = run [ $perl, qw{-e print"-"x20000;<STDIN>;} ], \$in, \$out ;
- ok( $r ) ;
-},
-sub { ok( ! $? ) },
-sub { ok( _map_fds, $fd_map ) },
-
-sub { ok( length $out, 20000 ) },
-sub { ok( $out !~ /[^-]/ ) },
-
-
-##
-## Long output run through twice
-##
-## Adapted from a stress test by Aaron Elkiss <aelkiss@wam.umd.edu>
-##
-sub {
- $h = start [$perl, qw( -pe BEGIN{$|=1}1 )], \$in, \$out;
-
- $in = "\n";
- $out = "";
- pump $h until length $out;
- ok $out eq "\n";
-},
-
-sub {
- my $long_string = "x" x 20000 . "DOC2\n";
- $in = $long_string;
- $out = "";
- my $ok_1 = eval {
- pump $h until $out =~ /DOC2/;
- 1;
- };
- my $x = $@;
- my $ok_2 = eval {
- finish $h;
- 1;
- };
-
- $x = $@ if $ok_1 && ! $ok_2;
-
- if ( $ok_1 && $ok_2 ) {
- ok $long_string eq $out;
- }
- else {
- $x =~ s/(x+)/sprintf "...%d \"x\" chars...", length $1/e;
- ok $x, "";
- }
-},
-
-##
-## child function, scalar ref I & O redirection, succinct mode.
-##
-skip_unless_subs {
- $in = $text ;
- $out = 'REPLACE ME' ;
- $err = 'REPLACE ME' ;
- $fd_map = _map_fds ;
- $r = run(
- sub { while (<>) { print ; print STDERR uc( $_ ) } },
- \$in, \$out, \$err
- ) ;
- ok( $r ) ;
-},
-skip_unless_subs { ok ! $? },
-skip_unless_subs { ok( _map_fds, $fd_map ) },
-
-skip_unless_subs { eok( $in, $text ) },
-skip_unless_subs { eok( $out, $text ) },
-skip_unless_subs { eok( $err, uc( $text ) ) },
-
-##
-## here document as input
-##
-sub {
- $out = 'REPLACE ME' ;
- $err = 'REPLACE ME' ;
- $fd_map = _map_fds ;
- $r = run \@perl, \<<TOHERE, \$out, \$err ;
-$emitter_script
-TOHERE
- ok( $r ) ;
-},
-sub { ok( ! $? ) },
-sub { ok( _map_fds, $fd_map ) },
-
-sub { eok( $out, $text ) },
-sub { eok( $err, uc( $text ) ) },
-
-##
-## undef as input
-##
-sub {
- $out = 'REPLACE ME' ;
- $err = 'REPLACE ME' ;
- $fd_map = _map_fds ;
- $r = run \@perl, \undef, \$out, \$err ;
- ok( $r ) ;
-},
-sub { ok( ! $? ) },
-sub { ok( _map_fds, $fd_map ) },
-
-sub { eok( $out, '' ) },
-sub { eok( $err, '' ) },
-
-##
-## filehandle input redirection
-##
-sub {
- $out = 'REPLACE ME' ;
- $err = 'REPLACE ME' ;
- $fd_map = _map_fds ;
- spit( $in_file, $emitter_script ) ;
- open( F, "<$in_file" ) or die "$! $in_file" ;
- $r = run \@perl, \*F, \$out, \$err ;
- close F ;
- unlink $in_file or warn "$! $in_file" ;
- ok( $r ) ;
-},
-sub { ok( ! $? ) },
-sub { ok( _map_fds, $fd_map ) },
-
-sub { eok( $out, $text ) },
-sub { eok( $err, uc( $text ) ) },
-
-##
-## input redirection via caller writing directly to a pipe
-##
-sub {
- $out = 'REPLACE ME' ;
- $err = 'REPLACE ME' ;
- $fd_map = _map_fds ;
- $h = start \@perl, '<pipe', \*IN, '>', \$out, '2>', \$err ;
- ## Assume this won't block...
- print IN $emitter_script ;
- close IN or warn $! ;
- $r = $h->finish ;
- ok( $r ) ;
-},
-sub { ok( ! $? ) },
-sub { ok( _map_fds, $fd_map ) },
-
-sub { eok( $out, $text ) },
-sub { eok( $err, uc( $text ) ) },
-
-##
-## filehandle input redirection, passed via *F{IO}
-##
-sub {
- $out = 'REPLACE ME' ;
- $err = 'REPLACE ME' ;
- $fd_map = _map_fds ;
- spit( $in_file, $emitter_script ) ;
- open( F, "<$in_file" ) or die "$! $in_file" ;
- $r = run \@perl, *F{IO}, \$out, \$err ;
- close F ;
- _unlink $in_file;
- ok( $r ) ;
-},
-sub { ok( ! $? ) },
-sub { ok( _map_fds, $fd_map ) },
-
-sub { eok( $out, $text ) },
-sub { eok( $err, uc( $text ) ) },
-
-##
-## filehandle output redirection
-##
-sub {
- $fd_map = _map_fds ;
- open( OUT, ">$out_file" ) or die "$! $out_file" ;
- open( ERR, ">$err_file" ) or die "$! $err_file" ;
- print OUT "out: " ;
- print ERR uc( "err: " ) ;
- $r = run \@emitter, \undef, \*OUT, \*ERR ;
- print OUT " more out data" ;
- print ERR uc( " more err data" ) ;
- close OUT ;
- close ERR ;
- $out = slurp( $out_file ) ;
- $err = slurp( $err_file ) ;
- _unlink $out_file;
- _unlink $err_file;
- ok( $r ) ;
-},
-sub { ok( ! $? ) },
-sub { ok( _map_fds, $fd_map ) },
-
-sub { eok( $out, "out: $text more out data" ) },
-sub { eok( $err, uc( "err: $text more err data" ) ) },
-
-##
-## filehandle output redirection via a pipe that is returned to the caller
-##
-sub {
- $fd_map = _map_fds ;
- my $r = run \@emitter, \undef, '>pipe', \*OUT, '2>pipe', \*ERR ;
- $out = '' ;
- $err = '' ;
- read OUT, $out, 10000 or warn $!;
- read ERR, $err, 10000 or warn $!;
- close OUT or warn $! ;
- close ERR or warn $! ;
- ok( $r ) ;
-},
-sub { ok( ! $? ) },
-sub { ok( _map_fds, $fd_map ) },
-
-sub { eok( $out, $text ) },
-sub { eok( $err, uc( $text ) ) },
-
-##
-## sub I & O redirection
-##
-sub {
- $in = $emitter_script ;
- $out = undef ;
- $err = undef ;
- $fd_map = _map_fds ;
- $r = run(
- \@perl,
- '<', sub { my $f = $in ; $in = undef ; return $f },
- '>', sub { $out .= shift },
- '2>', sub { $err .= shift },
- ) ;
- ok( $r ) ;
-},
-sub { ok( ! $? ) },
-sub { ok( _map_fds, $fd_map ) },
-
-sub { eok( $out, $text ) },
-sub { eok( $err, uc( $text ) ) },
-
-##
-## input redirection from a file
-##
-sub {
- $out = undef ;
- $err = undef ;
- $fd_map = _map_fds ;
- spit( $in_file, $emitter_script ) ;
- $r = run(
- \@perl,
- "<$in_file",
- '>', sub { $out .= shift },
- '2>', sub { $err .= shift },
- ) ;
- _unlink $in_file;
- ok( $r ) ;
-},
-sub { ok( ! $? ) },
-sub { ok( _map_fds, $fd_map ) },
-
-sub { eok( $out, $text ) },
-sub { eok( $err, uc( $text ) ) },
-
-##
-## reading input from a non standard fd
-##
-skip_unless_high_fds {
- $out = undef ;
- $err = undef ;
- $fd_map = _map_fds ;
- $r = run(
- ## FreeBSD doesn't guarantee that fd 3 or 4 are available, so
- ## don't assume, go for 5.
- [ @perl, '-le', 'open( STDIN, "<&5" ) or die $! ; print <STDIN>' ],
- "5<", \"Hello World",
- '>', \$out,
- '2>', \$err,
- ) ;
- ok( $r ) ;
-},
-skip_unless_high_fds { ok( ! $? ) },
-skip_unless_high_fds { ok( _map_fds, $fd_map ) },
-
-skip_unless_high_fds { eok( $out, $text ) },
-skip_unless_high_fds { eok( $err, '' ) },
-
-##
-## duping input descriptors and an input descriptor > 0
-##
-skip_unless_high_fds {
- $in = $emitter_script ;
- $out = 'REPLACE ME' ;
- $err = 'REPLACE ME' ;
- $fd_map = _map_fds ;
- $r = run(
- \@perl,
- '>', \$out,
- '2>', \$err,
- '3<', \$in,
- '0<&3',
- ) ;
- ok( $r ) ;
-},
-skip_unless_high_fds { ok( ! $? ) },
-skip_unless_high_fds { ok( _map_fds, $fd_map ) },
-skip_unless_high_fds { eok( $in, $emitter_script ) },
-skip_unless_high_fds { eok( $out, $text ) },
-skip_unless_high_fds { eok( $err, uc( $text ) ) },
-
-##
-## closing input descriptors
-##
-sub {
- $out = 'REPLACE ME' ;
- $err = 'REPLACE ME' ;
- $fd_map = _map_fds ;
- spit( $in_file, $emitter_script ) ;
- $r = run(
- [ @perl, '-e', '$l = readline *STDIN or die $! ; print $l' ],
- '>', \$out,
- '2>', \$err,
- '<', $in_file,
- '0<&-',
- ) ;
- _unlink $in_file;
- ok( ! $r ) ;
-},
-sub { ok( $? ) },
-sub { ok( _map_fds, $fd_map ) },
-sub { eok( $out, '' ) },
-#sub { ok( $err =~ /file descriptor/i ? "Bad file descriptor error" : $err, "Bad file descriptor error" ) },
-# XXX This should be use Errno; if $!{EBADF}. --rs
-sub { ok( length $err ? "Bad file descriptor error" : $err, "Bad file descriptor error" ) },
-
-##
-## input redirection from a non-existent file
-##
-sub {
- $out = 'REPLACE ME' ;
- $err = 'REPLACE ME' ;
- $fd_map = _map_fds ;
- my $bad_file = "$in_file.nonexistant" ;
- _unlink $bad_file if -e $bad_file;
- eval {
- $r = run \@perl, ">$out_file", "<$bad_file" ;
- } ;
- if ( $@ =~ /\Q$bad_file\E/ ) {
- ok 1 ;
- }
- else {
- ok $@, "qr/\Q$bad_file\E/" ;
- }
-},
-sub { ok( _map_fds, $fd_map ) },
-
-##
-## output redirection to a file w/ creation or truncation
-##
-sub {
- $fd_map = _map_fds ;
- _unlink $out_file if -x $out_file;
- _unlink $err_file if -x $err_file;
- $r = run(
- \@emitter,
- ">$out_file",
- "2>$err_file",
- ) ;
- $out = slurp( $out_file ) ;
- $err = slurp( $err_file ) ;
- ok( $r ) ;
-},
-sub { ok( ! $? ) },
-sub { ok( _map_fds, $fd_map ) },
-
-sub { eok( $out, $text ) },
-sub { eok( $err, uc( $text ) ) },
-
-##
-## output file redirection, w/ truncation
-##
-sub {
- $fd_map = _map_fds ;
- spit( $out_file, 'out: ' ) ;
- spit( $err_file, 'ERR: ' ) ;
- $r = run(
- \@emitter,
- ">$out_file",
- "2>$err_file",
- ) ;
- $out = slurp( $out_file ) ; _unlink $out_file;
- $err = slurp( $err_file ) ; _unlink $err_file;
- ok( $r ) ;
-},
-sub { ok( ! $? ) },
-sub { ok( _map_fds, $fd_map ) },
-
-sub { eok( $out, $text ) },
-sub { eok( $err, uc( $text ) ) },
-
-##
-## output file redirection w/ append
-##
-sub {
- spit( $out_file, 'out: ' ) ;
- spit( $err_file, 'ERR: ' ) ;
- $fd_map = _map_fds ;
- $r = run(
- \@emitter,
- ">>$out_file",
- "2>>$err_file",
- ) ;
- $out = slurp( $out_file ) ;
- _unlink $out_file;
- $err = slurp( $err_file ) ;
- _unlink $err_file;
- ok( $r ) ;
-},
-sub { ok( ! $? ) },
-sub { ok( _map_fds, $fd_map ) },
-
-sub { eok( $out, "out: $text" ) },
-sub { eok( $err, uc( "err: $text" ) ) },
-##
-## dup()ing output descriptors
-##
-sub {
- $out = 'REPLACE ME' ;
- $err = 'REPLACE ME' ;
- $fd_map = _map_fds ;
- $r = run \@emitter, '>', \$out, '2>', \$err, '2>&1' ;
- ok( $r ) ;
-},
-sub { ok( ! $? ) },
-sub { ok( _map_fds, $fd_map ) },
-sub { $out =~ /(?:$text){2}/i ? ok 1 : ok $out, "qr/($text){2}/i" },
-sub { eok( $err, '' ) },
-
-##
-## stderr & stdout redirection to the same file via >&word
-##
-sub {
- $fd_map = _map_fds ;
- _unlink $out_file if -x $out_file;
- $r = run \@emitter, ">&$out_file" ;
- $out = slurp( $out_file ) ;
- ok( $r ) ;
-},
-sub { ok( ! $? ) },
-sub { ok( _map_fds, $fd_map ) },
-
-sub { ok( $out =~ qr/(?:$text){2}/i ) },
-
-##
-## Non-zero exit value, command with args, no redirects.
-##
-sub {
- $fd_map = _map_fds ;
- $r = run [ @perl, '-e', 'exit(42)' ] ;
- ok( !$r ) ;
-},
-sub { ok( $?, 42 << 8 ) },
-sub { ok( _map_fds, $fd_map ) },
-
-##
-## Zero exit value, command with args, no redirects.
-##
-sub {
- $fd_map = _map_fds ;
- $r = run [ @perl, qw{ -e exit }] ;
- ok( $r ) ;
-},
-sub { ok( ! $? ) },
-sub { ok( _map_fds, $fd_map ) },
-
-##
-## dup()ing output descriptors that collide.
-##
-## This test assumes that our caller doesn't leave a lot of fds opened,
-## and assumes that $out_file will be opened on fd 3, 4 or 5.
-##
-skip_unless_high_fds {
- $out = 'REPLACE ME' ;
- $err = 'REPLACE ME' ;
- _unlink $out_file if -x $out_file;
- $fd_map = _map_fds ;
- $r = run(
- \@emitter,
- "<", \"",
- "3>&1", "4>&1", "5>&1",
- ">$out_file",
- '2>', \$err,
- ) ;
- $out = slurp( $out_file ) ;
- _unlink $out_file;
- ok( $r ) ;
-},
-skip_unless_high_fds { ok( ! $? ) },
-skip_unless_high_fds { ok( _map_fds, $fd_map ) },
-skip_unless_high_fds { eok( $out, $text ) },
-skip_unless_high_fds { eok( $err, uc( $text ) ) },
-
-##
-## Pipelining
-##
-sub {
- $out = 'REPLACE ME' ;
- $err = 'REPLACE ME' ;
- $fd_map = _map_fds ;
- $r = run(
- [ @perl, '-lane', 'print STDERR "1:$_" ; print uc($F[0])," ",$F[1]'],
- \"Hello World",
- '|',[ @perl, '-lane', 'print STDERR "2:$_" ; print $F[0]," ",lc($F[1])'],
- \$out,
- \$err,
- ) ;
- ok( $r ) ;
-},
-sub { ok( ! $? ) },
-sub { ok( _map_fds, $fd_map ) },
-sub { eok( $out, "HELLO world\n" ) },
-sub { eok( $err, "1:Hello World\n2:HELLO World\n" ) },
-
-##
-## Parallel (unpiplined) processes
-##
-sub {
- $out = 'REPLACE ME' ;
- $err = 'REPLACE ME' ;
- $fd_map = _map_fds ;
- $r = run(
- [ @perl, '-lane', 'print STDERR "1:$_" ; print uc($F[0])," ",$F[1]' ],
- \"Hello World",
- '&', [ @perl, '-lane', 'print STDERR "2:$_" ; print $F[0]," ",lc( $F[1] )' ],
- \"Hello World",
- \$out,
- \$err,
- ) ;
- ok( $r ) ;
-},
-sub { ok( ! $? ) },
-sub { ok( _map_fds, $fd_map ) },
-sub { ok( $out =~ qr/^(?:HELLO World\n|Hello world\n){2}$/s ) },
-sub { ok( $err =~ qr/^(?:[12]:Hello World.*){2}$/s ) },
-
-##
-## A few error cases...
-##
-sub {
- eval { $r = run \@perl, '<', [], [] } ;
- ok( $@ =~ qr/not allowed/ ) ;
-},
-
-sub {
- eval { $r = run \@perl, '>', [], [] } ;
- ok( $@ =~ qr/not allowed/ ) ;
-},
-
-(
- map {
- my $foo = $_ ;
- sub {
- eval { $r = run $foo, [] } ;
- ok( $@ =~ qr/command/ ) ;
- }
- } qw( | & < > >& 1>&2 >file <file 2<&1 <&- 3<&- )
-),
-sub {
- $out = 'REPLACE ME' ;
- $err = 'REPLACE ME' ;
- $fd_map = _map_fds ;
- eval {
- $r = run( \@emitter, '>', \$out, '2>', \$err,
- _simulate_fork_failure => 1
- ) ;
- } ;
- ok( $@ ) ;
-},
-sub { ok( ! $? ) },
-sub { ok( _map_fds, $fd_map ) },
-
-sub { eok( $out, '' ) },
-sub { eok( $err, '' ) },
-
-sub {
- $fd_map = _map_fds ;
- eval {
- $r = run \@perl, '<file', _simulate_open_failure => 1 ;
- } ;
- ok( $@ ) ;
-},
-sub { ok( ! $? ) },
-sub { ok( _map_fds, $fd_map ) },
-
-sub {
- $fd_map = _map_fds ;
- eval {
- $r = run \@perl, '>file', _simulate_open_failure => 1 ;
- } ;
- ok( $@ ) ;
-},
-sub { ok( ! $? ) },
-sub { ok( _map_fds, $fd_map ) },
-
-##
-## harness, pump, run
-##
-sub {
- $in = 'SHOULD BE UNCHANGED' ;
- $out = 'REPLACE ME' ;
- $err = 'REPLACE ME' ;
- $? = 99 ;
- $fd_map = _map_fds ;
- $h = start(
- [ @perl, '-pe', 'BEGIN { $| = 1 } print STDERR uc($_)' ],
- \$in, \$out, \$err,
- ) ;
- ok( isa( $h, 'IPC::Run' ) ) ;
-},
-sub { ok( $?, 99 ) },
-
-sub { eok( $in, 'SHOULD BE UNCHANGED' ) },
-sub { eok( $out, '' ) },
-sub { eok( $err, '' ) },
-sub { ok( $h->pumpable ) },
-
-sub {
- $in = '' ;
- $? = 0 ;
- pump_nb $h for ( 1..100 ) ;
- ok( 1 ) ;
-},
-sub { eok( $in, '' ) },
-sub { eok( $out, '' ) },
-sub { eok( $err, '' ) },
-sub { ok( $h->pumpable ) },
-
-sub {
- $in = $text ;
- $? = 0 ;
- pump $h until $out =~ /Hello World/ ;
- ok( 1 ) ;
-},
-sub { ok( ! $? ) },
-sub { eok( $in, '' ) },
-sub { eok( $out, $text ) },
-sub { ok( $h->pumpable ) },
-
-sub { ok( $h->finish ) },
-sub { ok( ! $? ) },
-sub { ok( _map_fds, $fd_map ) },
-sub { eok( $out, $text ) },
-sub { eok( $err, uc( $text ) ) },
-sub { ok( ! $h->pumpable ) },
-
-##
-## start, run, run, run. See Tom run. A do-run-run, a-do-run-run.
-##
-sub {
- $in = 'SHOULD BE UNCHANGED' ;
- $out = 'REPLACE ME' ;
- $err = 'REPLACE ME' ;
- $fd_map = _map_fds ;
- $h = start(
- [ @perl, '-pe', 'binmode STDOUT ; binmode STDERR ; BEGIN { $| = 1 } print STDERR uc($_)' ],
- \$in, \$out, \$err,
- ) ;
- ok( isa( $h, 'IPC::Run' ) ) ;
-},
-
-sub { eok( $in, 'SHOULD BE UNCHANGED' ) },
-sub { eok( $out, '' ) },
-sub { eok( $err, '' ) },
-sub { ok( $h->pumpable ) },
-
-sub {
- $in = $text ;
- ok( $h->finish )
-},
-sub { ok( ! $? ) },
-sub { ok( _map_fds, $fd_map ) },
-sub { eok( $in, '' ) },
-sub { eok( $out, $text ) },
-sub { eok( $err, uc( $text ) ) },
-sub { ok( ! $h->pumpable ) },
-
-sub {
- $in = $text ;
- $out = 'REPLACE ME' ;
- $err = 'REPLACE ME' ;
- ok( $h->run )
-},
-sub { ok( ! $? ) },
-sub { ok( _map_fds, $fd_map ) },
-sub { eok( $in, $text ) },
-sub { eok( $out, $text ) },
-sub { eok( $err, uc( $text ) ) },
-sub { ok( ! $h->pumpable ) },
-
-sub {
- $in = $text ;
- $out = 'REPLACE ME' ;
- $err = 'REPLACE ME' ;
- ok( $h->run )
-},
-sub { ok( ! $? ) },
-sub { ok( _map_fds, $fd_map ) },
-sub { eok( $in, $text ) },
-sub { eok( $out, $text ) },
-sub { eok( $err, uc( $text ) ) },
-sub { ok( ! $h->pumpable ) },
-
-##
-## Output filters
-##
-sub {
- $out = 'REPLACE ME' ;
- $err = 'REPLACE ME' ;
- $fd_map = _map_fds ;
- $r = run(
- \@emitter,
- '>',
- \&alt_casing_filter,
- \&case_inverting_filter,
- \$out,
- '2>', \$err,
- ) ;
- ok( $r ) ;
-},
-sub { ok( ! $? ) },
-sub { ok( _map_fds, $fd_map ) },
-
-sub { eok( $out, "HeLlO WoRlD\n" ) },
-sub { eok( $err, uc( $text ) ) },
-
-##
-## Input filters
-##
-sub {
- $out = 'REPLACE ME' ;
- $err = 'REPLACE ME' ;
- $fd_map = _map_fds ;
- $in = $text ;
- $r = run(
- [ @perl, '-pe', 'binmode STDOUT ; binmode STDERR ; print STDERR uc $_' ],
- '0<',
- \&case_inverting_filter,
- \&alt_casing_filter,
- \$in,
- '1>', \$out,
- '2>', \$err,
- ) ;
- ok( $r ) ;
-},
-sub { ok( ! $? ) },
-sub { ok( _map_fds, $fd_map ) },
-
-sub { eok( $in, $text ) },
-sub { eok( $out, "HeLlO WoRlD\n" ) },
-sub { eok( $err, uc( $text ) ) },
-) ;
-
-plan tests => scalar @tests, todo => [ 69 ] ;
-
-# Must do this this late as plan uses localtime, and localtime on darwin opens
-# a file descriptor. Quite probably other operating systems do file descriptor
-# things during the test setup.
-
-$fd_map = _map_fds ;
-
-$_->() for ( @tests ) ;
diff --git a/lib/IPC/Run/t/signal.t b/lib/IPC/Run/t/signal.t
deleted file mode 100644
index 399bd527e4..0000000000
--- a/lib/IPC/Run/t/signal.t
+++ /dev/null
@@ -1,90 +0,0 @@
-#!/usr/bin/perl -w
-
-=head1 NAME
-
-signal.t - Test suite IPC::Run->signal
-
-=cut
-
-BEGIN {
- if( $ENV{PERL_CORE} ) {
- use Cwd;
- $^X = Cwd::abs_path($^X);
- $^X = qq("$^X") if $^X =~ /\s/;
- chdir '../lib/IPC/Run' if -d '../lib/IPC/Run';
- unshift @INC, 'lib', '../..';
- }
-}
-
-use strict ;
-
-use Test ;
-
-use IPC::Run qw( :filters :filter_imp start run filter_tests ) ;
-use UNIVERSAL qw( isa ) ;
-
-sub Win32_MODE() ;
-*Win32_MODE = \&IPC::Run::Win32_MODE ;
-
-## Win32 does not support a lot of things that Unix does. These
-## skip_unless subs help that.
-##
-## TODO: There are also a few things that Win32 supports (passing Win32 OS
-## handles) that we should test for, conversely.
-sub skip_unless_signals(&) {
- if ( Win32_MODE ) {
- return sub {
- skip "$^O does not support signals", 0 ;
- } ;
- }
- shift ;
-}
-
-use IPC::Run qw( start ) ;
-
-my @receiver = (
- $^X,
- '-e',
- <<'END_RECEIVER',
- my $which = " " ;
- sub s{ $which = $_[0] } ;
- $SIG{$_}=\&s for (qw(USR1 USR2));
- $| = 1 ;
- print "Ok\n";
- for (1..10) { sleep 1 ; print $which, "\n" }
-END_RECEIVER
-) ;
-
-my $h ;
-my $out ;
-
-my @tests = (
-skip_unless_signals {
- $h = start \@receiver, \undef, \$out ;
- pump $h until $out =~ /Ok/ ;
- ok 1 ;
-},
-skip_unless_signals {
- $out = "" ;
- $h->signal( "USR2" ) ;
- pump $h ;
- $h->signal( "USR1" ) ;
- pump $h ;
- $h->signal( "USR2" ) ;
- pump $h ;
- $h->signal( "USR1" ) ;
- pump $h ;
- ok $out, "USR2\nUSR1\nUSR2\nUSR1\n" ;
-},
-
-skip_unless_signals {
- $h->signal( "TERM" ) ;
- finish $h ;
- ok( 1 ) ;
-},
-
-) ;
-
-plan tests => scalar @tests ;
-
-$_->() for ( @tests ) ;
diff --git a/lib/IPC/Run/t/timeout.t b/lib/IPC/Run/t/timeout.t
deleted file mode 100644
index 394f10998d..0000000000
--- a/lib/IPC/Run/t/timeout.t
+++ /dev/null
@@ -1,117 +0,0 @@
-#!/usr/bin/perl -w
-
-=head1 NAME
-
-timeout.t - Test suite for IPC::Run timeouts
-
-=cut
-
-BEGIN {
- if( $ENV{PERL_CORE} ) {
- use Cwd;
- $^X = Cwd::abs_path($^X);
- $^X = qq("$^X") if $^X =~ /\s/;
- chdir '../lib/IPC/Run' if -d '../lib/IPC/Run';
- unshift @INC, 'lib', '../..';
- }
-}
-
-
-## Separate from run.t so run.t is not too slow.
-
-use strict ;
-
-use Test ;
-
-use IPC::Run qw( harness timeout ) ;
-use UNIVERSAL qw( isa ) ;
-
-my $h ;
-my $t ;
-my $in ;
-my $out ;
-my $started ;
-
-my @tests = (
-
-sub {
- $h = harness( [ $^X ], \$in, \$out, $t = timeout( 1 ) ) ;
- ok( isa( $h, 'IPC::Run' ) ) ;
-},
-sub { ok( !! $t->is_reset ) },
-sub { ok( ! $t->is_running ) },
-sub { ok( ! $t->is_expired ) },
-
-sub {
- $started = time ;
- $h->start ;
- ok( 1 ) ;
-},
-sub { ok( ! $t->is_reset ) },
-sub { ok( !! $t->is_running ) },
-sub { ok( ! $t->is_expired ) },
-
-sub {
- $in = '' ;
- eval { $h->pump };
- # Older perls' Test.pms don't know what to do with qr//s
- $@ =~ /IPC::Run: timeout/ ? ok( 1 ) : ok( $@, qr/IPC::Run: timeout/ ) ;
-},
-
-sub {
- my $elapsed = time - $started ;
- $elapsed >= 1 ? ok( 1 ) : ok( $elapsed, ">= 1" ) ;
-},
-
-sub { ok( $t->interval, 1 ) },
-sub { ok( ! $t->is_reset ) },
-sub { ok( ! $t->is_running ) },
-sub { ok( !! $t->is_expired ) },
-
-##
-## Starting from an expired state
-##
-sub {
- $started = time ;
- $h->start ;
- ok( 1 ) ;
-},
-sub { ok( ! $t->is_reset ) },
-sub { ok( !! $t->is_running ) },
-sub { ok( ! $t->is_expired ) },
-sub {
- $in = '' ;
- eval { $h->pump };
- $@ =~ /IPC::Run: timeout/ ? ok( 1 ) : ok( $@, qr/IPC::Run: timeout/ ) ;
-},
-sub { ok( ! $t->is_reset ) },
-sub { ok( ! $t->is_running ) },
-sub { ok( !! $t->is_expired ) },
-
-sub {
- my $elapsed = time - $started ;
- $elapsed >= 1 ? ok( 1 ) : ok( $elapsed, ">= 1" ) ;
-},
-
-sub {
- $h = harness( [ $^X ], \$in, \$out, timeout( 1 ) ) ;
- $started = time ;
- $h->start ;
- $in = '' ;
- eval { $h->pump };
- $@ =~ /IPC::Run: timeout/ ? ok( 1 ) : ok( $@, qr/IPC::Run: timeout/ ) ;
-},
-
-sub {
- my $elapsed = time - $started ;
- $elapsed >= 1 ? ok( 1 ) : ok( $elapsed, ">= 1" ) ;
-},
-
-) ;
-
-
-
-plan tests => scalar @tests ;
-
-$_->() for ( @tests ) ;
-
diff --git a/lib/IPC/Run/t/timer.t b/lib/IPC/Run/t/timer.t
deleted file mode 100644
index 85ab5cb65b..0000000000
--- a/lib/IPC/Run/t/timer.t
+++ /dev/null
@@ -1,150 +0,0 @@
-#!/usr/bin/perl -w
-
-=head1 NAME
-
-timer.t - Test suite for IPC::Run::Timer
-
-=cut
-
-BEGIN {
- if( $ENV{PERL_CORE} ) {
- use Cwd;
- $^X = Cwd::abs_path($^X);
- $^X = qq("$^X") if $^X =~ /\s/;
- chdir '../lib/IPC/Run' if -d '../lib/IPC/Run';
- unshift @INC, 'lib', '../..';
- }
-}
-
-use strict ;
-
-use Test ;
-
-use IPC::Run qw( run ) ;
-use IPC::Run::Timer qw( :all ) ;
-use UNIVERSAL qw( isa ) ;
-
-my $t ;
-my $started ;
-
-my @tests = (
-
-sub {
- $t = timer(
-# debug => 1,
- 1,
- ) ;
- ok( ref $t, 'IPC::Run::Timer' ) ;
-},
-
-sub { ok( $t->interval, 1 ) },
-
-sub { $t->interval( 0 ) ; ok( $t->interval, 0 ) },
-sub { $t->interval( 0.1 ) ; ok( $t->interval > 0 ) },
-sub { $t->interval( 1 ) ; ok( $t->interval >= 1 ) },
-sub { $t->interval( 30 ) ; ok( $t->interval >= 30 ) },
-sub { $t->interval( 30.1 ) ; ok( $t->interval > 30 ) },
-sub { $t->interval( 30.1 ) ; ok( $t->interval <= 31 ) },
-
-sub { $t->interval( "1:0" ) ; ok( $t->interval, 60 ) },
-sub { $t->interval( "1:0:0" ) ; ok( $t->interval, 3600 ) },
-sub { $t->interval( "1:1:1" ) ; ok( $t->interval, 3661 ) },
-sub { $t->interval( "1:1:1.1" ) ; ok( $t->interval > 3661 ) },
-sub { $t->interval( "1:1:1.1" ) ; ok( $t->interval <= 3662 ) },
-sub { $t->interval( "1:1:1:1" ) ; ok( $t->interval, 90061 ) },
-
-sub {
- $t->reset ;
- $t->interval( 5 ) ;
- $t->start( 1, 0 ) ;
- ok( ! $t->is_expired ) ;
-},
-sub { ok( !! $t->is_running ) },
-sub { ok( ! $t->is_reset ) },
-
-sub { ok( !! $t->check( 0 ) ) },
-sub { ok( ! $t->is_expired ) },
-sub { ok( !! $t->is_running ) },
-sub { ok( ! $t->is_reset ) },
-sub { ok( !! $t->check( 1 ) ) },
-sub { ok( ! $t->is_expired ) },
-sub { ok( !! $t->is_running ) },
-sub { ok( ! $t->is_reset ) },
-sub { ok( ! $t->check( 2 ) ) },
-sub { ok( !! $t->is_expired ) },
-sub { ok( ! $t->is_running ) },
-sub { ok( ! $t->is_reset ) },
-sub { ok( ! $t->check( 3 ) ) },
-sub { ok( !! $t->is_expired ) },
-sub { ok( ! $t->is_running ) },
-sub { ok( ! $t->is_reset ) },
-
-## Restarting from the expired state.
-sub {
- $t->start( undef, 0 ) ;
- ok( ! $t->is_expired ) ;
-},
-sub { ok( !! $t->is_running ) },
-sub { ok( ! $t->is_reset ) },
-
-sub { ok( !! $t->check( 0 ) ) },
-sub { ok( ! $t->is_expired ) },
-sub { ok( !! $t->is_running ) },
-sub { ok( ! $t->is_reset ) },
-sub { ok( !! $t->check( 1 ) ) },
-sub { ok( ! $t->is_expired ) },
-sub { ok( !! $t->is_running ) },
-sub { ok( ! $t->is_reset ) },
-sub { ok( ! $t->check( 2 ) ) },
-sub { ok( !! $t->is_expired ) },
-sub { ok( ! $t->is_running ) },
-sub { ok( ! $t->is_reset ) },
-sub { ok( ! $t->check( 3 ) ) },
-sub { ok( !! $t->is_expired ) },
-sub { ok( ! $t->is_running ) },
-sub { ok( ! $t->is_reset ) },
-
-## Restarting while running
-sub {
- $t->start( 1, 0 ) ;
- $t->start( undef, 0 ) ;
- ok( ! $t->is_expired ) ;
-},
-sub { ok( !! $t->is_running ) },
-sub { ok( ! $t->is_reset ) },
-
-sub { ok( !! $t->check( 0 ) ) },
-sub { ok( ! $t->is_expired ) },
-sub { ok( !! $t->is_running ) },
-sub { ok( ! $t->is_reset ) },
-sub { ok( !! $t->check( 1 ) ) },
-sub { ok( ! $t->is_expired ) },
-sub { ok( !! $t->is_running ) },
-sub { ok( ! $t->is_reset ) },
-sub { ok( ! $t->check( 2 ) ) },
-sub { ok( !! $t->is_expired ) },
-sub { ok( ! $t->is_running ) },
-sub { ok( ! $t->is_reset ) },
-sub { ok( ! $t->check( 3 ) ) },
-sub { ok( !! $t->is_expired ) },
-sub { ok( ! $t->is_running ) },
-sub { ok( ! $t->is_reset ) },
-
-sub {
- my $got ;
- eval {
- $got = "timeout fired" ;
- run [$^X, '-e', 'sleep 3'], timeout 1 ;
- $got = "timeout didn't fire" ;
- } ;
- ok $got, "timeout fired", "timer firing in run()" ;
-},
-
-) ;
-
-
-
-plan tests => scalar @tests ;
-
-$_->() for ( @tests ) ;
-
diff --git a/lib/IPC/Run/t/win32_compile.t b/lib/IPC/Run/t/win32_compile.t
deleted file mode 100644
index a71a377df8..0000000000
--- a/lib/IPC/Run/t/win32_compile.t
+++ /dev/null
@@ -1,92 +0,0 @@
-#!/usr/bin/perl -w
-
-=head1 NAME
-
-win32_compile.t - See if IPC::Run::Win32Helper compiles, even on Unix
-
-=cut
-
-BEGIN {
- if( $ENV{PERL_CORE} ) {
- use Cwd;
- $^X = Cwd::abs_path($^X);
- $^X = qq("$^X") if $^X =~ /\s/;
- chdir '../lib/IPC/Run' if -d '../lib/IPC/Run';
- unshift @INC, 'lib', '../..';
- }
-}
-
-use strict ;
-
-use Test ;
-
-BEGIN {
- unless ( eval "require 5.006" ) {
- ## NOTE: I'm working around this here because I don't want this
- ## test to fail on non-Win32 systems with older Perls. Makefile.PL
- ## does the require 5.6.0 to protect folks on Windows.
- plan tests => 1;
- skip "perl5.00503's Socket.pm does not export IPPROTO_TCP", 1, 1;
- exit 0;
- }
-
-
- $INC{$_} = 1 for qw( Win32/Process.pm Win32API/File.pm ) ;
-
- package Win32API::File ;
-
- use vars qw( @ISA @EXPORT ) ;
-
- @ISA = qw( Exporter ) ;
- @EXPORT = qw(
- GetOsFHandle
- OsFHandleOpen
- OsFHandleOpenFd
- FdGetOsFHandle
- SetHandleInformation
- SetFilePointer
-
- HANDLE_FLAG_INHERIT
- INVALID_HANDLE_VALUE
-
- createFile
- WriteFile
- ReadFile
- CloseHandle
-
- FILE_ATTRIBUTE_TEMPORARY
- FILE_FLAG_DELETE_ON_CLOSE
- FILE_FLAG_WRITE_THROUGH
-
- FILE_BEGIN
- ) ;
-
- eval "sub $_ { 1 }" for @EXPORT ;
-
- use Exporter ;
-
- package Win32::Process ;
-
- use vars qw( @ISA @EXPORT ) ;
-
- @ISA = qw( Exporter ) ;
- @EXPORT = qw(
- NORMAL_PRIORITY_CLASS
- ) ;
-
- eval "sub $_ {}" for @EXPORT ;
-
- use Exporter ;
-}
-
-sub Socket::IPPROTO_TCP() { undef }
-
-package main ;
-
-use IPC::Run::Win32Helper ;
-use IPC::Run::Win32IO ;
-
-plan tests => 1 ;
-
-ok 1 ;
-