summaryrefslogtreecommitdiff
path: root/src/s6-portable-utils/s6-tail.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/s6-portable-utils/s6-tail.c')
-rw-r--r--src/s6-portable-utils/s6-tail.c199
1 files changed, 199 insertions, 0 deletions
diff --git a/src/s6-portable-utils/s6-tail.c b/src/s6-portable-utils/s6-tail.c
new file mode 100644
index 0000000..a3091cd
--- /dev/null
+++ b/src/s6-portable-utils/s6-tail.c
@@ -0,0 +1,199 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <errno.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/allreadwrite.h>
+#include <skalibs/types.h>
+#include <skalibs/buffer.h>
+#include <skalibs/strerr.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/skamisc.h>
+#include <skalibs/siovec.h>
+
+#define USAGE "s6-tail [ -c chars | -n lines | -1..9 ] [ file ]"
+
+typedef int tail_func (int, size_t) ;
+typedef tail_func *tail_func_ref ;
+
+static int tail_pluslines (int fd, size_t n)
+{
+ if (n) n-- ;
+ {
+ char buf[BUFFER_INSIZE] ;
+ buffer b = BUFFER_INIT(&buffer_read, fd, buf, BUFFER_INSIZE) ;
+ size_t count = 0 ;
+ while (count < n)
+ {
+ ssize_t r = buffer_fill(&b) ;
+ if (r <= 0) return !r ;
+ while (!buffer_isempty(&b) && (count < n))
+ {
+ struct iovec v[2] ;
+ size_t i ;
+ buffer_rpeek(&b, v) ;
+ i = siovec_bytechr(v, 2, '\n') ;
+ if (i < buffer_len(&b))
+ {
+ count++ ; i++ ;
+ }
+ buffer_rseek(&b, i) ;
+ }
+ }
+ b.op = &buffer_write ;
+ b.fd = 1 ;
+ if (!buffer_flush(&b)) return 0 ;
+ }
+ return (fd_cat(fd, 1) >= 0) ;
+}
+
+static int tail_pluschars (int fd, size_t n)
+{
+ if (n-- > 1)
+ {
+ int nil = open_write("/dev/null") ;
+ if (nil < 0) return 0 ;
+ if (!fd_catn(fd, nil, n))
+ {
+ fd_close(nil) ;
+ return 0 ;
+ }
+ fd_close(nil) ;
+ }
+ return (fd_cat(fd, 1) >= 0) ;
+}
+
+static int tail_minuslines (int fd, size_t n)
+{
+ char buf[BUFFER_INSIZE] ;
+ buffer b = BUFFER_INIT(&buffer_read, fd, buf, BUFFER_INSIZE) ;
+ size_t head = 0, tail = 0 ;
+ stralloc tab[n+1] ;
+ for (; head <= n ; head++) tab[head] = stralloc_zero ;
+ head = 0 ;
+ for (;;)
+ {
+ int r ;
+ r = skagetln(&b, tab + tail, '\n') ;
+ if (!r) break ;
+ if (r < 0)
+ {
+ if (errno == EPIPE) break ;
+ else goto err ;
+ }
+ tail = (tail + 1) % (n+1) ;
+ if (tail == head)
+ {
+ tab[head].len = 0 ;
+ head = (head + 1) % (n+1) ;
+ }
+ }
+ buffer_init(&b, &buffer_write, 1, buf, BUFFER_INSIZE) ;
+ for (; head != tail ; head = (head + 1) % (n+1))
+ {
+ if (buffer_put(&b, tab[head].s, tab[head].len) < tab[head].len)
+ goto err ;
+ }
+ for (head = 0 ; head <= n ; head++) stralloc_free(tab + head) ;
+ return buffer_flush(&b) ;
+ err:
+ for (head = 0 ; head <= n ; head++) stralloc_free(tab + head) ;
+ return 0 ;
+}
+
+static int tail_minuschars (int fd, size_t n)
+{
+ char buf[BUFFER_INSIZE + n] ;
+ buffer b = BUFFER_INIT(&buffer_read, fd, buf, BUFFER_INSIZE + n) ;
+ for (;;)
+ {
+ ssize_t r = buffer_fill(&b) ;
+ if (!r) break ;
+ if (r < 0) return 0 ;
+ buffer_rseek(&b, buffer_len(&b)) ;
+ buffer_unget(&b, n) ;
+ }
+ b.op = &buffer_write ;
+ b.fd = 1 ;
+ return buffer_flush(&b) ;
+}
+
+int main (int argc, char const *const *argv)
+{
+ tail_func_ref f = &tail_minuslines ;
+ unsigned int n = 10 ;
+ int gotit = 0 ;
+ PROG = "s6-tail" ;
+ {
+ subgetopt l = SUBGETOPT_ZERO ;
+ for (;;)
+ {
+ int opt = subgetopt_r(argc, argv, "123456789n:c:", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case '1' :
+ case '2' :
+ case '3' :
+ case '4' :
+ case '5' :
+ case '6' :
+ case '7' :
+ case '8' :
+ case '9' :
+ {
+ if (gotit) strerr_dieusage(100, USAGE) ;
+ gotit = 1 ;
+ f = &tail_minuslines ;
+ n = opt - '0' ;
+ break ;
+ }
+ case 'n':
+ {
+ if (gotit) strerr_dieusage(100, USAGE) ;
+ gotit = 1 ;
+ f = &tail_minuslines ;
+ if (*l.arg == '-') l.arg++ ;
+ else if (*l.arg == '+')
+ {
+ f = &tail_pluslines ;
+ l.arg++ ;
+ }
+ if (!uint0_scan(l.arg, &n)) strerr_dieusage(100, USAGE) ;
+ break ;
+ }
+ case 'c':
+ {
+ if (gotit) strerr_dieusage(100, USAGE) ;
+ gotit = 1 ;
+ f = &tail_minuschars ;
+ if (*l.arg == '-') l.arg++ ;
+ else if (*l.arg == '+')
+ {
+ f = &tail_pluschars ;
+ l.arg++ ;
+ }
+ if (!uint0_scan(l.arg, &n)) strerr_dieusage(100, USAGE) ;
+ break ;
+ }
+ default : strerr_dieusage(100, USAGE) ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ }
+ if (!argc)
+ {
+ if (!(*f)(0, n))
+ strerr_diefu1sys(111, "tail stdin") ;
+ }
+ else
+ {
+ int fd = open_readb(argv[0]) ;
+ if (fd == -1) strerr_diefu3sys(111, "open ", argv[0], " for reading") ;
+ if (!(*f)(fd, n))
+ strerr_diefu2sys(111, "tail ", argv[0]) ;
+ fd_close(fd) ;
+ }
+ return 0 ;
+}