|
Packit |
d18d0a |
NAME
|
|
Packit |
d18d0a |
Capture::Tiny - Capture STDOUT and STDERR from Perl, XS or external
|
|
Packit |
d18d0a |
programs
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
VERSION
|
|
Packit |
d18d0a |
version 0.46
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
SYNOPSIS
|
|
Packit |
d18d0a |
use Capture::Tiny ':all';
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
# capture from external command
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
($stdout, $stderr, $exit) = capture {
|
|
Packit |
d18d0a |
system( $cmd, @args );
|
|
Packit |
d18d0a |
};
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
# capture from arbitrary code (Perl or external)
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
($stdout, $stderr, @result) = capture {
|
|
Packit |
d18d0a |
# your code here
|
|
Packit |
d18d0a |
};
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
# capture partial or merged output
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
$stdout = capture_stdout { ... };
|
|
Packit |
d18d0a |
$stderr = capture_stderr { ... };
|
|
Packit |
d18d0a |
$merged = capture_merged { ... };
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
# tee output
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
($stdout, $stderr) = tee {
|
|
Packit |
d18d0a |
# your code here
|
|
Packit |
d18d0a |
};
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
$stdout = tee_stdout { ... };
|
|
Packit |
d18d0a |
$stderr = tee_stderr { ... };
|
|
Packit |
d18d0a |
$merged = tee_merged { ... };
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
DESCRIPTION
|
|
Packit |
d18d0a |
Capture::Tiny provides a simple, portable way to capture almost anything
|
|
Packit |
d18d0a |
sent to STDOUT or STDERR, regardless of whether it comes from Perl, from
|
|
Packit |
d18d0a |
XS code or from an external program. Optionally, output can be teed so
|
|
Packit |
d18d0a |
that it is captured while being passed through to the original
|
|
Packit |
d18d0a |
filehandles. Yes, it even works on Windows (usually). Stop guessing
|
|
Packit |
d18d0a |
which of a dozen capturing modules to use in any particular situation
|
|
Packit |
d18d0a |
and just use this one.
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
USAGE
|
|
Packit |
d18d0a |
The following functions are available. None are exported by default.
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
capture
|
|
Packit |
d18d0a |
($stdout, $stderr, @result) = capture \&cod;;
|
|
Packit |
d18d0a |
$stdout = capture \&cod;;
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
The "capture" function takes a code reference and returns what is sent
|
|
Packit |
d18d0a |
to STDOUT and STDERR as well as any return values from the code
|
|
Packit |
d18d0a |
reference. In scalar context, it returns only STDOUT. If no output was
|
|
Packit |
d18d0a |
received for a filehandle, it returns an empty string for that
|
|
Packit |
d18d0a |
filehandle. Regardless of calling context, all output is captured --
|
|
Packit |
d18d0a |
nothing is passed to the existing filehandles.
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
It is prototyped to take a subroutine reference as an argument. Thus, it
|
|
Packit |
d18d0a |
can be called in block form:
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
($stdout, $stderr) = capture {
|
|
Packit |
d18d0a |
# your code here ...
|
|
Packit |
d18d0a |
};
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
Note that the coderef is evaluated in list context. If you wish to force
|
|
Packit |
d18d0a |
scalar context on the return value, you must use the "scalar" keyword.
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
($stdout, $stderr, $count) = capture {
|
|
Packit |
d18d0a |
my @list = qw/one two three/;
|
|
Packit |
d18d0a |
return scalar @list; # $count will be 3
|
|
Packit |
d18d0a |
};
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
Also note that within the coderef, the @_ variable will be empty. So
|
|
Packit |
d18d0a |
don't use arguments from a surrounding subroutine without copying them
|
|
Packit |
d18d0a |
to an array first:
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
sub wont_work {
|
|
Packit |
d18d0a |
my ($stdout, $stderr) = capture { do_stuff( @_ ) }; # WRONG
|
|
Packit |
d18d0a |
...
|
|
Packit |
d18d0a |
}
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
sub will_work {
|
|
Packit |
d18d0a |
my @args = @_;
|
|
Packit |
d18d0a |
my ($stdout, $stderr) = capture { do_stuff( @args ) }; # RIGHT
|
|
Packit |
d18d0a |
...
|
|
Packit |
d18d0a |
}
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
Captures are normally done to an anonymous temporary filehandle. To
|
|
Packit |
d18d0a |
capture via a named file (e.g. to externally monitor a long-running
|
|
Packit |
d18d0a |
capture), provide custom filehandles as a trailing list of option pairs:
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
my $out_fh = IO::File->new("out.txt", "w+");
|
|
Packit |
d18d0a |
my $err_fh = IO::File->new("out.txt", "w+");
|
|
Packit |
d18d0a |
capture { ... } stdout => $out_fh, stderr => $err_fh;
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
The filehandles must be read/write and seekable. Modifying the files or
|
|
Packit |
d18d0a |
filehandles during a capture operation will give unpredictable results.
|
|
Packit |
d18d0a |
Existing IO layers on them may be changed by the capture.
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
When called in void context, "capture" saves memory and time by not
|
|
Packit |
d18d0a |
reading back from the capture handles.
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
capture_stdout
|
|
Packit |
d18d0a |
($stdout, @result) = capture_stdout \&cod;;
|
|
Packit |
d18d0a |
$stdout = capture_stdout \&cod;;
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
The "capture_stdout" function works just like "capture" except only
|
|
Packit |
d18d0a |
STDOUT is captured. STDERR is not captured.
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
capture_stderr
|
|
Packit |
d18d0a |
($stderr, @result) = capture_stderr \&cod;;
|
|
Packit |
d18d0a |
$stderr = capture_stderr \&cod;;
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
The "capture_stderr" function works just like "capture" except only
|
|
Packit |
d18d0a |
STDERR is captured. STDOUT is not captured.
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
capture_merged
|
|
Packit |
d18d0a |
($merged, @result) = capture_merged \&cod;;
|
|
Packit |
d18d0a |
$merged = capture_merged \&cod;;
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
The "capture_merged" function works just like "capture" except STDOUT
|
|
Packit |
d18d0a |
and STDERR are merged. (Technically, STDERR is redirected to the same
|
|
Packit |
d18d0a |
capturing handle as STDOUT before executing the function.)
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
Caution: STDOUT and STDERR output in the merged result are not
|
|
Packit |
d18d0a |
guaranteed to be properly ordered due to buffering.
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
tee
|
|
Packit |
d18d0a |
($stdout, $stderr, @result) = tee \&cod;;
|
|
Packit |
d18d0a |
$stdout = tee \&cod;;
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
The "tee" function works just like "capture", except that output is
|
|
Packit |
d18d0a |
captured as well as passed on to the original STDOUT and STDERR.
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
When called in void context, "tee" saves memory and time by not reading
|
|
Packit |
d18d0a |
back from the capture handles, except when the original STDOUT OR STDERR
|
|
Packit |
d18d0a |
were tied or opened to a scalar handle.
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
tee_stdout
|
|
Packit |
d18d0a |
($stdout, @result) = tee_stdout \&cod;;
|
|
Packit |
d18d0a |
$stdout = tee_stdout \&cod;;
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
The "tee_stdout" function works just like "tee" except only STDOUT is
|
|
Packit |
d18d0a |
teed. STDERR is not teed (output goes to STDERR as usual).
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
tee_stderr
|
|
Packit |
d18d0a |
($stderr, @result) = tee_stderr \&cod;;
|
|
Packit |
d18d0a |
$stderr = tee_stderr \&cod;;
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
The "tee_stderr" function works just like "tee" except only STDERR is
|
|
Packit |
d18d0a |
teed. STDOUT is not teed (output goes to STDOUT as usual).
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
tee_merged
|
|
Packit |
d18d0a |
($merged, @result) = tee_merged \&cod;;
|
|
Packit |
d18d0a |
$merged = tee_merged \&cod;;
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
The "tee_merged" function works just like "capture_merged" except that
|
|
Packit |
d18d0a |
output is captured as well as passed on to STDOUT.
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
Caution: STDOUT and STDERR output in the merged result are not
|
|
Packit |
d18d0a |
guaranteed to be properly ordered due to buffering.
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
LIMITATIONS
|
|
Packit |
d18d0a |
Portability
|
|
Packit |
d18d0a |
Portability is a goal, not a guarantee. "tee" requires fork, except on
|
|
Packit |
d18d0a |
Windows where "system(1, @cmd)" is used instead. Not tested on any
|
|
Packit |
d18d0a |
particularly esoteric platforms yet. See the CPAN Testers Matrix
|
|
Packit |
d18d0a |
<http://matrix.cpantesters.org/?dist=Capture-Tiny> for test result by
|
|
Packit |
d18d0a |
platform.
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
PerlIO layers
|
|
Packit |
d18d0a |
Capture::Tiny does its best to preserve PerlIO layers such as ':utf8' or
|
|
Packit |
d18d0a |
':crlf' when capturing (only for Perl 5.8.1+) . Layers should be applied
|
|
Packit |
d18d0a |
to STDOUT or STDERR *before* the call to "capture" or "tee". This may
|
|
Packit |
d18d0a |
not work for tied filehandles (see below).
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
Modifying filehandles before capturing
|
|
Packit |
d18d0a |
Generally speaking, you should do little or no manipulation of the
|
|
Packit |
d18d0a |
standard IO filehandles prior to using Capture::Tiny. In particular,
|
|
Packit |
d18d0a |
closing, reopening, localizing or tying standard filehandles prior to
|
|
Packit |
d18d0a |
capture may cause a variety of unexpected, undesirable and/or unreliable
|
|
Packit |
d18d0a |
behaviors, as described below. Capture::Tiny does its best to compensate
|
|
Packit |
d18d0a |
for these situations, but the results may not be what you desire.
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
Closed filehandles
|
|
Packit |
d18d0a |
Capture::Tiny will work even if STDIN, STDOUT or STDERR have been
|
|
Packit |
d18d0a |
previously closed. However, since they will be reopened to capture or
|
|
Packit |
d18d0a |
tee output, any code within the captured block that depends on finding
|
|
Packit |
d18d0a |
them closed will, of course, not find them to be closed. If they started
|
|
Packit |
d18d0a |
closed, Capture::Tiny will close them again when the capture block
|
|
Packit |
d18d0a |
finishes.
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
Note that this reopening will happen even for STDIN or a filehandle not
|
|
Packit |
d18d0a |
being captured to ensure that the filehandle used for capture is not
|
|
Packit |
d18d0a |
opened to file descriptor 0, as this causes problems on various
|
|
Packit |
d18d0a |
platforms.
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
Prior to Perl 5.12, closed STDIN combined with PERL_UNICODE=D leaks
|
|
Packit |
d18d0a |
filehandles and also breaks tee() for undiagnosed reasons. So don't do
|
|
Packit |
d18d0a |
that.
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
Localized filehandles
|
|
Packit |
d18d0a |
If code localizes any of Perl's standard filehandles before capturing,
|
|
Packit |
d18d0a |
the capture will affect the localized filehandles and not the original
|
|
Packit |
d18d0a |
ones. External system calls are not affected by localizing a filehandle
|
|
Packit |
d18d0a |
in Perl and will continue to send output to the original filehandles
|
|
Packit |
d18d0a |
(which will thus not be captured).
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
Scalar filehandles
|
|
Packit |
d18d0a |
If STDOUT or STDERR are reopened to scalar filehandles prior to the call
|
|
Packit |
d18d0a |
to "capture" or "tee", then Capture::Tiny will override the output
|
|
Packit |
d18d0a |
filehandle for the duration of the "capture" or "tee" call and then, for
|
|
Packit |
d18d0a |
"tee", send captured output to the output filehandle after the capture
|
|
Packit |
d18d0a |
is complete. (Requires Perl 5.8)
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
Capture::Tiny attempts to preserve the semantics of STDIN opened to a
|
|
Packit |
d18d0a |
scalar reference, but note that external processes will not be able to
|
|
Packit |
d18d0a |
read from such a handle. Capture::Tiny tries to ensure that external
|
|
Packit |
d18d0a |
processes will read from the null device instead, but this is not
|
|
Packit |
d18d0a |
guaranteed.
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
Tied output filehandles
|
|
Packit |
d18d0a |
If STDOUT or STDERR are tied prior to the call to "capture" or "tee",
|
|
Packit |
d18d0a |
then Capture::Tiny will attempt to override the tie for the duration of
|
|
Packit |
d18d0a |
the "capture" or "tee" call and then send captured output to the tied
|
|
Packit |
d18d0a |
filehandle after the capture is complete. (Requires Perl 5.8)
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
Capture::Tiny may not succeed resending UTF-8 encoded data to a tied
|
|
Packit |
d18d0a |
STDOUT or STDERR filehandle. Characters may appear as bytes. If the tied
|
|
Packit |
d18d0a |
filehandle is based on Tie::StdHandle, then Capture::Tiny will attempt
|
|
Packit |
d18d0a |
to determine appropriate layers like ":utf8" from the underlying
|
|
Packit |
d18d0a |
filehandle and do the right thing.
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
Tied input filehandle
|
|
Packit |
d18d0a |
Capture::Tiny attempts to preserve the semantics of tied STDIN, but this
|
|
Packit |
d18d0a |
requires Perl 5.8 and is not entirely predictable. External processes
|
|
Packit |
d18d0a |
will not be able to read from such a handle.
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
Unless having STDIN tied is crucial, it may be safest to localize STDIN
|
|
Packit |
d18d0a |
when capturing:
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
my ($out, $err) = do { local *STDIN; capture { ... } };
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
Modifying filehandles during a capture
|
|
Packit |
d18d0a |
Attempting to modify STDIN, STDOUT or STDERR *during* "capture" or "tee"
|
|
Packit |
d18d0a |
is almost certainly going to cause problems. Don't do that.
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
Forking inside a capture
|
|
Packit |
d18d0a |
Forks aren't portable. The behavior of filehandles during a fork is even
|
|
Packit |
d18d0a |
less so. If Capture::Tiny detects that a fork has occurred within a
|
|
Packit |
d18d0a |
capture, it will shortcut in the child process and return empty strings
|
|
Packit |
d18d0a |
for captures. Other problems may occur in the child or parent, as well.
|
|
Packit |
d18d0a |
Forking in a capture block is not recommended.
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
Using threads
|
|
Packit |
d18d0a |
Filehandles are global. Mixing up I/O and captures in different threads
|
|
Packit |
d18d0a |
without coordination is going to cause problems. Besides, threads are
|
|
Packit |
d18d0a |
officially discouraged.
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
Dropping privileges during a capture
|
|
Packit |
d18d0a |
If you drop privileges during a capture, temporary files created to
|
|
Packit |
d18d0a |
facilitate the capture may not be cleaned up afterwards.
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
No support for Perl 5.8.0
|
|
Packit |
d18d0a |
It's just too buggy when it comes to layers and UTF-8. Perl 5.8.1 or
|
|
Packit |
d18d0a |
later is recommended.
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
Limited support for Perl 5.6
|
|
Packit |
d18d0a |
Perl 5.6 predates PerlIO. UTF-8 data may not be captured correctly.
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
ENVIRONMENT
|
|
Packit |
d18d0a |
PERL_CAPTURE_TINY_TIMEOUT
|
|
Packit |
d18d0a |
Capture::Tiny uses subprocesses internally for "tee". By default,
|
|
Packit |
d18d0a |
Capture::Tiny will timeout with an error if such subprocesses are not
|
|
Packit |
d18d0a |
ready to receive data within 30 seconds (or whatever is the value of
|
|
Packit |
d18d0a |
$Capture::Tiny::TIMEOUT). An alternate timeout may be specified by
|
|
Packit |
d18d0a |
setting the "PERL_CAPTURE_TINY_TIMEOUT" environment variable. Setting it
|
|
Packit |
d18d0a |
to zero will disable timeouts. NOTE, this does not timeout the code
|
|
Packit |
d18d0a |
reference being captured -- this only prevents Capture::Tiny itself from
|
|
Packit |
d18d0a |
hanging your process waiting for its child processes to be ready to
|
|
Packit |
d18d0a |
proceed.
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
SEE ALSO
|
|
Packit |
d18d0a |
This module was inspired by IO::CaptureOutput, which provides similar
|
|
Packit |
d18d0a |
functionality without the ability to tee output and with more
|
|
Packit |
d18d0a |
complicated code and API. IO::CaptureOutput does not handle layers or
|
|
Packit |
d18d0a |
most of the unusual cases described in the "Limitations" section and I
|
|
Packit |
d18d0a |
no longer recommend it.
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
There are many other CPAN modules that provide some sort of output
|
|
Packit |
d18d0a |
capture, albeit with various limitations that make them appropriate only
|
|
Packit |
d18d0a |
in particular circumstances. I'm probably missing some. The long list is
|
|
Packit |
d18d0a |
provided to show why I felt Capture::Tiny was necessary.
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
* IO::Capture
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
* IO::Capture::Extended
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
* IO::CaptureOutput
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
* IPC::Capture
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
* IPC::Cmd
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
* IPC::Open2
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
* IPC::Open3
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
* IPC::Open3::Simple
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
* IPC::Open3::Utils
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
* IPC::Run
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
* IPC::Run::SafeHandles
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
* IPC::Run::Simple
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
* IPC::Run3
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
* IPC::System::Simple
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
* Tee
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
* IO::Tee
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
* File::Tee
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
* Filter::Handle
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
* Tie::STDERR
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
* Tie::STDOUT
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
* Test::Output
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
SUPPORT
|
|
Packit |
d18d0a |
Bugs / Feature Requests
|
|
Packit |
d18d0a |
Please report any bugs or feature requests through the issue tracker at
|
|
Packit |
d18d0a |
<https://github.com/dagolden/Capture-Tiny/issues>. You will be notified
|
|
Packit |
d18d0a |
automatically of any progress on your issue.
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
Source Code
|
|
Packit |
d18d0a |
This is open source software. The code repository is available for
|
|
Packit |
d18d0a |
public review and contribution under the terms of the license.
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
<https://github.com/dagolden/Capture-Tiny>
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
git clone https://github.com/dagolden/Capture-Tiny.git
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
AUTHOR
|
|
Packit |
d18d0a |
David Golden <dagolden@cpan.org>
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
CONTRIBUTORS
|
|
Packit |
d18d0a |
* Dagfinn Ilmari Mannsåker <ilmari@ilmari.org>
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
* David E. Wheeler <david@justatheory.com>
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
* fecundf <not.com+github@gmail.com>
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
* Graham Knop <haarg@haarg.org>
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
* Peter Rabbitson <ribasushi@cpan.org>
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
COPYRIGHT AND LICENSE
|
|
Packit |
d18d0a |
This software is Copyright (c) 2009 by David Golden.
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
This is free software, licensed under:
|
|
Packit |
d18d0a |
|
|
Packit |
d18d0a |
The Apache License, Version 2.0, January 2004
|
|
Packit |
d18d0a |
|