diff options
Diffstat (limited to 'src/skaembutils/s6-head.c')
-rw-r--r-- | src/skaembutils/s6-head.c | 159 |
1 files changed, 159 insertions, 0 deletions
diff --git a/src/skaembutils/s6-head.c b/src/skaembutils/s6-head.c new file mode 100644 index 0000000..1ef24c0 --- /dev/null +++ b/src/skaembutils/s6-head.c @@ -0,0 +1,159 @@ +/* ISC license. */ + +#include <errno.h> +#include <skalibs/allreadwrite.h> +#include <skalibs/sgetopt.h> +#include <skalibs/bytestr.h> +#include <skalibs/uint.h> +#include <skalibs/buffer.h> +#include <skalibs/siovec.h> +#include <skalibs/strerr2.h> +#include <skalibs/djbunix.h> + +#define USAGE "s6-head [ -S ] [ -1..9 | -n lines | -c chars ] [ file... ]" +#define dieusage() strerr_dieusage(100, USAGE) + +typedef int headfunc_t (int, unsigned int) ; +typedef headfunc_t *headfunc_t_ref ; + +static int dolines (int fd, unsigned int lines) +{ + char buf[BUFFER_INSIZE] ; + buffer in = BUFFER_INIT(&buffer_read, fd, buf, BUFFER_INSIZE) ; + buffer out = BUFFER_INIT(&buffer_write, 1, buf, BUFFER_INSIZE) ; + siovec_t v[2] ; + while (lines) + { + unsigned int w = 0 ; + register int r = buffer_fill(&in) ; + if (r <= 0) return !r ; + out.c.n = in.c.n ; out.c.p = in.c.p ; + buffer_rpeek(&in, v) ; + for (;;) + { + unsigned int n = siovec_len(v, 2) ; + register unsigned int i ; + if (!n) break ; + i = siovec_bytechr(v, 2, '\n') ; + if (i < n) + { + w += i+1 ; + siovec_seek(v, 2, i+1) ; + if (!--lines) + { + out.c.n = (out.c.p + w) % out.c.a ; + break ; + } + } + else siovec_seek(v, 2, i) ; + } + if (!buffer_flush(&out)) return 0 ; + in.c.n = out.c.n ; in.c.p = out.c.p ; + } + return 1 ; +} + +static int safedolines (int fd, unsigned int lines) +{ + char tmp[lines] ; + while (lines) + { + unsigned int r = allread(fd, tmp, lines) ; + if ((r < lines) && (errno != EPIPE)) return 0 ; + lines -= byte_count(tmp, r, '\n') ; + if (buffer_put(buffer_1, tmp, r) < (int)r) return 0 ; + } + if (!buffer_flush(buffer_1)) return 0 ; + return 1 ; +} + +static int safedochars (int fd, unsigned int chars) +{ + return (fd_catn(fd, 1, chars) >= chars) ; +} + +int main (int argc, char const *const *argv) +{ + headfunc_t_ref f ; + unsigned int lines = 10 ; + int islines = 1, safe = 0 ; + PROG = "s6-head" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + int done = 0 ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "S123456789n:c:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'S' : safe = 1 ; break ; + case '1' : + case '2' : + case '3' : + case '4' : + case '5' : + case '6' : + case '7' : + case '8' : + case '9' : + { + if (done) dieusage() ; + islines = 1 ; + lines = opt - '0' ; + done = 1 ; + break ; + } + case 'n' : + { + if (done || !uint0_scan(l.arg, &lines)) + strerr_dieusage(100, USAGE) ; + islines = 1 ; + done = 1 ; + break ; + } + case 'c' : + { + if (done || !uint0_scan(l.arg, &lines)) + strerr_dieusage(100, USAGE) ; + islines = 0 ; + done = 1 ; + break ; + } + default : strerr_dieusage(100, USAGE) ; + } + } + argc -= l.ind ; argv += l.ind ; + } + if (argc) safe = 0 ; + f = islines ? safe ? &safedolines : &dolines : &safedochars ; + if (!argc) + { + if (!(*f)(0, lines)) + strerr_diefu1sys(111, "head stdin") ; + } + else + { + unsigned int i = 0 ; + for (; argv[i] ; i++) + { + int fd ; + if (argc >= 2) + { + if (i) buffer_putnoflush(buffer_1, "\n", 1) ; + buffer_putnoflush(buffer_1, "==> ", 4) ; + if ((buffer_puts(buffer_1, argv[i]) <= 0) + || (buffer_putflush(buffer_1, " <==\n", 5) < 0)) + strerr_diefu1sys(111, "write to stdout") ; + } + if ((argv[i][0] == '-') && !argv[i][1]) fd = 0 ; + else fd = open_readb(argv[i]) ; + if (fd == -1) + strerr_diefu3sys(111, "open ", argv[i], " for reading") ; + if (!(*f)(fd, lines)) + strerr_diefu2sys(111, "head ", argv[i]) ; + fd_close(fd) ; + } + } + return 0 ; +} |