diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2017-07-21 18:34:23 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2017-07-21 18:50:53 +0300 |
commit | 969dab4c1af666d381124fd481821bc936fc94ed (patch) | |
tree | b7bc4b2e75b8cc3f64e240162f3bedff23e9c58b | |
parent | 0c0ae29bc7a71fc2cb6d0acf403b1e72715fbe48 (diff) | |
download | runcap-969dab4c1af666d381124fd481821bc936fc94ed.tar.gz runcap-969dab4c1af666d381124fd481821bc936fc94ed.tar.bz2 |
Fix tell/seek operations
* Makefile (clean): descend into t as well.
* t/Makefile (clean): New goal.
* runcap.h (runcap_seek): Return off_t.
* seek.c (runcap_seek): Change return value. Fix operation.
* tell.c (runcap_tell): Bugfix.
* t/rt.c: New option -r for testing seek.
* t/08seek.t: New testcase.
* t/09seek.t: New testcase.
-rw-r--r-- | Makefile | 5 | ||||
-rw-r--r-- | runcap.h | 4 | ||||
-rw-r--r-- | seek.c | 10 | ||||
-rwxr-xr-x | t/08seek.t | 43 | ||||
-rwxr-xr-x | t/09seek.t | 44 | ||||
-rw-r--r-- | t/Makefile | 11 | ||||
-rw-r--r-- | t/rt.c | 113 | ||||
-rw-r--r-- | tell.c | 1 |
8 files changed, 218 insertions, 13 deletions
@@ -51,9 +51,12 @@ libruncap.a: $(OBJECTS) ar $(ARFLAGS) libruncap.a $(OBJECTS) ranlib libruncap.a -clean: +clean: subdirs-clean rm -f libruncap.a $(OBJECTS) +subdirs-clean: + @$(MAKE) -C t clean + install: install-lib install-headers install-man install-lib: libruncap.a @@ -90,12 +90,12 @@ runcap_get_capture(struct runcap *rc, int stream) 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); +off_t 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); + return runcap_seek(rc, stream, 0, 0) != 0; } #endif @@ -19,7 +19,7 @@ #include <errno.h> #include "runcap.h" -int +off_t runcap_seek(struct runcap *rc, int sd, off_t off, int whence) { struct stream_capture *cap; @@ -32,7 +32,7 @@ runcap_seek(struct runcap *rc, int sd, off_t off, int whence) cur = runcap_tell(rc, sd); switch (whence) { case SEEK_CUR: - off = cur; + off = cur + off; break; case SEEK_END: @@ -52,8 +52,8 @@ runcap_seek(struct runcap *rc, int sd, off_t off, int whence) return -1; } - cur -= cap->sc_level; - + cur -= cap->sc_cur; + if (cur <= off && off <= cur + cap->sc_level) { cap->sc_cur = off - cur; } else if (cap->sc_storfd != -1) { @@ -65,7 +65,7 @@ runcap_seek(struct runcap *rc, int sd, off_t off, int whence) errno = EINVAL; return -1; } - return 0; + return off; } diff --git a/t/08seek.t b/t/08seek.t new file mode 100755 index 0000000..c2312bd --- /dev/null +++ b/t/08seek.t @@ -0,0 +1,43 @@ +#! ./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 seek + +TC_EXPECT out <<EOT +res=0 +exit code: 0 +stdout: 71 lines, 4051 bytes +stderr: 0 lines, 0 bytes +READ 0: + the Rabbit-Hole + +Alice was beginning to get very tired of sitting by her sister on the +bank, and of +READ 1: + on the +ba +READ 2: +nk, and of +READ 3: +tten up so +EOT + +rt -r stdout:100:15:0\ + -r stdout:10:-20:1\ + -r stdout:10:0:1\ + -r stdout:10:-20:2\ + -- genout $testdir/INPUT diff --git a/t/09seek.t b/t/09seek.t new file mode 100755 index 0000000..87628f1 --- /dev/null +++ b/t/09seek.t @@ -0,0 +1,44 @@ +#! ./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 seek, short buffer + +TC_EXPECT out <<EOT +res=0 +exit code: 0 +stdout: 71 lines, 4051 bytes +stderr: 0 lines, 0 bytes +READ 0: + the Rabbit-Hole + +Alice was beginning to get very tired of sitting by her sister on the +bank, and of +READ 1: + on the +ba +READ 2: +nk, and of +READ 3: +tten up so +EOT + +rt -s 16\ + -r stdout:100:15:0\ + -r stdout:10:-20:1\ + -r stdout:10:0:1\ + -r stdout:10:-20:2\ + -- genout $testdir/INPUT @@ -14,7 +14,8 @@ # 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 +TESTPROGS=genout rt +check: $(TESTPROGS) @./testsuite CPPFLAGS=-I.. @@ -37,13 +38,17 @@ TESTSUITE =\ 03longout.t\ 04stdin.t\ 05stdin.t\ - 06mon.t + 06mon.t\ + 08seek.t\ + 09seek.t DISTDIR = $(TOPDISTDIR)/t DISTFILES = Makefile rt.c genout.c $(TESTSUITE) INPUT -.PHONY: distdir +.PHONY: distdir clean distdir: test -d $(DISTDIR) || mkdir $(DISTDIR) cp $(DISTFILES) $(DISTDIR) +clean: + rm -rf $(TESTPROGS) testsuite.log testsuite.dir @@ -50,8 +50,13 @@ usage(int code) fprintf(fp, " -S all|stderr|stdout selects capture for the next -m or -s option\n"); fprintf(fp, " -f FILE reads stdin from FILE\n"); fprintf(fp, " -i inline read (use before -f)\n"); + fprintf(fp, " -n all|stderr|stdout print lines from the requested capture\n"); fprintf(fp, " -m monitors each line recevied from the program (see -S)\n"); fprintf(fp, " -p PROGNAME sets program name to use instead of COMMAND\n"); + fprintf(fp, " -r STREAM[:COUNT:OFF:WHENCE]\n"); + fprintf(fp, " read and print COUNT bytes from STREAM (stdout or\n"); + fprintf(fp, " stderr) starting from OFFset located using WHENCE\n"); + fprintf(fp, " (0, 1, 2).\n"); fprintf(fp, " -s SIZE sets capture size (see -S)\n"); fprintf(fp, " -t SECONDS sets execution timeout\n"); fputc('\n', fp); @@ -119,6 +124,94 @@ nl(struct runcap *rc, int stream) printf("%s listing ends\n", what); } +struct readreq +{ + int what; + unsigned long count; + long off; + int whence; +}; + +void +readreq_parse(struct readreq *req, char *arg) +{ + char *s; + int i = 0; + + s = strchr(arg, ':'); + if (s) + *s = 0; + req->what = whatarg(arg); + req->count = 0; + req->off = 0; + req->whence = 0; + if (s) + *s++ = ':'; + + arg = s; + while (*arg) { + switch (i++) { + case 0: + req->count = strtoul(arg, &s, 10); + break; + + case 1: + req->off = strtol(arg, &s, 10); + break; + + case 2: + req->whence = strtol(arg, &s, 10); + if (!(0 <= req->whence && req->whence <= 2)) { + error("bad whence: %s", arg); + exit(1); + } + break; + + default: + error("too many parts in argument: %s", arg); + exit(1); + } + + if (*s == ':') + arg = s + 1; + else if (*s) { + error("malformed argument: %s", arg); + exit(1); + } else + arg = s; + } +} + +void +readreq_do(struct runcap *rc, struct readreq *req) +{ + int res; + size_t i; + + if (runcap_seek(rc, req->what, req->off, req->whence) == -1) { + perror("runcap_seek"); + exit(1); + } + + if (req->count == 0) + req->count = rc->rc_cap[req->what].sc_size; + + for (i = 0; i < req->count; i++) { + char c; + + res = runcap_getc(rc, req->what, &c); + if (res == 0) { + error("unexpected eof at byte %zu\n", i); + break; + } + if (res == -1) { + error("%s at byte %zu\n", strerror (errno), i); + break; + } + putchar(c); + } +} + int main(int argc, char **argv) { @@ -127,6 +220,8 @@ main(int argc, char **argv) int what = WA_ALL; int inopt = 0; int numlines = WA_NONE; + struct readreq rq[10]; + int rqn = 0, i; int c; int fd; @@ -142,7 +237,7 @@ main(int argc, char **argv) progname++; else progname = argv[0]; - while ((c = getopt(argc, argv, "?f:in:mp:S:s:t:")) != EOF) { + while ((c = getopt(argc, argv, "?f:in:mp:r:S:s:t:")) != EOF) { switch (c) { case 'f': fd = open(optarg, O_RDONLY); @@ -200,6 +295,14 @@ main(int argc, char **argv) case 'n': numlines = whatarg(optarg); break; + case 'r': + /* -r WHAT:N[:OFF:WHENCE] */ + if (rqn == sizeof(rq)/sizeof(rq[0])) { + error("too many read requests"); + break; + } + readreq_parse(&rq[rqn++], optarg); + break; case 'm': if (what & WA_STDOUT) { rc.rc_cap[RUNCAP_STDOUT].sc_linemon = linemon; @@ -277,6 +380,12 @@ main(int argc, char **argv) nl(&rc, RUNCAP_STDOUT); if (numlines & WA_STDERR) nl(&rc, RUNCAP_STDERR); - + + for (i = 0; i < rqn; i++) { + printf("READ %d:\n", i); + readreq_do(&rc, &rq[i]); + putchar('\n'); + } + return 0; } @@ -33,6 +33,7 @@ runcap_tell(struct runcap *rc, int sd) off = lseek(cap->sc_storfd, 0, SEEK_CUR); if (off == -1) return -1; + off -= cap->sc_level; } else off = 0; |