aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore7
-rw-r--r--Makefile9
-rw-r--r--getc.c54
-rw-r--r--getl.c90
-rw-r--r--runcap.h49
-rw-r--r--seek.c73
-rw-r--r--t/.gitignore4
-rwxr-xr-xt/00simple.t30
-rwxr-xr-xt/01lines.t101
-rwxr-xr-xt/02two.t51
-rwxr-xr-xt/03longout.t101
-rwxr-xr-xt/04stdin.t50
-rwxr-xr-xt/05stdin.t50
-rw-r--r--t/INPUT71
-rw-r--r--t/Makefile28
-rw-r--r--t/genout.c80
-rw-r--r--t/rt.c39
-rwxr-xr-xt/testsuite137
-rw-r--r--tell.c41
19 files changed, 1053 insertions, 12 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..f6aee58
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,7 @@
+*.o
+*.a
+*~
+core
+.emacs.*
+.gdbinit
+tmp
diff --git a/Makefile b/Makefile
index b0cec8a..4eb3a1c 100644
--- a/Makefile
+++ b/Makefile
@@ -28,7 +28,7 @@ MKHIER = install -d
# Compiler options
O = -ggdb -Wall
-SOURCES = runcap.c
+SOURCES = runcap.c getc.c getl.c seek.c tell.c
OBJECTS = $(SOURCES:.c=.o)
HEADERS = runcap.h
@@ -38,6 +38,8 @@ ARFLAGS = cru
all: libruncap.a
+$(OBJECTS): $(HEADERS)
+
libruncap.a: $(OBJECTS)
ar $(ARFLAGS) libruncap.a $(OBJECTS)
ranlib libruncap.a
@@ -55,3 +57,8 @@ install-headers: runcap.h
install-man:;
# $(MKHIER) $(DESTDIR)$(MAN2DIR)
# $(INSTALL) runcap.2 $(DESTDIR)$(MAN2DIR)
+
+.PHONY: check
+check:
+ $(MAKE) -C t check
+
diff --git a/getc.c b/getc.c
new file mode 100644
index 0000000..3c203d9
--- /dev/null
+++ b/getc.c
@@ -0,0 +1,54 @@
+/* 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
+ 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 "runcap.h"
+
+int
+runcap_getc(struct runcap *rc, int stream, char *cp)
+{
+ struct filecapture *fp;
+
+ if (!cp) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ fp = runcap_filecapture(rc, stream);
+ if (!fp)
+ return -1;
+
+ if (fp->fc_level == fp->fc_linestart) {
+ if (fp->fc_tmpfd != -1) {
+ ssize_t r = read(fp->fc_tmpfd, fp->fc_base,
+ fp->fc_size);
+ if (r < 0)
+ return -1;
+ else if (r == 0)
+ return 0;
+ fp->fc_level = r;
+ fp->fc_linestart = 0;
+ } else {
+ return 0;
+ }
+ }
+ *cp = fp->fc_base[fp->fc_linestart++];
+ return 1;
+}
+
diff --git a/getl.c b/getl.c
new file mode 100644
index 0000000..d989aa9
--- /dev/null
+++ b/getl.c
@@ -0,0 +1,90 @@
+/* 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
+ 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 "runcap.h"
+
+ssize_t
+runcap_getline(struct runcap *rc, int stream, char **pstr, size_t *psize)
+{
+ struct filecapture *fp;
+ char *str;
+ size_t size;
+ size_t n;
+ char c;
+ int res;
+
+ if (!pstr || !psize) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ str = *pstr;
+ size = *psize;
+
+ fp = runcap_filecapture(rc, stream);
+ if (!fp)
+ return -1;
+
+ if (!str || size == 0) {
+ /* Initial allocation */
+ size = 16;
+ str = malloc(size);
+ if (!str)
+ return -1;
+ *pstr = str;
+ *psize = size;
+ }
+
+ n = 0;
+ while ((res = runcap_getc(rc, stream, &c)) == 1) {
+ if (n == size) {
+ char *p;
+ size_t sz;
+
+ if (size >= (size_t) -1 / 3 * 2) {
+ errno = ENOMEM;
+ return -1;
+ }
+ sz = size + (size + 1) / 2;
+ p = realloc(str, sz);
+ if (!p)
+ return -1;
+ *pstr = str = p;
+ *psize = size = sz;
+ }
+ str[n++] = c;
+ if (c == '\n')
+ break;
+ }
+
+ if (res == -1)
+ return -1;
+
+ if (n == size) {
+ char *p = realloc(str, size + 1);
+ if (!p)
+ return -1;
+ *pstr = str = p;
+ *psize = ++size;
+ }
+ str[n] = 0;
+ return n;
+}
+
diff --git a/runcap.h b/runcap.h
index b677f05..517cd43 100644
--- a/runcap.h
+++ b/runcap.h
@@ -19,16 +19,17 @@
struct filecapture
{
- size_t fc_size;
- size_t fc_leng;
- size_t fc_nlines;
- size_t fc_linestart;
- char * fc_base;
- size_t fc_level;
+ size_t fc_size; /* size of the buffer */
+ off_t fc_leng; /* total length of captured data */
+ size_t fc_nlines; /* number of captured lines */
+ size_t fc_linestart; /* current offset in buffer */
+ char * fc_base; /* buffer space */
+ size_t fc_level; /* number of characters currently in buffer */
void (*fc_linemon) (const char *, size_t, void *);
- void *fc_monarg;
- int fc_tmpfd;
- int fc_fd;
+ /* Line monitor function */
+ void *fc_monarg; /* Line monitor argument */
+ int fc_tmpfd; /* Storage file descriptor */
+ int fc_fd; /* Input descriptor */
};
#define FC_BUFSIZE 4096
@@ -66,4 +67,34 @@ void filecapture_free(struct filecapture *fc);
int runcap(struct runcap *rc, int flags);
void runcap_free(struct runcap *rc);
+static inline struct filecapture *
+runcap_filecapture(struct runcap *rc, int stream)
+{
+ struct filecapture *fp;
+
+ if (stream != RUNCAP_STDOUT && stream != RUNCAP_STDERR) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ fp = &rc->rc_cap[stream];
+
+ if (!fp->fc_base || fp->fc_size == 0) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return fp;
+}
+
+int runcap_getc(struct runcap *rc, int stream, char *cp);
+ssize_t runcap_getline(struct runcap *rc, int stream, char **pstr, size_t *psize);
+off_t runcap_tell(struct runcap *rc, int stream);
+int runcap_seek(struct runcap *rc, int stream, off_t off, int whence);
+
+static inline int
+runcap_rewind(struct runcap *rc, int stream)
+{
+ return runcap_seek(rc, stream, 0, 0);
+}
+
#endif
diff --git a/seek.c b/seek.c
new file mode 100644
index 0000000..5b5b111
--- /dev/null
+++ b/seek.c
@@ -0,0 +1,73 @@
+/* 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
+ 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 <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+#include "runcap.h"
+
+int
+runcap_seek(struct runcap *rc, int stream, off_t off, int whence)
+{
+ struct filecapture *fp;
+ off_t cur;
+
+ fp = runcap_filecapture(rc, stream);
+ if (!fp)
+ return -1;
+
+ cur = runcap_tell(rc, stream);
+ switch (whence) {
+ case SEEK_CUR:
+ off = cur;
+ break;
+
+ case SEEK_END:
+ off = fp->fc_leng + off;
+ break;
+
+ case SEEK_SET:
+ break;
+
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (off < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ cur -= fp->fc_level;
+
+ if (cur <= off && off <= cur + fp->fc_level) {
+ fp->fc_linestart = off - cur;
+ } else if (fp->fc_tmpfd != -1) {
+ if (lseek(fp->fc_tmpfd, off, SEEK_SET) == -1)
+ return -1;
+ fp->fc_level = 0;
+ fp->fc_linestart = 0;
+ } else {
+ errno = EINVAL;
+ return -1;
+ }
+ return 0;
+}
+
+
+
+
diff --git a/t/.gitignore b/t/.gitignore
new file mode 100644
index 0000000..bb11f3b
--- /dev/null
+++ b/t/.gitignore
@@ -0,0 +1,4 @@
+/testsuite.dir
+/testsuite.log
+/rt
+/genout
diff --git a/t/00simple.t b/t/00simple.t
new file mode 100755
index 0000000..31a4da2
--- /dev/null
+++ b/t/00simple.t
@@ -0,0 +1,30 @@
+#! ./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
+# 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/>.
+
+TC_TITLE simple capture
+
+TC_EXPECT out <<EOT
+res=0
+exit code: 0
+stdout: 71 lines, 4051 bytes
+stderr: 0 lines, 0 bytes
+EOT
+
+rt genout $testdir/INPUT
+
+
+
diff --git a/t/01lines.t b/t/01lines.t
new file mode 100755
index 0000000..0d20dcd
--- /dev/null
+++ b/t/01lines.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
+# 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/>.
+
+TC_TITLE read lines
+
+TC_EXPECT out <<EOT
+res=0
+exit code: 0
+stdout: 71 lines, 4051 bytes
+stderr: 0 lines, 0 bytes
+stdout listing:
+ 1: CHAPTER I. Down the Rabbit-Hole
+ 2:
+ 3: Alice was beginning to get very tired of sitting by her sister on the
+ 4: bank, and of having nothing to do: once or twice she had peeped into the
+ 5: book her sister was reading, but it had no pictures or conversations
+ 6: in it, 'and what is the use of a book,' thought Alice 'without
+ 7: pictures or conversations?'
+ 8:
+ 9: So she was considering in her own mind (as well as she could, for the
+ 10: hot day made her feel very sleepy and stupid), whether the pleasure of
+ 11: making a daisy-chain would be worth the trouble of getting up and picking
+ 12: the daisies, when suddenly a White Rabbit with pink eyes ran close by her.
+ 13:
+ 14: There was nothing so very remarkable in that; nor did Alice think it
+ 15: so very much out of the way to hear the Rabbit say to itself, 'Oh
+ 16: dear! Oh dear! I shall be late!' (when she thought it over afterwards,
+ 17: it occurred to her that she ought to have wondered at this, but at the
+ 18: time it all seemed quite natural); but when the Rabbit actually took a
+ 19: watch out of its waistcoat-pocket, and looked at it, and then hurried on,
+ 20: Alice started to her feet, for it flashed across her mind that she had
+ 21: never before seen a rabbit with either a waistcoat-pocket, or a watch
+ 22: to take out of it, and burning with curiosity, she ran across the field
+ 23: after it, and fortunately was just in time to see it pop down a large
+ 24: rabbit-hole under the hedge.
+ 25:
+ 26: In another moment down went Alice after it, never once considering how
+ 27: in the world she was to get out again.
+ 28:
+ 29: The rabbit-hole went straight on like a tunnel for some way, and then
+ 30: dipped suddenly down, so suddenly that Alice had not a moment to think
+ 31: about stopping herself before she found herself falling down a very
+ 32: deep well.
+ 33:
+ 34: Either the well was very deep, or she fell very slowly, for she had plenty
+ 35: of time as she went down to look about her and to wonder what was going
+ 36: to happen next. First, she tried to look down and make out what she was
+ 37: coming to, but it was too dark to see anything; then she looked at the
+ 38: sides of the well, and noticed that they were filled with cupboards
+ 39: and book-shelves; here and there she saw maps and pictures hung upon
+ 40: pegs. She took down a jar from one of the shelves as she passed; it was
+ 41: labelled 'ORANGE MARMALADE', but to her great disappointment it was
+ 42: empty: she did not like to drop the jar for fear of killing somebody,
+ 43: so managed to put it into one of the cupboards as she fell past it.
+ 44:
+ 45: 'Well!' thought Alice to herself, 'after such a fall as this,
+ 46: I shall think nothing of tumbling down stairs! How brave they'll all
+ 47: think me at home! Why, I wouldn't say anything about it, even if I
+ 48: fell off the top of the house!' (Which was very likely true.)
+ 49:
+ 50: Down, down, down. Would the fall never come to an end! 'I wonder how
+ 51: many miles I've fallen by this time?' she said aloud. 'I must be
+ 52: getting somewhere near the centre of the earth. Let me see: that would be
+ 53: four thousand miles down, I think--' (for, you see, Alice had learnt
+ 54: several things of this sort in her lessons in the schoolroom, and though
+ 55: this was not a very good opportunity for showing off her knowledge,
+ 56: as there was no one to listen to her, still it was good practice to
+ 57: say it over) '--yes, that's about the right distance--but then I
+ 58: wonder what Latitude or Longitude I've got to?' (Alice had no idea
+ 59: what Latitude was, or Longitude either, but thought they were nice grand
+ 60: words to say.)
+ 61:
+ 62: Presently she began again. 'I wonder if I shall fall right through
+ 63: the earth! How funny it'll seem to come out among the people that
+ 64: walk with their heads downward! The Antipathies, I think--' (she was
+ 65: rather glad there was no one listening, this time, as it didn't sound
+ 66: at all the right word) '--but I shall have to ask them what the name
+ 67: of the country is, you know. Please, Ma'am, is this New Zealand or
+ 68: Australia?' (and she tried to curtsey as she spoke--fancy curtseying
+ 69: as you're falling through the air! Do you think you could manage
+ 70: it?) 'And what an ignorant little girl she'll think me for asking! No,
+ 71: it'll never do to ask: perhaps I shall see it written up somewhere.'
+stdout listing ends
+EOT
+
+rt -n stdout -- genout $testdir/INPUT
+
diff --git a/t/02two.t b/t/02two.t
new file mode 100755
index 0000000..5abf3d9
--- /dev/null
+++ b/t/02two.t
@@ -0,0 +1,51 @@
+#! ./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
+# 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/>.
+
+TC_TITLE capture both stdout and stder
+
+TC_EXPECT out <<EOT
+res=0
+exit code: 0
+stdout: 8 lines, 337 bytes
+stderr: 11 lines, 734 bytes
+stdout listing:
+ 1: CHAPTER I. Down the Rabbit-Hole
+ 2:
+ 3: Alice was beginning to get very tired of sitting by her sister on the
+ 4: bank, and of having nothing to do: once or twice she had peeped into the
+ 5: book her sister was reading, but it had no pictures or conversations
+ 6: in it, 'and what is the use of a book,' thought Alice 'without
+ 7: pictures or conversations?'
+ 8:
+stdout listing ends
+stderr listing:
+ 1: There was nothing so very remarkable in that; nor did Alice think it
+ 2: so very much out of the way to hear the Rabbit say to itself, 'Oh
+ 3: dear! Oh dear! I shall be late!' (when she thought it over afterwards,
+ 4: it occurred to her that she ought to have wondered at this, but at the
+ 5: time it all seemed quite natural); but when the Rabbit actually took a
+ 6: watch out of its waistcoat-pocket, and looked at it, and then hurried on,
+ 7: Alice started to her feet, for it flashed across her mind that she had
+ 8: never before seen a rabbit with either a waistcoat-pocket, or a watch
+ 9: to take out of it, and burning with curiosity, she ran across the field
+ 10: after it, and fortunately was just in time to see it pop down a large
+ 11: rabbit-hole under the hedge.
+stderr listing ends
+EOT
+
+rt -n all -- genout -l 337 -o $testdir/INPUT -s 628 -l 734 -e $testdir/INPUT
+
diff --git a/t/03longout.t b/t/03longout.t
new file mode 100755
index 0000000..8204168
--- /dev/null
+++ b/t/03longout.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
+# 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/>.
+
+TC_TITLE file buffered output
+
+TC_EXPECT out <<EOT
+res=0
+exit code: 0
+stdout: 71 lines, 4051 bytes
+stderr: 0 lines, 0 bytes
+stdout listing:
+ 1: CHAPTER I. Down the Rabbit-Hole
+ 2:
+ 3: Alice was beginning to get very tired of sitting by her sister on the
+ 4: bank, and of having nothing to do: once or twice she had peeped into the
+ 5: book her sister was reading, but it had no pictures or conversations
+ 6: in it, 'and what is the use of a book,' thought Alice 'without
+ 7: pictures or conversations?'
+ 8:
+ 9: So she was considering in her own mind (as well as she could, for the
+ 10: hot day made her feel very sleepy and stupid), whether the pleasure of
+ 11: making a daisy-chain would be worth the trouble of getting up and picking
+ 12: the daisies, when suddenly a White Rabbit with pink eyes ran close by her.
+ 13:
+ 14: There was nothing so very remarkable in that; nor did Alice think it
+ 15: so very much out of the way to hear the Rabbit say to itself, 'Oh
+ 16: dear! Oh dear! I shall be late!' (when she thought it over afterwards,
+ 17: it occurred to her that she ought to have wondered at this, but at the
+ 18: time it all seemed quite natural); but when the Rabbit actually took a
+ 19: watch out of its waistcoat-pocket, and looked at it, and then hurried on,
+ 20: Alice started to her feet, for it flashed across her mind that she had
+ 21: never before seen a rabbit with either a waistcoat-pocket, or a watch
+ 22: to take out of it, and burning with curiosity, she ran across the field
+ 23: after it, and fortunately was just in time to see it pop down a large
+ 24: rabbit-hole under the hedge.
+ 25:
+ 26: In another moment down went Alice after it, never once considering how
+ 27: in the world she was to get out again.
+ 28:
+ 29: The rabbit-hole went straight on like a tunnel for some way, and then
+ 30: dipped suddenly down, so suddenly that Alice had not a moment to think
+ 31: about stopping herself before she found herself falling down a very
+ 32: deep well.
+ 33:
+ 34: Either the well was very deep, or she fell very slowly, for she had plenty
+ 35: of time as she went down to look about her and to wonder what was going
+ 36: to happen next. First, she tried to look down and make out what she was
+ 37: coming to, but it was too dark to see anything; then she looked at the
+ 38: sides of the well, and noticed that they were filled with cupboards
+ 39: and book-shelves; here and there she saw maps and pictures hung upon
+ 40: pegs. She took down a jar from one of the shelves as she passed; it was
+ 41: labelled 'ORANGE MARMALADE', but to her great disappointment it was
+ 42: empty: she did not like to drop the jar for fear of killing somebody,
+ 43: so managed to put it into one of the cupboards as she fell past it.
+ 44:
+ 45: 'Well!' thought Alice to herself, 'after such a fall as this,
+ 46: I shall think nothing of tumbling down stairs! How brave they'll all
+ 47: think me at home! Why, I wouldn't say anything about it, even if I
+ 48: fell off the top of the house!' (Which was very likely true.)
+ 49:
+ 50: Down, down, down. Would the fall never come to an end! 'I wonder how
+ 51: many miles I've fallen by this time?' she said aloud. 'I must be
+ 52: getting somewhere near the centre of the earth. Let me see: that would be
+ 53: four thousand miles down, I think--' (for, you see, Alice had learnt
+ 54: several things of this sort in her lessons in the schoolroom, and though
+ 55: this was not a very good opportunity for showing off her knowledge,
+ 56: as there was no one to listen to her, still it was good practice to
+ 57: say it over) '--yes, that's about the right distance--but then I
+ 58: wonder what Latitude or Longitude I've got to?' (Alice had no idea
+ 59: what Latitude was, or Longitude either, but thought they were nice grand
+ 60: words to say.)
+ 61:
+ 62: Presently she began again. 'I wonder if I shall fall right through
+ 63: the earth! How funny it'll seem to come out among the people that
+ 64: walk with their heads downward! The Antipathies, I think--' (she was
+ 65: rather glad there was no one listening, this time, as it didn't sound
+ 66: at all the right word) '--but I shall have to ask them what the name
+ 67: of the country is, you know. Please, Ma'am, is this New Zealand or
+ 68: Australia?' (and she tried to curtsey as she spoke--fancy curtseying
+ 69: as you're falling through the air! Do you think you could manage
+ 70: it?) 'And what an ignorant little girl she'll think me for asking! No,
+ 71: it'll never do to ask: perhaps I shall see it written up somewhere.'
+stdout listing ends
+EOT
+
+rt -s 16 -n stdout -- genout $testdir/INPUT
+
diff --git a/t/04stdin.t b/t/04stdin.t
new file mode 100755
index 0000000..9b05e16
--- /dev/null
+++ b/t/04stdin.t
@@ -0,0 +1,50 @@
+#! ./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
+# 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/>.
+
+TC_TITLE supply stdin
+
+TC_EXPECT out <<EOT
+res=0
+exit code: 0
+stdout: 8 lines, 337 bytes
+stderr: 11 lines, 734 bytes
+stdout listing:
+ 1: CHAPTER I. Down the Rabbit-Hole
+ 2:
+ 3: Alice was beginning to get very tired of sitting by her sister on the
+ 4: bank, and of having nothing to do: once or twice she had peeped into the
+ 5: book her sister was reading, but it had no pictures or conversations
+ 6: in it, 'and what is the use of a book,' thought Alice 'without
+ 7: pictures or conversations?'
+ 8:
+stdout listing ends
+stderr listing:
+ 1: There was nothing so very remarkable in that; nor did Alice think it
+ 2: so very much out of the way to hear the Rabbit say to itself, 'Oh
+ 3: dear! Oh dear! I shall be late!' (when she thought it over afterwards,
+ 4: it occurred to her that she ought to have wondered at this, but at the
+ 5: time it all seemed quite natural); but when the Rabbit actually took a
+ 6: watch out of its waistcoat-pocket, and looked at it, and then hurried on,
+ 7: Alice started to her feet, for it flashed across her mind that she had
+ 8: never before seen a rabbit with either a waistcoat-pocket, or a watch
+ 9: to take out of it, and burning with curiosity, she ran across the field
+ 10: after it, and fortunately was just in time to see it pop down a large
+ 11: rabbit-hole under the hedge.
+stderr listing ends
+EOT
+
+rt -n all -f $testdir/INPUT -- genout -l 337 -o - -s 628 -l 734 -e $testdir/INPUT
diff --git a/t/05stdin.t b/t/05stdin.t
new file mode 100755
index 0000000..6f9de3d
--- /dev/null
+++ b/t/05stdin.t
@@ -0,0 +1,50 @@
+#! ./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
+# 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/>.
+
+TC_TITLE pipe stdin
+
+TC_EXPECT out <<EOT
+res=0
+exit code: 0
+stdout: 8 lines, 337 bytes
+stderr: 11 lines, 734 bytes
+stdout listing:
+ 1: CHAPTER I. Down the Rabbit-Hole
+ 2:
+ 3: Alice was beginning to get very tired of sitting by her sister on the
+ 4: bank, and of having nothing to do: once or twice she had peeped into the
+ 5: book her sister was reading, but it had no pictures or conversations
+ 6: in it, 'and what is the use of a book,' thought Alice 'without
+ 7: pictures or conversations?'
+ 8:
+stdout listing ends
+stderr listing:
+ 1: There was nothing so very remarkable in that; nor did Alice think it
+ 2: so very much out of the way to hear the Rabbit say to itself, 'Oh
+ 3: dear! Oh dear! I shall be late!' (when she thought it over afterwards,
+ 4: it occurred to her that she ought to have wondered at this, but at the
+ 5: time it all seemed quite natural); but when the Rabbit actually took a
+ 6: watch out of its waistcoat-pocket, and looked at it, and then hurried on,
+ 7: Alice started to her feet, for it flashed across her mind that she had
+ 8: never before seen a rabbit with either a waistcoat-pocket, or a watch
+ 9: to take out of it, and burning with curiosity, she ran across the field
+ 10: after it, and fortunately was just in time to see it pop down a large
+ 11: rabbit-hole under the hedge.
+stderr listing ends
+EOT
+
+rt -n all -i -f $testdir/INPUT -- genout -l 337 -o - -s 628 -l 734 -e $testdir/INPUT
diff --git a/t/INPUT b/t/INPUT
new file mode 100644
index 0000000..d1113a3
--- /dev/null
+++ b/t/INPUT
@@ -0,0 +1,71 @@
+CHAPTER I. Down the Rabbit-Hole
+
+Alice was beginning to get very tired of sitting by her sister on the
+bank, and of having nothing to do: once or twice she had peeped into the
+book her sister was reading, but it had no pictures or conversations
+in it, 'and what is the use of a book,' thought Alice 'without
+pictures or conversations?'
+
+So she was considering in her own mind (as well as she could, for the
+hot day made her feel very sleepy and stupid), whether the pleasure of
+making a daisy-chain would be worth the trouble of getting up and picking
+the daisies, when suddenly a White Rabbit with pink eyes ran close by her.
+
+There was nothing so very remarkable in that; nor did Alice think it
+so very much out of the way to hear the Rabbit say to itself, 'Oh
+dear! Oh dear! I shall be late!' (when she thought it over afterwards,
+it occurred to her that she ought to have wondered at this, but at the
+time it all seemed quite natural); but when the Rabbit actually took a
+watch out of its waistcoat-pocket, and looked at it, and then hurried on,
+Alice started to her feet, for it flashed across her mind that she had
+never before seen a rabbit with either a waistcoat-pocket, or a watch
+to take out of it, and burning with curiosity, she ran across the field
+after it, and fortunately was just in time to see it pop down a large
+rabbit-hole under the hedge.
+
+In another moment down went Alice after it, never once considering how
+in the world she was to get out again.
+
+The rabbit-hole went straight on like a tunnel for some way, and then
+dipped suddenly down, so suddenly that Alice had not a moment to think
+about stopping herself before she found herself falling down a very
+deep well.
+
+Either the well was very deep, or she fell very slowly, for she had plenty
+of time as she went down to look about her and to wonder what was going
+to happen next. First, she tried to look down and make out what she was
+coming to, but it was too dark to see anything; then she looked at the
+sides of the well, and noticed that they were filled with cupboards
+and book-shelves; here and there she saw maps and pictures hung upon
+pegs. She took down a jar from one of the shelves as she passed; it was
+labelled 'ORANGE MARMALADE', but to her great disappointment it was
+empty: she did not like to drop the jar for fear of killing somebody,
+so managed to put it into one of the cupboards as she fell past it.
+
+'Well!' thought Alice to herself, 'after such a fall as this,
+I shall think nothing of tumbling down stairs! How brave they'll all
+think me at home! Why, I wouldn't say anything about it, even if I
+fell off the top of the house!' (Which was very likely true.)
+
+Down, down, down. Would the fall never come to an end! 'I wonder how
+many miles I've fallen by this time?' she said aloud. 'I must be
+getting somewhere near the centre of the earth. Let me see: that would be
+four thousand miles down, I think--' (for, you see, Alice had learnt
+several things of this sort in her lessons in the schoolroom, and though
+this was not a very good opportunity for showing off her knowledge,
+as there was no one to listen to her, still it was good practice to
+say it over) '--yes, that's about the right distance--but then I
+wonder what Latitude or Longitude I've got to?' (Alice had no idea
+what Latitude was, or Longitude either, but thought they were nice grand
+words to say.)
+
+Presently she began again. 'I wonder if I shall fall right through
+the earth! How funny it'll seem to come out among the people that
+walk with their heads downward! The Antipathies, I think--' (she was
+rather glad there was no one listening, this time, as it didn't sound
+at all the right word) '--but I shall have to ask them what the name
+of the country is, you know. Please, Ma'am, is this New Zealand or
+Australia?' (and she tried to curtsey as she spoke--fancy curtseying
+as you're falling through the air! Do you think you could manage
+it?) 'And what an ignorant little girl she'll think me for asking! No,
+it'll never do to ask: perhaps I shall see it written up somewhere.'
diff --git a/t/Makefile b/t/Makefile
new file mode 100644
index 0000000..a8c8bac
--- /dev/null
+++ b/t/Makefile
@@ -0,0 +1,28 @@
+# 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
+# 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/>.
+
+check: genout rt
+ @./testsuite
+
+CPPFLAGS=-I..
+CFLAGS = -ggdb -Wall
+
+VPATH=..
+
+rt: rt.o libruncap.a
+ cc $(CFLAGS) -o$@ $^
+
+genout: genout.o
diff --git a/t/genout.c b/t/genout.c
new file mode 100644
index 0000000..fcfd40b