aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Make.am3
-rw-r--r--README258
-rw-r--r--configure.ac18
-rw-r--r--getc.c2
-rw-r--r--getl.c2
-rw-r--r--install.am2
-rw-r--r--read.c69
-rw-r--r--runcap.3167
-rw-r--r--runcap.c119
-rw-r--r--runcap.h31
-rw-r--r--runcap.m42
-rw-r--r--seek.c2
-rw-r--r--shared.am2
-rw-r--r--static.am2
-rw-r--r--t/Makefile.am10
-rw-r--r--t/atlocal.in2
-rw-r--r--t/env.at28
-rw-r--r--t/genout.c17
-rw-r--r--t/linemon00.at2
-rw-r--r--t/linemon01.at2
-rw-r--r--t/linemon02.at39
-rw-r--r--t/linemon03.at100
-rw-r--r--t/lines.at2
-rw-r--r--t/longout.at2
-rw-r--r--t/nocap.at33
-rw-r--r--t/pipe.at2
-rw-r--r--t/read.at65
-rw-r--r--t/redirect.at69
-rw-r--r--t/rt.c288
-rw-r--r--t/seek00.at2
-rw-r--r--t/seek01.at2
-rw-r--r--t/simple.at2
-rw-r--r--t/stdin.at2
-rw-r--r--t/testsuite.at9
-rw-r--r--t/two.at2
-rw-r--r--tell.c2
36 files changed, 1201 insertions, 160 deletions
diff --git a/Make.am b/Make.am
index 7d5f0e6..f9247d8 100644
--- a/Make.am
+++ b/Make.am
@@ -1,5 +1,5 @@
# Main Makefile.am source for runcap
-# Copyright (C) 2017 Sergey Poznyakoff
+# Copyright (C) 2017-2024 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
@@ -32,6 +32,7 @@
RUNCAP_SRC = \
getc.c\
getl.c\
+ read.c\
runcap.c\
seek.c\
tell.c
diff --git a/README b/README
index 91f5b16..b0be4be 100644
--- a/README
+++ b/README
@@ -1,36 +1,204 @@
-#+TITLE: runcap
-
* Overview
The function *runcap* runs an external command, and waits for its
termination, optionally capturing its standard output and standard
error streams, and piping data to its standard input.
-* Building as a standalone library
+Upon return from the function, the caller can obtain the termination
+status of the program and access the captured output data.
+
+* Usage
+
+The function *runcap* is defined as follows:
+
+#+BEGIN_SRC C
+int runcap(struct runcap *rc, int flags);
+#+END_SRC
+
+The =rc= argument points to the structure that controls the execution
+of the program. It contains the input and output members. The only
+member of this structure that must be initialized on input is
+=rc_argv=, which points to the array of pointers to
+null-terminated strings that represent the command name and the
+argument list to that program. Initialization of the rest of input
+members is optional. For each input member, there is a corresponding
+flag in =flags= which must be set, if the member is initialized. For
+example, if the =rc_timeout= member is set (indicating maximum time
+the program execution is allowed to take), then the =RCF_TIMEOUT= flag
+must be set.
+
+Upon return, the function returns the execution status of the
+program, and initializes the output members of =rc= to hold the
+standard output and standard error streams captured from the command.
+Special functions are provided to read these streams.
+
+For a detailed description of *runcap* and accompanying functions,
+please see the [[http://man.gnu.org.ua/manpage/?3+runcap][runcap]](3) manual. In this chapter we will illustrate
+the usage of the *runcap* library by examples.
+
+The example below defines the function *runcom* with the following
+prototype:
+
+#+BEGIN_SRC C
+int runcom(char *cmd, char *in, char **out, char **err);
+#+END_SRC
+
+The function runs the command line given in the ~cmd~ argument
+using =/bin/sh= and returns the data it printed on its standard output
+and error streams in the memory locations pointed to by the arguments
+~out~ and ~err~, correspondingly. If any of these arguments is
+=NULL=, capturing of the corresponding stream will be disabled.
+
+The function returns program exit status on success, -1 if the program
+terminated on signal and -2 on error. This example implements only
+rudimentary error handling, in order to minimize the amount of
+irrelevant code.
+
+#+BEGIN_SRC C
+int runcom(char *cmd, char *in, char **out, char **err)
+{
+ int status;
+ char *p;
+ char c;
+ char *argv[] = { "/bin/sh", "-c", cmd, NULL };
+ struct runcap rc;
+ int rcflags = RCF_TIMEOUT;
+
+ /* Declare the command line to be run. The rc_argv filed is the
+ * only field that must be initialized on input.
+ */
+ rc.rc_argv = argv;
+
+ /* Set maximum execution timeout. The presense of this setting is
+ * indicated by the RCF_TIMEOUT flag in rcflags.
+ */
+ rc.rc_timeout = 10;
+
+ /* If the input string is supplied, initialize the input stream and
+ * raise the RCF_STDIN flag to indicate that it has been initialized.
+ */
+ if (in) {
+ rc.rc_cap[RUNCAP_STDIN].sc_base = in;
+ rc.rc_cap[RUNCAP_STDIN].sc_size = strlen(in);
+ rc.rc_cap[RUNCAP_STDIN].sc_fd = -1;
+ rcflags |= RCF_STDIN;
+ }
+
+ /* If out argument is NULL, disable capturing program's stdout. To
+ * disable capturing a stream, it suffices to initialize its sc_size
+ * field to zero and raise the corresponding RCF_*_SIZE bit in flags.
+ */
+ if (!out) {
+ rc.rc_cap[RUNCAP_STDOUT].sc_size = 0;
+ rcflags |= RCF_STDOUT_SIZE;
+ }
+
+ /* Same for the stderr: */
+ if (!err) {
+ rc.rc_cap[RUNCAP_STDERR].sc_size = 0;
+ rcflags |= RCF_STDERR_SIZE;
+ }
+
+ /* Run the command. The runcap function returns 0 on success. On
+ * error, it returns -1 and sets the errno variable. Its value is
+ * also duplicated in the rc_errno member of struct runcap.
+ */
+ if (runcap(&rc, rcflags)) {
+ perror("runcap");
+ return -2;
+ }
+
+ /* Upon return, the sc_leng member of the capturing structure for
+ * stdout and stderr contains total amount of bytes in the corresponding
+ * stream. The stream can be read using the runcap_getc and
+ * runcap_getline functions.
+ */
+ if (rc.rc_cap[RUNCAP_STDOUT].sc_leng) {
+ p = malloc(rc.rc_cap[RUNCAP_STDOUT].sc_leng + 1);
+ assert(p != NULL);
+ *out = p;
+ while (runcap_getc(&rc, RUNCAP_STDOUT, &c))
+ *p++ = c;
+ *p = 0;
+ } else
+ *out = NULL;
+
+ if (rc.rc_cap[RUNCAP_STDERR].sc_leng) {
+ p = malloc(rc.rc_cap[RUNCAP_STDERR].sc_leng + 1);
+ assert(p != NULL);
+ *err = p;
+ while (runcap_getc(&rc, RUNCAP_STDERR, &c))
+ *p++ = c;
+ *p = 0;
+ } else
+ *err = NULL;
+
+ /* Analyze the exit status of the command */
+ if (WIFEXITED(rc.rc_status)) {
+ status = WEXITSTATUS(rc.rc_status);
+ } else {
+ status = -1;
+
+ if (WIFSIGNALED(rc.rc_status)) {
+ fprintf(stderr, "%s terminated on signal %d\n",
+ argv[0], WTERMSIG(rc.rc_status));
+ } else {
+ fprintf(stderr, "%s terminated with unrecognized status: %d\n",
+ argv[0], rc.rc_status);
+ }
+ }
+ return status;
+}
+#+END_SRC
+
+* Downloading
-The following steps will build the project as a standalone installable
-library:
+To clone the project from the repository, run
+
+#+BEGIN_SRC shell-script
+git clone git://git.gnu.org.ua/runcap.git
+#+END_SRC
+
+* Building
+
+The project can be used either a standalone library, or as a shared
+or static convenience library embedded in another project. If you
+cloned the project from the git repository, you will need to
+bootstrap it first. To do so, change to the =runcap= directory and
+run
+#+BEGIN_SRC shell-script
+ autoreconf -I. -f -i -s
+#+END_SRC
+
+Use the =RUNCAP_BUILD= environment variable to indicate the type of
+the build you need. Allowed values are:
+
+- install :: Build standalone installable library (default).
+- shared :: Build shared convenience library.
+- static :: Build static convenience library.
+
+Once bootstrapped, the project can be built with the usual sequence
+of commands:
1. Configure the package
#+BEGIN_SRC shell-script
./configure
#+END_SRC
-2. Build the project.
+2. Build it
#+BEGIN_SRC shell-script
make
#+END_SRC
-3. Install the files (normally run as root).
+3. If building installable library, install it (normally run as root).
#+BEGIN_SRC shell-script
make install
- #+END_SRC
-
+ #+END_SRC
This will install the files *libruncap.so* and *libruncap.a* to the
system library directory, and the header file *runcap.h* to the
system include directory.
* Incorporating as a submodule
-To incorporate *runcap* as a submodule to your project, follow these
+To incorporate *runcap* to your project as a submodule, follow these
steps:
1. Change to your project's toplevel directory.
@@ -45,24 +213,82 @@ steps:
#+END_SRC
4. Add it to your toplevel *Makefile.am*.
#+BEGIN_SRC make
- ACLOCAL_AMFLAGS = -I grecs/am
+ ACLOCAL_AMFLAGS = -I runcap
SUBDIRS = runcap
#+END_SRC
-5. Edit your *configure.ac*
- #+BEGIN_SRC automake
+5. Edit your *configure.ac*. Add the following line:
+ #+BEGIN_SRC autoconf
RUNCAP_SETUP
#+END_SRC
-6.
+6. Add the following to the *Makefile.am* file which builds the target
+ that uses on the *runcap* library:
#+BEGIN_SRC make
AM_CPPFLAGS = @RUNCAP_INC@
- AM_LDADD = @RUNCAP_LDADD@
+ LDADD = @RUNCAP_LDADD@
#+END_SRC
+* RUNCAP_SETUP autoconf macro
+
+The *RUNCAP_SETUP* macro initializes the *runcap* library. It should
+be used in the *configure.ac* file or in one of the files included to
+it. The macro invocation syntax is:
+
+#+BEGIN_SRC autoconf
+RUNCAP_SETUP(DIR, TYPE)
+#+END_SRC
+
+Both arguments are optional:
+
+- DIR :: Name of the subdirectory where the *runcap* sources
+ reside. If omitted, =runcap= is assumed. When
+ building *runcap* as a standalone library, it is set to *.*
+ (a dot).
+- TYPE :: Build type: =install=, =shared=, or =static=. Defaults to
+ =static=.
+
+This macro defines the following *make* variables:
+
+- RUNTIME_INC :: *cpp* options to access the =runcap.h=
+ include file. Use it in the convenient
+ =_CPPFLAGS= *make* variable.
+- RUNCAP_LDADD :: Lists the pathname of the *runcap*
+ library. Use it in the =LDADD=
+ or =prog_LDADD= *make* variable.
+- RUNCAP_BUILD_TYPE :: Type of the build.
+
+
+* Copyright
+
+Copyright (C) 2017-2024 Sergey Poznyakoff
+
+Permission is granted to anyone to make or distribute verbatim copies
+of this document as received, in any medium, provided that the
+copyright notice and this permission notice are preserved,
+thus giving the recipient permission to redistribute in turn.
+
+Permission is granted to distribute modified versions
+of this document, or of portions of it,
+under the above conditions, provided also that they
+carry prominent notices stating who last changed them.
+
+* Document settings :noexport:
+
+Please ignore this section. It supplies the variables necessary for
+proper rendering of this document.
+
+:PROPERTIES:
+:VISIBILITY: folded
+:END:
+
+#+TITLE: runcap
+#+STARTUP: showall
+#+EXCLUDE_TAGS: noexport
+#+HTML_HEAD: <link rel="stylesheet" type="text/css" href="style1.css" />
+#+OPTIONS: ^:nil
# Local Variables:
# mode: org
# paragraph-separate: "[ ^L]*$"
# version-control: never
# End:
-
diff --git a/configure.ac b/configure.ac
index d4aa1a3..b4c1098 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,5 +1,21 @@
+# This file is part of runcap. -*- Autoconf -*-
+# Copyright (C) 2017-2024 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/>.
+
AC_PREREQ(2.63)
-AC_INIT([libruncap], [1.0], [gray+runcap@gnu.org.ua])
+AC_INIT([libruncap], [m4_esyscmd_s([git describe])], [gray+runcap@gnu.org.ua])
AC_CONFIG_SRCDIR([runcap.c])
AM_INIT_AUTOMAKE([1.11 foreign tar-ustar silent-rules])
diff --git a/getc.c b/getc.c
index eb7607a..6c84446 100644
--- a/getc.c
+++ b/getc.c
@@ -1,5 +1,5 @@
/* runcap - run program and capture its output
- Copyright (C) 2017 Sergey Poznyakoff
+ Copyright (C) 2017-2024 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
diff --git a/getl.c b/getl.c
index 4bd352a..41efeae 100644
--- a/getl.c
+++ b/getl.c
@@ -1,5 +1,5 @@
/* runcap - run program and capture its output
- Copyright (C) 2017 Sergey Poznyakoff
+ Copyright (C) 2017-2024 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
diff --git a/install.am b/install.am
index 3426aee..fe82833 100644
--- a/install.am
+++ b/install.am
@@ -1,5 +1,5 @@
# Automake settings for a standalone installable build of runcap.
-# See Make.am for details.
+# See Make.am for details, including licensing.
include Make.am
lib_LTLIBRARIES=libruncap.la
libruncap_la_SOURCES = $(RUNCAP_SRC)
diff --git a/read.c b/read.c
new file mode 100644
index 0000000..102f32f
--- /dev/null
+++ b/read.c
@@ -0,0 +1,69 @@
+/* runcap - run program and capture its output
+ Copyright (C) 2019-2024 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/>. */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include "runcap.h"
+
+ssize_t
+runcap_read(struct runcap *rc, int sd, char *buf, size_t size)
+{
+ struct stream_capture *cap;
+ ssize_t nread = 0;
+
+ if (!buf) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (size == 0)
+ return 0;
+
+ cap = runcap_get_capture(rc, sd);
+ if (!cap)
+ return -1;
+
+ while (size) {
+ size_t avail = cap->sc_level - cap->sc_cur;
+ if (avail == 0) {
+ if (cap->sc_storfd != -1) {
+ ssize_t r = read(cap->sc_storfd,
+ cap->sc_base,
+ cap->sc_size);
+ if (r < 0)
+ return -1;
+ else if (r == 0)
+ break;
+ avail = r;
+ cap->sc_level = r;
+ cap->sc_cur = 0;
+ } else {
+ break;
+ }
+ }
+
+ if (avail > size)
+ avail = size;
+ memcpy(buf + nread, cap->sc_base + cap->sc_cur, avail);
+
+ cap->sc_cur += avail;
+ nread += avail;
+ size -= avail;
+ }
+
+ return nread;
+}
diff --git a/runcap.3 b/runcap.3
index 4a425d6..2bd3648 100644
--- a/runcap.3
+++ b/runcap.3
@@ -1,5 +1,5 @@
.\" This file is part of runcap -*- nroff -*-
-.\" Copyright (C) 2017 Sergey Poznyakoff
+.\" Copyright (C) 2017-2024 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
@@ -13,7 +13,7 @@
.\"
.\" You should have received a copy of the GNU General Public License
.\" along with runcap. If not, see <http://www.gnu.org/licenses/>.
-.TH RUNCAP 2 "August 22, 2017" "RUNCAP" "User Commands"
+.TH RUNCAP 2 "March 14, 2024" "RUNCAP" "User Commands"
.SH NAME
runcap \- run external process and capture its stdout and stderr
.SH SYNOPSIS
@@ -67,13 +67,14 @@ The \fBstruct runcap\fR is defined as follows:
.nf
struct runcap
{
- char *rc_program; /* [\fIIN\fR] (Path)name of the program to run */
- char **rc_argv; /* [\fIIN\fR] Argument vector */
- unsigned rc_timeout; /* [\fIIN\fR] Execution timeout */
+ char *rc_program; /* [\fIIN\fR] (Path)name of the program to run. */
+ char **rc_argv; /* [\fIIN\fR] Argument vector. */
+ char **rc_env; /* [\fIIN\fR] Environment variables. */
+ unsigned rc_timeout; /* [\fIIN\fR] Execution timeout. */
struct stream_capture rc_cap[3];
- pid_t rc_pid; /* [\fIOUT\fR] PID of the process */
- int rc_status; /* [\fIOUT\fR] Termination status */
- int rc_errno; /* [\fIOUT\fR] System error code */
+ pid_t rc_pid; /* [\fIOUT\fR] PID of the process. */
+ int rc_status; /* [\fIOUT\fR] Termination status. */
+ int rc_errno; /* [\fIOUT\fR] System error code. */
};
.fi
.in
@@ -96,13 +97,21 @@ Time to wait for the program termination, in seconds. If initialized,
the \fBRCF_TIMEOUT\fR bit must be set in \fIflags\fR. If not set,
.B runcap
will wait indefinitely.
+.TP
+.B rc_env
+A
+.BR NULL -terminated
+array of environment variables. Each element (except the last
+.BR NULL )
+has the form "\fIname\fR=\fIvalue\fR". If initialized, the
+\fBRCF_ENV\fR bit must be set in \fIflags\fR.
.PP
The three streams associated with the running command are described by
the
.B rc_cap
array. Its elements correspond to the standard input, output and
error streams. Upon successful return, the captured
-data from stdin and stdout can be retrieved using the \fBruncap_getc\fR, and
+data from stdout and stderr can be retrieved using the \fBruncap_getc\fR, and
\fBruncap_getline\fR functions (see below). For convenience, the
following constants are defined in \fBruncap.h\fR to refer to the
corresponding streams:
@@ -110,8 +119,8 @@ corresponding streams:
.BR RUNCAP_STDOUT ,
.BR RUNCAP_STDERR .
.PP
-The following fields are warranted to be present in \fBstruct
-stream_capture\fR:
+The \fBstream_capture\fR structure contains the following members
+accessible to the caller:
.PP
.in +4n
.nf
@@ -125,10 +134,12 @@ struct stream_capture
off_t sc_leng; /* [\fIOUT\fR] total length of captured data */
size_t sc_nlines; /* [\fIOUT\fR] number of captured lines */
- /* The following two are available only in \fBrc_cap[RUNCAP_STDIN]\fR,
- see the subsection \fBSupplying standard input\fR below, for
- details). */
- size_t sc_fd; /* Input file descriptor */
+ 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.
+ */
+ int sc_fd; /* Input file descriptor */
char *sc_base; /* Input data */
};
.fi
@@ -143,7 +154,10 @@ Size of the buffer (\fBsize_t\fR). If set, the \fBRCF_STDOUT_SIZE\fR
\fBRUNCAP_STDERR\fR) bit must be set in \fIflags\fR. If the amount of
input data exceeds the buffer size, the data are saved in the disk
file, therefore setting a larger buffer can improve performance.
-Setting \fBsc_size\fR to 0 disables capturing.
+
+Setting \fBsc_size\fR to 0 disables capturing. Same effect is
+achieved by setting the \fBRCF_STDOUT_NOCAP\fR or
+\fBRCF_STDERR_NOCAP\fR flag.
Default buffer size is 4096 bytes.
.TP
@@ -153,7 +167,7 @@ A pointer to the line monitor function. If set, the
\fBRCF_STDERR_LINEMON\fR (for \fBRUNCAP_STDERR\fR) bit must be set in
\fIflags\fR.
.sp
-The line monitor function allows the caller to monitor the arrival of
+Line monitor function allows the caller to monitor the arrival of
the new data in the corresponding stream. It is declared as follows:
.sp
.BI "void linemon(const char *" line ", size_t " size ", void *" data )
@@ -162,19 +176,55 @@ where \fIline\fR is the line that has been read, \fIsize\fR is its
length, and \fIdata\fR is an opaque pointer to application-specific
data, supplied in the \fBsc_monarg\fR member.
.sp
-The line monitor function is invoked each time a newline character is
-encountered in the stream, or when the stream buffer becomes full (and,
-therefore is about to be flushed into the storage file) and some
-characters remain unreported. This means that, if the
-\fBsc_linemon\fR function is designed to log each input line, it
-should keep the state of processing (e.g. in the \fIdata\fR argument),
-and concatenate the \fIline\fR parameters until
-\fBline[size-1] == '\\n'\fR.
+The line monitor function is invoked to notify the caller about
+arrival of new data on the stream. There are 4 cases:
+.RS +8
+.nr step 1 1
+.IP \n[step].
+A newline newline character is encountered in the stream.
+.sp
+The \fIline\fR argument points to the beginning of the captured
+line. The \fIsize\fR argument contains number of characters in the
+line, including the newline character.
+.IP \n+[step].
+The stream buffer is full (and therefore is about to be flushed into
+the storage file) and contains unfinished line.
+.sp
+The \fIline\fR
+argument points to the beginning of the captured line fragment. The
+\fIsize\fR argument contains number of characters in the
+fragment.
+.IP \n+[step].
+No more characters are available in the stream and the
+stream buffer contains unreported characters.
+.sp
+Argument assignment is the same as in previous case.
+.IP \n+[step].
+The stream reached EOF.
+.sp
+The \fIsize\fR argument is 0. The
+\fIline\fR argument is not meaningful, although it does point to a
+valid memory location.
+.PP
+Consequently, if the \fBsc_linemon\fR function needs to process full
+lines. it has to keep the state of processing (e.g. in the \fBdata\fR
+argument), and concatenate the line parameters until the following
+condition is met:
+.sp
+.EX
+ size == 0 || line[size-1] == '\\n'
+.EE
+.RE
.TP
.B sc_monarg
The value of the \fIdata\fR parameter for the \fBsc_linemon\fR
-function. It can be initialized only if \fBsc_linemon\fR is
-initialized as well.
+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]
@@ -223,11 +273,15 @@ Total length of captured stderr.
.B rc_cap[RUNCAP_STDERR].sc_nlines
Number of lines in the captured stderr.
.PP
-The actual data can be retrieved using the \fBruncap_getc\fR, and
+The last captured line can be partial, i.e. not terminated with a
+newline character. It will be counted in the stream's \fBsc_nlines\fR
+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 the captured data from the \fBstruct runcap\fR object pointed
+retrieve captured data from the \fBstruct runcap\fR object pointed
to by its \fIrc\fR argument. The stream to retrieve data from is
identified by the \fIstream\fR argument, whose valid values are
\fBRUNCAP_STDOUT\fR (or \fB1\fR) or \fBRUNCAP_STDERR\fR (or \fB2\fR).
@@ -300,6 +354,57 @@ 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
+\fBrc_cap\fR element to 0 and indicate that by setting the
+\fBRCF_STDOUT_SIZE\fR (for stdout) or \fBRCF_STDERR_SIZE\fR (for
+stderr) flag. This was the method used in first releases of runcap.
+.PP
+Another way is to use the \fBRCF_STDOUT_NOCAP\fR and/or
+\fBRCF_STDERR_NOCAP\fR flag in the call to \fBruncap\fR.
+.PP
+Both methods are equivalent.
+.PP
+When capturing of a particular stream is disabled, the corresponding
+file descriptor of the program being invoked is inherited from the
+calling program. This means, e.g., that if you disable stderr
+capturing, error diagnostics from the programs that \fBruncap\fR runs
+will appear at the stderr of your program. Upon return from
+\fBruncap\fR both
+.B sc_leng
+and
+.B sc_nlines
+fields of the corresponding \fBrc_cap\fR structure will be zero.
+.PP
+This is changed if line monitor is installed on the stream with
+disabled capturing. In that case, the output from the program will be
+intercepted, passed to the line monitor function and discarded. Upon
+return, both
+.B sc_leng
+and
+.B sc_nlines
+will reflect the actual length in bytes and number of lines in the
+processed material.
+.B sc_nlines
+field.
.SH RETURN VALUE
Upon successful completion,
.B runcap()
@@ -380,7 +485,7 @@ archive(void)
.SH AUTHORS
Sergey Poznyakoff
.SH COPYRIGHT
-Copyright \(co 2017 Sergey Poznyakoff
+Copyright \(co 2017--2024 Sergey Poznyakoff
.br
.na
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
diff --git a/runcap.c b/runcap.c
index 6790dc4..56f6974 100644
--- a/runcap.c
+++ b/runcap.c
@@ -1,5 +1,5 @@
/* runcap - run program and capture its output
- Copyright (C) 2017 Sergey Poznyakoff
+ Copyright (C) 2017-2024 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
@@ -23,17 +23,24 @@
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
-
+#include <string.h>
#include "runcap.h"
-int
-stream_capture_init(struct stream_capture *cap, size_t size)
+static int
+stream_capture_init(struct stream_capture *cap, size_t size, int flags)
{
if (!cap) {
errno = EINVAL;
return -1;
}
+ if (size == 0) {
+ if (flags & RCF_SC_LINEMON)
+ size = STRCAP_BUFSIZE;
+ flags |= RCF_SC_NOCAP;
+ } else if ((flags & RCF_SC_NOCAP) && !(flags & RCF_SC_LINEMON))
+ size = 0;
+
if (size) {
cap->sc_base = malloc(size);
if (!cap->sc_base)
@@ -45,8 +52,16 @@ stream_capture_init(struct stream_capture *cap, size_t size)
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 (!(flags & RCF_SC_LINEMON)) {
+ cap->sc_linemon = NULL;
+ cap->sc_monarg = NULL;
+ }
+ cap->sc_flags = flags;
+
return 0;
}
@@ -59,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);
@@ -68,7 +87,7 @@ stream_capture_reset(struct stream_capture *cap)
}
}
-void
+static void
stream_capture_free(struct stream_capture *cap)
{
stream_capture_reset(cap);
@@ -108,18 +127,20 @@ 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_storfd == -1) {
- int fd;
- char tmpl[] = "/tmp/rcXXXXXX";
- fd = mkstemp(tmpl);
- if (fd == -1)
+ if (!(cap->sc_flags & RCF_SC_NOCAP)) {
+ if (cap->sc_storfd == -1) {
+ int fd;
+ char tmpl[] = "/tmp/rcXXXXXX";
+ fd = mkstemp(tmpl);
+ if (fd == -1)
+ return -1;
+ unlink(tmpl);
+ cap->sc_storfd = fd;
+ }
+ res = full_write(cap->sc_storfd, cap->sc_base, cap->sc_level);
+ if (res)
return -1;
- unlink(tmpl);
- cap->sc_storfd = fd;
}
- res = full_write(cap->sc_storfd, cap->sc_base, cap->sc_level);
- if (res)
- return -1;
cap->sc_level = 0;
cap->sc_cur = 0;
return 0;
@@ -136,13 +157,24 @@ stream_capture_get(struct stream_capture *cap, int *feof)
return -1;
}
- rc = read(cap->sc_fd, cap->sc_base + cap->sc_level, cap->sc_size - cap->sc_level);
+ rc = read(cap->sc_fd, cap->sc_base + cap->sc_level,
+ cap->sc_size - cap->sc_level);
if (rc == -1) {