aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2020-01-30 14:08:23 +0200
committerSergey Poznyakoff <gray@gnu.org.ua>2020-01-30 14:08:23 +0200
commit3bc027ef68aa1f876bfc3b7d1e60ea5d478ddade (patch)
treebc328f31a472eb5cb7dee6f7d7afbea79d129288
parent99f77c702c0213b9c22c21f4e9568e509d332903 (diff)
downloadruncap-3bc027ef68aa1f876bfc3b7d1e60ea5d478ddade.tar.gz
runcap-3bc027ef68aa1f876bfc3b7d1e60ea5d478ddade.tar.bz2
Optionally store the captured stream in a file.v1.3
* runcap.3: Document the sc_storfd member and the RCF_STDOUT_STORFD and RCF_STDERR_STORFD flags. * runcap.c (stream_capture_init): Rewrite. (stream_capture_reset): Don't close sc_storfd if RCF_SC_STORFD flag is set. (runcap_start): Redirect output to the corresponding sc_storfd descriptor if so requested. * runcap.h (stream_capture): Replace sc_nocap with sc_flags. (RCF_SC_SIZE,RCF_SC_LINEMON) (RCF_SC_NOCAP,RCF_SC_STORFD): New stream-specific constants. (RCF_SC_TO_FLAG,RCF_FLAG_TO_SC): New macros. Rewrite existing RCF_STDOUT_* and RCF_STDERR_* flags using RCF_SC_TO_FLAG. (RCF_STDOUT_STORFD,RCF_STDERR_STORFD): New flags. * t/redirect.at: New test case. * t/Makefile.am: Add new test. * t/testsuite.at: Likewise. * t/rt.c: New option -o: send output to a file.
-rw-r--r--runcap.327
-rw-r--r--runcap.c50
-rw-r--r--runcap.h28
-rw-r--r--t/Makefile.am1
-rw-r--r--t/redirect.at69
-rw-r--r--t/rt.c33
-rw-r--r--t/testsuite.at1
7 files changed, 178 insertions, 31 deletions
diff --git a/runcap.3 b/runcap.3
index 39ef89f..8133448 100644
--- a/runcap.3
+++ b/runcap.3
@@ -125,6 +125,8 @@ struct stream_capture
off_t sc_leng; /* [\fIOUT\fR] total length of captured data */
size_t sc_nlines; /* [\fIOUT\fR] number of captured lines */
+ int sc_storfd; /* [\fIIN\fR] storage file descriptor */
+
/* The following two are available only in \fBrc_cap[RUNCAP_STDIN]\fR
For details. see the subsection \fBSupplying standard input\fR.
*/
@@ -208,6 +210,12 @@ condition is met:
.B sc_monarg
The value of the \fIdata\fR parameter for the \fBsc_linemon\fR
function.
+.TP
+.B sc_storfd
+File descriptor of the storage file for that stream. If initialized, the
+\fBRCF_STDOUT_STORFD\fR or \fBRCF_STDERR_STORFD\fR bit must be set in
+\fIflags\fR. All data obtained by that stream will be written to this
+descriptor, in addition to the normal processing.
.SS Supplying standard input
The
.B rc_cap[RUNCAP_STDIN]
@@ -262,7 +270,7 @@ anyways.
.PP
Captured data can be retrieved using the \fBruncap_getc\fR, and
\fBruncap_getline\fR functions, described below.
-.SH Examining output
+.SS Examining output
Upon return from \fBruncap\fR the following functions can be used to
retrieve captured data from the \fBstruct runcap\fR object pointed
to by its \fIrc\fR argument. The stream to retrieve data from is
@@ -337,6 +345,23 @@ and
runcap_rewind(rc, stream) <=> runcap_seek(rc, stream, 0, SEEK_SET)
.fi
.in
+.SS Redirecting output
+In addition to the normal processing, the output sent to a particular
+stream can be redirected to a file. To do so, initialize the
+\fBsc_storfd\fR member of the output stream to the file descriptor
+and set the \fBRCF_STDOUT_STORFD\fR or \fBRCF_STDERR_STORFD\fR
+flag (depending on the stream). For example, the following code
+redirects the standard output of the program to the file "outfile":
+.PP
+.in +4n
+.nf
+int fd = open("outfile", O_CREAT|O_RDWR|O_TRUNC, 0600);
+assert(fd != -1);
+rc->rc_cap[RUNCAP_STDOUT].sc_storfd = fd;
+
+runcap(&rc, RCF_STDOUT_STORFD);
+.fi
+.in
.SS Disabling capturing
Two ways are provided to disable capturing of a particular stream.
First, you can set the \fBsc_size\fR member of the corresponding
diff --git a/runcap.c b/runcap.c
index 15a8578..f2c80bf 100644
--- a/runcap.c
+++ b/runcap.c
@@ -27,8 +27,7 @@
#include "runcap.h"
static int
-stream_capture_init(struct stream_capture *cap, size_t size, int linemon,
- int nocap)
+stream_capture_init(struct stream_capture *cap, size_t size, int flags)
{
if (!cap) {
errno = EINVAL;
@@ -36,10 +35,10 @@ stream_capture_init(struct stream_capture *cap, size_t size, int linemon,
}
if (size == 0) {
- if (linemon)
+ if (flags & RCF_SC_LINEMON)
size = STRCAP_BUFSIZE;
- nocap = 1;
- } else if (nocap && !linemon)
+ flags |= RCF_SC_NOCAP;
+ } else if ((flags & RCF_SC_NOCAP) && !(flags & RCF_SC_LINEMON))
size = 0;
if (size) {
@@ -53,14 +52,15 @@ stream_capture_init(struct stream_capture *cap, size_t size, int linemon,
cap->sc_level = 0;
cap->sc_nlines = 0;
cap->sc_cur = 0;
- cap->sc_storfd = -1;
+ if (!(flags & RCF_SC_STORFD))
+ cap->sc_storfd = -1;
cap->sc_fd = -1;
- if (!linemon) {
+ if (!(flags & RCF_SC_LINEMON)) {
cap->sc_linemon = NULL;
cap->sc_monarg = NULL;
}
- cap->sc_nocap = nocap;
+ cap->sc_flags = flags;
return 0;
}
@@ -74,8 +74,12 @@ stream_capture_reset(struct stream_capture *cap)
cap->sc_cur = 0;
if (cap->sc_storfd >= 0) {
- close(cap->sc_storfd);
- cap->sc_storfd = -1;
+ if (cap->sc_flags & RCF_SC_STORFD) {
+ /* FIXME: Restore fd position? */;
+ } else {
+ close(cap->sc_storfd);
+ cap->sc_storfd = -1;
+ }
}
if (cap->sc_fd >= 0) {
close(cap->sc_fd);
@@ -123,7 +127,7 @@ stream_capture_flush(struct stream_capture *cap)
cap->sc_linemon(cap->sc_base + cap->sc_cur,
cap->sc_level - cap->sc_cur,
cap->sc_monarg);
- if (!cap->sc_nocap) {
+ if (!(cap->sc_flags & RCF_SC_NOCAP)) {
if (cap->sc_storfd == -1) {
int fd;
char tmpl[] = "/tmp/rcXXXXXX";
@@ -192,7 +196,9 @@ stream_capture_get(struct stream_capture *cap, int *feof)
}
}
- if (cap->sc_nocap && cap->sc_linemon && cap->sc_level > cap->sc_cur) {
+ if ((cap->sc_flags & RCF_SC_NOCAP)
+ && cap->sc_linemon
+ && cap->sc_level > cap->sc_cur) {
memmove(cap->sc_base, cap->sc_base + cap->sc_cur,
cap->sc_level - cap->sc_cur);
cap->sc_level -= cap->sc_cur;
@@ -279,13 +285,19 @@ runcap_start(struct runcap *rc)
if (p[RUNCAP_STDOUT][0] >= 0) {
dup2(p[RUNCAP_STDOUT][1], RUNCAP_STDOUT);
close(p[RUNCAP_STDOUT][0]);
- }
+ } else if (rc->rc_cap[RUNCAP_STDOUT].sc_storfd != -1) {
+ dup2(rc->rc_cap[RUNCAP_STDOUT].sc_storfd,
+ RUNCAP_STDOUT);
+ }
if (p[RUNCAP_STDERR][0] >= 0) {
dup2(p[RUNCAP_STDERR][1], RUNCAP_STDERR);
close(p[RUNCAP_STDERR][0]);
- }
-
+ } else if (rc->rc_cap[RUNCAP_STDERR].sc_storfd != -1) {
+ dup2(rc->rc_cap[RUNCAP_STDERR].sc_storfd,
+ RUNCAP_STDERR);
+ }
+
i = open("/dev/null", O_RDONLY);
if (i == -1)
i = sysconf(_SC_OPEN_MAX) - 1;
@@ -478,15 +490,14 @@ runcap_init(struct runcap *rc, int flags)
rc->rc_cap[RUNCAP_STDIN].sc_size;
rc->rc_cap[RUNCAP_STDIN].sc_cur = 0;
rc->rc_cap[RUNCAP_STDIN].sc_storfd = -1;
- } else if (stream_capture_init(&rc->rc_cap[RUNCAP_STDIN], 0, 0, 0))
+ } else if (stream_capture_init(&rc->rc_cap[RUNCAP_STDIN], 0, 0))
return -1;
res = stream_capture_init(&rc->rc_cap[RUNCAP_STDOUT],
(flags & RCF_STDOUT_SIZE)
? rc->rc_cap[RUNCAP_STDOUT].sc_size
: STRCAP_BUFSIZE,
- flags & RCF_STDOUT_LINEMON,
- flags & RCF_STDOUT_NOCAP);
+ RCF_FLAG_TO_SC(flags, RUNCAP_STDOUT));
if (res)
return res;
@@ -494,8 +505,7 @@ runcap_init(struct runcap *rc, int flags)
(flags & RCF_STDERR_SIZE)
? rc->rc_cap[RUNCAP_STDERR].sc_size
: STRCAP_BUFSIZE,
- flags & RCF_STDERR_LINEMON,
- flags & RCF_STDERR_NOCAP);
+ RCF_FLAG_TO_SC(flags, RUNCAP_STDERR));
if (res)
return res;
diff --git a/runcap.h b/runcap.h
index b28e650..c1b5f1f 100644
--- a/runcap.h
+++ b/runcap.h
@@ -31,7 +31,7 @@ struct stream_capture
void (*sc_linemon)(const char *, size_t, void *);
/* Line monitor function */
void *sc_monarg; /* Line monitor argument */
- int sc_nocap; /* If 1, capturing is disabled */
+ int sc_flags; /* Stream flags */
};
#define STRCAP_BUFSIZE 4096
@@ -58,12 +58,24 @@ struct runcap
#define RCF_PROGRAM 0x0001 /* rc_program is set */
#define RCF_TIMEOUT 0x0002 /* rc_timeout is set */
#define RCF_STDIN 0x0004 /* rc_cap[RUNCAP_STDIN] is set */
-#define RCF_STDOUT_SIZE 0x0008 /* rc_cap[RUNCAP_STDOUT].sc_size is set */
-#define RCF_STDOUT_LINEMON 0x0010 /* rc_cap[RUNCAP_STDOUT].sc_linemon is set */
-#define RCF_STDERR_SIZE 0x0020 /* rc_cap[RUNCAP_STDERR].sc_size is set */
-#define RCF_STDERR_LINEMON 0x0040 /* rc_cap[RUNCAP_STDERR].sc_linemon is set */
-#define RCF_STDOUT_NOCAP 0x0080 /* disable stdout capturing */
-#define RCF_STDERR_NOCAP 0x0100 /* disable stderr capturing */
+
+#define RCF_SC_SIZE 0x1 /* sc_size is set */
+#define RCF_SC_LINEMON 0x2 /* sc_linemon is set*/
+#define RCF_SC_NOCAP 0x4 /* capturing is disabled */
+#define RCF_SC_STORFD 0x8 /* sc_storfd is set */
+
+#define RCF_SC_TO_FLAG(f,s) ((f) << (4*(s)))
+#define RCF_FLAG_TO_SC(f,s) (((f) >> (4*(s))) & 0xf)
+
+#define RCF_STDOUT_SIZE RCF_SC_TO_FLAG(RCF_SC_SIZE, RUNCAP_STDOUT)
+#define RCF_STDOUT_LINEMON RCF_SC_TO_FLAG(RCF_SC_LINEMON, RUNCAP_STDOUT)
+#define RCF_STDOUT_NOCAP RCF_SC_TO_FLAG(RCF_SC_NOCAP, RUNCAP_STDOUT)
+#define RCF_STDOUT_STORFD RCF_SC_TO_FLAG(RCF_SC_STORFD, RUNCAP_STDOUT)
+
+#define RCF_STDERR_SIZE RCF_SC_TO_FLAG(RCF_SC_SIZE, RUNCAP_STDERR)
+#define RCF_STDERR_LINEMON RCF_SC_TO_FLAG(RCF_SC_LINEMON, RUNCAP_STDERR)
+#define RCF_STDERR_NOCAP RCF_SC_TO_FLAG(RCF_SC_NOCAP, RUNCAP_STDERR)
+#define RCF_STDERR_STORFD RCF_SC_TO_FLAG(RCF_SC_STORFD, RUNCAP_STDERR)
int runcap(struct runcap *rc, int flags);
void runcap_free(struct runcap *rc);
@@ -80,7 +92,7 @@ runcap_get_capture(struct runcap *rc, int stream)
fp = &rc->rc_cap[stream];
- if (!fp->sc_base || fp->sc_size == 0 || fp->sc_nocap) {
+ if (!fp->sc_base || fp->sc_size == 0 || (fp->sc_flags & RCF_SC_NOCAP)) {
errno = EINVAL;
return NULL;
}
diff --git a/t/Makefile.am b/t/Makefile.am
index 4b812c3..df58a15 100644
--- a/t/Makefile.am
+++ b/t/Makefile.am
@@ -53,6 +53,7 @@ TESTSUITE_AT = \
linemon02.at\
linemon03.at\
nocap.at\
+ redirect.at\
seek00.at\
seek01.at
# Add more files here
diff --git a/t/redirect.at b/t/redirect.at
new file mode 100644
index 0000000..98dc96a
--- /dev/null
+++ b/t/redirect.at
@@ -0,0 +1,69 @@
+# Testcase for runcap - run program and capture its output -*- autotest -*-
+# Copyright (C) 2017-2020 Sergey Poznyakoff
+#
+# Runcap is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 3 of the License, or (at your
+# option) any later version.
+#
+# Runcap is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with Runcap. If not, see <http://www.gnu.org/licenses/>.
+
+AT_SETUP([redirect stdout])
+AT_KEYWORDS([redirect])
+AT_CHECK([set -e
+rt -S stdout -o file -- genout $INPUT
+cmp file $INPUT],
+[0],
+[res=0
+exit code: 0
+stdout: 71 lines, 4051 bytes
+stderr: 0 lines, 0 bytes
+])
+AT_CLEANUP
+
+AT_SETUP([redirect stdout with zero buffer size])
+AT_KEYWORDS([redirect])
+AT_CHECK([set -e
+rt -S stdout -s 0 -o file -- genout $INPUT
+cmp file $INPUT],
+[0],
+[res=0
+exit code: 0
+stdout: 0 lines, 0 bytes
+stderr: 0 lines, 0 bytes
+])
+AT_CLEANUP
+
+AT_SETUP([redirect stdout without capturing])
+AT_KEYWORDS([redirect])
+AT_CHECK([set -e
+rt -S stdout -N -o file -- genout $INPUT
+cmp file $INPUT],
+[0],
+[res=0
+exit code: 0
+stdout: 0 lines, 0 bytes
+stderr: 0 lines, 0 bytes
+])
+AT_CLEANUP
+
+
+AT_SETUP([redirect stderr])
+AT_KEYWORDS([redirect])
+AT_CHECK([set -e
+rt -S stderr -o file -- genout -e $INPUT
+cmp file $INPUT],
+[0],
+[res=0
+exit code: 0
+stdout: 0 lines, 0 bytes
+stderr: 71 lines, 4051 bytes
+])
+AT_CLEANUP
+
diff --git a/t/rt.c b/t/rt.c
index 06ecdc1..4b2e7ca 100644
--- a/t/rt.c
+++ b/t/rt.c
@@ -264,7 +264,21 @@ readreq_do(struct runcap *rc, struct readreq *req)
}
}
}
-
+
+void
+open_outfile(char *file, int stream, struct runcap *rc, int *flags)
+{
+ int fd;
+
+ fd = open(file, O_CREAT|O_TRUNC|O_RDWR, 0644);
+ if (fd == -1) {
+ error("can't open %s: %s\n", file, strerror(errno));
+ exit(1);
+ }
+ rc->rc_cap[stream].sc_storfd = fd;
+ *flags |= RCF_SC_TO_FLAG(RCF_SC_STORFD, stream);
+}
+
int
main(int argc, char **argv)
{
@@ -285,12 +299,14 @@ main(int argc, char **argv)
{ "stderr" }
};
+ char *outfile[RUNCAP_NBUF] = { NULL, NULL, NULL };
+
progname = strrchr(argv[0], '/');
if (progname)
progname++;
else
progname = argv[0];
- while ((c = getopt(argc, argv, "?f:iNn:mp:r:S:s:t:")) != EOF) {
+ while ((c = getopt(argc, argv, "?f:iNn:mo:p:r:S:s:t:")) != EOF) {
switch (c) {
case 'f':
fd = open(optarg, O_RDONLY);
@@ -353,6 +369,14 @@ main(int argc, char **argv)
rcf |= RCF_STDERR_NOCAP;
}
break;
+ case 'o':
+ if (what & WA_STDOUT) {
+ outfile[RUNCAP_STDOUT] = optarg;
+ }
+ if (what & WA_STDERR) {
+ outfile[RUNCAP_STDERR] = optarg;
+ }
+ break;
case 'n':
numlines = whatarg(optarg);
break;
@@ -413,6 +437,11 @@ main(int argc, char **argv)
} else
rc.rc_argv = argv + optind;
+ if (outfile[RUNCAP_STDOUT])
+ open_outfile(outfile[RUNCAP_STDOUT], RUNCAP_STDOUT, &rc, &rcf);
+ if (outfile[RUNCAP_STDERR])
+ open_outfile(outfile[RUNCAP_STDERR], RUNCAP_STDERR, &rc, &rcf);
+
c = runcap(&rc, rcf);
printf("res=%d\n", c);
diff --git a/t/testsuite.at b/t/testsuite.at
index 8a81aff..d0f45b3 100644
--- a/t/testsuite.at
+++ b/t/testsuite.at
@@ -31,4 +31,5 @@ m4_include([linemon03.at])
m4_include([seek00.at])
m4_include([seek01.at])
m4_include([read.at])
+m4_include([redirect.at])

Return to:

Send suggestions and report system problems to the System administrator.