aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2020-01-30 17:03:55 +0200
committerSergey Poznyakoff <gray@gnu.org.ua>2020-01-30 17:07:21 +0200
commit821e391af97d414cf0e468e2b8ee8243d11e8c77 (patch)
treeece9c338bde41027e1c81baa477ab0c927e9a7d9
parent98ecb66f95cf705fad1d45fde3d1bbe387b9b69e (diff)
downloadposixruncapture-821e391af97d414cf0e468e2b8ee8243d11e8c77.tar.gz
posixruncapture-821e391af97d414cf0e468e2b8ee8243d11e8c77.tar.bz2
Accept glob (file handle) or string (file name) with stderr and stdout keyword arguments
* runcap: Upgrade. * Capture.xs: Pass stdout/stderr value without dereferencing it. * capture.h (line_closure): New member "fd". * capture.c (capture_set_output) (capture_close_output): New functions. (capture_new): Use capture_set_output. * lib/POSIX/Run/Capture.pm: Document changes. * Makefile.PL: Declare TEST_REQUIRES. * t/10file.t: New file.
-rw-r--r--Capture.xs6
-rw-r--r--Makefile.PL4
-rw-r--r--capture.c85
-rw-r--r--capture.h9
-rw-r--r--lib/POSIX/Run/Capture.pm24
m---------runcap0
-rw-r--r--t/10file.t24
7 files changed, 112 insertions, 40 deletions
diff --git a/Capture.xs b/Capture.xs
index 19da27b..8cd1d8a 100644
--- a/Capture.xs
+++ b/Capture.xs
@@ -39,11 +39,7 @@ capture_new(package, ...)
croak("argv must be an array ref");
} else if (strcmp(kw, "stdout") == 0
|| strcmp(kw, "stderr") == 0) {
- if (SvROK(val)
- && SvTYPE(SvRV(val)) == SVt_PVCV) {
- cb[kw[3] == 'o' ? 0 : 1] = SvRV(val);
- } else
- croak("%s must be a code ref", kw);
+ cb[kw[3] == 'o' ? 0 : 1] = val;
} else if (strcmp(kw, "timeout") == 0) {
if (SvIOK(val)) {
timeout = SvUV(val);
diff --git a/Makefile.PL b/Makefile.PL
index 4aee252..7338001 100644
--- a/Makefile.PL
+++ b/Makefile.PL
@@ -9,6 +9,10 @@ WriteMakefile(
VERSION_FROM => 'lib/POSIX/Run/Capture.pm',
LICENSE => 'gpl_3',
PREREQ_PM => {}, # e.g., Module::Name => 1.1
+ TEST_REQUIRES => {
+ 'File::Temp' => 0.23,
+ 'File::Cmp' => 1.06
+ },
MIN_PERL_VERSION => 5.006,
ABSTRACT_FROM => 'lib/POSIX/Run/Capture.pm',
AUTHOR => 'Sergey Poznyakoff <gray@gnu.org>',
diff --git a/capture.c b/capture.c
index 454a779..6468d84 100644
--- a/capture.c
+++ b/capture.c
@@ -119,6 +119,61 @@ free_argv(struct capture *cp)
}
}
+static void
+capture_set_output(struct capture *cp, SV *cb[2], int strno)
+{
+ SV *sv = cb[strno-1];
+
+ if (sv == &PL_sv_undef)
+ return;
+
+ if (SvROK(sv)) {
+ sv = SvRV(sv);
+
+ // FIXME: Do we need SvGETMAGIC (sv);?
+ if (SvTYPE(sv) == SVt_PVCV) {
+ SvREFCNT_inc(sv);
+ cp->closure[strno-1].cv = sv;
+ cp->rc.rc_cap[strno].sc_linemon = line_monitor;
+ cp->rc.rc_cap[strno].sc_monarg = &cp->closure[strno-1];
+ cp->flags |= RCF_SC_TO_FLAG(RCF_SC_LINEMON, strno);
+ } else if (SvTYPE(sv) == SVt_PVGV) {
+ int fd;
+
+ // FIXME: Check if sv is writable
+ SvREFCNT_inc(sv);
+ cp->rc.rc_cap[strno].sc_storfd =
+ PerlIO_fileno(IoOFP(sv_2io(sv)));
+ cp->flags |= RCF_SC_TO_FLAG(RCF_SC_STORFD, strno);
+ } else {
+ static char *what[] = { "stdout", "stderr" };
+ croak("%s must be scalar, glob or code ref",
+ what[strno-1]);
+ }
+ } else {
+ char *filename = SvPV_nolen(sv);
+ int fd = open(filename, O_CREAT|O_TRUNC|O_RDWR, 0777);
+ if (fd == -1) {
+ croak("can't open file %s for writing: %s",
+ filename, strerror(errno));
+ }
+ cp->rc.rc_cap[strno].sc_storfd = fd;
+ cp->flags |= RCF_SC_TO_FLAG(RCF_SC_STORFD, strno);
+ cp->closure[strno-1].fd = fd;
+ }
+}
+
+static void
+capture_close_output(struct capture *cp, int strno)
+{
+ strno--;
+ free(cp->closure[strno].str);
+ if (cp->closure[strno].cv != &PL_sv_undef)
+ SvREFCNT_dec(cp->closure[strno].cv);
+ if (cp->closure[strno].fd != -1)
+ close(cp->closure[strno].fd);
+}
+
struct capture *
capture_new(SV *program, ARGV argv, unsigned timeout, SV *cb[2], SV *input)
{
@@ -129,7 +184,8 @@ capture_new(SV *program, ARGV argv, unsigned timeout, SV *cb[2], SV *input)
if (!cp)
croak_nomem();
memset(cp, 0, sizeof *cp);
-
+ cp->closure[0].fd = cp->closure[1].fd = -1;
+
cp->rc.rc_argv = argv;
cp->program = program;
@@ -144,22 +200,8 @@ capture_new(SV *program, ARGV argv, unsigned timeout, SV *cb[2], SV *input)
cp->flags |= RCF_TIMEOUT;
}
- cp->closure[0].cv = cb[0];
- if (cb[0] != &PL_sv_undef) {
- SvREFCNT_inc(cb[0]);
- cp->rc.rc_cap[RUNCAP_STDOUT].sc_linemon = line_monitor;
- cp->rc.rc_cap[RUNCAP_STDOUT].sc_monarg = &cp->closure[0];
- cp->flags |= RCF_STDOUT_LINEMON;
- }
-
- cp->closure[1].cv = cb[1];
- if (cb[1] != &PL_sv_undef) {
- SvREFCNT_inc(cb[1]);
- cp->closure[1].cv = cb[1];
- cp->rc.rc_cap[RUNCAP_STDERR].sc_linemon = line_monitor;
- cp->rc.rc_cap[RUNCAP_STDERR].sc_monarg = &cp->closure[1];
- cp->flags |= RCF_STDERR_LINEMON;
- }
+ capture_set_output(cp, cb, RUNCAP_STDOUT);
+ capture_set_output(cp, cb, RUNCAP_STDERR);
cp->input = &PL_sv_undef;
capture_set_input(cp, input);
@@ -179,14 +221,9 @@ capture_DESTROY(struct capture *cp)
*/
cp->rc.rc_cap[RUNCAP_STDIN].sc_base = NULL;
cp->rc.rc_cap[RUNCAP_STDIN].sc_fd = -1;
-
- free(cp->closure[0].str);
- if (cp->closure[0].cv != &PL_sv_undef)
- SvREFCNT_dec(cp->closure[0].cv);
- free(cp->closure[1].str);
- if (cp->closure[1].cv != &PL_sv_undef)
- SvREFCNT_dec(cp->closure[1].cv);
+ capture_close_output(cp, RUNCAP_STDOUT);
+ capture_close_output(cp, RUNCAP_STDERR);
free_argv(cp);
runcap_free(&cp->rc);
diff --git a/capture.h b/capture.h
index cad375d..fc06eb6 100644
--- a/capture.h
+++ b/capture.h
@@ -9,10 +9,11 @@
struct line_closure
{
- char *str;
- size_t len;
- size_t size;
- SV *cv;
+ char *str; /* Line buffer */
+ size_t len; /* Length of the collected line in buffer */
+ size_t size; /* Line buffer size */
+ SV *cv; /* Ref to Perl callback sub */
+ int fd; /* Close this descriptor at destroy, unless -1 */
};
struct capture {
diff --git a/lib/POSIX/Run/Capture.pm b/lib/POSIX/Run/Capture.pm
index b6d78b5..57c7d57 100644
--- a/lib/POSIX/Run/Capture.pm
+++ b/lib/POSIX/Run/Capture.pm
@@ -28,7 +28,7 @@ our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
#our @EXPORT = qw();
-our $VERSION = '1.00';
+our $VERSION = '1.00_01';
require XSLoader;
XSLoader::load('POSIX::Run::Capture', $VERSION);
@@ -109,8 +109,8 @@ Creates a new capture object. There are three possible invocation modes.
new POSIX::Run::Capture(argv => [ $command, @args ],
program => $prog,
stdin => $fh_or_string,
- stdout => sub { ... },
- stderr => sub { ... },
+ stdout => $ref_or_string,
+ stderr => $ref_or_string,
timeout => $n)
When named arguments are used, the following keywords are allowed:
@@ -131,7 +131,7 @@ Sets the pathname of binary file to run.
Supplies standard input for the command. The argument can be a string or
a file handle.
-=item B<stdout>
+=item B<stdout> =E<gt> I<$coderef>
Sets the I<line monitor> function for standard output. Line monitor is
invoked each time a complete line is read, or the EOF is hit on the standard
@@ -145,10 +145,20 @@ following example monitor function prints its argument to STDOUT:
Notice that the last line read can lack the teminating newline character.
-=item B<stderr>
+=item B<stdout> =E<gt> I<FH>
-Sets the I<line monitor> function for standard error stream. See the
-description above.
+Capture standard output and write it to the file handle I<FH>.
+
+=item B<stdout> =E<gt> I<NAME>
+
+Capture standard output and write it to the file I<NAME>. If the file
+exists, it will be truncated.
+
+=item B<stderr> =E<gt> I<$arg>
+
+Sets the I<line monitor> function for standard error or redirects it to
+the file handle or file, depending on the type of I<$arg>. See the
+description of B<stdout> above.
=item B<timeout>
diff --git a/runcap b/runcap
-Subproject 12c5eac517cb62728b5525cab25aff245086525
+Subproject 3bc027ef68aa1f876bfc3b7d1e60ea5d478ddad
diff --git a/t/10file.t b/t/10file.t
new file mode 100644
index 0000000..a5f9045
--- /dev/null
+++ b/t/10file.t
@@ -0,0 +1,24 @@
+# -*- perl -*-
+
+use lib 't', 'lib';
+
+use strict;
+use warnings;
+use TestCapture;
+use Test::More tests => 2;
+use File::Temp;
+use File::Cmp qw/fcmp/;
+use File::Spec;
+
+our($catbin, $input, $content);
+
+my $fh = new File::Temp;
+TestCapture({ argv => [$catbin, $input],
+ stdout => $fh });
+ok(fcmp($fh->filename, $input));
+
+$fh = new File::Temp;
+TestCapture({ argv => [$catbin, $input],
+ stdout => $fh->filename });
+ok(fcmp($fh->filename, $input));
+

Return to:

Send suggestions and report system problems to the System administrator.