authorSergey Poznyakoff <>2017-07-19 20:27:00 (GMT)
committer Sergey Poznyakoff <>2017-07-19 20:27:00 (GMT)
commitcf87f3c78915f9cbfd900c9c34033cb924f912e7 (patch) (side-by-side diff)
parent99d911104d7c03608398d47c74f1ce3c39bff211 (diff)
Invokee linemon for partial lines as well
Diffstat (more/less context) (ignore whitespace changes)
5 files changed, 149 insertions, 13 deletions
diff --git a/runcap.3 b/runcap.3
index aa51e4e..3c7161e 100644
--- a/runcap.3
+++ b/runcap.3
@@ -153,13 +153,22 @@ A pointer to the line monitor function. If set, the
The line monitor function allows the caller to monitor the arrival of
-each input line in the corresponding stream. Its signature is:
+the new data in the corresponding stream. Its signature is:
.BI "void linemon(const char *" line ", size_t " size ", void *" data )
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.
+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.
.B sc_monarg
The value of the \fIdata\fR parameter for the \fBsc_linemon\fR
diff --git a/runcap.c b/runcap.c
index bbe735f..f36d5b1 100644
--- a/runcap.c
+++ b/runcap.c
@@ -113,6 +113,10 @@ stream_capture_flush(struct stream_capture *cap)
if (cap->sc_level == 0)
return 0;
+ if (cap->sc_linemon && cap->sc_cur < cap->sc_level)
+ 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";
@@ -155,8 +159,8 @@ stream_capture_get(struct stream_capture *cap, int *feof)
if (c == '\n') {
if (cap->sc_linemon)
cap->sc_linemon(cap->sc_base + cap->sc_cur,
- cap->sc_level - cap->sc_cur,
- cap->sc_monarg);
+ cap->sc_level - cap->sc_cur,
+ cap->sc_monarg);
cap->sc_cur = cap->sc_level;
@@ -439,7 +443,8 @@ runcap_init(struct runcap *rc, int flags)
res = stream_capture_alloc(&rc->rc_cap[RUNCAP_STDOUT],
- res = stream_capture_init(&rc->rc_cap[RUNCAP_STDOUT], STRCAP_BUFSIZE);
+ res = stream_capture_init(&rc->rc_cap[RUNCAP_STDOUT],
if (res)
return res;
@@ -452,7 +457,8 @@ runcap_init(struct runcap *rc, int flags)
res = stream_capture_alloc(&rc->rc_cap[RUNCAP_STDERR],
- res = stream_capture_init(&rc->rc_cap[RUNCAP_STDERR], STRCAP_BUFSIZE);
+ res = stream_capture_init(&rc->rc_cap[RUNCAP_STDERR],
if (res)
return res;
@@ -526,7 +532,11 @@ runcap(struct runcap *rc, int flags)
lseek(rc->rc_cap[RUNCAP_STDERR].sc_storfd, 0, SEEK_SET);
- return rc->rc_errno == 0 ? 0 : -1;
+ if (rc->rc_errno) {
+ errno = rc->rc_errno;
+ return -1;
+ }
+ return 0;
diff --git a/t/02two.t b/t/02two.t
index 5abf3d9..142bbce 100755
--- a/t/02two.t
+++ b/t/02two.t
@@ -15,7 +15,7 @@
# You should have received a copy of the GNU General Public License along
# with Runcap. If not, see <>.
-TC_TITLE capture both stdout and stder
+TC_TITLE capture both stdout and stderr
diff --git a/t/07mon.t b/t/07mon.t
new file mode 100755
index 0000000..4224f87
--- a/dev/null
+++ b/t/07mon.t
@@ -0,0 +1,101 @@
+#! ./testsuite
+# testsuite for runcap - run program and capture its output
+# Copyright (C) 2017 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
+# 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 <>.
+TC_TITLE line monitor, short buffer
+[stdout]: CHAPTER I. Down the Rabbit-Hole
+[stdout]: Alice was beginning to get very tired of sitting by her sister on the
+[stdout]: bank, and of having nothing to do: once or twice she had peeped into the
+[stdout]: book her sister was reading, but it had no pictures or conversations
+[stdout]: in it, 'and what is the use of a book,' thought Alice 'without
+[stdout]: pictures or conversations?'
+[stdout]: So she was considering in her own mind (as well as she could, for the
+[stdout]: hot day made her feel very sleepy and stupid), whether the pleasure of
+[stdout]: making a daisy-chain would be worth the trouble of getting up and picking
+[stdout]: the daisies, when suddenly a White Rabbit with pink eyes ran close by her.
+[stdout]: There was nothing so very remarkable in that; nor did Alice think it
+[stdout]: so very much out of the way to hear the Rabbit say to itself, 'Oh
+[stdout]: dear! Oh dear! I shall be late!' (when she thought it over afterwards,
+[stdout]: it occurred to her that she ought to have wondered at this, but at the
+[stdout]: time it all seemed quite natural); but when the Rabbit actually took a
+[stdout]: watch out of its waistcoat-pocket, and looked at it, and then hurried on,
+[stdout]: Alice started to her feet, for it flashed across her mind that she had
+[stdout]: never before seen a rabbit with either a waistcoat-pocket, or a watch
+[stdout]: to take out of it, and burning with curiosity, she ran across the field
+[stdout]: after it, and fortunately was just in time to see it pop down a large
+[stdout]: rabbit-hole under the hedge.
+[stdout]: In another moment down went Alice after it, never once considering how
+[stdout]: in the world she was to get out again.
+[stdout]: The rabbit-hole went straight on like a tunnel for some way, and then
+[stdout]: dipped suddenly down, so suddenly that Alice had not a moment to think
+[stdout]: about stopping herself before she found herself falling down a very
+[stdout]: deep well.
+[stdout]: Either the well was very deep, or she fell very slowly, for she had plenty
+[stdout]: of time as she went down to look about her and to wonder what was going
+[stdout]: to happen next. First, she tried to look down and make out what she was
+[stdout]: coming to, but it was too dark to see anything; then she looked at the
+[stdout]: sides of the well, and noticed that they were filled with cupboards
+[stdout]: and book-shelves; here and there she saw maps and pictures hung upon
+[stdout]: pegs. She took down a jar from one of the shelves as she passed; it was
+[stdout]: labelled 'ORANGE MARMALADE', but to her great disappointment it was
+[stdout]: empty: she did not like to drop the jar for fear of killing somebody,
+[stdout]: so managed to put it into one of the cupboards as she fell past it.
+[stdout]: 'Well!' thought Alice to herself, 'after such a fall as this,
+[stdout]: I shall think nothing of tumbling down stairs! How brave they'll all
+[stdout]: think me at home! Why, I wouldn't say anything about it, even if I
+[stdout]: fell off the top of the house!' (Which was very likely true.)
+[stdout]: Down, down, down. Would the fall never come to an end! 'I wonder how
+[stdout]: many miles I've fallen by this time?' she said aloud. 'I must be
+[stdout]: getting somewhere near the centre of the earth. Let me see: that would be
+[stdout]: four thousand miles down, I think--' (for, you see, Alice had learnt
+[stdout]: several things of this sort in her lessons in the schoolroom, and though
+[stdout]: this was not a very good opportunity for showing off her knowledge,
+[stdout]: as there was no one to listen to her, still it was good practice to
+[stdout]: say it over) '--yes, that's about the right distance--but then I
+[stdout]: wonder what Latitude or Longitude I've got to?' (Alice had no idea
+[stdout]: what Latitude was, or Longitude either, but thought they were nice grand
+[stdout]: words to say.)
+[stdout]: Presently she began again. 'I wonder if I shall fall right through
+[stdout]: the earth! How funny it'll seem to come out among the people that
+[stdout]: walk with their heads downward! The Antipathies, I think--' (she was
+[stdout]: rather glad there was no one listening, this time, as it didn't sound
+[stdout]: at all the right word) '--but I shall have to ask them what the name
+[stdout]: of the country is, you know. Please, Ma'am, is this New Zealand or
+[stdout]: Australia?' (and she tried to curtsey as she spoke--fancy curtseying
+[stdout]: as you're falling through the air! Do you think you could manage
+[stdout]: it?) 'And what an ignorant little girl she'll think me for asking! No,
+[stdout]: it'll never do to ask: perhaps I shall see it written up somewhere.'
+exit code: 0
+stdout: 71 lines, 4051 bytes
+stderr: 0 lines, 0 bytes
+rt -s 16 -m -- genout $testdir/INPUT
diff --git a/t/rt.c b/t/rt.c
index 2edc12b..9dbbfa8 100644
--- a/t/rt.c
+++ b/t/rt.c
@@ -76,12 +76,21 @@ whatarg(char const *arg)
+struct linemon_closure
+ char const *prefix;
+ int cont;
static void
linemon(const char *ptr, size_t len, void *data)
- fprintf(stdout, "[%s]: ", (char*) data);
- fwrite(ptr, len-1, 1, stdout);
- fputc('\n', stdout);
+ struct linemon_closure *clos = data;
+ if (!clos->cont)
+ fprintf(stdout, "[%s]: ", clos->prefix);
+ fwrite(ptr, len, 1, stdout);
+ clos->cont = ptr[len-1] != '\n';
static void
@@ -122,7 +131,12 @@ main(int argc, char **argv)
int c;
int fd;
unsigned long size;
+ static struct linemon_closure cl[] = {
+ { "stdout" },
+ { "stderr" }
+ };
progname = strrchr(argv[0], '/');
if (progname)
@@ -189,12 +203,14 @@ main(int argc, char **argv)
case 'm':
if (what & WA_STDOUT) {
rc.rc_cap[RUNCAP_STDOUT].sc_linemon = linemon;
- rc.rc_cap[RUNCAP_STDOUT].sc_monarg = "stdout";
+ rc.rc_cap[RUNCAP_STDOUT].sc_monarg =
if (what & WA_STDERR) {
rc.rc_cap[RUNCAP_STDERR].sc_linemon = linemon;
- rc.rc_cap[RUNCAP_STDERR].sc_monarg = "stderr";
+ rc.rc_cap[RUNCAP_STDERR].sc_monarg =

Return to:

Send suggestions and report system problems to the System administrator.